INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

Started by Peter Geogheganalmost 11 years ago79 messages
#1Peter Geoghegan
pg@heroku.com
4 attachment(s)

Attached patch series forms what I'm calling V3.0 of the INSERT ... ON
CONFLICT IGNORE/UPDATE feature. (Sorry about all the threads. I feel
this development justifies a new thread, though.)

This features a new way of organizing the code. Heikki and I are now
in agreement that the best way of incrementally committing the work is
to do ON CONFLICT IGNORE first (perhaps someone else can review ON
CONFLICT UPDATE). This patch series is organized so that the first
commit adds tests, documentation and code that only relates to the
IGNORE variant. RLS support is still split out as a separate commit,
which is not intended to actually be committed separately (from the
main ON CONFLICT UPDATE commit). As before, I've just done that to
highlight that part for the benefit of RLS subject matter experts. The
RTE permissions split patch is also broken out, but I believe that
there is consensus that that could sensibly be committed on its own.

There are some minor changes to the code/documentation itself:

* Ran the code through pg_indent.

* Documented ON CONFLICT UPDATE as a MERGE alternative under
"unsupported SQL features" (sql_features.txt).

* Minor tweaks of comments here and there.

* Logical decoding fixes.

* Call ExecCheckPlanOutput() for an ON CONFLICT UPDATE auxiliary query, too.

I should point out that I'm aware of the following open issues around
the patch series (most of these apply to the base IGNORE commit, so
Heikki should of course look out for them):

* Andres and I are still hashing out details of whether or not certain
assumptions made around handling super deletion within logical
decoding are safe [1]/messages/by-id/20150303111342.GF2579@alap3.anarazel.de. Consider the decoding stuff suspect for now.

* There is still an unresolved semantics issue with unique index
inference and non-default opclasses. I think it's sufficient that the
available/defined unique indexes dictate our idea of a unique
violation (that necessitates taking the alternative path). Even in a
world where there exists a non-default opclass with an "equals"
operator that does not match that of the default opclass (that is not
really the world we live in, because the only counter-example known is
made that way specifically to *discourage* its use by users), this
seems okay to me. It seems okay to me because surely the relevant
definition of equality is the one actually chosen for the available
unique index. If there exists an ambiguity for some strange reason
(i.e. there are two unique indexes, on the same attribute(s), but with
different "equals" operators), then its a costing issue, so the
behavior given is essentially non-predictable (it could end up being
either...but hey, those are the semantics). I have a very hard time
imagining how that could ever be the case, even when we have (say)
case insensitive opclasses for the text type. A case insensitive
opclass is stricter than a case sensitive opclass. Why would a user
ever want both on the same attribute(s) of the same table? Is the user
really more or less expecting to never get a unique violation on the
non-arbitrating unique index, despite all this?

If reviewers are absolutely insistent that this theoretical ambiguity
is a problem, we can add an optional CREATE INDEX style opclass
specification (I'm already using the IndexElems representation from
CREATE INDEX for the inference specification, actually, so that would
be easy enough). I really have a hard time believing that the ugliness
is a problem for those hypothetical users that eventually consider
"equals" operator ambiguity among opclasses among available unique
indexes to be a problem. I haven't just gone and implemented this
already because I didn't want to document that an opclass
specification will be accepted. Since there is literally no reason why
anyone would care today, I see no reason to add what IMV would really
just be cruft.

* I'm flying blind with the SEPostgres changes. Unfortunately, it's
not very possible to test SEPostgres on my machine. (Does not apply to
IGNORE variant.)

As always, the jjanes_upsert tool [2]https://github.com/petergeoghegan/jjanes_upsert -- Peter Geoghegan has proven invaluable with
testing this patch (Thanks Jeff!). Reviewers should look to that tool
for ideas on how the patch might be broken. It caught a number of race
conditions with exclusion constraints in the past, for example. We're
in good shape with those now (and have been since V2.3 - see "livelock
insurance" comments within the IGNORE commit).

Thoughts?

[1]: /messages/by-id/20150303111342.GF2579@alap3.anarazel.de
[2]: https://github.com/petergeoghegan/jjanes_upsert -- Peter Geoghegan
--
Peter Geoghegan

Attachments:

0004-RLS-support-for-ON-CONFLICT-UPDATE.patchtext/x-patch; charset=US-ASCII; name=0004-RLS-support-for-ON-CONFLICT-UPDATE.patchDownload
From 2b0d4187d74d2fbb2d8815a41cb83e964fff406c Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@heroku.com>
Date: Tue, 6 Jan 2015 16:32:21 -0800
Subject: [PATCH 4/4] RLS support for ON CONFLICT UPDATE

Row-Level Security policies may apply to UPDATE commands or INSERT
commands only.  UPDATE RLS policies can have both USING() security
barrier quals, and CHECK options (INSERT RLS policies may only have
CHECK options, though).  It is necessary to carefully consider the
behavior of RLS policies in the context of INSERT with ON CONFLICT
UPDATE, since ON CONFLICT UPDATE is more or less a new top-level
command, conceptually quite different to two separate statements (an
INSERT and an UPDATE).

The approach taken is to "bunch together" both sets of policies, and to
enforce them in 3 different places against three different slots (3
different stages of query processing in the executor).

Note that UPDATE policy USING() barrier quals are always treated as
CHECK options.  It is thought that silently failing when USING() barrier
quals are not satisfied is a more surprising outcome, even if it is
closer to the existing behavior of UPDATE statements.  This is because
the user's intent to UPDATE one particular row based on simple criteria
is quite clear with ON CONFLICT UPDATE.

The 3 places that RLS policies are enforced are:

* Against row actually inserted, after insertion proceeds successfully
  (INSERT-applicable policies only).

* Against row in target table that caused conflict.  The implementation
  is careful not to leak the contents of that row in diagnostic
  messages (INSERT-applicable *and* UPDATE-applicable policies).

* Against the version of the row added by to the relation after
  ExecUpdate() is called (INSERT-applicable *and* UPDATE-applicable
  policies).
---
 doc/src/sgml/ref/alter_policy.sgml        |  7 ++-
 doc/src/sgml/ref/create_policy.sgml       | 37 ++++++++++--
 src/backend/executor/execMain.c           | 25 +++++---
 src/backend/executor/nodeModifyTable.c    | 53 ++++++++++++++++-
 src/backend/nodes/copyfuncs.c             |  1 +
 src/backend/nodes/equalfuncs.c            |  1 +
 src/backend/nodes/outfuncs.c              |  1 +
 src/backend/nodes/readfuncs.c             |  1 +
 src/backend/rewrite/rewriteHandler.c      |  2 +
 src/backend/rewrite/rowsecurity.c         | 94 ++++++++++++++++++++++++++-----
 src/include/executor/executor.h           |  3 +-
 src/include/nodes/parsenodes.h            |  1 +
 src/test/regress/expected/rowsecurity.out | 90 +++++++++++++++++++++++++++++
 src/test/regress/sql/rowsecurity.sql      | 73 ++++++++++++++++++++++++
 14 files changed, 357 insertions(+), 32 deletions(-)

diff --git a/doc/src/sgml/ref/alter_policy.sgml b/doc/src/sgml/ref/alter_policy.sgml
index 6d03db5..65cd85c 100644
--- a/doc/src/sgml/ref/alter_policy.sgml
+++ b/doc/src/sgml/ref/alter_policy.sgml
@@ -93,8 +93,11 @@ ALTER POLICY <replaceable class="parameter">name</replaceable> ON <replaceable c
       The USING expression for the policy.  This expression will be added as a
       security-barrier qualification to queries which use the table
       automatically.  If multiple policies are being applied for a given
-      table then they are all combined and added using OR.  The USING
-      expression applies to records which are being retrieved from the table.
+      table then they are all combined and added using OR (except as noted in
+      the <xref linkend="sql-createpolicy"> documentation for
+      <command>INSERT</command> with <literal> ON CONFLICT UPDATE</literal>).
+      The USING expression applies to records which are being retrieved from the
+      table.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_policy.sgml b/doc/src/sgml/ref/create_policy.sgml
index 868a6c1..f17192e 100644
--- a/doc/src/sgml/ref/create_policy.sgml
+++ b/doc/src/sgml/ref/create_policy.sgml
@@ -70,11 +70,12 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
    Policies can be applied for specific commands or for specific roles.  The
    default for newly created policies is that they apply for all commands and
    roles, unless otherwise specified.  If multiple policies apply to a given
-   query, they will be combined using OR.  Further, for commands which can have
-   both USING and WITH CHECK policies (ALL and UPDATE), if no WITH CHECK policy
-   is defined then the USING policy will be used for both what rows are visible
-   (normal USING case) and which rows will be allowed to be added (WITH CHECK
-   case).
+   query, they will be combined using OR (except as noted for
+   <command>INSERT</command> with <literal> ON CONFLICT UPDATE</literal>).
+   Further, for commands which can have both USING and WITH CHECK policies (ALL
+   and UPDATE), if no WITH CHECK policy is defined then the USING policy will
+   be used for both what rows are visible (normal USING case) and which rows
+   will be allowed to be added (WITH CHECK case).
   </para>
 
   <para>
@@ -255,6 +256,19 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
          as it only ever applies in cases where records are being added to the
          relation.
        </para>
+       <para>
+         Note that <literal>INSERT</literal> with <literal>ON CONFLICT
+         UPDATE</literal> requires that an <literal>INSERT</literal> policy WITH
+         CHECK expression also passes for both any existing tuple in the target
+         table that necessitates that the <literal>UPDATE</literal> path be
+         taken, and the final tuple added back into the relation.
+         <literal>INSERT</literal> policies are separately combined using
+         <literal>OR</literal>, and this distinct set of policy expressions must
+         always pass, regardless of whether any or all <literal>UPDATE</literal>
+         policies also pass (in the same tuple check).  However, successfully
+         inserted tuples are not subject to <literal>UPDATE</literal> policy
+         enforcement.
+       </para>
       </listitem>
      </varlistentry>
 
@@ -263,7 +277,9 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
       <listitem>
        <para>
          Using <literal>UPDATE</literal> for a policy means that it will apply
-         to <literal>UPDATE</literal> commands.  As <literal>UPDATE</literal>
+         to <literal>UPDATE</literal> commands (or auxiliary <literal>ON
+         CONFLICT UPDATE</literal> clauses of <literal>INSERT</literal>
+         commands).  As <literal>UPDATE</literal>
          involves pulling an existing record and then making changes to some
          portion (but possibly not all) of the record, the
          <literal>UPDATE</literal> policy accepts both a USING expression and
@@ -279,6 +295,15 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
          used for both <literal>USING</literal> and
          <literal>WITH CHECK</literal> cases.
        </para>
+       <para>
+         Note that <literal>INSERT</literal> with <literal>ON CONFLICT
+         UPDATE</literal> requires that an <literal>UPDATE</literal> policy
+         USING expression always be treated as a WITH CHECK
+         expression.  This <literal>UPDATE</literal> policy must
+         always pass, regardless of whether any
+         <literal>INSERT</literal> policy also passes in the same
+         tuple check.
+       </para>
       </listitem>
      </varlistentry>
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 3649b46..9464f4e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1697,7 +1697,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
  */
 void
 ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
-					 TupleTableSlot *slot, EState *estate)
+					 TupleTableSlot *slot, bool detail,
+					 bool onlyInsert, EState *estate)
 {
 	Relation	rel = resultRelInfo->ri_RelationDesc;
 	TupleDesc	tupdesc = RelationGetDescr(rel);
@@ -1722,6 +1723,15 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
 		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
 
 		/*
+		 * INSERT ... ON CONFLICT UPDATE callers may require that not all WITH
+		 * CHECK OPTIONs associated with resultRelInfo are enforced at all
+		 * stages of query processing. (UPDATE-related policies are not
+		 * enforced in respect of a successfully inserted tuple).
+		 */
+		if (onlyInsert && wco->commandType == CMD_UPDATE)
+			continue;
+
+		/*
 		 * WITH CHECK OPTION checks are intended to ensure that the new tuple
 		 * is visible (in the case of a view) or that it passes the
 		 * 'with-check' policy (in the case of row security).
@@ -1732,16 +1742,17 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
 		 */
 		if (!ExecQual((List *) wcoExpr, econtext, false))
 		{
-			char	   *val_desc;
+			char	   *val_desc = NULL;
 			Bitmapset  *modifiedCols;
 
 			modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
 			modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
-			val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
-													 slot,
-													 tupdesc,
-													 modifiedCols,
-													 64);
+			if (detail)
+				val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
+														 slot,
+														 tupdesc,
+														 modifiedCols,
+														 64);
 
 			ereport(ERROR,
 					(errcode(ERRCODE_WITH_CHECK_OPTION_VIOLATION),
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 6cffe6b..d1574d5 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -454,7 +454,8 @@ vlock:
 
 	/* Check any WITH CHECK OPTION constraints */
 	if (resultRelInfo->ri_WithCheckOptions != NIL)
-		ExecWithCheckOptions(resultRelInfo, slot, estate);
+		ExecWithCheckOptions(resultRelInfo, slot, true, spec == SPEC_INSERT,
+							 estate);
 
 	/* Process RETURNING if present */
 	if (resultRelInfo->ri_projectReturning)
@@ -948,7 +949,7 @@ lreplace:;
 
 	/* Check any WITH CHECK OPTION constraints */
 	if (resultRelInfo->ri_WithCheckOptions != NIL)
-		ExecWithCheckOptions(resultRelInfo, slot, estate);
+		ExecWithCheckOptions(resultRelInfo, slot, true, false, estate);
 
 	/* Process RETURNING if present */
 	if (resultRelInfo->ri_projectReturning)
@@ -1134,6 +1135,54 @@ ExecLockUpdateTuple(ResultRelInfo *resultRelInfo,
 
 			slot = EvalPlanQualNext(&onConflict->mt_epqstate);
 
+			/*
+			 * For RLS with ON CONFLICT UPDATE, security quals are always
+			 * treated as WITH CHECK options, even when there were separate
+			 * security quals and explicit WITH CHECK options (ordinarily,
+			 * security quals are only treated as WITH CHECK options when there
+			 * are no explicit WITH CHECK options).  Also, CHECK OPTIONs
+			 * (originating either explicitly, or implicitly as security quals)
+			 * for both UPDATE and INSERT policies (or ALL policies) are
+			 * checked (as CHECK OPTIONs) at three different points for three
+			 * distinct but related tuples/slots in the context of ON CONFLICT
+			 * UPDATE.  There are three relevant ExecWithCheckOptions() calls:
+			 *
+			 * * After successful insertion, within ExecInsert(), against the
+			 * inserted tuple.  This only includes INSERT-applicable policies.
+			 *
+			 * * Here, after row locking but before calling ExecUpdate(), on
+			 * the existing tuple in the target relation (which we cannot leak
+			 * details of).  This is conceptually like a security barrier qual
+			 * for the purposes of the auxiliary update, although unlike
+			 * regular updates that require security barrier quals we prefer to
+			 * raise an error (by treating the security barrier quals as CHECK
+			 * OPTIONS) rather than silently not affect rows, because the
+			 * intent to update seems clear and unambiguous for ON CONFLICT
+			 * UPDATE.  This includes both INSERT-applicable and
+			 * UPDATE-applicable policies.
+			 *
+			 * * On the final tuple created by the update within ExecUpdate (if
+			 * any).  This is also subject to INSERT policy enforcement, unlike
+			 * conventional ExecUpdate() calls for UPDATE statements -- it
+			 * includes both INSERT-applicable and UPDATE-applicable policies.
+			 */
+			if (resultRelInfo->ri_WithCheckOptions != NIL)
+			{
+				TupleTableSlot *opts;
+
+				/* Construct temp slot for locked tuple from target */
+				opts = MakeSingleTupleTableSlot(slot->tts_tupleDescriptor);
+				ExecStoreTuple(copyTuple, opts, InvalidBuffer, false);
+
+				/*
+				 * Check, but without leaking contents of tuple;  user only
+				 * supplied one conflicting value or composition of values, and
+				 * not the entire tuple.
+				 */
+				ExecWithCheckOptions(resultRelInfo, opts, false, false,
+									 estate);
+			}
+
 			if (!TupIsNull(slot))
 				*returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, slot,
 										planSlot, &onConflict->mt_epqstate,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 34535ff..b3f025d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2078,6 +2078,7 @@ _copyWithCheckOption(const WithCheckOption *from)
 
 	COPY_STRING_FIELD(viewname);
 	COPY_NODE_FIELD(qual);
+	COPY_SCALAR_FIELD(commandType);
 	COPY_SCALAR_FIELD(cascaded);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fd3911e..2e37199 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2387,6 +2387,7 @@ _equalWithCheckOption(const WithCheckOption *a, const WithCheckOption *b)
 {
 	COMPARE_STRING_FIELD(viewname);
 	COMPARE_NODE_FIELD(qual);
+	COMPARE_SCALAR_FIELD(commandType);
 	COMPARE_SCALAR_FIELD(cascaded);
 
 	return true;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 442c547..1a9880d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2343,6 +2343,7 @@ _outWithCheckOption(StringInfo str, const WithCheckOption *node)
 
 	WRITE_STRING_FIELD(viewname);
 	WRITE_NODE_FIELD(qual);
+	WRITE_ENUM_FIELD(commandType, CmdType);
 	WRITE_BOOL_FIELD(cascaded);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 319db56..f9910f3 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -272,6 +272,7 @@ _readWithCheckOption(void)
 
 	READ_STRING_FIELD(viewname);
 	READ_NODE_FIELD(qual);
+	READ_ENUM_FIELD(commandType, CmdType);
 	READ_BOOL_FIELD(cascaded);
 
 	READ_DONE();
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 8ef80d6..36d40cf 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1767,6 +1767,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 				List			   *quals = NIL;
 
 				wco = (WithCheckOption *) makeNode(WithCheckOption);
+				wco->commandType = parsetree->commandType;
 				quals = lcons(wco->qual, quals);
 
 				activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
@@ -2935,6 +2936,7 @@ rewriteTargetView(Query *parsetree, Relation view)
 			wco->viewname = pstrdup(RelationGetRelationName(view));
 			wco->qual = NULL;
 			wco->cascaded = cascaded;
+			wco->commandType = viewquery->commandType;
 
 			parsetree->withCheckOptions = lcons(wco,
 												parsetree->withCheckOptions);
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 7669130..09f1ac3 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -56,12 +56,14 @@
 #include "utils/syscache.h"
 #include "tcop/utility.h"
 
-static List *pull_row_security_policies(CmdType cmd, Relation relation,
-										Oid user_id);
+static List *pull_row_security_policies(CmdType cmd, bool onConflict,
+										Relation relation, Oid user_id);
 static void process_policies(List *policies, int rt_index,
 							 Expr **final_qual,
 							 Expr **final_with_check_qual,
-							 bool *hassublinks);
+							 bool *hassublinks,
+							 Expr **spec_with_check_eval,
+							 bool onConflict);
 static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id);
 
 /*
@@ -88,6 +90,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 	Expr			   *rowsec_with_check_expr = NULL;
 	Expr			   *hook_expr = NULL;
 	Expr			   *hook_with_check_expr = NULL;
+	Expr			   *hook_spec_with_check_expr = NULL;
 
 	List			   *rowsec_policies;
 	List			   *hook_policies = NIL;
@@ -149,8 +152,9 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 	/* Grab the built-in policies which should be applied to this relation. */
 	rel = heap_open(rte->relid, NoLock);
 
-	rowsec_policies = pull_row_security_policies(root->commandType, rel,
-												 user_id);
+	rowsec_policies = pull_row_security_policies(root->commandType,
+												 root->specClause == SPEC_INSERT,
+												 rel, user_id);
 
 	/*
 	 * Check if this is only the default-deny policy.
@@ -168,7 +172,9 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 
 	/* Now that we have our policies, build the expressions from them. */
 	process_policies(rowsec_policies, rt_index, &rowsec_expr,
-					 &rowsec_with_check_expr, &hassublinks);
+					 &rowsec_with_check_expr, &hassublinks,
+					 &hook_spec_with_check_expr,
+					 root->specClause == SPEC_INSERT);
 
 	/*
 	 * Also, allow extensions to add their own policies.
@@ -198,7 +204,9 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 
 		/* Build the expression from any policies returned. */
 		process_policies(hook_policies, rt_index, &hook_expr,
-						 &hook_with_check_expr, &hassublinks);
+						 &hook_with_check_expr, &hassublinks,
+						 &hook_spec_with_check_expr,
+						 root->specClause == SPEC_INSERT);
 	}
 
 	/*
@@ -230,6 +238,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 			wco->viewname = RelationGetRelationName(rel);
 			wco->qual = (Node *) rowsec_with_check_expr;
 			wco->cascaded = false;
+			wco->commandType = root->commandType;
 			root->withCheckOptions = lcons(wco, root->withCheckOptions);
 		}
 
@@ -244,6 +253,23 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 			wco->viewname = RelationGetRelationName(rel);
 			wco->qual = (Node *) hook_with_check_expr;
 			wco->cascaded = false;
+			wco->commandType = root->commandType;
+			root->withCheckOptions = lcons(wco, root->withCheckOptions);
+		}
+
+		/*
+		 * Also add the expression, if any, returned from the extension that
+		 * applies to auxiliary UPDATE within ON CONFLICT UPDATE.
+		 */
+		if (hook_spec_with_check_expr)
+		{
+			WithCheckOption	   *wco;
+
+			wco = (WithCheckOption *) makeNode(WithCheckOption);
+			wco->viewname = RelationGetRelationName(rel);
+			wco->qual = (Node *) hook_spec_with_check_expr;
+			wco->cascaded = false;
+			wco->commandType = CMD_UPDATE;
 			root->withCheckOptions = lcons(wco, root->withCheckOptions);
 		}
 	}
@@ -288,7 +314,8 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
  *
  */
 static List *
-pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
+pull_row_security_policies(CmdType cmd, bool onConflict, Relation relation,
+						   Oid user_id)
 {
 	List			   *policies = NIL;
 	ListCell		   *item;
@@ -322,7 +349,9 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
 				if (policy->polcmd == ACL_INSERT_CHR
 					&& check_role_for_policy(policy->roles, user_id))
 					policies = lcons(policy, policies);
-				break;
+				if (!onConflict)
+					break;
+				/* FALL THRU */
 			case CMD_UPDATE:
 				if (policy->polcmd == ACL_UPDATE_CHR
 					&& check_role_for_policy(policy->roles, user_id))
@@ -384,26 +413,41 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
  */
 static void
 process_policies(List *policies, int rt_index, Expr **qual_eval,
-				 Expr **with_check_eval, bool *hassublinks)
+				 Expr **with_check_eval, bool *hassublinks,
+				 Expr **spec_with_check_eval, bool onConflict)
 {
 	ListCell		   *item;
 	List			   *quals = NIL;
 	List			   *with_check_quals = NIL;
+	List			   *conflict_update_quals = NIL;
 
 	/*
 	 * Extract the USING and WITH CHECK quals from each of the policies
-	 * and add them to our lists.
+	 * and add them to our lists.  CONFLICT UPDATE quals are always treated
+	 * as CHECK OPTIONS.
 	 */
 	foreach(item, policies)
 	{
 		RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
 
 		if (policy->qual != NULL)
-			quals = lcons(copyObject(policy->qual), quals);
+		{
+			if (!onConflict || policy->polcmd != ACL_UPDATE_CHR)
+				quals = lcons(copyObject(policy->qual), quals);
+			else
+				conflict_update_quals = lcons(copyObject(policy->qual), quals);
+		}
 
 		if (policy->with_check_qual != NULL)
-			with_check_quals = lcons(copyObject(policy->with_check_qual),
-									 with_check_quals);
+		{
+			if (!onConflict || policy->polcmd != ACL_UPDATE_CHR)
+				with_check_quals = lcons(copyObject(policy->with_check_qual),
+										 with_check_quals);
+			else
+				conflict_update_quals =
+					lcons(copyObject(policy->with_check_qual),
+						  conflict_update_quals);
+		}
 
 		if (policy->hassublinks)
 			*hassublinks = true;
@@ -420,6 +464,10 @@ process_policies(List *policies, int rt_index, Expr **qual_eval,
 	/*
 	 * If we end up with only USING quals, then use those as
 	 * WITH CHECK quals also.
+	 *
+	 * For the INSERT with ON CONFLICT UPDATE case, we always enforce that the
+	 * UPDATE's USING quals are treated like WITH CHECK quals, enforced against
+	 * the target relation's tuple in multiple places.
 	 */
 	if (with_check_quals == NIL)
 		with_check_quals = copyObject(quals);
@@ -453,6 +501,24 @@ process_policies(List *policies, int rt_index, Expr **qual_eval,
 	else
 		*with_check_eval = (Expr*) linitial(with_check_quals);
 
+	/*
+	 * For INSERT with ON CONFLICT UPDATE, *both* sets of WITH CHECK options
+	 * (from any INSERT policy and any UPDATE policy) are enforced.
+	 *
+	 * These are handled separately because enforcement of each type of WITH
+	 * CHECK option is based on the point in query processing of INSERT ... ON
+	 * CONFLICT UPDATE.  The INSERT path does not enforce UPDATE related CHECK
+	 * OPTIONs.
+	 */
+	if (conflict_update_quals != NIL)
+	{
+		if (list_length(conflict_update_quals) > 1)
+			*spec_with_check_eval = makeBoolExpr(AND_EXPR,
+												 conflict_update_quals, -1);
+		else
+			*spec_with_check_eval = (Expr*) linitial(conflict_update_quals);
+	}
+
 	return;
 }
 
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 00f9c9c..743ce20 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -195,7 +195,8 @@ extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
 				TupleTableSlot *slot, EState *estate);
 extern void ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
-					 TupleTableSlot *slot, EState *estate);
+					 TupleTableSlot *slot, bool detail, bool onlyInsert,
+					 EState *estate);
 extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti);
 extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
 extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a623f3d..0292610 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -875,6 +875,7 @@ typedef struct WithCheckOption
 	NodeTag		type;
 	char	   *viewname;		/* name of view that specified the WCO */
 	Node	   *qual;			/* constraint qual to check */
+	CmdType		commandType;	/* select|insert|update|delete */
 	bool		cascaded;		/* true = WITH CASCADED CHECK OPTION */
 } WithCheckOption;
 
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index f41bef1..c34c423 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1187,6 +1187,96 @@ NOTICE:  f_leak => yyyyyy
 (3 rows)
 
 --
+-- INSERT ... ON CONFLICT UPDATE and Row-level security
+--
+-- Would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Can't insert new violating tuple, either:
+INSERT INTO document VALUES (22, 11, 2, 'rls_regress_user2', 'mediocre novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- INSERT path is taken here, so UPDATE targelist doesn't matter:
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+ did | cid | dlevel |      dauthor      |        dtitle        
+-----+-----+--------+-------------------+----------------------
+  33 |  22 |      1 | rls_regress_user1 | okay science fiction
+(1 row)
+
+-- Update path will now taken for same query, so UPDATE targelist now matters
+-- (this is the same query as the last, but now fails):
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+SET SESSION AUTHORIZATION rls_regress_user0;
+DROP POLICY p1 ON document;
+CREATE POLICY p1 ON document FOR SELECT USING (true);
+CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
+CREATE POLICY p3 ON document FOR UPDATE
+  USING (cid = (SELECT cid from category WHERE cname = 'novel'))
+  WITH CHECK (dauthor = current_user);
+SET SESSION AUTHORIZATION rls_regress_user1;
+-- Again, would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked, which is what
+-- failed to satisfy WITH CHECK options - not row proposed for insertion by
+-- user):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Again, can't insert new violating tuple, either (unsuccessfully inserted tuple
+-- values are reported here, though)
+--
+-- Violates actual CHECK OPTION within UPDATE:
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user2', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Violates USING qual for UPDATE policy p3, interpreted here as CHECK OPTION.
+--
+-- UPDATE path is taken, but UPDATE fails purely because *existing* row to be
+-- updated is not a "novel"/cid 11 (row is not leaked, even though we have
+-- SELECT privileges sufficient to see the row in this instance):
+INSERT INTO document VALUES (33, 11, 1, 'rls_regress_user1', 'Some novel, replaces sci-fi')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Fine (we UPDATE):
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user1', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
+ did | cid | dlevel |      dauthor      |     dtitle     
+-----+-----+--------+-------------------+----------------
+   2 |  11 |      2 | rls_regress_user1 | my first novel
+(1 row)
+
+-- Fine (we INSERT, so "cid = 33" isn't evaluated):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+ did | cid | dlevel |      dauthor      |      dtitle      
+-----+-----+--------+-------------------+------------------
+  78 |  11 |      1 | rls_regress_user1 | some other novel
+(1 row)
+
+-- Fail (same query, but we UPDATE, so "cid = 33" is evaluated at end of
+-- UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Fail (we UPDATE, so dauthor assignment is evaluated at end of UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = 'rls_regress_user2';
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Don't fail because INSERT doesn't satisfy WITH CHECK option that originated
+-- as a barrier/USING() qual from the UPDATE.  Note that the UPDATE path
+-- *isn't* taken, and so UPDATE-related policy does not apply:
+INSERT INTO document VALUES (88, 33, 1, 'rls_regress_user1', 'technology book, can only insert')
+    ON CONFLICT (did) UPDATE SET dtitle = upper(EXCLUDED.dtitle) RETURNING *;
+ did | cid | dlevel |      dauthor      |              dtitle              
+-----+-----+--------+-------------------+----------------------------------
+  88 |  33 |      1 | rls_regress_user1 | technology book, can only insert
+(1 row)
+
+--
 -- ROLE/GROUP
 --
 SET SESSION AUTHORIZATION rls_regress_user0;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index ed7adbf..5c660d5 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -436,6 +436,79 @@ DELETE FROM only t1 WHERE f_leak(b) RETURNING oid, *, t1;
 DELETE FROM t1 WHERE f_leak(b) RETURNING oid, *, t1;
 
 --
+-- INSERT ... ON CONFLICT UPDATE and Row-level security
+--
+
+-- Would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+
+-- Can't insert new violating tuple, either:
+INSERT INTO document VALUES (22, 11, 2, 'rls_regress_user2', 'mediocre novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+
+-- INSERT path is taken here, so UPDATE targelist doesn't matter:
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+
+-- Update path will now taken for same query, so UPDATE targelist now matters
+-- (this is the same query as the last, but now fails):
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+
+SET SESSION AUTHORIZATION rls_regress_user0;
+DROP POLICY p1 ON document;
+
+CREATE POLICY p1 ON document FOR SELECT USING (true);
+CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
+CREATE POLICY p3 ON document FOR UPDATE
+  USING (cid = (SELECT cid from category WHERE cname = 'novel'))
+  WITH CHECK (dauthor = current_user);
+
+SET SESSION AUTHORIZATION rls_regress_user1;
+
+-- Again, would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked, which is what
+-- failed to satisfy WITH CHECK options - not row proposed for insertion by
+-- user):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+
+-- Again, can't insert new violating tuple, either (unsuccessfully inserted tuple
+-- values are reported here, though)
+--
+-- Violates actual CHECK OPTION within UPDATE:
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user2', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor;
+
+-- Violates USING qual for UPDATE policy p3, interpreted here as CHECK OPTION.
+--
+-- UPDATE path is taken, but UPDATE fails purely because *existing* row to be
+-- updated is not a "novel"/cid 11 (row is not leaked, even though we have
+-- SELECT privileges sufficient to see the row in this instance):
+INSERT INTO document VALUES (33, 11, 1, 'rls_regress_user1', 'Some novel, replaces sci-fi')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+-- Fine (we UPDATE):
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user1', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
+-- Fine (we INSERT, so "cid = 33" isn't evaluated):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+-- Fail (same query, but we UPDATE, so "cid = 33" is evaluated at end of
+-- UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+-- Fail (we UPDATE, so dauthor assignment is evaluated at end of UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = 'rls_regress_user2';
+-- Don't fail because INSERT doesn't satisfy WITH CHECK option that originated
+-- as a barrier/USING() qual from the UPDATE.  Note that the UPDATE path
+-- *isn't* taken, and so UPDATE-related policy does not apply:
+INSERT INTO document VALUES (88, 33, 1, 'rls_regress_user1', 'technology book, can only insert')
+    ON CONFLICT (did) UPDATE SET dtitle = upper(EXCLUDED.dtitle) RETURNING *;
+
+--
 -- ROLE/GROUP
 --
 SET SESSION AUTHORIZATION rls_regress_user0;
-- 
1.9.1

0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patchtext/x-patch; charset=US-ASCII; name=0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patchDownload
From 39bf413ffbc8e881846abf97d4942399aad7649a Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <peter.geoghegan86@gmail.com>
Date: Tue, 3 Mar 2015 21:43:21 -0800
Subject: [PATCH 3/4] Support INSERT ... ON CONFLICT UPDATE

This non-standard INSERT clause allows DML statement authors to specify
that in the event of each of any of the tuples being inserted
duplicating an existing tuple in terms of a value or set of values
constrained by a unique index, an alternative UPDATE path may be taken.
The implementation will go to UPDATE the existing tuple whose value is
duplicated by a value within one single tuple proposed for insertion.
The implementation loops until either an insert or an UPDATE occurs.  No
existing tuple may be affected more than once per INSERT - the statement
is "deterministic" (in the lexicon of the SQL standard's discussion of
MERGE).  Like INSERT ... ON CONFLICT IGNORE, the implementation uses the
speculative insertion infrastructure.

READ COMMITTED isolation level is permitted to UPDATE a tuple even where
no version is visible to the command's MVCC snapshot.  Similarly, any
query predicate associated with the UPDATE portion of the new statement
need only satisfy an already locked, conclusively committed and visible
conflict tuple.  When the predicate isn't satisfied, the tuple is still
locked, which implies that at READ COMMITTED, a tuple may be locked
without any version being visible to the command's MVCC snapshot.

The "unique index inference" clause is made mandatory for ON CONFLICT
UPDATE (unlike ON CONFLICT IGNORE), which should address concerns about
spuriously taking an incorrect alternative ON CONFLICT path (i.e. the
wrong unique index is used for arbitration of whether or not to take the
alternative path) due to there being more than one would-be unique
violation.

The auxiliary ModifyTable plan used by the UPDATE portion of the new
statement is not formally a subplan of its parent INSERT ModifyTable
plan.  Rather, it's an independently planned subquery, whose execution
is tightly driven by its parent.  Special auxiliary state pertaining to
the auxiliary UPDATE is tracked by its parent through all stages of
query execution.

The implementation imposes some restrictions on child auxiliary UPDATE
plans, which make the plans comport with their parent to the extent
required during the executor stage.  The parent and child share the same
target ResultRelation, for example.  One user-visible consequences of
this is that the special auxiliary UPDATE query cannot have subselects
within its targetlist or WHERE clause.  UPDATEs may not reference any
other table, and UPDATE FROM is disallowed.  INSERT's RETURNING clause
projects tuples successfully inserted and updated.  If an ON CONFLICT
UPDATE's WHERE clause is not satisfied in respect of some slot/tuple,
the post-update tuple is not projected (since an UPDATE didn't occur
- although the row is still locked).

To support referencing tuples proposed for insertion but found to have
forced the implementation to take the alternative (UPDATE) path, a new
primnode, ExcludedExpr is added.  This allows parse analysis to deal
with a distinct alias RTE. However, after rewriting, excluded tuples are
referenced through special ad-hoc ExcludedExpr Vars (the planner
therefore only has to generate a simple subquery with a sequential scan,
that is only actually ever executed using the EvalPlanQual()
infrastructure).  Note that pg_stat_statements does not fingerprint
ExludedExpr, because it cannot appear in the post-parse-analysis,
pre-rewrite Query tree.  (pg_stat_statements does not fingerprint every
primnode anyway, mostly because some are only expected in utility
statements).  Other existing Node handling sites that don't expect to
see primnodes that appear only after rewriting (ExcludedExpr may be in
its own subcategory here in that it is the only such non-utility related
Node) do not have an ExcludedExpr case added either.
---
 contrib/pg_stat_statements/pg_stat_statements.c    |   2 +
 contrib/postgres_fdw/expected/postgres_fdw.out     |   3 +
 contrib/postgres_fdw/postgres_fdw.c                |   4 +
 contrib/postgres_fdw/sql/postgres_fdw.sql          |   1 +
 doc/src/sgml/ddl.sgml                              |   9 +-
 doc/src/sgml/fdwhandler.sgml                       |   9 +-
 doc/src/sgml/mvcc.sgml                             |  24 ++
 doc/src/sgml/plpgsql.sgml                          |  14 +-
 doc/src/sgml/postgres-fdw.sgml                     |   8 +-
 doc/src/sgml/protocol.sgml                         |  13 +-
 doc/src/sgml/ref/create_rule.sgml                  |   6 +-
 doc/src/sgml/ref/create_table.sgml                 |   2 +-
 doc/src/sgml/ref/create_trigger.sgml               |   5 +-
 doc/src/sgml/ref/create_view.sgml                  |  23 +-
 doc/src/sgml/ref/insert.sgml                       | 259 ++++++++++++----
 doc/src/sgml/ref/set_constraints.sgml              |   2 +-
 doc/src/sgml/trigger.sgml                          |  49 +++-
 src/backend/catalog/sql_features.txt               |   2 +-
 src/backend/commands/explain.c                     |  78 ++++-
 src/backend/executor/README                        |  77 ++++-
 src/backend/executor/execMain.c                    |   3 +-
 src/backend/executor/execQual.c                    |  54 ++++
 src/backend/executor/nodeModifyTable.c             | 325 ++++++++++++++++++++-
 src/backend/nodes/copyfuncs.c                      |  19 ++
 src/backend/nodes/equalfuncs.c                     |  13 +
 src/backend/nodes/nodeFuncs.c                      |  43 +++
 src/backend/nodes/outfuncs.c                       |  13 +
 src/backend/nodes/readfuncs.c                      |  16 +
 src/backend/optimizer/path/tidpath.c               |   8 +-
 src/backend/optimizer/plan/createplan.c            |   4 +-
 src/backend/optimizer/plan/planner.c               |  51 ++++
 src/backend/optimizer/plan/setrefs.c               |  32 +-
 src/backend/optimizer/plan/subselect.c             |   6 +
 src/backend/optimizer/util/plancat.c               |   9 +-
 src/backend/parser/analyze.c                       |  60 +++-
 src/backend/parser/gram.y                          |  27 ++
 src/backend/parser/parse_clause.c                  | 103 +++++--
 src/backend/parser/parse_expr.c                    |   6 +-
 src/backend/parser/parse_node.c                    |   8 +-
 src/backend/rewrite/rewriteHandler.c               | 103 +++++++
 src/backend/tcop/pquery.c                          |  16 +-
 src/backend/utils/adt/ruleutils.c                  |  39 +++
 src/bin/psql/common.c                              |   5 +-
 src/include/nodes/execnodes.h                      |  11 +
 src/include/nodes/nodes.h                          |   6 +-
 src/include/nodes/parsenodes.h                     |   4 +-
 src/include/nodes/plannodes.h                      |   1 +
 src/include/nodes/primnodes.h                      |  47 +++
 src/include/optimizer/planmain.h                   |   3 +-
 src/include/parser/parse_node.h                    |   1 +
 .../expected/insert-conflict-update-2.out          |  23 ++
 .../expected/insert-conflict-update-3.out          |  26 ++
 .../isolation/expected/insert-conflict-update.out  |  23 ++
 src/test/isolation/isolation_schedule              |   3 +
 .../isolation/specs/insert-conflict-update-2.spec  |  41 +++
 .../isolation/specs/insert-conflict-update-3.spec  |  69 +++++
 .../isolation/specs/insert-conflict-update.spec    |  40 +++
 src/test/regress/expected/insert_conflict.out      | 186 ++++++++++++
 src/test/regress/expected/privileges.out           |   7 +-
 src/test/regress/expected/rules.out                |  12 +
 src/test/regress/expected/subselect.out            |  22 ++
 src/test/regress/expected/triggers.out             | 102 ++++++-
 src/test/regress/expected/update.out               |  27 ++
 src/test/regress/expected/with.out                 |  74 +++++
 src/test/regress/input/constraints.source          |   5 +
 src/test/regress/output/constraints.source         |  15 +-
 src/test/regress/sql/insert_conflict.sql           | 127 ++++++++
 src/test/regress/sql/privileges.sql                |   5 +-
 src/test/regress/sql/rules.sql                     |   8 +
 src/test/regress/sql/subselect.sql                 |  14 +
 src/test/regress/sql/triggers.sql                  |  69 ++++-
 src/test/regress/sql/update.sql                    |  14 +
 src/test/regress/sql/with.sql                      |  37 +++
 73 files changed, 2399 insertions(+), 176 deletions(-)
 create mode 100644 src/test/isolation/expected/insert-conflict-update-2.out
 create mode 100644 src/test/isolation/expected/insert-conflict-update-3.out
 create mode 100644 src/test/isolation/expected/insert-conflict-update.out
 create mode 100644 src/test/isolation/specs/insert-conflict-update-2.spec
 create mode 100644 src/test/isolation/specs/insert-conflict-update-3.spec
 create mode 100644 src/test/isolation/specs/insert-conflict-update.spec

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 46f5189..414ec83 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2201,6 +2201,8 @@ JumbleQuery(pgssJumbleState *jstate, Query *query)
 	APP_JUMB(query->specClause);
 	JumbleExpr(jstate, (Node *) query->arbiterExpr);
 	JumbleExpr(jstate, query->arbiterWhere);
+	if (query->onConflict)
+		JumbleQuery(jstate, (Query *) query->onConflict);
 	JumbleExpr(jstate, (Node *) query->returningList);
 	JumbleExpr(jstate, (Node *) query->groupClause);
 	JumbleExpr(jstate, query->havingQual);
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 76d36e6..5133386 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -2331,6 +2331,9 @@ INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
 ERROR:  relation "ft1" is not an ordinary table
 HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) UPDATE SET c3 = 'ffg'; -- unsupported
+ERROR:  relation "ft1" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 ERROR:  new row for relation "T 1" violates check constraint "c2positive"
 DETAIL:  Failing row contains (1111, -2, null, null, null, null, ft1       , null).
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 8e20680..8de3046 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1223,6 +1223,10 @@ postgresPlanForeignModify(PlannerInfo *root,
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("postgres_fdw does not support ON CONFLICT unique index inference")));
+	else if (plan->spec == SPEC_INSERT)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("postgres_fdw does not support ON CONFLICT UPDATE")));
 	else if (plan->spec == SPEC_IGNORE)
 		ignore = true;
 
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 0078747..e01d34e 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -374,6 +374,7 @@ ALTER TABLE "S 1"."T 1" ADD CONSTRAINT c2positive CHECK (c2 >= 0);
 INSERT INTO ft1(c1, c2) VALUES(11, 12);  -- duplicate key
 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) UPDATE SET c3 = 'ffg'; -- unsupported
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 UPDATE ft1 SET c2 = -c2 WHERE c1 = 1;  -- c2positive
 
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index dd3fc38..7b43a10 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2429,7 +2429,7 @@ VALUES ('Albany', NULL, NULL, 'NY');
 
   <para>
    There is limited inheritance support for <command>INSERT</command>
-   commands with <literal>ON CONFLICT IGNORE</> clauses.  Tables with
+   commands with <literal>ON CONFLICT</> clauses.  Tables with
    children are not generally accepted as targets.  One notable
    exception is that such tables are accepted as targets for
    <command>INSERT</command> commands with <literal>ON CONFLICT
@@ -2438,7 +2438,7 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <emphasis>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 <command>INSERT</command>
-   with <literal>ON CONFLICT IGNORE</>.
+   with <literal>ON CONFLICT</>.
   </para>
 
   <para>
@@ -2533,6 +2533,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
    not <literal>INSERT</literal> or <literal>ALTER TABLE ...
    RENAME</literal>) typically default to including child tables and
    support the <literal>ONLY</literal> notation to exclude them.
+   <literal>INSERT</literal> with an <literal>ON CONFLICT
+   UPDATE</literal> clause does not support the
+   <literal>ONLY</literal> notation, and so in effect tables with
+   inheritance children are not supported for the <literal>ON
+   CONFLICT</literal> variant.
    Commands that do database maintenance and tuning
    (e.g., <literal>REINDEX</literal>, <literal>VACUUM</literal>)
    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 46b8db8..0c3dcb5 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1015,10 +1015,11 @@ GetForeignServerByName(const char *name, bool missing_ok);
     </para>
 
     <para>
-     <command>INSERT</> with an <literal>ON CONFLICT</> clause is not
-     supported with a unique index inference specification.  When
-     planning an <command>INSERT</>, <function>PlanForeignModify</>
-     should reject these cases.
+     <command>INSERT</> with an <literal>ON CONFLICT</> clause is not supported
+     with a unique index inference specification (this implies that <literal>ON
+     CONFLICT UPDATE</> is never supported, since the specification is
+     mandatory there).  When planning an <command>INSERT</>,
+     <function>PlanForeignModify</> should reject these cases.
     </para>
 
   </sect1>
diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml
index a0d6867..5e310d7 100644
--- a/doc/src/sgml/mvcc.sgml
+++ b/doc/src/sgml/mvcc.sgml
@@ -326,6 +326,30 @@
    </para>
 
    <para>
+    <command>INSERT</command> with an <literal>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 <command>INSERT</command>, the
+    <command>UPDATE</command> may affect that row, even though it may be the
+    case that <emphasis>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 <literal>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).
+   </para>
+
+   <para>
+    <command>INSERT</command> with an <literal>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 <command>INSERT</command>
+    snapshot.  Again, this is only the case in Read Committed mode.
+   </para>
+
+   <para>
     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 69a0885..59a5945 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -2607,7 +2607,11 @@ END;
     <para>
 
     This example uses exception handling to perform either
-    <command>UPDATE</> or <command>INSERT</>, as appropriate:
+    <command>UPDATE</> or <command>INSERT</>, as appropriate.  It is
+    recommended that applications use <command>INSERT</> with
+    <literal>ON CONFLICT UPDATE</> rather than actually emulating this
+    pattern.  This example serves only to illustrate use of
+    <application>PL/pgSQL</application> control flow structures:
 
 <programlisting>
 CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
@@ -3771,9 +3775,11 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
     <command>INSERT</> and <command>UPDATE</> operations, the return value
     should be <varname>NEW</>, which the trigger function may modify to
     support <command>INSERT RETURNING</> and <command>UPDATE RETURNING</>
-    (this will also affect the row value passed to any subsequent triggers).
-    For <command>DELETE</> operations, the return value should be
-    <varname>OLD</>.
+    (this will also affect the row value passed to any subsequent triggers,
+    or passed to a special <varname>EXCLUDED</> alias reference within
+    an <command>INSERT</> statement with an <literal>ON CONFLICT UPDATE</>
+    clause).  For <command>DELETE</> operations, the return
+    value should be <varname>OLD</>.
    </para>
 
    <para>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 81d4441..fa39661 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -69,9 +69,11 @@
  </para>
 
  <para>
-  <filename>postgres_fdw</> supports <command>INSERT</command>
-  statements with an <literal>ON CONFLICT IGNORE</> clause, provided a
-  unique index inference specification is omitted.
+  Note that <filename>postgres_fdw</> currently lacks support for
+  <command>INSERT</command> statements with an <literal>ON CONFLICT
+  UPDATE</> clause.  However, the <literal>ON CONFLICT IGNORE</>
+  clause is supported, provided a unique index inference specification
+  is omitted.
  </para>
 
  <para>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 3a753a0..ac13d32 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -2998,9 +2998,16 @@ CommandComplete (B)
         <literal>INSERT <replaceable>oid</replaceable>
         <replaceable>rows</replaceable></literal>, where
         <replaceable>rows</replaceable> is the number of rows
-        inserted. <replaceable>oid</replaceable> is the object ID
-        of the inserted row if <replaceable>rows</replaceable> is 1
-        and the target table has OIDs;
+        inserted. However, if and only if <literal>ON CONFLICT
+        UPDATE</> is specified, then the tag is <literal>UPSERT
+        <replaceable>oid</replaceable>
+        <replaceable>rows</replaceable></literal>, where
+        <replaceable>rows</replaceable> is the number of rows inserted
+        <emphasis>or updated</emphasis>.
+        <replaceable>oid</replaceable> is the object ID of the
+        inserted row if <replaceable>rows</replaceable> is 1 and the
+        target table has OIDs, and (for the <literal>UPSERT</literal>
+        tag), the row was actually inserted rather than updated;
         otherwise <replaceable>oid</replaceable> is 0.
        </para>
 
diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml
index 045f5d1..34a4ae1 100644
--- a/doc/src/sgml/ref/create_rule.sgml
+++ b/doc/src/sgml/ref/create_rule.sgml
@@ -137,11 +137,11 @@ CREATE [ OR REPLACE ] RULE <replaceable class="parameter">name</replaceable> AS
       The event is one of <literal>SELECT</literal>,
       <literal>INSERT</literal>, <literal>UPDATE</literal>, or
       <literal>DELETE</literal>.  Note that an
-      <command>INSERT</command> containing an <literal>ON CONFLICT
-      IGNORE</literal> clause cannot be used on tables that have
+      <command>INSERT</command> containing an <literal>ON
+      CONFLICT</literal> clause cannot be used on tables that have
       either <literal>INSERT</literal> or <literal>UPDATE</literal>
       rules.  Consider using an updatable view instead, which have
-      limited support for <literal>ON CONFLICT IGNORE</literal>.
+      limited support for <literal>ON CONFLICT IGNORE</literal> only.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 4733a6c..a9c1124 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -711,7 +711,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
       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 <command>INSERT</command> statement
-      that includes an <literal>ON CONFLICT</> clause.
+      that includes an <literal>ON CONFLICT UPDATE</> clause.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_trigger.sgml b/doc/src/sgml/ref/create_trigger.sgml
index aae0b41..1b75b1a 100644
--- a/doc/src/sgml/ref/create_trigger.sgml
+++ b/doc/src/sgml/ref/create_trigger.sgml
@@ -76,7 +76,10 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
    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 <literal>FOR
-   EACH STATEMENT</literal> triggers).
+   EACH STATEMENT</literal> triggers).  Note that since
+   <command>INSERT</command> with an <literal>ON CONFLICT UPDATE</>
+   clause is considered an <command>INSERT</command> statement, no
+   <command>UPDATE</command> statement level trigger will be fired.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index 2b68121..599c1cb 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -385,18 +385,25 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     <xref linkend="rules-privileges">).
    </para>
    <para>
-    <command>INSERT</command> with an <literal>ON CONFLICT IGNORE</>
-    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 <literal>IGNORE</> path
-    - then updatable views are not supported.  For example:
+    <command>INSERT</command> with an <literal>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 <command>UPDATE</command> or
+    <literal>IGNORE</> path - then updatable views are not supported.
+    Since this specification is already mandatory for
+    <command>INSERT</command> with <literal>ON CONFLICT UPDATE</>,
+    this implies that only the <literal>ON CONFLICT IGNORE</> variant
+    is supported, and only when there is no such specification.  For
+    example:
    </para>
    <para>
 <programlisting>
 -- 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;
 
diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml
index 1395c48..a53b0bf 100644
--- a/doc/src/sgml/ref/insert.sgml
+++ b/doc/src/sgml/ref/insert.sgml
@@ -24,7 +24,14 @@ PostgreSQL documentation
 [ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
 INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
     { DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) [, ...] | <replaceable class="PARAMETER">query</replaceable> }
-    [ ON CONFLICT [ ( { <replaceable class="parameter">column_name_index</replaceable> | ( <replaceable class="parameter">expression_index</replaceable> ) } [, ...] [ WHERE <replaceable class="PARAMETER">index_condition</replaceable> ] ) ] IGNORE]
+    [ ON CONFLICT [ ( { <replaceable class="parameter">column_name_index</replaceable> | ( <replaceable class="parameter">expression_index</replaceable> ) } [, ...] [ WHERE <replaceable class="PARAMETER">index_condition</replaceable> ] ) ]
+      { IGNORE | UPDATE
+        SET { <replaceable class="PARAMETER">column_name</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } |
+              ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] )
+            } [, ...]
+        [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
+      }
+    ]
     [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
@@ -35,13 +42,13 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
   <para>
    <command>INSERT</command> 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
-   <literal>IGNORE</literal> path can optionally be specified, to be
-   taken in the event of detecting that proceeding with insertion
-   would result in a conflict (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.
+   more rows resulting from a query.  An alternative path
+   (<literal>IGNORE</literal> or <literal>UPDATE</literal>) can
+   optionally be specified, to be taken in the event of detecting that
+   proceeding with insertion would result in a conflict (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.
   </para>
 
   <para>
@@ -70,30 +77,109 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    take as an alternative to raising a conflict related error.
    <literal>ON CONFLICT IGNORE</> simply avoids inserting any
    individual row when it is determined that a conflict related error
-   would otherwise need to be raised.
+   would otherwise need to be raised.  <literal>ON CONFLICT UPDATE</>
+   has the system take an <command>UPDATE</command> path in respect of
+   such rows instead.  <literal>ON CONFLICT UPDATE</> guarantees an
+   atomic <command>INSERT</command> or <command>UPDATE</command>
+   outcome - provided there is no incidental error, one of those two
+   outcomes is guaranteed, even under high concurrency.
   </para>
 
   <para>
-   <literal>ON CONFLICT IGNORE</> optionally accepts a
-   <emphasis>unique index inference</emphasis> specification, which
-   consists of one or more <replaceable
-   class="PARAMETER">column_name_index</replaceable> columns and/or
-   <replaceable class="PARAMETER">expression_index</replaceable>
-   expressions on columns, appearing between parenthesis.  These are
-   used to infer a single unique index to limit pre-checking for
-   conflicts to (if no appropriate index is available, an error is
-   raised).  A subset of the table to limit the check for conflicts to
-   can optionally also be specified using <replaceable
+   <literal>ON CONFLICT UPDATE</> optionally accepts a
+   <literal>WHERE</> clause <replaceable>condition</>.  When provided,
+   the statement only proceeds with updating if the
+   <replaceable>condition</> is satisfied.  Otherwise, unlike a
+   conventional <command>UPDATE</command>, the row is still locked for
+   update.  Note that the <replaceable>condition</> is evaluated last,
+   after a conflict has been identified as a candidate to update.
+  </para>
+
+  <para>
+   <literal>ON CONFLICT UPDATE</> is effectively an auxiliary query of
+   its parent <command>INSERT</command>.  Two special aliases are
+   visible when <literal>ON CONFLICT UPDATE</> is specified -
+   <varname>TARGET</> and <varname>EXCLUDED</>.  The first alias is a
+   standard, generic alias for the target relation, while the second
+   alias refers to rows originally proposed for insertion.  Both
+   aliases can be used in the auxiliary query targetlist and
+   <literal>WHERE</> clause, while the <varname>TARGET</> alias can be
+   used anywhere within the entire statement (e.g., within the
+   <literal>RETURNING</> clause).  This allows expressions (in
+   particular, assignments) to reference rows originally proposed for
+   insertion.  Note that the effects of all per-row <literal>BEFORE
+   INSERT</> triggers are carried forward.  This is particularly
+   useful for multi-insert <literal>ON CONFLICT UPDATE</> statements;
+   when inserting or updating multiple rows, constants or parameter
+   values need only appear once.
+  </para>
+
+  <para>
+   There are several restrictions on the <literal>ON CONFLICT
+   UPDATE</> clause that do not apply to <command>UPDATE</command>
+   statements.  Subqueries may not appear in either the
+   <command>UPDATE</command> targetlist, nor its <literal>WHERE</>
+   clause (although simple multi-assignment expressions are
+   supported).  <literal>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.
+  </para>
+
+  <para>
+   <command>INSERT</command> with an <literal>ON CONFLICT UPDATE</>
+   clause is a <quote>deterministic</quote> statement.  This means
+   that the command will not be allowed to affect any single existing
+   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
+   <command>INSERT</command> statement with <literal>ON CONFLICT
+   UPDATE</literal>, rows proposed for insertion will never take the
+   alternative path (provided that a <literal>BEFORE ROW
+   INSERT</literal> 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 <literal>WHERE</> clause <replaceable>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).  However, the implementation checks for cardinality
+   violations after locking the row, and before updating (or
+   considering updating), so a cardinality violation may be raised
+   despite the fact that the row would not otherwise have gone on to
+   be updated if and only if the existing row was updated by the
+   <literal>ON CONFLICT UPDATE</literal> command at least once
+   already.
+  </para>
+
+  <para>
+   <literal>ON CONFLICT UPDATE</> requires a <emphasis>unique index
+   inference</emphasis> specification, which consists of one or more
+   <replaceable class="PARAMETER">column_name_index</replaceable>
+   columns and/or <replaceable
+   class="PARAMETER">expression_index</replaceable> expressions on
+   columns, appearing between parenthesis.  These are used to infer a
+   single unique index to limit pre-checking for conflicts to (if no
+   appropriate index is available, an error is raised).  A subset of
+   the table to limit the check for conflicts to can optionally also
+   be specified using <replaceable
    class="PARAMETER">index_condition</replaceable>.  Note that any
    available unique index must only cover at least that subset in
    order to be arbitrate taking the alternative path;  it need not
    match exactly, and so a non-partial unique index that otherwise
-   matches is applicable.  Note that omitting the specification
+   matches is applicable.  <literal>ON CONFLICT IGNORE</> makes an
+   inference specification optional;  omitting the specification
    indicates a total indifference to where any conflict could occur,
    which isn't always appropriate.  At times, it may be desirable for
    <literal>ON CONFLICT IGNORE</> to <emphasis>not</emphasis> suppress
    a conflict related error associated with an index where that isn't
-   explicitly anticipated.
+   explicitly anticipated.  Note that <literal>ON CONFLICT UPDATE</>
+   assignment may result in a uniqueness violation, just as with a
+   conventional <command>UPDATE</command>.
   </para>
 
   <para>
@@ -120,22 +206,24 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    seldom of great interest.  A conflict is either a unique violation
    from a unique constraint (or unique index), or an exclusion
    violation from an exclusion constraint.  Only unique indexes can be
-   inferred with a unique index inference specification.  In contrast
-   to the rules around certain other SQL clauses, like the
-   <literal>DISTINCT</literal> clause, the definition of a duplicate
-   (a conflict) is based on whatever unique indexes happen to be
-   defined on columns on the table.  This means that if a user-defined
-   type has multiple sort orders, and the "equals" operator of any of
-   those available sort orders happens to be inconsistent (which goes
-   against an unenforced convention of
-   <productname>PostgreSQL</productname>), the exact behavior depends
-   on the choice of operator class when the unique index was created
-   initially, and not any other consideration such as the default
-   operator class for the type of each indexed column.  If there are
-   multiple unique indexes available that seem like equally suitable
-   candidates, but with inconsistent definitions of "equals", then the
-   system chooses whatever it estimates to be the cheapest one to use
-   as an arbiter of taking the alternative
+   inferred with a unique index inference specification, which is
+   required for the <command>UPDATE</command> variant, so in effect
+   only unique constraints (and unique indexes) are supported by the
+   <command>UPDATE</command> variant.  In contrast to the rules around
+   certain other SQL clauses, like the <literal>DISTINCT</literal>
+   clause, the definition of a duplicate (a conflict) is based on
+   whatever unique indexes happen to be defined on columns on the
+   table.  This means that if a user-defined type has multiple sort
+   orders, and the "equals" operator of any of those available sort
+   orders happens to be inconsistent (which goes against an unenforced
+   convention of <productname>PostgreSQL</productname>), the exact
+   behavior depends on the choice of operator class when the unique
+   index was created initially, and not any other consideration such
+   as the default operator class for the type of each indexed column.
+   If there are multiple unique indexes available that seem like
+   equally suitable candidates, but with inconsistent definitions of
+   "equals", then the system chooses whatever it estimates to be the
+   cheapest one to use as an arbiter of taking the alternative
    <command>UPDATE</command>/<literal>IGNORE</literal> path.
   </para>
 
@@ -161,24 +249,40 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
 
   <para>
    The optional <literal>RETURNING</> clause causes <command>INSERT</>
-   to compute and return value(s) based on each row actually inserted.
+   to compute and return value(s) based on each row actually inserted
+   (or updated, if an <literal>ON CONFLICT UPDATE</> clause was used).
    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
    <literal>RETURNING</> list is identical to that of the output list
-   of <command>SELECT</>.
+   of <command>SELECT</>.  Only rows that were successfully inserted
+   or updated will be returned.  If a row was locked but not updated
+   because an <literal>ON CONFLICT UPDATE</> <literal>WHERE</> clause
+   did not pass, the row will not be returned.  Since
+   <literal>RETURNING</> is not part of the <command>UPDATE</>
+   auxiliary query, the special <literal>ON CONFLICT UPDATE</> aliases
+   (<varname>TARGET</> and <varname>EXCLUDED</>) may not be
+   referenced;  only the row as it exists after updating (or
+   inserting) is returned.
   </para>
 
   <para>
    You must have <literal>INSERT</literal> privilege on a table in
-   order to insert into it.  If a column list is specified, you only
-   need <literal>INSERT</literal> privilege on the listed columns.
-   Use of the <literal>RETURNING</> clause requires <literal>SELECT</>
-   privilege on all columns mentioned in <literal>RETURNING</>.
-   If you use the <replaceable
-   class="PARAMETER">query</replaceable> clause to insert rows from a
-   query, you of course need to have <literal>SELECT</literal> privilege on
-   any table or column used in the query.
+   order to insert into it, as well as <literal>UPDATE
+   privilege</literal> if and only if <literal>ON CONFLICT UPDATE</>
+   is specified.  If a column list is specified, you only need
+   <literal>INSERT</literal> privilege on the listed columns.
+   Similarly, when <literal>ON CONFLICT UPDATE</> is specified, you
+   only need <literal>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 <literal>ON CONFLICT UPDATE</>
+   expressions or <replaceable>condition</>.  Use of the
+   <literal>RETURNING</> clause requires <literal>SELECT</> privilege
+   on all columns mentioned in <literal>RETURNING</>.  If you use the
+   <replaceable class="PARAMETER">query</replaceable> clause to insert
+   rows from a query, you of course need to have
+   <literal>SELECT</literal> privilege on any table or column used in
+   the query.
   </para>
  </refsect1>
 
@@ -222,7 +326,11 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
       The name of a column in the table named by <replaceable class="PARAMETER">table_name</replaceable>.
       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 <literal>ON CONFLICT UPDATE</>, do not
+      include the table's name in the specification of a target
+      column.  For example, <literal>INSERT ... ON CONFLICT UPDATE tab
+      SET TARGET.col = 1</> is invalid.
      </para>
     </listitem>
    </varlistentry>
@@ -311,6 +419,18 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="PARAMETER">condition</replaceable></term>
+    <listitem>
+     <para>
+      An expression that returns a value of type <type>boolean</type>.
+      Only rows for which this expression returns <literal>true</>
+      will be updated, although all rows will be locked when the
+      <literal>ON CONFLICT UPDATE</> path is taken.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+
     <term><replaceable class="PARAMETER">output_expression</replaceable></term>
     <listitem>
      <para>
@@ -343,20 +463,29 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
 <screen>
 INSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</replaceable>
 </screen>
+   However, in the event of an <literal>ON CONFLICT UPDATE</> clause
+   (but <emphasis>not</emphasis> in the event of an <literal>ON
+   CONFLICT IGNORE</> clause), the command tag reports the number of
+   rows inserted or updated together, of the form
+<screen>
+UPSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</replaceable>
+</screen>
    The <replaceable class="parameter">count</replaceable> is the number
    of rows inserted.  If <replaceable class="parameter">count</replaceable>
    is exactly one, and the target table has OIDs, then
    <replaceable class="parameter">oid</replaceable> is the
-   <acronym>OID</acronym> assigned to the inserted row.  Otherwise
-   <replaceable class="parameter">oid</replaceable> is zero.
+   <acronym>OID</acronym>
+   assigned to the inserted row (but not if there is only a single
+   updated row).  Otherwise <replaceable
+   class="parameter">oid</replaceable> is zero..
   </para>
 
   <para>
    If the <command>INSERT</> command contains a <literal>RETURNING</>
    clause, the result will be similar to that of a <command>SELECT</>
    statement containing the columns and values defined in the
-   <literal>RETURNING</> list, computed over the row(s) inserted by the
-   command.
+   <literal>RETURNING</> list, computed over the row(s) inserted or
+   updated by the command.
   </para>
  </refsect1>
 
@@ -459,6 +588,18 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
 </programlisting>
   </para>
   <para>
+   Insert or update new distributors as appropriate.  Assumes a unique
+   index has been defined that constrains values appearing in the
+   <literal>did</literal> column.  Note that an <varname>EXCLUDED</>
+   expression is used to reference values originally proposed for
+   insertion:
+<programlisting>
+  INSERT INTO distributors (did, dname)
+  VALUES (5, 'Gizmo transglobal'), (6, 'Associated Computing, inc')
+  ON CONFLICT (did) UPDATE SET dname = EXCLUDED.dname
+</programlisting>
+  </para>
+  <para>
    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.
@@ -472,6 +613,20 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
 </programlisting>
   </para>
   <para>
+   Insert or update new distributors as appropriate.  Example assumes
+   a unique index has been defined that constrains values appearing in
+   the <literal>did</literal> column.  <literal>WHERE</> clause is
+   used to limit the rows actually updated (any existing row not
+   updated will still be locked, though):
+<programlisting>
+  -- Don't update existing distributors based in a certain ZIP code
+  INSERT INTO distributors (did, dname) VALUES (8, 'Anvil Distribution')
+  ON CONFLICT (did) UPDATE
+  SET dname = EXCLUDED.dname || ' (formerly ' || TARGET.dname || ')'
+  WHERE TARGET.zipcode != '21201'
+</programlisting>
+  </para>
+  <para>
    Insert new distributor if possible;  otherwise
    <literal>IGNORE</literal>.  Example assumes a unique index has been
    defined that constrains values appearing in the
diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml
index 5030f3c..1e0a2f8 100644
--- a/doc/src/sgml/ref/set_constraints.sgml
+++ b/doc/src/sgml/ref/set_constraints.sgml
@@ -73,7 +73,7 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
    that were created with this clause cannot be used as arbiters of
    whether or not to take the alternative path with an
    <command>INSERT</command> statement that includes an <literal>ON
-   CONFLICT</> clause.
+   CONFLICT UPDATE</> clause.
    <literal>NOT NULL</> and <literal>CHECK</> constraints are
    always checked immediately when a row is inserted or modified
    (<emphasis>not</> at the end of the statement).
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index f94aea1..5141690 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -40,14 +40,17 @@
     On tables and foreign tables, triggers can be defined to execute either
     before or after any <command>INSERT</command>, <command>UPDATE</command>,
     or <command>DELETE</command> operation, either once per modified row,
-    or once per <acronym>SQL</acronym> statement.
-    <command>UPDATE</command> triggers can moreover be set to fire only if
-    certain columns are mentioned in the <literal>SET</literal> clause of the
-    <command>UPDATE</command> statement.
-    Triggers can also fire for <command>TRUNCATE</command> statements.
-    If a trigger event occurs, the trigger's function is called at the
-    appropriate time to handle the event.  Foreign tables do not support the
-    TRUNCATE statement at all.
+    or once per <acronym>SQL</acronym> statement.  If an
+    <command>INSERT</command> contains an <literal>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 a reference to
+    an <varname>EXCLUDED</> column appears.  <command>UPDATE</command>
+    triggers can moreover be set to fire only if certain columns are
+    mentioned in the <literal>SET</literal> clause of the
+    <command>UPDATE</command> statement.  Triggers can also fire for
+    <command>TRUNCATE</command> statements.  If a trigger event occurs,
+    the trigger's function is called at the appropriate time to handle the
+    event.  Foreign tables do not support the TRUNCATE statement at all.
    </para>
 
    <para>
@@ -119,6 +122,36 @@
    </para>
 
    <para>
+    If an <command>INSERT</command> contains an <literal>ON CONFLICT
+    UPDATE</> clause, it is possible that the effects of all row-level
+    <literal>BEFORE</> <command>INSERT</command> triggers and all
+    row-level BEFORE <command>UPDATE</command> triggers can both be
+    applied in a way that is apparent from the final state of the updated
+    row, if an <varname>EXCLUDED</> column is referenced.  There need not
+    be an <varname>EXCLUDED</> column reference for both sets of BEFORE
+    row-level triggers to execute, though.  The possibility of surprising
+    outcomes should be considered when there are both <literal>BEFORE</>
+    <command>INSERT</command> and <literal>BEFORE</>
+    <command>UPDATE</command> 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 <command>UPDATE</command>
+    triggers are executed when <literal>ON CONFLICT UPDATE</> is
+    specified, regardless of whether or not any rows were affected by
+    the <command>UPDATE</command>.  An <command>INSERT</command> with
+    an <literal>ON CONFLICT UPDATE</> clause will execute
+    statement-level <literal>BEFORE</> <command>INSERT</command>
+    triggers first, then statement-level <literal>BEFORE</>
+    <command>UPDATE</command> triggers, followed by statement-level
+    <literal>AFTER</> <command>UPDATE</command> triggers and finally
+    statement-level <literal>AFTER</> <command>INSERT</command>
+    triggers.  <literal>ON CONFLICT UPDATE</> is not supported on
+    views (Only <literal>ON CONFLICT IGNORE</> is supported on
+    updatable views); therefore, unpredictable interactions with
+    <literal>INSTEAD OF</> triggers are not possible.
+   </para>
+
+   <para>
     Trigger functions invoked by per-statement triggers should always
     return <symbol>NULL</symbol>. Trigger functions invoked by per-row
     triggers can return a table row (a value of
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 3329264..ca28928 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -229,7 +229,7 @@ F311	Schema definition statement	02	CREATE TABLE for persistent base tables	YES
 F311	Schema definition statement	03	CREATE VIEW	YES	
 F311	Schema definition statement	04	CREATE VIEW: WITH CHECK OPTION	YES	
 F311	Schema definition statement	05	GRANT statement	YES	
-F312	MERGE statement			NO	
+F312	MERGE statement			NO	Consider INSERT ... ON CONFLICT UPDATE
 F313	Enhanced MERGE statement			NO	
 F314	MERGE statement with DELETE branch			NO	
 F321	User authorization			YES	
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 8d2d0af..48e7640 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -103,7 +103,8 @@ static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
 static void ExplainScanTarget(Scan *plan, ExplainState *es);
 static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
-static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es);
+static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es,
+								  List *ancestors);
 static void ExplainMemberNodes(List *plans, PlanState **planstates,
 				   List *ancestors, ExplainState *es);
 static void ExplainSubPlans(List *plans, List *ancestors,
@@ -762,6 +763,9 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 			ExplainPreScanMemberNodes(((ModifyTable *) plan)->plans,
 								  ((ModifyTableState *) planstate)->mt_plans,
 									  rels_used);
+			if (((ModifyTable *) plan)->onConflictPlan)
+				ExplainPreScanNode(((ModifyTableState *) planstate)->onConflict,
+								   rels_used);
 			break;
 		case T_Append:
 			ExplainPreScanMemberNodes(((Append *) plan)->appendplans,
@@ -863,6 +867,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	const char *custom_name = NULL;
 	int			save_indent = es->indent;
 	bool		haschildren;
+	bool		suppresschildren = false;
+	ModifyTable *mtplan;
 
 	switch (nodeTag(plan))
 	{
@@ -871,13 +877,33 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			break;
 		case T_ModifyTable:
 			sname = "ModifyTable";
-			switch (((ModifyTable *) plan)->operation)
+			mtplan = (ModifyTable *) plan;
+			switch (mtplan->operation)
 			{
 				case CMD_INSERT:
 					pname = operation = "Insert";
 					break;
 				case CMD_UPDATE:
-					pname = operation = "Update";
+					if (mtplan->spec == SPEC_NONE)
+					{
+						pname = operation = "Update";
+					}
+					else
+					{
+						Assert(mtplan->spec == SPEC_UPDATE);
+
+						pname = operation = "Conflict Update";
+
+						/*
+						 * Do not display child sequential scan/result node.
+						 * Quals from child will be directly attributed to
+						 * ModifyTable node, since we prefer to avoid
+						 * displaying scan node to users, as it is merely an
+						 * implementation detail; it is never executed in the
+						 * conventional way.
+						 */
+						suppresschildren = true;
+					}
 					break;
 				case CMD_DELETE:
 					pname = operation = "Delete";
@@ -1457,7 +1483,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										   planstate, es);
 			break;
 		case T_ModifyTable:
-			show_modifytable_info((ModifyTableState *) planstate, es);
+			show_modifytable_info((ModifyTableState *) planstate, es,
+								  ancestors);
 			break;
 		case T_Hash:
 			show_hash_info((HashState *) planstate, es);
@@ -1585,7 +1612,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		planstate->subPlan;
 	if (haschildren)
 	{
-		ExplainOpenGroup("Plans", "Plans", false, es);
+		 if (!suppresschildren)
+			ExplainOpenGroup("Plans", "Plans", false, es);
 		/* Pass current PlanState as head of ancestors list for children */
 		ancestors = lcons(planstate, ancestors);
 	}
@@ -1608,9 +1636,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	switch (nodeTag(plan))
 	{
 		case T_ModifyTable:
-			ExplainMemberNodes(((ModifyTable *) plan)->plans,
-							   ((ModifyTableState *) planstate)->mt_plans,
-							   ancestors, es);
+			if (((ModifyTable *) plan)->spec != SPEC_UPDATE)
+				ExplainMemberNodes(((ModifyTable *) plan)->plans,
+								   ((ModifyTableState *) planstate)->mt_plans,
+								   ancestors, es);
+			if (((ModifyTable *) plan)->onConflictPlan)
+				ExplainNode(((ModifyTableState *) planstate)->onConflict,
+							ancestors, "Member", NULL, es);
 			break;
 		case T_Append:
 			ExplainMemberNodes(((Append *) plan)->appendplans,
@@ -1648,7 +1680,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	if (haschildren)
 	{
 		ancestors = list_delete_first(ancestors);
-		ExplainCloseGroup("Plans", "Plans", false, es);
+
+		if (!suppresschildren)
+			ExplainCloseGroup("Plans", "Plans", false, es);
 	}
 
 	/* in text format, undo whatever indentation we added */
@@ -2236,6 +2270,12 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 			if (es->verbose)
 				namespace = get_namespace_name(get_rel_namespace(rte->relid));
 			objecttag = "Relation Name";
+
+			/*
+			 * ON CONFLICT's "TARGET" alias will not appear in output for
+			 * auxiliary ModifyTable as its alias, because target
+			 * resultRelation is shared between parent and auxiliary queries
+			 */
 			break;
 		case T_FunctionScan:
 			{
@@ -2314,7 +2354,8 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
  * Show extra information for a ModifyTable node
  */
 static void
-show_modifytable_info(ModifyTableState *mtstate, ExplainState *es)
+show_modifytable_info(ModifyTableState *mtstate, ExplainState *es,
+					  List *ancestors)
 {
 	FdwRoutine *fdwroutine = mtstate->resultRelInfo->ri_FdwRoutine;
 
@@ -2336,6 +2377,23 @@ show_modifytable_info(ModifyTableState *mtstate, ExplainState *es)
 										 0,
 										 es);
 	}
+	else if (mtstate->spec == SPEC_UPDATE)
+	{
+		PlanState *ps = (*mtstate->mt_plans);
+
+		/*
+		 * Seqscan node is always used, unless optimizer determined that
+		 * predicate precludes ever updating, in which case a simple Result
+		 * node is possible
+		 */
+		Assert(IsA(ps->plan, SeqScan) || IsA(ps->plan, Result));
+
+		/* Attribute child scan node's qual to ModifyTable node */
+		show_scan_qual(ps->plan->qual, "Filter", ps, ancestors, es);
+
+		if (ps->plan->qual)
+			show_instrumentation_count("Rows Removed by Filter", 1, ps, es);
+	}
 }
 
 /*
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 862c312..b5a5c33 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -205,17 +205,19 @@ Speculative insertion
 ---------------------
 
 Speculative insertion is a process that the executor manages for the benefit of
-INSERT...ON CONFLICT IGNORE.  Supported indexes include nbtree unique
+INSERT...ON CONFLICT UPDATE/IGNORE.  Supported indexes include nbtree unique
 indexes (nbtree is currently the only amcanunique index access method), or
 exclusion constraint indexes (exclusion constraints are considered a
-generalization of unique constraints).
-
-The primary user-visible goal for INSERT ... ON CONFLICT is to guarantee either
-an insert, or a conclusive determination that an insert cannot go ahead (due to
-a conclusively committed/visible conflict).  A would-be conflict (and the
-associated index) are the arbiters of whether or not the alternative (IGNORE)
-path is taken.  The implementation more or less tries to insert until one or
-the other of those two outcomes is reached.  There are some non-obvious hazards
+generalization of unique constraints).  Only ON CONFLICT IGNORE is supported
+with exclusion constraints.
+
+The primary user-visible goal for INSERT...ON CONFLICT UPDATE is to guarantee
+either an insert or update under normal operating conditions in READ COMMITTED
+mode (where serialization failures are just as unacceptable as they are with
+regular UPDATEs).  A would-be conflict (and the associated index) are the
+arbiters of whether or not the alternative (UPDATE/IGNORE) path is taken.  The
+implementation more or less tries to update or insert until one or the other of
+those two outcomes occurs successfully.  There are some non-obvious hazards
 involved that are carefully avoided.  These hazards relate to concurrent
 activity causing conflicts for the implementation, which must be handled.
 
@@ -228,12 +230,37 @@ must lock the row, and then verify that there is no conflict.  Only then do we
 UPDATE.  Theoretically, some individual session could loop forever, although
 under high concurrency one session always proceeds.
 
+There are 2 sources of conflicts for ON CONFLICT UPDATE:
+
+1. Conflicts from going to update (having found a conflict during the
+pre-check), and finding the tuple changed (which may or may not involve new,
+distinct constrained values in later tuple versions -- for simplicity, we don't
+bother with considering that).  This is not a conflict that the IGNORE variant
+considers.
+
+2. Conflicts from inserting a tuple (having not found a conflict during the
+pre-check), and only then finding a conflict at insertion time (when inserting
+index tuples, and finding a conflicting one when a buffer lock is held on an
+index page in the ordinary course of insertion).  This can happen if a
+concurrent insertion occurs after the pre-check, but before physical index
+tuple insertion.
+
 The first step in the loop is to perform a pre-check.  The indexes are scanned
 for existing conflicting values.  At this point, we may have to wait until the
 end of another xact (or xact's promise token -- more on that later), iff it
 isn't immediately conclusive that there is or is not a conflict (when we finish
-the pre-check, there is a conclusion about there either being or
-not being a conflict).
+the pre-check, there is a preliminary conclusion about there either being or
+not being a conflict -- but the conclusion only holds if there are no
+subsequent concurrent conflicts).  If a conclusively committed conflict tuple
+is detected during the first step, the executor goes to lock and update the row
+(for ON CONFLICT UPDATE -- otherwise, for ON CONFLICT IGNORE, we're done).  The
+TID to lock (and potentially UPDATE) can only be determined during the first
+step.  If locking the row finds a concurrent conflict (which may be from a
+concurrent UPDATE that hasn't even physically inspected the arbiter index yet)
+then we restart the loop from the very beginning.  We restart from scratch
+because all bets are off;  it's possible that the process will find no conflict
+the second time around, and will successfully insert, or will UPDATE another
+tuple that is not even part of the same UPDATE chain as first time around.
 
 The second step (skipped when a conflict is found) is to insert a heap tuple
 and related index tuples opportunistically.  This uses the same mechanism as
@@ -273,3 +300,31 @@ could be involved in "unprincipled deadlocks":  deadlocks where there is no
 user-visible mutual dependency, and yet an implementation related mutual
 dependency is unexpectedly introduced.  The user might be left with no
 reasonable way of avoiding these deadlocks, which would not be okay.
+
+Speculative insertion and EvalPlanQual()
+----------------------------------------
+
+Updating the tuple involves locking it first (to establish a definitive tuple
+to consider evaluating the additional UPDATE qual against).  The EvalPlanQual()
+mechanism (or, rather, some associated infrastructure) is reused for the
+benefit of auxiliary UPDATE expression evaluation.
+
+Locking first deviates from how conventional UPDATEs work, but allows the
+implementation to consider the possibility of conflicts first, and then, having
+reached a definitive conclusion, separately evaluate.
+
+ExecLockUpdateTuple() is somewhat similar to EvalPlanQual(), except it locks
+the TID reported as conflicting, and upon successfully locking, installs that
+into the UPDATE's EPQ slot.  There is no UPDATE chain to walk -- rather, new
+tuples to check the qual against come from continuous attempts at locking a
+tuple conclusively (avoiding conflicts).  The qual (if any) is then evaluated.
+Note that at READ COMMITTED, it's possible that *no* version of the tuple is
+visible, and yet it may still be updated.  Similarly,  since we do not walk the
+UPDATE chain, concurrent READ COMMITTED INSERT ... ON CONFLICT UPDATE sessions
+always attempt to lock the conclusively visible tuple, without regard to any
+other tuple version (repeatable read isolation level and up must consider MVCC
+visibility, though).  A further implication of this  is that the
+MVCC-snapshot-visible row version is denied the opportunity to prevent the
+UPDATE from taking place, should it not pass our qual (while a later version
+does pass it).  This is fundamentally similar to updating a tuple when no
+version is visible, though.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 5012f87..3649b46 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2122,7 +2122,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * the latest version of the row was deleted, so we need do
 			 * nothing.  (Should be safe to examine xmin without getting
 			 * buffer's content lock, since xmin never changes in an existing
-			 * non-promise tuple.)
+			 * non-promise tuple, and there is no reason to lock a promise
+			 * tuple until it is clear that it has been fulfilled.)
 			 */
 			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
 									 priorXmax))
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d94fe58..6e29cf6 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -181,6 +181,9 @@ static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 						bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 					  bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalExcluded(ExcludedExprState *excludedExpr,
+				 ExprContext *econtext, bool *isNull,
+				 ExprDoneCond *isDone);
 
 
 /* ----------------------------------------------------------------
@@ -4333,6 +4336,33 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 	return 0;					/* keep compiler quiet */
 }
 
+/* ----------------------------------------------------------------
+ * ExecEvalExcluded
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalExcluded(ExcludedExprState *excludedExpr, ExprContext *econtext,
+				 bool *isNull, ExprDoneCond *isDone)
+{
+	/*
+	 * ExcludedExpr is essentially an expression that adapts its single Var
+	 * argument to refer to the expression context inner slot's tuple, which is
+	 * reserved for the purpose of referencing EXCLUDED.* tuples within ON
+	 * CONFLICT UPDATE auxiliary queries' EPQ expression context (ON CONFLICT
+	 * UPDATE makes special use of the EvalPlanQual() mechanism to update).
+	 *
+	 * nodeModifyTable.c assigns its own table slot in the auxiliary queries'
+	 * EPQ expression state (originating in the parent INSERT node) on the
+	 * assumption that it may only be used by ExcludedExpr, and on the
+	 * assumption that the inner slot is not otherwise useful.  This occurs in
+	 * advance of the expression evaluation for UPDATE (which calls here are
+	 * part of) once per slot proposed for insertion, and works because of
+	 * restrictions on the structure of ON CONFLICT UPDATE auxiliary queries.
+	 *
+	 * Just evaluate nested Var.
+	 */
+	return ExecEvalScalarVar(excludedExpr->arg, econtext, isNull, isDone);
+}
 
 /*
  * ExecEvalExprSwitchContext
@@ -5065,6 +5095,30 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			state = (ExprState *) makeNode(ExprState);
 			state->evalfunc = ExecEvalCurrentOfExpr;
 			break;
+		case T_ExcludedExpr:
+			{
+				ExcludedExpr *excludedexpr = (ExcludedExpr *) node;
+				ExcludedExprState *cstate = makeNode(ExcludedExprState);
+				Var		   *contained = (Var *) excludedexpr->arg;
+
+				/*
+				 * varno forced to INNER_VAR -- see remarks within
+				 * ExecLockUpdateTuple().
+				 *
+				 * We rely on the assumption that the only place that
+				 * ExcludedExpr may appear is where EXCLUDED Var references
+				 * originally appeared after parse analysis.  The rewriter
+				 * replaces these with ExcludedExpr that reference the
+				 * corresponding Var within the ON CONFLICT UPDATE target RTE.
+				 */
+				Assert(IsA(contained, Var));
+
+				contained->varno = INNER_VAR;
+				cstate->arg = ExecInitExpr((Expr *) contained, parent);
+				state = (ExprState *) cstate;
+				state->evalfunc = (ExprStateEvalFunc) ExecEvalExcluded;
+			}
+			break;
 		case T_TargetEntry:
 			{
 				TargetEntry *tle = (TargetEntry *) node;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 536536e..6cffe6b 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -54,6 +54,15 @@
 #include "utils/tqual.h"
 
 
+static bool ExecLockUpdateTuple(ResultRelInfo *resultRelInfo,
+					ItemPointer conflictTid,
+					TupleTableSlot *planSlot,
+					TupleTableSlot *insertSlot,
+					ModifyTableState *onConflict,
+					EState *estate,
+					bool canSetTag,
+					TupleTableSlot **returning);
+
 /*
  * Verify that the tuples to be produced by INSERT or UPDATE match the
  * target relation's rowtype
@@ -194,6 +203,7 @@ ExecCheckHeapTupleVisible(EState *estate,
 static TupleTableSlot *
 ExecInsert(TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
+		   ModifyTableState *onConflict,
 		   Oid arbiterIndex,
 		   SpecCmd spec,
 		   EState *estate,
@@ -297,8 +307,9 @@ ExecInsert(TupleTableSlot *slot,
 		 * If we are performing speculative insertion, do a non-conclusive
 		 * check for conflicts.
 		 *
-		 * See the executor README for a full discussion of speculative
-		 * insertion.
+		 * Control returns here when there is 1) A row-locking conflict, or 2)
+		 * an insertion conflict.  See the executor README for a full
+		 * discussion of speculative insertion.
 		 */
 vlock:
 		if (spec != SPEC_NONE && resultRelInfo->ri_NumIndices > 0)
@@ -323,18 +334,35 @@ vlock:
 			if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
 										   arbiterIndex))
 			{
+				TupleTableSlot *returning = NULL;
+
 				/*
-				 * For the SPEC_IGNORE case, it's still often necessary to
-				 * verify that the tuple is visible to the executor's MVCC
-				 * snapshot.
+				 * Lock and consider updating in the SPEC_INSERT case.  For the
+				 * SPEC_IGNORE case, it's still often necessary to verify that
+				 * the tuple is visible to the executor's MVCC snapshot.
 				 */
-				if (spec == SPEC_IGNORE)
+				if (spec == SPEC_INSERT && !ExecLockUpdateTuple(resultRelInfo,
+																&conflictTid,
+																planSlot,
+																slot,
+																onConflict,
+																estate,
+																canSetTag,
+																&returning))
+					goto vlock;
+				else if (spec == SPEC_IGNORE)
 					ExecCheckHeapTupleVisible(estate, resultRelInfo, &conflictTid);
 
 				/*
-				 * The IGNORE path projects no tuples
+				 * RETURNING may have been processed already -- the target
+				 * ResultRelInfo might have made representation within
+				 * ExecUpdate() that this is required.  Inserted and updated
+				 * tuples are projected indifferently for ON CONFLICT UPDATE
+				 * with RETURNING.
+				 *
+				 * Since there was no row conflict, we're done.
 				 */
-				return NULL;
+				return returning;
 			}
 
 			/*
@@ -930,6 +958,240 @@ lreplace:;
 	return NULL;
 }
 
+/* ----------------------------------------------------------------
+ * Try to lock tuple for update as part of speculative insertion.  If
+ * a qual originating from ON CONFLICT UPDATE is satisfied, update
+ * (but still lock row, even though it may not satisfy estate's
+ * snapshot).
+ *
+ * Returns value indicating if we're done (with or without an
+ * update), or if the executor must start from scratch.
+ * ----------------------------------------------------------------
+ */
+static bool
+ExecLockUpdateTuple(ResultRelInfo *resultRelInfo,
+					ItemPointer conflictTid,
+					TupleTableSlot *planSlot,
+					TupleTableSlot *insertSlot,
+					ModifyTableState *onConflict,
+					EState *estate,
+					bool canSetTag,
+					TupleTableSlot **returning)
+{
+	Relation	relation = resultRelInfo->ri_RelationDesc;
+	HeapTupleData tuple;
+	HeapTuple	copyTuple = NULL;
+	HeapUpdateFailureData hufd;
+	HTSU_Result test;
+	Buffer		buffer;
+	TupleTableSlot *slot;
+	ExprContext *econtext;
+
+	/*
+	 * Lock tuple for update.
+	 *
+	 * Like EvalPlanQualFetch(), don't follow updates.  There is no actual
+	 * benefit to doing so, since as discussed below, a conflict invalidates
+	 * our previous conclusion that the tuple is the conclusively committed
+	 * conflicting tuple.
+	 */
+	tuple.t_self = *conflictTid;
+	test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
+						   LockTupleExclusive, LockWaitBlock, false, &buffer,
+						   &hufd);
+
+	if (test == HeapTupleMayBeUpdated)
+		copyTuple = heap_copytuple(&tuple);
+
+	switch (test)
+	{
+		case HeapTupleInvisible:
+
+			/*
+			 * This may occur when an instantaneously invisible tuple is
+			 * blamed as a conflict because multiple rows are inserted with
+			 * the same constrained values.
+			 *
+			 * We cannot proceed, because to do so would leave users open to
+			 * the risk that the same row will be updated a second time in the
+			 * same command;  allowing a second update affecting a single row
+			 * within the same command a second time would leave the update
+			 * order undefined.  It is the user's responsibility to resolve
+			 * these self-duplicates in advance of proposing for insertion a
+			 * set of tuples, but warn them.  These problems are why SQL-2003
+			 * similarly specifies that for SQL MERGE, an exception must be
+			 * raised in the event of an attempt to update the same row twice.
+			 *
+			 * XXX It might be preferable to do something similar when a row
+			 * is locked twice (and not updated twice) by the same speculative
+			 * insertion, as if to take each lock acquisition as a indication
+			 * of a discrete, unfulfilled intent to update (perhaps in some
+			 * later command of the same xact).  This does not seem feasible,
+			 * though.
+			 */
+			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+				ereport(ERROR,
+						(errcode(ERRCODE_CARDINALITY_VIOLATION),
+						 errmsg("ON CONFLICT UPDATE command could not lock/update self-inserted tuple"),
+						 errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
+
+			/* This shouldn't happen */
+			elog(ERROR, "attempted to lock invisible tuple");
+			return false;		/* keep compiler quiet */
+		case HeapTupleSelfUpdated:
+
+			/*
+			 * XXX In practice this is dead code, since BEFORE triggers fire
+			 * prior to speculative insertion.  Since a dirty snapshot is used
+			 * to find possible conflict tuples, speculative insertion could
+			 * not have seen the old/MVCC-current row version at all (even if
+			 * it was only rendered old by this same command).
+			 */
+			elog(ERROR, "unexpected self-updated tuple");
+			return false;		/* keep compiler quiet */
+		case HeapTupleMayBeUpdated:
+
+			/*
+			 * Success -- we're done, as tuple is locked.  Verify that the
+			 * tuple is known to be visible to our snapshot under conventional
+			 * MVCC rules if the current isolation level mandates that.  In
+			 * READ COMMITTED mode, we can lock and update a tuple still in
+			 * progress according to our snapshot, but higher isolation levels
+			 * cannot avail of that, and must actively defend against doing
+			 * so. We might get a serialization failure within ExecUpdate()
+			 * anyway if this step was skipped, but this cannot be relied on,
+			 * for example because the auxiliary WHERE clause happened to not
+			 * be satisfied.
+			 */
+			ExecCheckHeapTupleVisible(estate, resultRelInfo, &tuple.t_data->t_ctid);
+
+			/*
+			 * This loosening of snapshot isolation for the benefit of READ
+			 * COMMITTED speculative insertions is used consistently:
+			 * speculative quals are only tested against already locked
+			 * tuples. It would be rather inconsistent to UPDATE when no tuple
+			 * version is MVCC-visible (which seems inevitable since we must
+			 * *do something* there, and "READ COMMITTED serialization
+			 * failures" are unappealing), while also avoiding updating here
+			 * entirely on the basis of a non-conclusive tuple version (the
+			 * version that happens to be visible to this command's MVCC
+			 * snapshot, or a subsequent non-conclusive version).
+			 *
+			 * In other words:  Only the final, conclusively locked tuple
+			 * (which must have the same value in the relevant constrained
+			 * attribute(s) as the value previously "value locked") matters.
+			 */
+
+			/* must provide our own instrumentation support */
+			if (onConflict->ps.instrument)
+				InstrStartNode(onConflict->ps.instrument);
+
+			/*
+			 * Conceptually, the parent ModifyTable is like a relation scan
+			 * node that uses a dirty snapshot, returning rows which the
+			 * auxiliary plan must operate on (if only to lock all such rows).
+			 * EvalPlanQual() is involved in the evaluation of their UPDATE,
+			 * regardless of whether or not the tuple is visible to the
+			 * command's MVCC Snapshot.
+			 */
+			EvalPlanQualBegin(&onConflict->mt_epqstate, onConflict->ps.state);
+
+			/*
+			 * Save EPQ expression context.  Auxiliary plan's scan node (which
+			 * would have been just initialized by EvalPlanQualBegin() on the
+			 * first time through here per query) cannot fail to provide one.
+			 */
+			econtext = onConflict->mt_epqstate.planstate->ps_ExprContext;
+
+			/*
+			 * UPDATE affects the same ResultRelation as INSERT in the context
+			 * of ON CONFLICT UPDATE, so parent's target rti is used
+			 */
+			EvalPlanQualSetTuple(&onConflict->mt_epqstate,
+								 resultRelInfo->ri_RangeTableIndex, copyTuple);
+
+			/*
+			 * Make available rejected tuple for referencing within UPDATE
+			 * expression (that is, make available a slot with the rejected
+			 * tuple, possibly already modified by BEFORE INSERT row
+			 * triggers).
+			 *
+			 * This is for the benefit of any ExcludedExpr that may appear
+			 * within UPDATE's targetlist or WHERE clause.  The EXCLUDED tuple
+			 * may be referenced as an ExcludedExpr, which exist purely for
+			 * our benefit.  The nested ExcludedExpr's Var will necessarily
+			 * have an INNER_VAR varno on the assumption that the inner slot
+			 * of the EPQ scan plan state's expression context will contain
+			 * the EXCLUDED heaptuple slot (that is, on the assumption that
+			 * during expression evaluation, the ecxt_innertuple will be
+			 * assigned the insertSlot by this codepath, in advance of
+			 * expression evaluation).
+			 *
+			 * See handling of ExcludedExpr within handleRewrite.c and
+			 * execQual.c.
+			 */
+			econtext->ecxt_innertuple = insertSlot;
+
+			slot = EvalPlanQualNext(&onConflict->mt_epqstate);
+
+			if (!TupIsNull(slot))
+				*returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, slot,
+										planSlot, &onConflict->mt_epqstate,
+										onConflict->ps.state, canSetTag);
+
+			ReleaseBuffer(buffer);
+
+			/*
+			 * As when executing an UPDATE's ModifyTable node in the
+			 * conventional manner, reset the per-output-tuple ExprContext
+			 */
+			ResetPerTupleExprContext(onConflict->ps.state);
+
+			/* must provide our own instrumentation support */
+			if (onConflict->ps.instrument)
+				InstrStopNode(onConflict->ps.instrument, *returning ? 1 : 0);
+
+			return true;
+		case HeapTupleUpdated:
+			if (IsolationUsesXactSnapshot())
+				ereport(ERROR,
+						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+						 errmsg("could not serialize access due to concurrent update")));
+
+			/*
+			 * Tell caller to try again from the very start.  We don't use the
+			 * usual EvalPlanQual() looping pattern here, fundamentally
+			 * because we don't have a useful qual to verify the next tuple
+			 * with.  Our "qual" is really any user-supplied qual AND the
+			 * unique constraint "col OP value" implied by a speculative
+			 * insertion conflict.  However, because of the selective
+			 * evaluation of the former "qual" (the interactions with MVCC and
+			 * row locking), this is an over-simplification.
+			 *
+			 * We might devise a means of verifying, by way of binary equality
+			 * in a similar manner to HOT codepaths, if any unique indexed
+			 * columns changed, but this would only serve to ameliorate the
+			 * fundamental problem.  It might well not be good enough, because
+			 * those columns could change too.  It seems unlikely that working
+			 * harder here is worthwhile.
+			 *
+			 * At this point, all bets are off -- it might actually turn out
+			 * to be okay to proceed with insertion instead of locking now
+			 * (the tuple we attempted to lock could have been deleted, for
+			 * example).  On the other hand, it might not be okay, but for an
+			 * entirely different reason, with an entirely separate TID to
+			 * blame and lock.  This TID may not even be part of the same
+			 * update chain.
+			 */
+			ReleaseBuffer(buffer);
+			return false;
+		default:
+			elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
+	}
+
+	return false;
+}
+
 
 /*
  * Process BEFORE EACH STATEMENT triggers
@@ -941,6 +1203,9 @@ fireBSTriggers(ModifyTableState *node)
 	{
 		case CMD_INSERT:
 			ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
+			if (node->spec == SPEC_INSERT)
+				ExecBSUpdateTriggers(node->onConflict->state,
+									 node->resultRelInfo);
 			break;
 		case CMD_UPDATE:
 			ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
@@ -963,6 +1228,9 @@ fireASTriggers(ModifyTableState *node)
 	switch (node->operation)
 	{
 		case CMD_INSERT:
+			if (node->spec == SPEC_INSERT)
+				ExecASUpdateTriggers(node->onConflict->state,
+									 node->resultRelInfo);
 			ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
 			break;
 		case CMD_UPDATE:
@@ -990,6 +1258,7 @@ ExecModifyTable(ModifyTableState *node)
 {
 	EState	   *estate = node->ps.state;
 	CmdType		operation = node->operation;
+	ModifyTableState *onConflict = (ModifyTableState *) node->onConflict;
 	SpecCmd		spec = node->spec;
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
@@ -1161,8 +1430,9 @@ ExecModifyTable(ModifyTableState *node)
 		switch (operation)
 		{
 			case CMD_INSERT:
-				slot = ExecInsert(slot, planSlot, node->arbiterIndex, spec,
-								  estate, node->canSetTag);
+				slot = ExecInsert(slot, planSlot, onConflict,
+								  node->arbiterIndex, spec, estate,
+								  node->canSetTag);
 				break;
 			case CMD_UPDATE:
 				slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
@@ -1210,6 +1480,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 {
 	ModifyTableState *mtstate;
 	CmdType		operation = node->operation;
+	Plan	   *onConflictPlan = node->onConflictPlan;
 	int			nplans = list_length(node->plans);
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
@@ -1278,6 +1549,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			resultRelInfo->ri_IndexRelationDescs == NULL)
 			ExecOpenIndices(resultRelInfo, mtstate->spec != SPEC_NONE);
 
+		/*
+		 * ON CONFLICT UPDATE variant must have unique index to arbitrate on
+		 * taking alternative path
+		 */
+		Assert(node->spec != SPEC_INSERT || node->arbiterIndex != InvalidOid);
+
 		mtstate->arbiterIndex = node->arbiterIndex;
 
 		/* Now init the plan for this result rel */
@@ -1451,7 +1728,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 				break;
 			case CMD_UPDATE:
 			case CMD_DELETE:
-				junk_filter_needed = true;
+				junk_filter_needed = (node->spec == SPEC_NONE);
 				break;
 			default:
 				elog(ERROR, "unknown operation");
@@ -1509,12 +1786,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		}
 		else
 		{
-			if (operation == CMD_INSERT)
+			if (operation == CMD_INSERT || operation == CMD_UPDATE)
 				ExecCheckPlanOutput(mtstate->resultRelInfo->ri_RelationDesc,
 									subplan->targetlist);
 		}
 	}
 
+	/* Initialize auxiliary ModifyTable node, for ON CONFLICT UPDATE */
+	if (onConflictPlan)
+	{
+		Assert(mtstate->spec == SPEC_INSERT);
+
+		/*
+		 * ExecModifyTable() is never called for auxiliary update
+		 * ModifyTableState.  Execution of the auxiliary plan is driven by its
+		 * parent in an ad-hoc fashion.
+		 */
+		mtstate->onConflict = ExecInitNode(onConflictPlan, estate, eflags);
+	}
+
 	/*
 	 * Set up a tuple table slot for use for trigger output tuples. In a plan
 	 * containing multiple ModifyTable nodes, all can share one such slot, so
@@ -1530,9 +1820,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 * ModifyTable node too, but there's no need.)  Note the use of lcons not
 	 * lappend: we need later-initialized ModifyTable nodes to be shut down
 	 * before earlier ones.  This ensures that we don't throw away RETURNING
-	 * rows that need to be seen by a later CTE subplan.
+	 * rows that need to be seen by a later CTE subplan.  Do not append an
+	 * auxiliary ON CONFLICT UPDATE node either, since it must have a parent
+	 * SPEC_INSERT ModifyTable node that it is auxiliary to that directly
+	 * drives execution of what is logically a single unified statement (*that*
+	 * plan will be appended here, though).  If it must project updated rows,
+	 * that will only ever be done through the parent.
 	 */
-	if (!mtstate->canSetTag)
+	if (!mtstate->canSetTag && mtstate->spec != SPEC_UPDATE)
 		estate->es_auxmodifytables = lcons(mtstate,
 										   estate->es_auxmodifytables);
 
@@ -1585,6 +1880,8 @@ ExecEndModifyTable(ModifyTableState *node)
 	 */
 	for (i = 0; i < node->mt_nplans; i++)
 		ExecEndNode(node->mt_plans[i]);
+
+	ExecEndNode(node->onConflict);
 }
 
 void
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4f033a2..34535ff 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -182,6 +182,7 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_NODE_FIELD(plans);
 	COPY_SCALAR_FIELD(spec);
 	COPY_SCALAR_FIELD(arbiterIndex);
+	COPY_NODE_FIELD(onConflictPlan);
 	COPY_NODE_FIELD(withCheckOptionLists);
 	COPY_NODE_FIELD(returningLists);
 	COPY_NODE_FIELD(fdwPrivLists);
@@ -1782,6 +1783,19 @@ _copyCurrentOfExpr(const CurrentOfExpr *from)
 }
 
 /*
+ * _copyExcludedExpr
+ */
+static ExcludedExpr *
+_copyExcludedExpr(const ExcludedExpr *from)
+{
+	ExcludedExpr *newnode = makeNode(ExcludedExpr);
+
+	COPY_NODE_FIELD(arg);
+
+	return newnode;
+}
+
+/*
  * _copyTargetEntry
  */
 static TargetEntry *
@@ -2145,6 +2159,7 @@ _copyConflictClause(const ConflictClause *from)
 
 	COPY_SCALAR_FIELD(specclause);
 	COPY_NODE_FIELD(infer);
+	COPY_NODE_FIELD(updatequery);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
@@ -2558,6 +2573,7 @@ _copyQuery(const Query *from)
 	COPY_SCALAR_FIELD(specClause);
 	COPY_NODE_FIELD(arbiterExpr);
 	COPY_NODE_FIELD(arbiterWhere);
+	COPY_NODE_FIELD(onConflict);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(groupClause);
 	COPY_NODE_FIELD(havingQual);
@@ -4289,6 +4305,9 @@ copyObject(const void *from)
 		case T_CurrentOfExpr:
 			retval = _copyCurrentOfExpr(from);
 			break;
+		case T_ExcludedExpr:
+			retval = _copyExcludedExpr(from);
+			break;
 		case T_TargetEntry:
 			retval = _copyTargetEntry(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b99eab..fd3911e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -683,6 +683,14 @@ _equalCurrentOfExpr(const CurrentOfExpr *a, const CurrentOfExpr *b)
 }
 
 static bool
+_equalExcludedExpr(const ExcludedExpr *a, const ExcludedExpr *b)
+{
+	COMPARE_NODE_FIELD(arg);
+
+	return true;
+}
+
+static bool
 _equalTargetEntry(const TargetEntry *a, const TargetEntry *b)
 {
 	COMPARE_NODE_FIELD(expr);
@@ -868,6 +876,7 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_SCALAR_FIELD(specClause);
 	COMPARE_NODE_FIELD(arbiterExpr);
 	COMPARE_NODE_FIELD(arbiterWhere);
+	COMPARE_NODE_FIELD(onConflict);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(groupClause);
 	COMPARE_NODE_FIELD(havingQual);
@@ -2447,6 +2456,7 @@ _equalConflictClause(const ConflictClause *a, const ConflictClause *b)
 {
 	COMPARE_SCALAR_FIELD(specclause);
 	COMPARE_NODE_FIELD(infer);
+	COMPARE_NODE_FIELD(updatequery);
 	COMPARE_LOCATION_FIELD(location);
 
 	return true;
@@ -2721,6 +2731,9 @@ equal(const void *a, const void *b)
 		case T_CurrentOfExpr:
 			retval = _equalCurrentOfExpr(a, b);
 			break;
+		case T_ExcludedExpr:
+			retval = _equalExcludedExpr(a, b);
+			break;
 		case T_TargetEntry:
 			retval = _equalTargetEntry(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 44efc95..c6e811a 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -235,6 +235,13 @@ exprType(const Node *expr)
 		case T_CurrentOfExpr:
 			type = BOOLOID;
 			break;
+		case T_ExcludedExpr:
+			{
+				const ExcludedExpr *n = (const ExcludedExpr *) expr;
+
+				type = exprType((Node *) n->arg);
+			}
+			break;
 		case T_PlaceHolderVar:
 			type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
@@ -469,6 +476,12 @@ exprTypmod(const Node *expr)
 			return ((const CoerceToDomainValue *) expr)->typeMod;
 		case T_SetToDefault:
 			return ((const SetToDefault *) expr)->typeMod;
+		case T_ExcludedExpr:
+			{
+				const ExcludedExpr *n = (const ExcludedExpr *) expr;
+
+				return ((const Var *) n->arg)->vartypmod;
+			}
 		case T_PlaceHolderVar:
 			return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 		default:
@@ -894,6 +907,9 @@ exprCollation(const Node *expr)
 		case T_CurrentOfExpr:
 			coll = InvalidOid;	/* result is always boolean */
 			break;
+		case T_ExcludedExpr:
+			coll = exprCollation((Node *) ((const ExcludedExpr *) expr)->arg);
+			break;
 		case T_PlaceHolderVar:
 			coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
@@ -1089,6 +1105,12 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_CurrentOfExpr:
 			Assert(!OidIsValid(collation));		/* result is always boolean */
 			break;
+		case T_ExcludedExpr:
+			{
+				Var *v = (Var *) ((ExcludedExpr *) expr)->arg;
+				v->varcollid = collation;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			break;
@@ -1497,6 +1519,10 @@ exprLocation(const Node *expr)
 			/* just use argument's location */
 			loc = exprLocation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_ExcludedExpr:
+			/* just use nested expr's location */
+			loc = exprLocation((Node *) ((const ExcludedExpr *) expr)->arg);
+			break;
 		default:
 			/* for any other node type it's just unknown... */
 			loc = -1;
@@ -1926,6 +1952,8 @@ expression_tree_walker(Node *node,
 			break;
 		case T_PlaceHolderVar:
 			return walker(((PlaceHolderVar *) node)->phexpr, context);
+		case T_ExcludedExpr:
+			return walker(((ExcludedExpr *) node)->arg, context);
 		case T_AppendRelInfo:
 			{
 				AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -1978,6 +2006,8 @@ query_tree_walker(Query *query,
 		return true;
 	if (walker(query->arbiterWhere, context))
 		return true;
+	if (walker(query->onConflict, context))
+		return true;
 	if (walker((Node *) query->returningList, context))
 		return true;
 	if (walker((Node *) query->jointree, context))
@@ -2640,6 +2670,16 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_ExcludedExpr:
+			{
+				ExcludedExpr *excludedexpr = (ExcludedExpr *) node;
+				ExcludedExpr *newnode;
+
+				FLATCOPY(newnode, excludedexpr, ExcludedExpr);
+				MUTATE(newnode->arg, newnode->arg, Node *);
+				return (Node *) newnode;
+			}
+			break;
 		case T_AppendRelInfo:
 			{
 				AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -2721,6 +2761,7 @@ query_tree_mutator(Query *query,
 	MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
 	MUTATE(query->arbiterExpr, query->arbiterExpr, List *);
 	MUTATE(query->arbiterWhere, query->arbiterWhere, Node *);
+	MUTATE(query->onConflict, query->onConflict, Node *);
 	MUTATE(query->returningList, query->returningList, List *);
 	MUTATE(query->jointree, query->jointree, FromExpr *);
 	MUTATE(query->setOperations, query->setOperations, Node *);
@@ -3247,6 +3288,8 @@ raw_expression_tree_walker(Node *node,
 
 				if (walker(stmt->infer, context))
 					return true;
+				if (walker(stmt->updatequery, context))
+					return true;
 			}
 		case T_CommonTableExpr:
 			return walker(((CommonTableExpr *) node)->ctequery, context);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index fa41ce1..442c547 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -334,6 +334,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_NODE_FIELD(plans);
 	WRITE_ENUM_FIELD(spec, SpecType);
 	WRITE_OID_FIELD(arbiterIndex);
+	WRITE_NODE_FIELD(onConflictPlan);
 	WRITE_NODE_FIELD(withCheckOptionLists);
 	WRITE_NODE_FIELD(returningLists);
 	WRITE_NODE_FIELD(fdwPrivLists);
@@ -1432,6 +1433,14 @@ _outCurrentOfExpr(StringInfo str, const CurrentOfExpr *node)
 }
 
 static void
+_outExcludedExpr(StringInfo str, const ExcludedExpr *node)
+{
+	WRITE_NODE_TYPE("EXCLUDED");
+
+	WRITE_NODE_FIELD(arg);
+}
+
+static void
 _outTargetEntry(StringInfo str, const TargetEntry *node)
 {
 	WRITE_NODE_TYPE("TARGETENTRY");
@@ -2313,6 +2322,7 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_ENUM_FIELD(specClause, SpecType);
 	WRITE_NODE_FIELD(arbiterExpr);
 	WRITE_NODE_FIELD(arbiterWhere);
+	WRITE_NODE_FIELD(onConflict);
 	WRITE_NODE_FIELD(returningList);
 	WRITE_NODE_FIELD(groupClause);
 	WRITE_NODE_FIELD(havingQual);
@@ -3102,6 +3112,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_CurrentOfExpr:
 				_outCurrentOfExpr(str, obj);
 				break;
+			case T_ExcludedExpr:
+				_outExcludedExpr(str, obj);
+				break;
 			case T_TargetEntry:
 				_outTargetEntry(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fc31573..319db56 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -217,6 +217,7 @@ _readQuery(void)
 	READ_ENUM_FIELD(specClause, SpecCmd);
 	READ_NODE_FIELD(arbiterExpr);
 	READ_NODE_FIELD(arbiterWhere);
+	READ_NODE_FIELD(onConflict);
 	READ_NODE_FIELD(returningList);
 	READ_NODE_FIELD(groupClause);
 	READ_NODE_FIELD(havingQual);
@@ -1133,6 +1134,19 @@ _readCurrentOfExpr(void)
 }
 
 /*
+ * _readExcludedExpr
+ */
+static ExcludedExpr *
+_readExcludedExpr(void)
+{
+	READ_LOCALS(ExcludedExpr);
+
+	READ_NODE_FIELD(arg);
+
+	READ_DONE();
+}
+
+/*
  * _readTargetEntry
  */
 static TargetEntry *
@@ -1397,6 +1411,8 @@ parseNodeString(void)
 		return_value = _readSetToDefault();
 	else if (MATCH("CURRENTOFEXPR", 13))
 		return_value = _readCurrentOfExpr();
+	else if (MATCH("EXCLUDED", 8))
+		return_value = _readExcludedExpr();
 	else if (MATCH("TARGETENTRY", 11))
 		return_value = _readTargetEntry();
 	else if (MATCH("RANGETBLREF", 11))
diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c
index 1258961..263ff5f 100644
--- a/src/backend/optimizer/path/tidpath.c
+++ b/src/backend/optimizer/path/tidpath.c
@@ -255,13 +255,17 @@ create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
 	/*
 	 * We don't support pushing join clauses into the quals of a tidscan, but
 	 * it could still have required parameterization due to LATERAL refs in
-	 * its tlist.
+	 * its tlist.  To be tidy, we disallow TID scans as the unexecuted scan
+	 * node of an ON CONFLICT UPDATE auxiliary query, even though there is no
+	 * reason to think that would be harmful;  the optimizer should always
+	 * prefer a SeqScan or Result node (actually, we assert that it's one of
+	 * those two in several places, so accepting TID scans would break those).
 	 */
 	required_outer = rel->lateral_relids;
 
 	tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
 
-	if (tidquals)
+	if (tidquals && root->parse->specClause != SPEC_UPDATE)
 		add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
 												   required_outer));
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 47fe29c..3973c37 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4815,7 +4815,8 @@ make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, SpecCmd spec, int epqParam)
+				 List *rowMarks, Plan *onConflictPlan, SpecCmd spec,
+				 int epqParam)
 {
 	ModifyTable *node = makeNode(ModifyTable);
 	Plan	   *plan = &node->plan;
@@ -4867,6 +4868,7 @@ make_modifytable(PlannerInfo *root,
 	node->plans = subplans;
 	node->spec = spec;
 	node->arbiterIndex = InvalidOid;
+	node->onConflictPlan = onConflictPlan;
 	node->withCheckOptionLists = withCheckOptionLists;
 	node->returningLists = returningLists;
 	node->rowMarks = rowMarks;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b50cfe9..4a6d33f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -613,8 +613,58 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 											 withCheckOptionLists,
 											 returningLists,
 											 rowMarks,
+											 NULL,
 											 parse->specClause,
 											 SS_assign_special_param(root));
+
+			if (parse->onConflict)
+			{
+				Query	   *conflictQry = (Query *) parse->onConflict;
+				ModifyTable *parent = (ModifyTable *) plan;
+
+				/*
+				 * An ON CONFLICT UPDATE query is a subquery of its parent
+				 * INSERT ModifyTable, but isn't formally a subplan -- it's an
+				 * "auxiliary" plan.
+				 *
+				 * During execution, the auxiliary plan state is used to
+				 * execute the UPDATE query in an ad-hoc manner, driven by the
+				 * parent.  The executor will only ever execute the auxiliary
+				 * plan through its parent.  onConflictPlan is "auxiliary" to
+				 * its parent in the sense that it's strictly encapsulated
+				 * from other code (for example, the executor does not
+				 * separately track it within estate as a plan that needs to
+				 * have execution finished when it appears within a
+				 * data-modifying CTE -- only the parent is specifically
+				 * tracked for that purpose).
+				 *
+				 * There is a fundamental nexus between parent and auxiliary
+				 * plans that makes a fully unified representation seem
+				 * compelling (a "CMD_UPSERT" ModifyTable plan and Query).
+				 * That would obviate the need to specially track auxiliary
+				 * state across all stages of execution just for this case;
+				 * the optimizer would then not have to generate a
+				 * fully-formed, independent UPDATE subquery plan (with a
+				 * scanstate only useful for EvalPlanQual() re-evaluation).
+				 * However, it's convenient to plan each ModifyTable
+				 * separately, as doing so maximizes code reuse.  The
+				 * alternative must be to introduce abstractions that (for
+				 * example) allow a single "CMD_UPSERT" ModifyTable to have
+				 * two distinct types of targetlist (that will need to be
+				 * processed differently during parsing and rewriting anyway).
+				 * The auxiliary UPDATE plan is a good trade-off between a
+				 * fully-fledged "CMD_UPSERT" representation, and the opposite
+				 * extreme of tracking two separate ModifyTable nodes, joined
+				 * by a contrived join type, with (for example) odd properties
+				 * around tuple visibility not well encapsulated.  A contrived
+				 * join based design would also necessitate teaching
+				 * ModifyTable nodes to support rescan just for the benefit of
+				 * ON CONFLICT UPDATE.
+				 */
+				parent->onConflictPlan = subquery_planner(glob, conflictQry,
+														  root, hasRecursion,
+														  0, NULL);
+			}
 		}
 	}
 
@@ -1074,6 +1124,7 @@ inheritance_planner(PlannerInfo *root)
 									 withCheckOptionLists,
 									 returningLists,
 									 rowMarks,
+									 NULL,
 									 parse->specClause,
 									 SS_assign_special_param(root));
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 5dd48f8..5e979a1 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -780,9 +780,35 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 				 * global list.
 				 */
 				splan->resultRelIndex = list_length(root->glob->resultRelations);
-				root->glob->resultRelations =
-					list_concat(root->glob->resultRelations,
-								list_copy(splan->resultRelations));
+
+				if (!splan->onConflictPlan)
+				{
+					/*
+					 * Only actually append result relation for non-auxiliary
+					 * ModifyTable plans
+					 */
+					root->glob->resultRelations =
+						list_concat(root->glob->resultRelations,
+									list_copy(splan->resultRelations));
+				}
+				else
+				{
+					/*
+					 * Adjust rtoffset passed to child, to compensate for
+					 * dummy RTE left by EXCLUDED.* alias in auxiliary plan.
+					 * Plan will have same resultRelation from flattened range
+					 * table as its parent.
+					 */
+					splan->onConflictPlan =
+						set_plan_refs(root, splan->onConflictPlan,
+									  rtoffset - PRS2_OLD_VARNO);
+
+					/*
+					 * Set up the visible plan targetlist as being the same as
+					 * the parent.  Again, this is for the use of EXPLAIN only.
+					 */
+					splan->onConflictPlan->targetlist = splan->plan.targetlist;
+				}
 			}
 			break;
 		case T_Append:
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 5a1d539..d0d7467 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2345,6 +2345,12 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 													  valid_params,
 													  scan_params));
 				}
+
+				/*
+				 * No need to directly handle onConflictPlan here, since it
+				 * cannot have params (due to parse analysis enforced
+				 * restrictions prohibiting subqueries).
+				 */
 			}
 			break;
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index c6650ae..4e6a316 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -126,10 +126,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 	/*
 	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
-	 * Don't bother with indexes for an inheritance parent, either.
+	 * Don't bother with indexes for an inheritance parent or speculative
+	 * insertion UPDATE auxiliary queries, either.
 	 */
 	if (inhparent ||
-		(IgnoreSystemIndexes && IsSystemRelation(relation)))
+		(IgnoreSystemIndexes && IsSystemRelation(relation)) ||
+		root->parse->specClause == SPEC_UPDATE)
 		hasindex = false;
 	else
 		hasindex = relation->rd_rel->relhasindex;
@@ -437,7 +439,8 @@ infer_unique_index(PlannerInfo *root)
 	ListCell   *l;
 	List	   *indexList;
 
-	Assert(parse->specClause == SPEC_IGNORE);
+	Assert(parse->specClause == SPEC_INSERT ||
+		   parse->specClause == SPEC_IGNORE);
 
 	/*
 	 * We need not lock the relation since it was already locked, either by
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 168f970..0e17b03 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -388,6 +388,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 	qry->specClause = SPEC_NONE;
+	qry->onConflict = NULL;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -427,6 +428,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
 	qry->commandType = CMD_INSERT;
 	pstate->p_is_insert = true;
+	pstate->p_is_speculative = spec != SPEC_NONE;
 
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
@@ -474,11 +476,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 		sub_namespace = NIL;
 	}
 
+	/* INSERT with an ON CONFLICT clause forces the "target" alias */
+	if (pstate->p_is_speculative)
+		stmt->relation->alias = makeAlias("target", NIL);
+
 	/*
 	 * Must get write lock on INSERT target table before scanning SELECT, else
 	 * we will grab the wrong kind of initial lock if the target table is also
 	 * mentioned in the SELECT part.  Note that the target table is not added
-	 * to the joinlist or namespace.
+	 * to the joinlist or namespace.  Note also that additional requiredPerms
+	 * may be added to the target RTE iff there is an auxiliary UPDATE.
 	 */
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
 										 false, false, ACL_INSERT);
@@ -763,10 +770,39 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 	qry->specClause = spec;
 	qry->hasSubLinks = pstate->p_hasSubLinks;
+	qry->onConflict = NULL;
 
 	if (stmt->confClause)
 	{
 		/*
+		 * ON CONFLICT UPDATE requires special parse analysis of auxiliary
+		 * update Query
+		 */
+		if (stmt->confClause->updatequery)
+		{
+			ParseState *sub_pstate = make_parsestate(pstate);
+			Query	   *uqry;
+
+			/*
+			 * The optimizer is not prepared to accept a subquery RTE for a
+			 * non-CMD_SELECT Query.  The CMD_UPDATE Query is tracked as
+			 * special auxiliary state, while there is more or less analogous
+			 * auxiliary state tracked in later stages of query execution.
+			 *
+			 * Parent canSetTag only ever actually consulted, so no need to
+			 * set that here.
+			 */
+			uqry = transformStmt(sub_pstate, stmt->confClause->updatequery);
+			Assert(uqry->commandType == CMD_UPDATE &&
+				   uqry->specClause == SPEC_UPDATE);
+
+			/* Save auxiliary query */
+			qry->onConflict = (Node *) uqry;
+
+			free_parsestate(sub_pstate);
+		}
+
+		/*
 		 * Perform parse analysis of arbiter columns/expressions.  These are
 		 * later used to infer a unique index which arbitrates whether or not
 		 * to take the alternative ON CONFLICT path (i.e.  whether or not to
@@ -1023,6 +1059,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 	qry->specClause = SPEC_NONE;
+	qry->onConflict = NULL;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -1920,10 +1957,14 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	Node	   *qual;
 	ListCell   *origTargetList;
 	ListCell   *tl;
+	bool InhOption;
 
 	qry->commandType = CMD_UPDATE;
 	pstate->p_is_update = true;
 
+	/* for auxiliary UPDATEs, visit parent INSERT to set target table */
+	pstate->p_is_speculative = (stmt->relation == NULL);
+
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
@@ -1932,8 +1973,22 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
 
+	if (!pstate->p_is_speculative)
+	{
+		InhOption = interpretInhOption(stmt->relation->inhOpt);
+
+		qry->specClause = SPEC_NONE;
+	}
+	else
+	{
+		/* auxiliary UPDATE does not accept ONLY */
+		InhOption = false;
+
+		qry->specClause = SPEC_UPDATE;
+	}
+
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
-								  interpretInhOption(stmt->relation->inhOpt),
+										 InhOption,
 										 true,
 										 ACL_UPDATE);
 
@@ -1964,6 +2019,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+	qry->onConflict = NULL;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a76e038..5220fef 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -517,6 +517,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>	cte_list
 
 %type <list>	within_group_clause
+%type <node>	UpdateInsertStmt
 %type <node>	filter_clause
 %type <list>	window_clause window_definition_list opt_partition_clause
 %type <windef>	window_definition over_clause window_specification
@@ -9464,11 +9465,21 @@ insert_column_item:
 		;
 
 opt_on_conflict:
+			ON CONFLICT opt_conf_expr UpdateInsertStmt
+				{
+					$$ = makeNode(ConflictClause);
+					$$->specclause = SPEC_INSERT;
+					$$->infer = $3;
+					$$->updatequery = $4;
+					$$->location = @1;
+				}
+			|
 			ON CONFLICT opt_conf_expr IGNORE_P
 				{
 					$$ = makeNode(ConflictClause);
 					$$->specclause = SPEC_IGNORE;
 					$$->infer = $3;
+					$$->updatequery = NULL;
 					$$->location = @1;
 				}
 			| /*EMPTY*/
@@ -9590,6 +9601,22 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
 				}
 		;
 
+UpdateInsertStmt: UPDATE
+			SET set_clause_list
+			where_clause
+				{
+					UpdateStmt *n = makeNode(UpdateStmt);
+					/* NULL relation conveys auxiliary */
+					n->relation = NULL;
+					n->targetList = $3;
+					n->fromClause = NULL;
+					n->whereClause = $4;
+					n->returningList = NULL;
+					n->withClause = NULL;
+					$$ = (Node *)n;
+				}
+		;
+
 set_clause_list:
 			set_clause							{ $$ = $1; }
 			| set_clause_list ',' set_clause	{ $$ = list_concat($1,$3); }
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 029288b..0dca575 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -148,7 +148,9 @@ transformFromClause(ParseState *pstate, List *frmList)
  *	  We also open the target relation and acquire a write lock on it.
  *	  This must be done before processing the FROM list, in case the target
  *	  is also mentioned as a source relation --- we want to be sure to grab
- *	  the write lock before any read lock.
+ *	  the write lock before any read lock.  Note that when called during
+ *	  the parse analysis of an auxiliary UPDATE query, relation may be
+ *	  NULL, and the details are acquired from the parent.
  *
  *	  If alsoSource is true, add the target to the query's joinlist and
  *	  namespace.  For INSERT, we don't want the target to be joined to;
@@ -175,19 +177,79 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 
 	/*
 	 * Open target rel and grab suitable lock (which we will hold till end of
-	 * transaction).
+	 * transaction), iff this is not an auxiliary ON CONFLICT UPDATE.
 	 *
 	 * free_parsestate() will eventually do the corresponding heap_close(),
-	 * but *not* release the lock.
+	 * but *not* release the lock (again, iff this is not an auxiliary ON
+	 * CONFLICT UPDATE).
 	 */
-	pstate->p_target_relation = parserOpenTable(pstate, relation,
-												RowExclusiveLock);
+	if (!pstate->p_is_speculative || pstate->p_is_insert)
+	{
+		pstate->p_target_relation = parserOpenTable(pstate, relation,
+													RowExclusiveLock);
+
+		/*
+		 * Now build an RTE.
+		 */
+		rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
+											relation->alias, inh, false);
+
+		/*
+		 * Override addRangeTableEntry's default ACL_SELECT permissions check,
+		 * and instead mark target table as requiring exactly the specified
+		 * permissions.
+		 *
+		 * If we find an explicit reference to the rel later during parse
+		 * analysis, we will add the ACL_SELECT bit back again; see
+		 * markVarForSelectPriv and its callers.
+		 */
+		rte->requiredPerms = requiredPerms;
+	}
+	else
+	{
+		RangeTblEntry *exclRte;
+
+		/* auxilary UPDATE (of ON CONFLICT UPDATE) */
+		Assert(pstate->p_is_update);
+		/* target shared with parent */
+		pstate->p_target_relation =
+			pstate->parentParseState->p_target_relation;
+		rte = pstate->parentParseState->p_target_rangetblentry;
+
+		/*
+		 * When called for auxiliary UPDATE, same target RTE is processed here
+		 * for a second time.  Just append requiredPerms.  There is no need to
+		 * override addRangeTableEntry's default ACL_SELECT permissions check
+		 * now.
+		 */
+		rte->requiredPerms |= requiredPerms;
+
+		/*
+		 * Build EXCLUDED alias for target relation.  This can be used to
+		 * reference the tuple originally proposed for insertion from within
+		 * the ON CONFLICT UPDATE auxiliary query.	This is not visible in the
+		 * parent INSERT.
+		 *
+		 * NOTE: 'EXCLUDED' will always have a varno equal to 1 (at least
+		 * until rewriting, where the RTE is effectively discarded -- its Vars
+		 * are replaced with a special-purpose primnode, ExcludedExpr).
+		 */
+		exclRte = addRangeTableEntryForRelation(pstate,
+												pstate->p_target_relation,
+												makeAlias("excluded", NIL),
+												false, false);
+
+		/*
+		 * Add EXCLUDED RTE to namespace.  It does not matter that the RTE is
+		 * not added to the Query joinlist, since its Vars are merely
+		 * placeholders for ExcludedExpr.
+		 */
+		addRTEtoQuery(pstate, exclRte, false, true, true);
+
+		/* Append parent/our target to Query rtable (should be last) */
+		pstate->p_rtable = lappend(pstate->p_rtable, rte);
+	}
 
-	/*
-	 * Now build an RTE.
-	 */
-	rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
-										relation->alias, inh, false);
 	pstate->p_target_rangetblentry = rte;
 
 	/* assume new rte is at end */
@@ -195,17 +257,6 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 
 	/*
-	 * Override addRangeTableEntry's default ACL_SELECT permissions check, and
-	 * instead mark target table as requiring exactly the specified
-	 * permissions.
-	 *
-	 * If we find an explicit reference to the rel later during parse
-	 * analysis, we will add the ACL_SELECT bit back again; see
-	 * markVarForSelectPriv and its callers.
-	 */
-	rte->requiredPerms = requiredPerms;
-
-	/*
 	 * If UPDATE/DELETE, add table to joinlist and namespace.
 	 *
 	 * Note: some callers know that they can find the new ParseNamespaceItem
@@ -2290,6 +2341,16 @@ transformConflictClause(ParseState *pstate, ConflictClause *confClause,
 {
 	InferClause *infer = confClause->infer;
 
+	if (confClause->specclause == SPEC_INSERT && !infer)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("ON CONFLICT with UPDATE must contain columns or expressions to infer a unique index from"),
+				 parser_errposition(pstate,
+									exprLocation((Node *) confClause))));
+
+	Assert(confClause->specclause != SPEC_INSERT ||
+		   confClause->updatequery != NULL);
+
 	/* This obviates the need for historic snapshot support */
 	if (IsCatalogRelation(pstate->p_target_relation))
 		elog(ERROR, "ON CONFLICT not supported with catalog relations");
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 7829bcb..d66c50c 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1520,7 +1520,8 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 	/*
 	 * Check to see if the sublink is in an invalid place within the query. We
 	 * allow sublinks everywhere in SELECT/INSERT/UPDATE/DELETE, but generally
-	 * not in utility statements.
+	 * not in utility statements.  They're also disallowed within auxiliary ON
+	 * CONFLICT UPDATE commands, which we check for here.
 	 */
 	err = NULL;
 	switch (pstate->p_expr_kind)
@@ -1587,6 +1588,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 			 * which is sane anyway.
 			 */
 	}
+
+	if (pstate->p_is_speculative && pstate->p_is_update)
+		 err = _("cannot use subquery in ON CONFLICT UPDATE");
 	if (err)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 4130cbf..9a94fa4 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -84,7 +84,13 @@ free_parsestate(ParseState *pstate)
 				 errmsg("target lists can have at most %d entries",
 						MaxTupleAttributeNumber)));
 
-	if (pstate->p_target_relation != NULL)
+	/*
+	 * Don't close target relation for auxiliary ON CONFLICT UPDATE, since it
+	 * is managed by parent INSERT directly
+	 */
+	if (pstate->p_target_relation != NULL &&
+		(!pstate->p_is_speculative ||
+		 pstate->p_is_insert))
 		heap_close(pstate->p_target_relation, NoLock);
 
 	pfree(pstate);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 86f1fc9..8ef80d6 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -43,6 +43,12 @@ typedef struct acquireLocksOnSubLinks_context
 	bool		for_execute;	/* AcquireRewriteLocks' forExecute param */
 } acquireLocksOnSubLinks_context;
 
+typedef struct excluded_replace_context
+{
+	int			varno;			/* varno of EXLCUDED.* Vars */
+	int			rvarno;			/* replace varno */
+}	excluded_replace_context;
+
 static bool acquireLocksOnSubLinks(Node *node,
 					   acquireLocksOnSubLinks_context *context);
 static Query *rewriteRuleAction(Query *parsetree,
@@ -71,6 +77,10 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
 			 bool forUpdatePushedDown);
 static bool view_has_instead_trigger(Relation view, CmdType event);
 static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
+static Node *excluded_replace_vars(Node *expr,
+					  excluded_replace_context * context);
+static Node *excluded_replace_vars_callback(Var *var,
+							   replace_rte_variables_context *context);
 
 
 /*
@@ -3100,6 +3110,50 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 				/* Process just the main targetlist */
 				rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
 			}
+
+			if (parsetree->specClause == SPEC_INSERT)
+			{
+				Query	   *qry;
+				excluded_replace_context context;
+
+				/*
+				 * While user-defined rules will never be applied in the
+				 * auxiliary update query, normalization of tlist is still
+				 * required
+				 */
+				qry = (Query *) parsetree->onConflict;
+				rewriteTargetListIU(qry, rt_entry_relation, NULL);
+
+				/*
+				 * Replace OLD Vars (associated with the EXCLUDED.* alias)
+				 * with first (and only) "real" relation RTE in rtable.  This
+				 * allows the implementation to treat EXCLUDED.* as an alias
+				 * for the target relation, which is useful during parse
+				 * analysis, while ultimately having those references
+				 * rewritten as special ExcludedExpr references to the
+				 * corresponding Var in the target RTE.
+				 *
+				 * This is necessary because while we want a join-like syntax
+				 * for aesthetic reasons, the resemblance is superficial.  In
+				 * fact, execution of the ModifyTable node (and its direct
+				 * child auxiliary query) manages tupleslot state directly,
+				 * and is directly tasked with making available the
+				 * appropriate tupleslot to the expression context.
+				 *
+				 * This is a kludge, but appears necessary, since the slot
+				 * made available for referencing via ExcludedExpr is in fact
+				 * the slot just excluded from insertion by speculative
+				 * insertion (with the effects of BEFORE ROW INSERT triggers
+				 * carried). An ad-hoc method for making the excluded tuple
+				 * available within the auxiliary expression context is
+				 * appropriate.
+				 */
+				context.varno = PRS2_OLD_VARNO;
+				context.rvarno = PRS2_OLD_VARNO + 1;
+
+				parsetree->onConflict =
+					excluded_replace_vars(parsetree->onConflict, &context);
+			}
 		}
 		else if (event == CMD_UPDATE)
 		{
@@ -3421,3 +3475,52 @@ QueryRewrite(Query *parsetree)
 
 	return results;
 }
+
+/*
+ * Apply pullup variable replacement throughout an expression tree
+ *
+ * Returns modified tree, with user-specified rvarno replaced with varno.
+ */
+static Node *
+excluded_replace_vars(Node *expr, excluded_replace_context *context)
+{
+	/*
+	 * Don't recurse into subqueries;  they're forbidden in auxiliary ON
+	 * CONFLICT query
+	 */
+	return replace_rte_variables(expr,
+								 context->varno, 0,
+								 excluded_replace_vars_callback,
+								 (void *) context,
+								 NULL);
+}
+
+static Node *
+excluded_replace_vars_callback(Var *var,
+							   replace_rte_variables_context *context)
+{
+	excluded_replace_context *rcon = (excluded_replace_context *) context->callback_arg;
+	ExcludedExpr *n = makeNode(ExcludedExpr);
+
+	/* Replace with an enclosing ExcludedExpr */
+	var->varno = rcon->rvarno;
+	n->arg = (Node *) var;
+
+	/*
+	 * Would have to adjust varlevelsup if referenced item is from higher
+	 * query (should not happen)
+	 */
+	Assert(var->varlevelsup == 0);
+
+	if (var->varattno < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("cannot reference system column using EXCLUDED.* alias")));
+
+	if (var->varattno == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("cannot reference whole-row using EXCLUDED.* alias")));
+
+	return (Node *) n;
+}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 9c14e8a..41c4191 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -189,7 +189,8 @@ ProcessQuery(PlannedStmt *plan,
 	 */
 	if (completionTag)
 	{
-		Oid			lastOid;
+		Oid					lastOid;
+		ModifyTableState   *pstate;
 
 		switch (queryDesc->operation)
 		{
@@ -198,12 +199,16 @@ ProcessQuery(PlannedStmt *plan,
 						 "SELECT %u", queryDesc->estate->es_processed);
 				break;
 			case CMD_INSERT:
+				pstate = (((ModifyTableState *) queryDesc->planstate));
+				Assert(IsA(pstate, ModifyTableState));
+
 				if (queryDesc->estate->es_processed == 1)
 					lastOid = queryDesc->estate->es_lastoid;
 				else
 					lastOid = InvalidOid;
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-				   "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
+				   "%s %u %u", pstate->spec == SPEC_INSERT? "UPSERT":"INSERT",
+				   lastOid, queryDesc->estate->es_processed);
 				break;
 			case CMD_UPDATE:
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
@@ -1356,7 +1361,10 @@ PortalRunMulti(Portal portal, bool isTopLevel,
 	 * 0" here because technically there is no query of the matching tag type,
 	 * and printing a non-zero count for a different query type seems wrong,
 	 * e.g.  an INSERT that does an UPDATE instead should not print "0 1" if
-	 * one row was updated.  See QueryRewrite(), step 3, for details.
+	 * one row was updated (unless the ON CONFLICT UPDATE, or "UPSERT" variant
+	 * of INSERT was used to update the row, where it's logically a direct
+	 * effect of the top level command).  See QueryRewrite(), step 3, for
+	 * details.
 	 */
 	if (completionTag && completionTag[0] == '\0')
 	{
@@ -1366,6 +1374,8 @@ PortalRunMulti(Portal portal, bool isTopLevel,
 			sprintf(completionTag, "SELECT 0 0");
 		else if (strcmp(completionTag, "INSERT") == 0)
 			strcpy(completionTag, "INSERT 0 0");
+		else if (strcmp(completionTag, "UPSERT") == 0)
+			strcpy(completionTag, "UPSERT 0 0");
 		else if (strcmp(completionTag, "UPDATE") == 0)
 			strcpy(completionTag, "UPDATE 0");
 		else if (strcmp(completionTag, "DELETE") == 0)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 2fa30be..ee83eac 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5669,6 +5669,24 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
 		return NULL;
 	}
+	else if (var->varno == INNER_VAR)
+	{
+		/* Assume an EXCLUDED variable */
+		rte = rt_fetch(PRS2_OLD_VARNO, dpns->rtable);
+
+		/*
+		 * Sanity check:  EXCLUDED.* Vars should only appear in auxiliary ON
+		 * CONFLICT UPDATE queries.  Assert that rte and planstate are
+		 * consistent with that.
+		 */
+		Assert(rte->rtekind == RTE_RELATION);
+		Assert(IsA(dpns->planstate, SeqScanState) ||
+			   IsA(dpns->planstate, ResultState));
+
+		refname = "excluded";
+		colinfo = deparse_columns_fetch(PRS2_OLD_VARNO, dpns);
+		attnum = var->varattno;
+	}
 	else
 	{
 		elog(ERROR, "bogus varno: %d", var->varno);
@@ -6409,6 +6427,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 		case T_CoerceToDomainValue:
 		case T_SetToDefault:
 		case T_CurrentOfExpr:
+		case T_ExcludedExpr:
 			/* single words: always simple */
 			return true;
 
@@ -7634,6 +7653,26 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_ExcludedExpr:
+			{
+				ExcludedExpr *excludedexpr = (ExcludedExpr *) node;
+				Var		   *variable = (Var *) excludedexpr->arg;
+				bool		save_varprefix;
+
+				/*
+				 * Force parentheses because our caller probably assumed our
+				 * Var is a simple expression.
+				 */
+				appendStringInfoChar(buf, '(');
+				save_varprefix = context->varprefix;
+				/* Ensure EXCLUDED.* prefix is always visible */
+				context->varprefix = true;
+				get_rule_expr((Node *) variable, context, true);
+				context->varprefix = save_varprefix;
+				appendStringInfoChar(buf, ')');
+			}
+			break;
+
 		case T_List:
 			{
 				char	   *sep;
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 275bdcc..9302e41 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -894,9 +894,12 @@ PrintQueryResults(PGresult *results)
 				success = StoreQueryTuple(results);
 			else
 				success = PrintQueryTuples(results);
-			/* if it's INSERT/UPDATE/DELETE RETURNING, also print status */
+			/*
+			 * if it's INSERT/UPSERT/UPDATE/DELETE RETURNING, also print status
+			 */
 			cmdstatus = PQcmdStatus(results);
 			if (strncmp(cmdstatus, "INSERT", 6) == 0 ||
+				strncmp(cmdstatus, "UPSERT", 6) == 0 ||
 				strncmp(cmdstatus, "UPDATE", 6) == 0 ||
 				strncmp(cmdstatus, "DELETE", 6) == 0)
 				PrintQueryStatus(results);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5ee93b9..8913bc8 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -974,6 +974,16 @@ typedef struct DomainConstraintState
 	ExprState  *check_expr;		/* for CHECK, a boolean expression */
 } DomainConstraintState;
 
+/* ----------------
+ *		ExcludedExprState node
+ * ----------------
+ */
+typedef struct ExcludedExprState
+{
+	ExprState	xprstate;
+	ExprState  *arg;			/* the argument */
+} ExcludedExprState;
+
 
 /* ----------------------------------------------------------------
  *				 Executor State Trees
@@ -1097,6 +1107,7 @@ typedef struct ModifyTableState
 	List	  **mt_arowmarks;	/* per-subplan ExecAuxRowMark lists */
 	SpecCmd		spec;			/* reason for speculative insertion */
 	Oid			arbiterIndex;	/* unique index to arbitrate taking alt path */
+	PlanState  *onConflict; /* associated OnConflict state */
 	EPQState	mt_epqstate;	/* for evaluating EvalPlanQual rechecks */
 	bool		fireBSTriggers; /* do we need to fire stmt triggers? */
 } ModifyTableState;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 26c2f2a..8d6fba4 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -168,6 +168,7 @@ typedef enum NodeTag
 	T_CoerceToDomainValue,
 	T_SetToDefault,
 	T_CurrentOfExpr,
+	T_ExcludedExpr,
 	T_TargetEntry,
 	T_RangeTblRef,
 	T_JoinExpr,
@@ -207,6 +208,7 @@ typedef enum NodeTag
 	T_NullTestState,
 	T_CoerceToDomainState,
 	T_DomainConstraintState,
+	T_ExcludedExprState,
 
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
@@ -635,7 +637,9 @@ typedef enum JoinType
 typedef enum
 {
 	SPEC_NONE,		/* Not involved in speculative insertion */
-	SPEC_IGNORE		/* INSERT of "ON CONFLICT IGNORE" */
+	SPEC_IGNORE,	/* INSERT of "ON CONFLICT IGNORE" */
+	SPEC_INSERT,	/* INSERT of "ON CONFLICT UPDATE" */
+	SPEC_UPDATE		/* UPDATE of "ON CONFLICT UPDATE" */
 } SpecCmd;
 
 #endif   /* NODES_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 71309e1..a623f3d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -135,6 +135,7 @@ typedef struct Query
 	SpecCmd		specClause;		/* speculative insertion clause */
 	List	   *arbiterExpr;	/* Unique index arbiter exprs */
 	Node	   *arbiterWhere;	/* Unique index arbiter WHERE clause */
+	Node	   *onConflict;		/* ON CONFLICT Query */
 
 	List	   *returningList;	/* return-values list (of TargetEntry) */
 
@@ -1034,6 +1035,7 @@ typedef struct ConflictClause
 	NodeTag			type;
 	SpecCmd			specclause;		/* Variant specified */
 	InferClause	   *infer;			/* Optional index inference clause */
+	Node		   *updatequery;	/* Update parse stmt */
 	int				location;		/* token location, or -1 if unknown */
 } ConflictClause;
 
@@ -1113,7 +1115,7 @@ typedef struct DeleteStmt
 typedef struct UpdateStmt
 {
 	NodeTag		type;
-	RangeVar   *relation;		/* relation to update */
+	RangeVar   *relation;		/* relation to update (NULL for speculative) */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	Node	   *whereClause;	/* qualifications */
 	List	   *fromClause;		/* optional from clause for more tables */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 170084f..7366e2c 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -180,6 +180,7 @@ typedef struct ModifyTable
 	List	   *plans;			/* plan(s) producing source data */
 	SpecCmd		spec;			/* speculative insertion specification */
 	Oid			arbiterIndex;	/* Oid of ON CONFLICT arbiter index */
+	Plan	   *onConflictPlan;	/* Plan for ON CONFLICT UPDATE auxiliary query */
 	List	   *withCheckOptionLists;	/* per-target-table WCO lists */
 	List	   *returningLists; /* per-target-table RETURNING tlists */
 	List	   *fdwPrivLists;	/* per-target-table FDW private data lists */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index dbc5a35..032572c 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1148,6 +1148,53 @@ typedef struct CurrentOfExpr
 	int			cursor_param;	/* refcursor parameter number, or 0 */
 } CurrentOfExpr;
 
+/*
+ * ExcludedExpr - an EXCLUDED.* expression
+ *
+ * During parse analysis of ON CONFLICT UPDATE auxiliary queries, a dummy
+ * EXCLUDED range table entry is generated, which is actually just an alias for
+ * the target relation.  This is useful during parse analysis, allowing the
+ * parser to produce simple error messages, for example.  There is the
+ * appearance of a join within the auxiliary ON CONFLICT UPDATE, superficially
+ * similar to a join in an UPDATE ... FROM;  this is a limited, ad-hoc join
+ * though, as the executor needs to tightly control the referenced tuple/slot
+ * through which update evaluation references excluded values originally
+ * proposed for insertion.  Note that EXCLUDED.* values carry forward the
+ * effects of BEFORE ROW INSERT triggers.
+ *
+ * To implement a limited "join" for ON CONFLICT UPDATE auxiliary queries,
+ * during the rewrite stage, Vars referencing the alias EXCLUDED.* RTE are
+ * swapped with ExcludedExprs, which also contain Vars;  their Vars are
+ * equivalent, but reference the target instead.  The ExcludedExpr Var actually
+ * evaluates against varno INNER_VAR during expression evaluation (and not a
+ * varno INDEX_VAR associated with an entry in the flattened range table
+ * representing the target, which is necessarily being scanned whenever an
+ * ExcludedExpr is evaluated) while still being logically associated with the
+ * target.  The Var is only rigged to reference the inner slot during
+ * ExcludedExpr initialization.  The executor closely controls the evaluation
+ * expression, installing the EXCLUDED slot actually excluded from insertion
+ * into the inner slot of the child/auxiliary evaluation context in an ad-hoc
+ * fashion, which, after ExcludedExpr initialization, is expected (i.e. it is
+ * expected during ExcludedExpr evaluation that the parent insert will make
+ * each excluded tuple available in the inner slot in turn).  ExcludedExpr are
+ * only ever evaluated during special speculative insertion related EPQ
+ * expression evaluation, purely for the benefit of auxiliary UPDATE
+ * expressions.
+ *
+ * Aside from representing a logical choke point for this special expression
+ * evaluation, having a dedicated primnode also prevents the optimizer from
+ * considering various optimization that might otherwise be attempted.
+ * Obviously there is no useful join optimization possible within the auxiliary
+ * query, and an ExcludedExpr based post-rewrite query tree representation is a
+ * convenient way of preventing that, as well as related inapplicable
+ * optimizations concerning the equivalence of Vars.
+ */
+typedef struct ExcludedExpr
+{
+	Expr		xpr;
+	Node	   *arg;			/* argument (Var) */
+} ExcludedExpr;
+
 /*--------------------
  * TargetEntry -
  *	   a target entry (used in query target lists)
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index c3a0634..81a9058 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -85,7 +85,8 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, SpecCmd spec, int epqParam);
+				 List *rowMarks, Plan *onConflictPlan, SpecCmd spec,
+				 int epqParam);
 extern bool is_projection_capable_plan(Plan *plan);
 
 /*
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 3103b71..2b5804e 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -153,6 +153,7 @@ struct ParseState
 	bool		p_hasModifyingCTE;
 	bool		p_is_insert;
 	bool		p_is_update;
+	bool		p_is_speculative;
 	bool		p_locked_from_parent;
 	Relation	p_target_relation;
 	RangeTblEntry *p_target_rangetblentry;
diff --git a/src/test/isolation/expected/insert-conflict-update-2.out b/src/test/isolation/expected/insert-conflict-update-2.out
new file mode 100644
index 0000000..6a5ddfe
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-update-2.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: insert1 insert2 c1 select2 c2
+step insert1: INSERT INTO upsert(key, payload) VALUES('FooFoo', 'insert1') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, payload) VALUES('FOOFOO', 'insert2') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert2'; <waiting ...>
+step c1: COMMIT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            payload        
+
+FOOFOO         insert1 updated by insert2
+step c2: COMMIT;
+
+starting permutation: insert1 insert2 a1 select2 c2
+step insert1: INSERT INTO upsert(key, payload) VALUES('FooFoo', 'insert1') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, payload) VALUES('FOOFOO', 'insert2') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert2'; <waiting ...>
+step a1: ABORT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            payload        
+
+FOOFOO         insert2        
+step c2: COMMIT;
diff --git a/src/test/isolation/expected/insert-conflict-update-3.out b/src/test/isolation/expected/insert-conflict-update-3.out
new file mode 100644
index 0000000..29dd8b0
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-update-3.out
@@ -0,0 +1,26 @@
+Parsed test spec with 2 sessions
+
+starting permutation: update2 insert1 c2 select1surprise c1
+step update2: UPDATE colors SET is_active = true WHERE key = 1;
+step insert1: 
+    WITH t AS (
+        INSERT INTO colors(key, color, is_active)
+        VALUES(1, 'Brown', true), (2, 'Gray', true)
+        ON CONFLICT (key) UPDATE
+        SET color = EXCLUDED.color
+        WHERE TARGET.is_active)
+    SELECT * FROM colors ORDER BY key; <waiting ...>
+step c2: COMMIT;
+step insert1: <... completed>
+key            color          is_active      
+
+1              Red            f              
+2              Green          f              
+3              Blue           f              
+step select1surprise: SELECT * FROM colors ORDER BY key;
+key            color          is_active      
+
+1              Brown          t              
+2              Green          f              
+3              Blue           f              
+step c1: COMMIT;
diff --git a/src/test/isolation/expected/insert-conflict-update.out b/src/test/isolation/expected/insert-conflict-update.out
new file mode 100644
index 0000000..6976124
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-update.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: insert1 insert2 c1 select2 c2
+step insert1: INSERT INTO upsert(key, val) VALUES(1, 'insert1') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, val) VALUES(1, 'insert2') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert2'; <waiting ...>
+step c1: COMMIT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            val            
+
+1              insert1 updated by insert2
+step c2: COMMIT;
+
+starting permutation: insert1 insert2 a1 select2 c2
+step insert1: INSERT INTO upsert(key, val) VALUES(1, 'insert1') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, val) VALUES(1, 'insert2') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert2'; <waiting ...>
+step a1: ABORT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            val            
+
+1              insert2        
+step c2: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 59d14e9..50948a2 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -17,6 +17,9 @@ test: eval-plan-qual
 test: lock-update-delete
 test: lock-update-traversal
 test: insert-conflict-ignore
+test: insert-conflict-update
+test: insert-conflict-update-2
+test: insert-conflict-update-3
 test: delete-abort-savept
 test: delete-abort-savept-2
 test: aborted-keyrevoke
diff --git a/src/test/isolation/specs/insert-conflict-update-2.spec b/src/test/isolation/specs/insert-conflict-update-2.spec
new file mode 100644
index 0000000..3e6e944
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-update-2.spec
@@ -0,0 +1,41 @@
+# INSERT...ON CONFLICT UPDATE test
+#
+# This test shows a plausible scenario in which the user might wish to UPDATE a
+# value that is also constrained by the unique index that is the arbiter of
+# whether the alternative path should be taken.
+
+setup
+{
+  CREATE TABLE upsert (key text not null, payload text);
+  CREATE UNIQUE INDEX ON upsert(lower(key));
+}
+
+teardown
+{
+  DROP TABLE upsert;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert1" { INSERT INTO upsert(key, payload) VALUES('FooFoo', 'insert1') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert1'; }
+step "c1" { COMMIT; }
+step "a1" { ABORT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert2" { INSERT INTO upsert(key, payload) VALUES('FOOFOO', 'insert2') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert2'; }
+step "select2" { SELECT * FROM upsert; }
+step "c2" { COMMIT; }
+step "a2" { ABORT; }
+
+# One session (session 2) block-waits on another (session 1) to determine if it
+# should proceed with an insert or update.  The user can still usefully UPDATE
+# a column constrained by a unique index, as the example illustrates.
+permutation "insert1" "insert2" "c1" "select2" "c2"
+permutation "insert1" "insert2" "a1" "select2" "c2"
diff --git a/src/test/isolation/specs/insert-conflict-update-3.spec b/src/test/isolation/specs/insert-conflict-update-3.spec
new file mode 100644
index 0000000..94ae3df
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-update-3.spec
@@ -0,0 +1,69 @@
+# INSERT...ON CONFLICT UPDATE test
+#
+# Other INSERT...ON CONFLICT UPDATE isolation tests illustrate the "MVCC
+# violation" added to facilitate the feature, whereby a
+# not-visible-to-our-snapshot tuple can be updated by our command all the same.
+# This is generally needed to provide a guarantee of a successful INSERT or
+# UPDATE in READ COMMITTED mode.  This MVCC violation is quite distinct from
+# the putative "MVCC violation" that has existed in PostgreSQL for many years,
+# the EvalPlanQual() mechanism, because that mechanism always starts from a
+# tuple that is visible to the command's MVCC snapshot.  This test illustrates
+# a slightly distinct user-visible consequence of the same MVCC violation
+# generally associated with INSERT...ON CONFLICT UPDATE.  The impact of the
+# MVCC violation goes a little beyond updating MVCC-invisible tuples.
+#
+# With INSERT...ON CONFLICT UPDATE, the UPDATE predicate is only evaluated
+# once, on this conclusively-locked tuple, and not any other version of the
+# same tuple.  It is therefore possible (in READ COMMITTED mode) that the
+# predicate "fail to be satisfied" according to the command's MVCC snapshot.
+# It might simply be that there is no row version visible, but it's also
+# possible that there is some row version visible, but only as a version that
+# doesn't satisfy the predicate.  If, however, the conclusively-locked version
+# satisfies the predicate, that's good enough, and the tuple is updated.  The
+# MVCC-snapshot-visible row version is denied the opportunity to prevent the
+# UPDATE from taking place, because we don't walk the UPDATE chain in the usual
+# way.
+
+setup
+{
+  CREATE TABLE colors (key int4 PRIMARY KEY, color text, is_active boolean);
+  INSERT INTO colors (key, color, is_active) VALUES(1, 'Red', false);
+  INSERT INTO colors (key, color, is_active) VALUES(2, 'Green', false);
+  INSERT INTO colors (key, color, is_active) VALUES(3, 'Blue', false);
+}
+
+teardown
+{
+  DROP TABLE colors;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert1" {
+    WITH t AS (
+        INSERT INTO colors(key, color, is_active)
+        VALUES(1, 'Brown', true), (2, 'Gray', true)
+        ON CONFLICT (key) UPDATE
+        SET color = EXCLUDED.color
+        WHERE TARGET.is_active)
+    SELECT * FROM colors ORDER BY key;}
+step "select1surprise" { SELECT * FROM colors ORDER BY key; }
+step "c1" { COMMIT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "update2" { UPDATE colors SET is_active = true WHERE key = 1; }
+step "c2" { COMMIT; }
+
+# Perhaps surprisingly, the session 1 MVCC-snapshot-visible tuple (the tuple
+# with the pre-populated color 'Red') is denied the opportunity to prevent the
+# UPDATE from taking place -- only the conclusively-locked tuple version
+# matters, and so the tuple with key value 1 was updated to 'Brown' (but not
+# tuple with key value 2, since nothing changed there):
+permutation "update2" "insert1" "c2" "select1surprise" "c1"
diff --git a/src/test/isolation/specs/insert-conflict-update.spec b/src/test/isolation/specs/insert-conflict-update.spec
new file mode 100644
index 0000000..6529a0c
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-update.spec
@@ -0,0 +1,40 @@
+# INSERT...ON CONFLICT UPDATE test
+#
+# This test tries to expose problems with the interaction between concurrent
+# sessions.
+
+setup
+{
+  CREATE TABLE upsert (key int primary key, val text);
+}
+
+teardown
+{
+  DROP TABLE upsert;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert1" { INSERT INTO upsert(key, val) VALUES(1, 'insert1') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert1'; }
+step "c1" { COMMIT; }
+step "a1" { ABORT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert2" { INSERT INTO upsert(key, val) VALUES(1, 'insert2') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert2'; }
+step "select2" { SELECT * FROM upsert; }
+step "c2" { COMMIT; }
+step "a2" { ABORT; }
+
+# One session (session 2) block-waits on another (session 1) to determine if it
+# should proceed with an insert or update.  Notably, this entails updating a
+# tuple while there is no version of that tuple visible to the updating
+# session's snapshot.  This is permitted only in READ COMMITTED mode.
+permutation "insert1" "insert2" "c1" "select2" "c2"
+permutation "insert1" "insert2" "a1" "select2" "c2"
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
index a34d857..c192bd3 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -3,14 +3,196 @@
 --
 create table insertconflicttest(key int4, fruit text);
 --
+-- Single key tests
+--
+create unique index key_index on insertconflicttest(key);
+--
+-- Explain tests
+--
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit;
+                     QUERY PLAN                     
+----------------------------------------------------
+ Insert on insertconflicttest target
+   ->  Result
+   ->  Conflict Update on insertconflicttest target
+(3 rows)
+
+-- Should display qual actually attributable to internal sequential scan:
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Cawesh';
+                     QUERY PLAN                     
+----------------------------------------------------
+ Insert on insertconflicttest target
+   ->  Result
+   ->  Conflict Update on insertconflicttest target
+         Filter: (fruit <> 'Cawesh'::text)
+(4 rows)
+
+-- With EXCLUDED.* expression in scan node:
+explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) update set fruit = excluded.fruit where excluded.fruit != 'Elderberry';
+                        QUERY PLAN                        
+----------------------------------------------------------
+ Insert on insertconflicttest target
+   ->  Result
+   ->  Conflict Update on insertconflicttest target
+         Filter: ((excluded.fruit) <> 'Elderberry'::text)
+(4 rows)
+
+-- Does the same, but JSON format shows "Arbiter Index":
+explain (costs off, format json) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Lime' returning *;
+                    QUERY PLAN                    
+--------------------------------------------------
+ [                                               +
+   {                                             +
+     "Plan": {                                   +
+       "Node Type": "ModifyTable",               +
+       "Operation": "Insert",                    +
+       "Relation Name": "insertconflicttest",    +
+       "Alias": "target",                        +
+       "Arbiter Index": "key_index",             +
+       "Plans": [                                +
+         {                                       +
+           "Node Type": "Result",                +
+           "Parent Relationship": "Member"       +
+         },                                      +
+         {                                       +
+           "Node Type": "ModifyTable",           +
+           "Operation": "Conflict Update",       +
+           "Parent Relationship": "Member",      +
+           "Relation Name": "insertconflicttest",+
+           "Alias": "target",                    +
+           "Filter": "(fruit <> 'Lime'::text)"   +
+         }                                       +
+       ]                                         +
+     }                                           +
+   }                                             +
+ ]
+(1 row)
+
+-- Fails (no unique index inference specification, required for update variant):
+insert into insertconflicttest values (1, 'Apple') on conflict update set fruit = excluded.fruit;
+ERROR:  ON CONFLICT with UPDATE must contain columns or expressions to infer a unique index from
+LINE 1: ...nsert into insertconflicttest values (1, 'Apple') on conflic...
+                                                             ^
+-- inference succeeds:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) update set fruit = excluded.fruit;
+-- Succeed, since multi-assignment does not involve subquery:
+INSERT INTO insertconflicttest
+VALUES (1, 'Apple'), (2, 'Orange')
+ON CONFLICT (key) UPDATE SET (fruit, key) = (EXCLUDED.fruit, EXCLUDED.key);
+-- Don't accept original table name -- only TARGET.* alias:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = insertconflicttest.fruit;
+ERROR:  invalid reference to FROM-clause entry for table "insertconflicttest"
+LINE 1: ...(1, 'Apple') on conflict (key) update set fruit = insertconf...
+                                                             ^
+HINT:  Perhaps you meant to reference the table alias "excluded".
+-- inference fails:
+insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (6, 'Passionfruit') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index key_index;
+--
+-- Composite key tests
+--
+create unique index comp_key_index on insertconflicttest(key, fruit);
+-- inference succeeds:
+insert into insertconflicttest values (7, 'Raspberry') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (8, 'Lime') on conflict (fruit, key) update set fruit = excluded.fruit;
+-- inference fails:
+insert into insertconflicttest values (9, 'Banana') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (10, 'Blueberry') on conflict (key, key, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (11, 'Cherry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (12, 'Date') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index comp_key_index;
+--
+-- Partial index tests, no inference predicate specificied
+--
+create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
+create unique index expr_part_comp_key_index on insertconflicttest(key, lower(fruit)) where key < 5;
+-- inference fails:
+insert into insertconflicttest values (13, 'Grape') on conflict (key, fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (14, 'Raisin') on conflict (fruit, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (15, 'Cranberry') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (16, 'Melon') on conflict (key, key, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (17, 'Mulberry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (18, 'Pineapple') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index part_comp_key_index;
+drop index expr_part_comp_key_index;
+--
+-- Expression index tests
+--
+create unique index expr_key_index on insertconflicttest(lower(fruit));
+-- inference succeeds:
+insert into insertconflicttest values (20, 'Quince') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (21, 'Pomegranate') on conflict (lower(fruit), lower(fruit)) update set fruit = excluded.fruit;
+-- inference fails:
+insert into insertconflicttest values (22, 'Apricot') on conflict (upper(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index expr_key_index;
+--
+-- Expression index tests (with regular column)
+--
+create unique index expr_comp_key_index on insertconflicttest(key, lower(fruit));
+-- inference succeeds:
+insert into insertconflicttest values (24, 'Plum') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (25, 'Peach') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (26, 'Fig') on conflict (lower(fruit), key, lower(fruit), key) update set fruit = excluded.fruit;
+-- inference fails:
+insert into insertconflicttest values (27, 'Prune') on conflict (key, upper(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (28, 'Redcurrant') on conflict (fruit, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (29, 'Nectarine') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index expr_comp_key_index;
+--
+-- Non-spurious duplicate violation tests
+--
+create unique index key_index on insertconflicttest(key);
+create unique index fruit_index on insertconflicttest(fruit);
+-- succeeds, since UPDATE happens to update "fruit" to existing value:
+insert into insertconflicttest values (26, 'Fig') on conflict (key) update set fruit = excluded.fruit;
+-- fails, since UPDATE is to row with key value 26, and we're updating "fruit"
+-- to a value that happens to exist in another row ('peach'):
+insert into insertconflicttest values (26, 'Peach') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  duplicate key value violates unique constraint "fruit_index"
+DETAIL:  Key (fruit)=(Peach) already exists.
+-- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit"
+-- arbitrates that statement updates existing "Fig" row:
+insert into insertconflicttest values (25, 'Fig') on conflict (fruit) update set fruit = excluded.fruit;
+drop index key_index;
+drop index fruit_index;
+--
 -- Test partial unique index inference
 --
 create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
 -- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry') update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
 -- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
 ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit where fruit like '%berry') update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
 insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
 ERROR:  partial arbiter unique index has predicate that does not cover tuple proposed for insertion
 DETAIL:  ON CONFLICT inference clause implies that the tuple proposed for insertion must be covered by predicate for partial index "partial_key_index".
@@ -42,6 +224,9 @@ insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
 insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
 -- Tests proper for inheritance:
 -- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) update set altitude = excluded.altitude;
+ERROR:  relation "cities" has inheritance children
+HINT:  Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
 ERROR:  relation "cities" has inheritance children
 HINT:  Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.
@@ -49,6 +234,7 @@ HINT:  Only heap relations without inheritance children are accepted as targets
 -- There is at least limited support for relations with children:
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
 -- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) update set altitude = excluded.altitude;
 insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
 -- clean up
 drop table capitals;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 74b0450..bc44c45 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -269,7 +269,7 @@ SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying a
 ERROR:  permission denied for relation atest2
 -- Test column level permissions
 SET SESSION AUTHORIZATION regressuser1;
-CREATE TABLE atest5 (one int, two int, three int);
+CREATE TABLE atest5 (one int, two int unique, three int);
 CREATE TABLE atest6 (one int, two int, blue int);
 GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4;
 GRANT ALL (one) ON atest5 TO regressuser3;
@@ -367,6 +367,11 @@ UPDATE atest5 SET one = 8; -- fail
 ERROR:  permission denied for relation atest5
 UPDATE atest5 SET three = 5, one = 2; -- fail
 ERROR:  permission denied for relation atest5
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set three = 10; -- ok
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set one = 8; -- fails (due to UPDATE)
+ERROR:  permission denied for relation atest5
+INSERT INTO atest5(three) VALUES (4) ON CONFLICT (two) UPDATE set three = 10; -- fails (due to INSERT)
+ERROR:  permission denied for relation atest5
 SET SESSION AUTHORIZATION regressuser1;
 REVOKE ALL (one) ON atest5 FROM regressuser4;
 GRANT SELECT (one,two,blue) ON atest6 TO regressuser4;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e563514..ca77d1f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2360,6 +2360,18 @@ insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
   on conflict ignore;
 ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
 DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
+-- rule not fired, so fk violation
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict (id3a, id3b, id3c) update
+  set id3b = excluded.id3b;
+ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
+DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
+-- rule fired, so unsupported (only updatable views have limited support)
+insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0)
+  on conflict (id1a, id1b) update
+  set sl_avail = excluded.sl_avail;
+ERROR:  relation "shoelace" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
 			where (((rule_and_refint_t3.id3a = new.id3a)
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index b14410f..9ba3a44 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -639,6 +639,28 @@ from
 (0 rows)
 
 --
+-- Test case for subselect within UPDATE of INSERT...ON CONFLICT UPDATE
+--
+create temp table upsert(key int4 primary key, val text);
+insert into upsert values(1, 'val') on conflict (key) update set val = 'not seen';
+insert into upsert values(1, 'val') on conflict (key) update set val = 'unsupported ' || (select f1 from int4_tbl where f1 != 0 limit 1)::text;
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 1: ...conflict (key) update set val = 'unsupported ' || (select f1...
+                                                             ^
+select * from upsert;
+ key | val 
+-----+-----
+   1 | val
+(1 row)
+
+with aa as (select 'int4_tbl' u from int4_tbl limit 1)
+insert into upsert values (1, 'x'), (999, 'y')
+on conflict (key) update set val = (select u from aa)
+returning *;
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: on conflict (key) update set val = (select u from aa)
+                                           ^
+--
 -- Test case for cross-type partial matching in hashed subplan (bug #7597)
 --
 create temp table outer_7597 (f1 int4, f2 int4);
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index f1a5fde..77dfa06 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -274,7 +274,7 @@ drop sequence ttdummy_seq;
 -- tests for per-statement triggers
 --
 CREATE TABLE log_table (tstamp timestamp default timeofday()::timestamp);
-CREATE TABLE main_table (a int, b int);
+CREATE TABLE main_table (a int unique, b int);
 COPY main_table (a,b) FROM stdin;
 CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
 BEGIN
@@ -291,6 +291,14 @@ FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt');
 --
 CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table
 EXECUTE PROCEDURE trigger_func('after_upd_stmt');
+-- Both insert and update statement level triggers (before and after) should
+-- fire.  Doesn't fire UPDATE before trigger, but only because one isn't
+-- defined.
+INSERT INTO main_table (a, b) VALUES (5, 10) ON CONFLICT (a)
+  UPDATE SET b = EXCLUDED.b;
+NOTICE:  trigger_func(before_ins_stmt) called: action = INSERT, when = BEFORE, level = STATEMENT
+NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+NOTICE:  trigger_func(after_ins_stmt) called: action = INSERT, when = AFTER, level = STATEMENT
 CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table
 FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row');
 INSERT INTO main_table DEFAULT VALUES;
@@ -305,6 +313,8 @@ NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, lev
 -- UPDATE that effects zero rows should still call per-statement trigger
 UPDATE main_table SET a = a + 2 WHERE b > 100;
 NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+-- constraint now unneeded
+ALTER TABLE main_table DROP CONSTRAINT main_table_a_key;
 -- COPY should fire per-row and per-statement INSERT triggers
 COPY main_table (a, b) FROM stdin;
 NOTICE:  trigger_func(before_ins_stmt) called: action = INSERT, when = BEFORE, level = STATEMENT
@@ -1731,3 +1741,93 @@ select * from self_ref_trigger;
 drop table self_ref_trigger;
 drop function self_ref_trigger_ins_func();
 drop function self_ref_trigger_del_func();
+--
+-- Verify behavior of before and after triggers with INSERT...ON CONFLICT
+-- UPDATE
+--
+create table upsert (key int4 primary key, color text);
+create function upsert_before_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'before update (old): %', old.*::text;
+    raise warning 'before update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'before insert (new): %', new.*::text;
+    if new.key % 2 = 0 then
+      new.key := new.key + 1;
+      new.color := new.color || ' trig modified';
+      raise warning 'before insert (new, modified): %', new.*::text;
+    end if;
+  end if;
+  return new;
+end;
+$$;
+create trigger upsert_before_trig before insert or update on upsert
+  for each row execute procedure upsert_before_func();
+create function upsert_after_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'after update (old): %', new.*::text;
+    raise warning 'after update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'after insert (new): %', new.*::text;
+  end if;
+  return null;
+end;
+$$;
+create trigger upsert_after_trig after insert or update on upsert
+  for each row execute procedure upsert_after_func();
+insert into upsert values(1, 'black') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (1,black)
+WARNING:  after insert (new): (1,black)
+insert into upsert values(2, 'red') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (2,red)
+WARNING:  before insert (new, modified): (3,"red trig modified")
+WARNING:  after insert (new): (3,"red trig modified")
+insert into upsert values(3, 'orange') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (3,orange)
+WARNING:  before update (old): (3,"red trig modified")
+WARNING:  before update (new): (3,"updated red trig modified")
+WARNING:  after update (old): (3,"updated red trig modified")
+WARNING:  after update (new): (3,"updated red trig modified")
+insert into upsert values(4, 'green') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (4,green)
+WARNING:  before insert (new, modified): (5,"green trig modified")
+WARNING:  after insert (new): (5,"green trig modified")
+insert into upsert values(5, 'purple') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (5,purple)
+WARNING:  before update (old): (5,"green trig modified")
+WARNING:  before update (new): (5,"updated green trig modified")
+WARNING:  after update (old): (5,"updated green trig modified")
+WARNING:  after update (new): (5,"updated green trig modified")
+insert into upsert values(6, 'white') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (6,white)
+WARNING:  before insert (new, modified): (7,"white trig modified")
+WARNING:  after insert (new): (7,"white trig modified")
+insert into upsert values(7, 'pink') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (7,pink)
+WARNING:  before update (old): (7,"white trig modified")
+WARNING:  before update (new): (7,"updated white trig modified")
+WARNING:  after update (old): (7,"updated white trig modified")
+WARNING:  after update (new): (7,"updated white trig modified")
+insert into upsert values(8, 'yellow') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (8,yellow)
+WARNING:  before insert (new, modified): (9,"yellow trig modified")
+WARNING:  after insert (new): (9,"yellow trig modified")
+select * from upsert;
+ key |            color            
+-----+-----------------------------
+   1 | black
+   3 | updated red trig modified
+   5 | updated green trig modified
+   7 | updated white trig modified
+   9 | yellow trig modified
+(5 rows)
+
+drop table upsert;
+drop function upsert_before_func();
+drop function upsert_after_func();
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index 1de2a86..c646016 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -147,4 +147,31 @@ SELECT a, b, char_length(c) FROM update_test;
  42 |  12 |       10000
 (4 rows)
 
+ALTER TABLE update_test ADD constraint uuu UNIQUE(a);
+-- fail, update predicates are disallowed:
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a NOT IN (SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a NOT IN (SELECT a FROM update_test);
+                ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE EXISTS(SELECT b FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE EXISTS(SELECT b FROM update_test);
+              ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a IN (SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a IN (SELECT a FROM update_test);
+                ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ALL(SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a = ALL(SELECT a FROM update_test);
+                ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ANY(SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a = ANY(SELECT a FROM update_test);
+                ^
 DROP TABLE update_test;
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 524e0ef..41a1833 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -1806,6 +1806,80 @@ SELECT * FROM y;
   -400
 (22 rows)
 
+-- data-modifying WITH containing INSERT...ON CONFLICT UPDATE
+CREATE TABLE z AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
+ALTER TABLE z ADD UNIQUE (k);
+WITH t AS (
+    INSERT INTO z SELECT i, 'insert'
+    FROM generate_series(0, 16) i
+    ON CONFLICT (k) UPDATE SET v = TARGET.v || ', now update'
+    RETURNING *
+)
+SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k;
+ k |   v    | a 
+---+--------+---
+ 0 | insert | 0
+ 0 | insert | 0
+(2 rows)
+
+-- New query/snapshot demonstrates side-effects of previous query.
+SELECT * FROM z ORDER BY k;
+ k  |        v         
+----+------------------
+  0 | insert
+  1 | 1 v, now update
+  2 | insert
+  3 | insert
+  4 | 4 v, now update
+  5 | insert
+  6 | insert
+  7 | 7 v, now update
+  8 | insert
+  9 | insert
+ 10 | 10 v, now update
+ 11 | insert
+ 12 | insert
+ 13 | 13 v, now update
+ 14 | insert
+ 15 | insert
+ 16 | 16 v, now update
+(17 rows)
+
+--
+-- All these cases should fail, due to restrictions imposed upon the UPDATE
+-- portion of the query.
+--
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = ' update' WHERE target.k = (SELECT a FROM aa);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ...ICT (k) UPDATE SET v = ' update' WHERE target.k = (SELECT a ...
+                                                             ^
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 ))
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+DROP TABLE z;
 -- check that run to completion happens in proper ordering
 TRUNCATE TABLE y;
 INSERT INTO y SELECT generate_series(1, 3);
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index 8ec0054..46bce36 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -292,6 +292,11 @@ INSERT INTO UNIQUE_TBL VALUES (5, 'one');
 INSERT INTO UNIQUE_TBL (t) VALUES ('six');
 INSERT INTO UNIQUE_TBL (t) VALUES ('seven');
 
+INSERT INTO UNIQUE_TBL VALUES (5, 'five-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'five-upsert-update';
+INSERT INTO UNIQUE_TBL VALUES (6, 'six-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'six-upsert-update';
+-- should fail
+INSERT INTO UNIQUE_TBL VALUES (1, 'a'), (2, 'b'), (2, 'b') ON CONFLICT (i) UPDATE SET t = 'fails';
+
 SELECT '' AS five, * FROM UNIQUE_TBL;
 
 DROP TABLE UNIQUE_TBL;
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index 0d32a9eab..add3f0c 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -421,16 +421,23 @@ INSERT INTO UNIQUE_TBL VALUES (4, 'four');
 INSERT INTO UNIQUE_TBL VALUES (5, 'one');
 INSERT INTO UNIQUE_TBL (t) VALUES ('six');
 INSERT INTO UNIQUE_TBL (t) VALUES ('seven');
+INSERT INTO UNIQUE_TBL VALUES (5, 'five-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'five-upsert-update';
+INSERT INTO UNIQUE_TBL VALUES (6, 'six-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'six-upsert-update';
+-- should fail
+INSERT INTO UNIQUE_TBL VALUES (1, 'a'), (2, 'b'), (2, 'b') ON CONFLICT (i) UPDATE SET t = 'fails';
+ERROR:  ON CONFLICT UPDATE command could not lock/update self-inserted tuple
+HINT:  Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
 SELECT '' AS five, * FROM UNIQUE_TBL;
- five | i |   t   
-------+---+-------
+ five | i |         t          
+------+---+--------------------
       | 1 | one
       | 2 | two
       | 4 | four
-      | 5 | one
       |   | six
       |   | seven
-(6 rows)
+      | 5 | five-upsert-update
+      | 6 | six-upsert-insert
+(7 rows)
 
 DROP TABLE UNIQUE_TBL;
 CREATE TABLE UNIQUE_TBL (i int, t text,
diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql
index e330ecd..472d4ab 100644
--- a/src/test/regress/sql/insert_conflict.sql
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -4,15 +4,140 @@
 create table insertconflicttest(key int4, fruit text);
 
 --
+-- Single key tests
+--
+create unique index key_index on insertconflicttest(key);
+
+--
+-- Explain tests
+--
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit;
+-- Should display qual actually attributable to internal sequential scan:
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Cawesh';
+-- With EXCLUDED.* expression in scan node:
+explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) update set fruit = excluded.fruit where excluded.fruit != 'Elderberry';
+-- Does the same, but JSON format shows "Arbiter Index":
+explain (costs off, format json) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Lime' returning *;
+
+-- Fails (no unique index inference specification, required for update variant):
+insert into insertconflicttest values (1, 'Apple') on conflict update set fruit = excluded.fruit;
+
+-- inference succeeds:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) update set fruit = excluded.fruit;
+
+-- Succeed, since multi-assignment does not involve subquery:
+INSERT INTO insertconflicttest
+VALUES (1, 'Apple'), (2, 'Orange')
+ON CONFLICT (key) UPDATE SET (fruit, key) = (EXCLUDED.fruit, EXCLUDED.key);
+-- Don't accept original table name -- only TARGET.* alias:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = insertconflicttest.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (6, 'Passionfruit') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+
+drop index key_index;
+
+--
+-- Composite key tests
+--
+create unique index comp_key_index on insertconflicttest(key, fruit);
+
+-- inference succeeds:
+insert into insertconflicttest values (7, 'Raspberry') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (8, 'Lime') on conflict (fruit, key) update set fruit = excluded.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (9, 'Banana') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (10, 'Blueberry') on conflict (key, key, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (11, 'Cherry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (12, 'Date') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+
+drop index comp_key_index;
+
+--
+-- Partial index tests, no inference predicate specificied
+--
+create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
+create unique index expr_part_comp_key_index on insertconflicttest(key, lower(fruit)) where key < 5;
+
+-- inference fails:
+insert into insertconflicttest values (13, 'Grape') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (14, 'Raisin') on conflict (fruit, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (15, 'Cranberry') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (16, 'Melon') on conflict (key, key, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (17, 'Mulberry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (18, 'Pineapple') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+
+drop index part_comp_key_index;
+drop index expr_part_comp_key_index;
+
+--
+-- Expression index tests
+--
+create unique index expr_key_index on insertconflicttest(lower(fruit));
+
+-- inference succeeds:
+insert into insertconflicttest values (20, 'Quince') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (21, 'Pomegranate') on conflict (lower(fruit), lower(fruit)) update set fruit = excluded.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (22, 'Apricot') on conflict (upper(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) update set fruit = excluded.fruit;
+
+drop index expr_key_index;
+
+--
+-- Expression index tests (with regular column)
+--
+create unique index expr_comp_key_index on insertconflicttest(key, lower(fruit));
+
+-- inference succeeds:
+insert into insertconflicttest values (24, 'Plum') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (25, 'Peach') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (26, 'Fig') on conflict (lower(fruit), key, lower(fruit), key) update set fruit = excluded.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (27, 'Prune') on conflict (key, upper(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (28, 'Redcurrant') on conflict (fruit, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (29, 'Nectarine') on conflict (key) update set fruit = excluded.fruit;
+
+drop index expr_comp_key_index;
+
+--
+-- Non-spurious duplicate violation tests
+--
+create unique index key_index on insertconflicttest(key);
+create unique index fruit_index on insertconflicttest(fruit);
+
+-- succeeds, since UPDATE happens to update "fruit" to existing value:
+insert into insertconflicttest values (26, 'Fig') on conflict (key) update set fruit = excluded.fruit;
+-- fails, since UPDATE is to row with key value 26, and we're updating "fruit"
+-- to a value that happens to exist in another row ('peach'):
+insert into insertconflicttest values (26, 'Peach') on conflict (key) update set fruit = excluded.fruit;
+-- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit"
+-- arbitrates that statement updates existing "Fig" row:
+insert into insertconflicttest values (25, 'Fig') on conflict (fruit) update set fruit = excluded.fruit;
+
+drop index key_index;
+drop index fruit_index;
+
+--
 -- Test partial unique index inference
 --
 create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
 
 -- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry') update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
 
 -- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key) update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit where fruit like '%berry') update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
 
 drop index partial_key_index;
@@ -51,6 +176,7 @@ insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
 -- Tests proper for inheritance:
 
 -- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) update set altitude = excluded.altitude;
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
 
 -- Succeeds:
@@ -58,6 +184,7 @@ insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignor
 -- There is at least limited support for relations with children:
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
 -- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) update set altitude = excluded.altitude;
 insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
 
 -- clean up
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index f97a75a..861eac6 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -194,7 +194,7 @@ SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying a
 -- Test column level permissions
 
 SET SESSION AUTHORIZATION regressuser1;
-CREATE TABLE atest5 (one int, two int, three int);
+CREATE TABLE atest5 (one int, two int unique, three int);
 CREATE TABLE atest6 (one int, two int, blue int);
 GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4;
 GRANT ALL (one) ON atest5 TO regressuser3;
@@ -245,6 +245,9 @@ INSERT INTO atest5 VALUES (5,5,5); -- fail
 UPDATE atest5 SET three = 10; -- ok
 UPDATE atest5 SET one = 8; -- fail
 UPDATE atest5 SET three = 5, one = 2; -- fail
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set three = 10; -- ok
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set one = 8; -- fails (due to UPDATE)
+INSERT INTO atest5(three) VALUES (4) ON CONFLICT (two) UPDATE set three = 10; -- fails (due to INSERT)
 
 SET SESSION AUTHORIZATION regressuser1;
 REVOKE ALL (one) ON atest5 FROM regressuser4;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 5807331..31afee8 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -850,6 +850,14 @@ insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
 -- Ordinary table
 insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
   on conflict ignore;
+-- rule not fired, so fk violation
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict (id3a, id3b, id3c) update
+  set id3b = excluded.id3b;
+-- rule fired, so unsupported (only updatable views have limited support)
+insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0)
+  on conflict (id1a, id1b) update
+  set sl_avail = excluded.sl_avail;
 
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index 4be2e40..2be9cb7 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -374,6 +374,20 @@ from
   int4_tbl i4 on dummy = i4.f1;
 
 --
+-- Test case for subselect within UPDATE of INSERT...ON CONFLICT UPDATE
+--
+create temp table upsert(key int4 primary key, val text);
+insert into upsert values(1, 'val') on conflict (key) update set val = 'not seen';
+insert into upsert values(1, 'val') on conflict (key) update set val = 'unsupported ' || (select f1 from int4_tbl where f1 != 0 limit 1)::text;
+
+select * from upsert;
+
+with aa as (select 'int4_tbl' u from int4_tbl limit 1)
+insert into upsert values (1, 'x'), (999, 'y')
+on conflict (key) update set val = (select u from aa)
+returning *;
+
+--
 -- Test case for cross-type partial matching in hashed subplan (bug #7597)
 --
 
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index 0ea2c31..323ca1a 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -208,7 +208,7 @@ drop sequence ttdummy_seq;
 
 CREATE TABLE log_table (tstamp timestamp default timeofday()::timestamp);
 
-CREATE TABLE main_table (a int, b int);
+CREATE TABLE main_table (a int unique, b int);
 
 COPY main_table (a,b) FROM stdin;
 5	10
@@ -237,6 +237,12 @@ FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt');
 CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table
 EXECUTE PROCEDURE trigger_func('after_upd_stmt');
 
+-- Both insert and update statement level triggers (before and after) should
+-- fire.  Doesn't fire UPDATE before trigger, but only because one isn't
+-- defined.
+INSERT INTO main_table (a, b) VALUES (5, 10) ON CONFLICT (a)
+  UPDATE SET b = EXCLUDED.b;
+
 CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table
 FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row');
 
@@ -246,6 +252,9 @@ UPDATE main_table SET a = a + 1 WHERE b < 30;
 -- UPDATE that effects zero rows should still call per-statement trigger
 UPDATE main_table SET a = a + 2 WHERE b > 100;
 
+-- constraint now unneeded
+ALTER TABLE main_table DROP CONSTRAINT main_table_a_key;
+
 -- COPY should fire per-row and per-statement INSERT triggers
 COPY main_table (a, b) FROM stdin;
 30	40
@@ -1173,3 +1182,61 @@ select * from self_ref_trigger;
 drop table self_ref_trigger;
 drop function self_ref_trigger_ins_func();
 drop function self_ref_trigger_del_func();
+
+--
+-- Verify behavior of before and after triggers with INSERT...ON CONFLICT
+-- UPDATE
+--
+create table upsert (key int4 primary key, color text);
+
+create function upsert_before_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'before update (old): %', old.*::text;
+    raise warning 'before update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'before insert (new): %', new.*::text;
+    if new.key % 2 = 0 then
+      new.key := new.key + 1;
+      new.color := new.color || ' trig modified';
+      raise warning 'before insert (new, modified): %', new.*::text;
+    end if;
+  end if;
+  return new;
+end;
+$$;
+create trigger upsert_before_trig before insert or update on upsert
+  for each row execute procedure upsert_before_func();
+
+create function upsert_after_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'after update (old): %', new.*::text;
+    raise warning 'after update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'after insert (new): %', new.*::text;
+  end if;
+  return null;
+end;
+$$;
+create trigger upsert_after_trig after insert or update on upsert
+  for each row execute procedure upsert_after_func();
+
+insert into upsert values(1, 'black') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(2, 'red') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(3, 'orange') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(4, 'green') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(5, 'purple') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(6, 'white') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(7, 'pink') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(8, 'yellow') on conflict (key) update set color = 'updated ' || target.color;
+
+select * from upsert;
+
+drop table upsert;
+drop function upsert_before_func();
+drop function upsert_after_func();
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index e71128c..903f3fb 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -74,4 +74,18 @@ UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10;
 UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car';
 SELECT a, b, char_length(c) FROM update_test;
 
+ALTER TABLE update_test ADD constraint uuu UNIQUE(a);
+
+-- fail, update predicates are disallowed:
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a NOT IN (SELECT a FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE EXISTS(SELECT b FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a IN (SELECT a FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ALL(SELECT a FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ANY(SELECT a FROM update_test);
+
 DROP TABLE update_test;
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 1687c11..56478ca 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -795,6 +795,43 @@ SELECT * FROM t LIMIT 10;
 
 SELECT * FROM y;
 
+-- data-modifying WITH containing INSERT...ON CONFLICT UPDATE
+CREATE TABLE z AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
+ALTER TABLE z ADD UNIQUE (k);
+
+WITH t AS (
+    INSERT INTO z SELECT i, 'insert'
+    FROM generate_series(0, 16) i
+    ON CONFLICT (k) UPDATE SET v = TARGET.v || ', now update'
+    RETURNING *
+)
+SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k;
+
+-- New query/snapshot demonstrates side-effects of previous query.
+SELECT * FROM z ORDER BY k;
+
+--
+-- All these cases should fail, due to restrictions imposed upon the UPDATE
+-- portion of the query.
+--
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = ' update' WHERE target.k = (SELECT a FROM aa);
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1);
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 ))
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+
+DROP TABLE z;
+
 -- check that run to completion happens in proper ordering
 
 TRUNCATE TABLE y;
-- 
1.9.1

0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patchtext/x-patch; charset=US-ASCII; name=0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patchDownload
From 45fd3881f913735e4b6f56b531cb0e4494a33b08 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@heroku.com>
Date: Tue, 26 Aug 2014 21:28:40 -0700
Subject: [PATCH 2/4] Make UPDATE privileges distinct from INSERT privileges in
 RTEs

Previously, relation range table entries used a single Bitmapset field
representing which columns required either UPDATE or INSERT privileges,
despite the fact that INSERT and UPDATE privileges are separately
cataloged, and may be independently held.  This worked because
ExecCheckRTEPerms() was called with a ACL_INSERT or ACL_UPDATE
requiredPerms, and based on that it was evident which type of
optimizable statement was under consideration.  Since historically no
type of optimizable statement could directly INSERT and UPDATE at the
same time, there was no ambiguity as to which privileges were required.

This largely mechanical commit is required infrastructure for the
INSERT...ON CONFLICT UPDATE feature, which introduces an optimizable
statement that may be subject to both INSERT and UPDATE permissions
enforcement.  Tests follow in a later commit.

sepgsql is also affected by this commit.  Note that this commit
necessitates an initdb, since stored ACLs are broken.
---
 contrib/postgres_fdw/postgres_fdw.c       |   2 +-
 contrib/sepgsql/dml.c                     |  31 ++++++---
 src/backend/commands/copy.c               |   2 +-
 src/backend/commands/createas.c           |   2 +-
 src/backend/commands/trigger.c            |  22 +++---
 src/backend/executor/execMain.c           | 110 +++++++++++++++++++-----------
 src/backend/nodes/copyfuncs.c             |   3 +-
 src/backend/nodes/equalfuncs.c            |   3 +-
 src/backend/nodes/outfuncs.c              |   3 +-
 src/backend/nodes/readfuncs.c             |   3 +-
 src/backend/optimizer/plan/setrefs.c      |   6 +-
 src/backend/optimizer/prep/prepsecurity.c |   6 +-
 src/backend/optimizer/prep/prepunion.c    |   8 ++-
 src/backend/parser/analyze.c              |   4 +-
 src/backend/parser/parse_relation.c       |  21 ++++--
 src/backend/rewrite/rewriteHandler.c      |  52 ++++++++------
 src/include/nodes/parsenodes.h            |  14 ++--
 17 files changed, 177 insertions(+), 115 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 0143fb4..8e20680 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1202,7 +1202,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 		int			col;
 
 		col = -1;
-		while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
+		while ((col = bms_next_member(rte->updatedCols, col)) >= 0)
 		{
 			/* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
 			AttrNumber	attno = col + FirstLowInvalidHeapAttributeNumber;
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index 36c6a37..4a71753 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -145,7 +145,8 @@ fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
 static bool
 check_relation_privileges(Oid relOid,
 						  Bitmapset *selected,
-						  Bitmapset *modified,
+						  Bitmapset *inserted,
+						  Bitmapset *updated,
 						  uint32 required,
 						  bool abort_on_violation)
 {
@@ -231,8 +232,9 @@ check_relation_privileges(Oid relOid,
 	 * Check permissions on the columns
 	 */
 	selected = fixup_whole_row_references(relOid, selected);
-	modified = fixup_whole_row_references(relOid, modified);
-	columns = bms_union(selected, modified);
+	inserted = fixup_whole_row_references(relOid, inserted);
+	updated = fixup_whole_row_references(relOid, updated);
+	columns = bms_union(selected, bms_union(inserted, updated));
 
 	while ((index = bms_first_member(columns)) >= 0)
 	{
@@ -241,13 +243,16 @@ check_relation_privileges(Oid relOid,
 
 		if (bms_is_member(index, selected))
 			column_perms |= SEPG_DB_COLUMN__SELECT;
-		if (bms_is_member(index, modified))
+		if (bms_is_member(index, inserted))
 		{
-			if (required & SEPG_DB_TABLE__UPDATE)
-				column_perms |= SEPG_DB_COLUMN__UPDATE;
 			if (required & SEPG_DB_TABLE__INSERT)
 				column_perms |= SEPG_DB_COLUMN__INSERT;
 		}
+		if (bms_is_member(index, updated))
+		{
+			if (required & SEPG_DB_TABLE__UPDATE)
+				column_perms |= SEPG_DB_COLUMN__UPDATE;
+		}
 		if (column_perms == 0)
 			continue;
 
@@ -304,7 +309,7 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 			required |= SEPG_DB_TABLE__INSERT;
 		if (rte->requiredPerms & ACL_UPDATE)
 		{
-			if (!bms_is_empty(rte->modifiedCols))
+			if (!bms_is_empty(rte->updatedCols))
 				required |= SEPG_DB_TABLE__UPDATE;
 			else
 				required |= SEPG_DB_TABLE__LOCK;
@@ -333,7 +338,8 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 		{
 			Oid			tableOid = lfirst_oid(li);
 			Bitmapset  *selectedCols;
-			Bitmapset  *modifiedCols;
+			Bitmapset  *insertedCols;
+			Bitmapset  *updatedCols;
 
 			/*
 			 * child table has different attribute numbers, so we need to fix
@@ -341,15 +347,18 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 			 */
 			selectedCols = fixup_inherited_columns(rte->relid, tableOid,
 												   rte->selectedCols);
-			modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
-												   rte->modifiedCols);
+			insertedCols = fixup_inherited_columns(rte->relid, tableOid,
+												   rte->insertedCols);
+			updatedCols = fixup_inherited_columns(rte->relid, tableOid,
+												  rte->updatedCols);
 
 			/*
 			 * check permissions on individual tables
 			 */
 			if (!check_relation_privileges(tableOid,
 										   selectedCols,
-										   modifiedCols,
+										   insertedCols,
+										   updatedCols,
 										   required, abort_on_violation))
 				return false;
 		}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f85bd60..2d45eb3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -847,7 +847,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
 			FirstLowInvalidHeapAttributeNumber;
 
 			if (is_from)
-				rte->modifiedCols = bms_add_member(rte->modifiedCols, attno);
+				rte->insertedCols = bms_add_member(rte->insertedCols, attno);
 			else
 				rte->selectedCols = bms_add_member(rte->selectedCols, attno);
 		}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 54b2f38..e8f0d79 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -433,7 +433,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	rte->requiredPerms = ACL_INSERT;
 
 	for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++)
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
+		rte->insertedCols = bms_add_member(rte->insertedCols,
 								attnum - FirstLowInvalidHeapAttributeNumber);
 
 	ExecCheckRTPerms(list_make1(rte), true);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index e491c5b..847dc5c 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -71,8 +71,8 @@ static int	MyTriggerDepth = 0;
  * it uses, so we let them be duplicated.  Be sure to update both if one needs
  * to be changed, however.
  */
-#define GetModifiedColumns(relinfo, estate) \
-	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
+#define GetUpdatedColumns(relinfo, estate) \
+	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
 
 /* Local function prototypes */
 static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
@@ -2347,7 +2347,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	TriggerDesc *trigdesc;
 	int			i;
 	TriggerData LocTriggerData;
-	Bitmapset  *modifiedCols;
+	Bitmapset  *updatedCols;
 
 	trigdesc = relinfo->ri_TrigDesc;
 
@@ -2356,7 +2356,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	if (!trigdesc->trig_update_before_statement)
 		return;
 
-	modifiedCols = GetModifiedColumns(relinfo, estate);
+	updatedCols = GetUpdatedColumns(relinfo, estate);
 
 	LocTriggerData.type = T_TriggerData;
 	LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
@@ -2377,7 +2377,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 								  TRIGGER_TYPE_UPDATE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							modifiedCols, NULL, NULL))
+							updatedCols, NULL, NULL))
 			continue;
 
 		LocTriggerData.tg_trigger = trigger;
@@ -2402,7 +2402,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	if (trigdesc && trigdesc->trig_update_after_statement)
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
 							  false, NULL, NULL, NIL,
-							  GetModifiedColumns(relinfo, estate));
+							  GetUpdatedColumns(relinfo, estate));
 }
 
 TupleTableSlot *
@@ -2420,7 +2420,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	HeapTuple	oldtuple;
 	TupleTableSlot *newSlot;
 	int			i;
-	Bitmapset  *modifiedCols;
+	Bitmapset  *updatedCols;
 	Bitmapset  *keyCols;
 	LockTupleMode lockmode;
 
@@ -2429,10 +2429,10 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	 * been modified, then we can use a weaker lock, allowing for better
 	 * concurrency.
 	 */
-	modifiedCols = GetModifiedColumns(relinfo, estate);
+	updatedCols = GetUpdatedColumns(relinfo, estate);
 	keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc,
 										 INDEX_ATTR_BITMAP_KEY);
-	if (bms_overlap(keyCols, modifiedCols))
+	if (bms_overlap(keyCols, updatedCols))
 		lockmode = LockTupleExclusive;
 	else
 		lockmode = LockTupleNoKeyExclusive;
@@ -2486,7 +2486,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 								  TRIGGER_TYPE_UPDATE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							modifiedCols, trigtuple, newtuple))
+							updatedCols, trigtuple, newtuple))
 			continue;
 
 		LocTriggerData.tg_trigtuple = trigtuple;
@@ -2556,7 +2556,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
 							  true, trigtuple, newtuple, recheckIndexes,
-							  GetModifiedColumns(relinfo, estate));
+							  GetUpdatedColumns(relinfo, estate));
 		if (trigtuple != fdw_trigtuple)
 			heap_freetuple(trigtuple);
 	}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index b69371e..5012f87 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -82,6 +82,9 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
 			ScanDirection direction,
 			DestReceiver *dest);
 static bool ExecCheckRTEPerms(RangeTblEntry *rte);
+static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
+						  Bitmapset *modifiedCols,
+						  AclMode requiredPerms);
 static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
 static char *ExecBuildSlotValueDescription(Oid reloid,
 							  TupleTableSlot *slot,
@@ -97,8 +100,10 @@ static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
  * it uses, so we let them be duplicated.  Be sure to update both if one needs
  * to be changed, however.
  */
-#define GetModifiedColumns(relinfo, estate) \
-	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
+#define GetUpdatedColumns(relinfo, estate) \
+	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
+#define GetInsertedColumns(relinfo, estate) \
+	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->insertedCols)
 
 /* end of local decls */
 
@@ -559,7 +564,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 	AclMode		remainingPerms;
 	Oid			relOid;
 	Oid			userid;
-	int			col;
 
 	/*
 	 * Only plain-relation RTEs need to be checked here.  Function RTEs are
@@ -597,6 +601,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 	remainingPerms = requiredPerms & ~relPerms;
 	if (remainingPerms != 0)
 	{
+		int			col = -1;
+
 		/*
 		 * If we lack any permissions that exist only as relation permissions,
 		 * we can fail straight away.
@@ -625,7 +631,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 					return false;
 			}
 
-			col = -1;
 			while ((col = bms_next_member(rte->selectedCols, col)) >= 0)
 			{
 				/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
@@ -648,43 +653,63 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 		}
 
 		/*
-		 * Basically the same for the mod columns, with either INSERT or
-		 * UPDATE privilege as specified by remainingPerms.
+		 * Basically the same for the mod columns, for both INSERT and UPDATE
+		 * privilege as specified by remainingPerms.
 		 */
-		remainingPerms &= ~ACL_SELECT;
-		if (remainingPerms != 0)
-		{
-			/*
-			 * When the query doesn't explicitly change any columns, allow the
-			 * query if we have permission on any column of the rel.  This is
-			 * to handle SELECT FOR UPDATE as well as possible corner cases in
-			 * INSERT and UPDATE.
-			 */
-			if (bms_is_empty(rte->modifiedCols))
-			{
-				if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms,
-											  ACLMASK_ANY) != ACLCHECK_OK)
-					return false;
-			}
+		if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid,
+																	  userid,
+																	  rte->insertedCols,
+																	  ACL_INSERT))
+			return false;
 
-			col = -1;
-			while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
-			{
-				/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
-				AttrNumber	attno = col + FirstLowInvalidHeapAttributeNumber;
+		if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid,
+																	  userid,
+																	  rte->updatedCols,
+																	  ACL_UPDATE))
+			return false;
+	}
+	return true;
+}
 
-				if (attno == InvalidAttrNumber)
-				{
-					/* whole-row reference can't happen here */
-					elog(ERROR, "whole-row update is not implemented");
-				}
-				else
-				{
-					if (pg_attribute_aclcheck(relOid, attno, userid,
-											  remainingPerms) != ACLCHECK_OK)
-						return false;
-				}
-			}
+/*
+ * ExecCheckRTEPermsModified
+ *		Check INSERT or UPDATE access permissions for a single RTE (these
+ *		are processed uniformly).
+ */
+static bool
+ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols,
+						  AclMode requiredPerms)
+{
+	int			col = -1;
+
+	/*
+	 * When the query doesn't explicitly update any columns, allow the
+	 * query if we have permission on any column of the rel.  This is
+	 * to handle SELECT FOR UPDATE as well as possible corner cases in
+	 * UPDATE.
+	 */
+	if (bms_is_empty(modifiedCols))
+	{
+		if (pg_attribute_aclcheck_all(relOid, userid, requiredPerms,
+									  ACLMASK_ANY) != ACLCHECK_OK)
+			return false;
+	}
+
+	while ((col = bms_next_member(modifiedCols, col)) >= 0)
+	{
+		/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
+		AttrNumber	attno = col + FirstLowInvalidHeapAttributeNumber;
+
+		if (attno == InvalidAttrNumber)
+		{
+			/* whole-row reference can't happen here */
+			elog(ERROR, "whole-row update is not implemented");
+		}
+		else
+		{
+			if (pg_attribute_aclcheck(relOid, attno, userid,
+									  requiredPerms) != ACLCHECK_OK)
+				return false;
 		}
 	}
 	return true;
@@ -1623,7 +1648,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				char	   *val_desc;
 				Bitmapset  *modifiedCols;
 
-				modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+				modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
+				modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
 				val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
 														 slot,
 														 tupdesc,
@@ -1649,7 +1675,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
 
-			modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+			modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
+			modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
 			val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
 													 slot,
 													 tupdesc,
@@ -1708,7 +1735,8 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
 
-			modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+			modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
+			modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
 			val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
 													 slot,
 													 tupdesc,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 393248a..4f033a2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2034,7 +2034,8 @@ _copyRangeTblEntry(const RangeTblEntry *from)
 	COPY_SCALAR_FIELD(requiredPerms);
 	COPY_SCALAR_FIELD(checkAsUser);
 	COPY_BITMAPSET_FIELD(selectedCols);
-	COPY_BITMAPSET_FIELD(modifiedCols);
+	COPY_BITMAPSET_FIELD(insertedCols);
+	COPY_BITMAPSET_FIELD(updatedCols);
 	COPY_NODE_FIELD(securityQuals);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index aa47887..8b99eab 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2352,7 +2352,8 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
 	COMPARE_SCALAR_FIELD(requiredPerms);
 	COMPARE_SCALAR_FIELD(checkAsUser);
 	COMPARE_BITMAPSET_FIELD(selectedCols);
-	COMPARE_BITMAPSET_FIELD(modifiedCols);
+	COMPARE_BITMAPSET_FIELD(insertedCols);
+	COMPARE_BITMAPSET_FIELD(updatedCols);
 	COMPARE_NODE_FIELD(securityQuals);
 
 	return true;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f92914a..fa41ce1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2468,7 +2468,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_UINT_FIELD(requiredPerms);
 	WRITE_OID_FIELD(checkAsUser);
 	WRITE_BITMAPSET_FIELD(selectedCols);
-	WRITE_BITMAPSET_FIELD(modifiedCols);
+	WRITE_BITMAPSET_FIELD(insertedCols);
+	WRITE_BITMAPSET_FIELD(updatedCols);
 	WRITE_NODE_FIELD(securityQuals);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index e492ef6..fc31573 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1258,7 +1258,8 @@ _readRangeTblEntry(void)
 	READ_UINT_FIELD(requiredPerms);
 	READ_OID_FIELD(checkAsUser);
 	READ_BITMAPSET_FIELD(selectedCols);
-	READ_BITMAPSET_FIELD(modifiedCols);
+	READ_BITMAPSET_FIELD(insertedCols);
+	READ_BITMAPSET_FIELD(updatedCols);
 	READ_NODE_FIELD(securityQuals);
 
 	READ_DONE();
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index ec828cd..5dd48f8 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -367,9 +367,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob)
  *
  * In the flat rangetable, we zero out substructure pointers that are not
  * needed by the executor; this reduces the storage space and copying cost
- * for cached plans.  We keep only the alias and eref Alias fields, which
- * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
- * which are needed for executor-startup permissions checking and for
+ * for cached plans.  We keep only the alias and eref Alias fields, which are
+ * needed by EXPLAIN, and the selectedCols, insertedCols and updatedCols
+ * bitmaps, which are needed for executor-startup permissions checking and for
  * trigger event checking.
  */
 static void
diff --git a/src/backend/optimizer/prep/prepsecurity.c b/src/backend/optimizer/prep/prepsecurity.c
index b382f13..ddd2c37 100644
--- a/src/backend/optimizer/prep/prepsecurity.c
+++ b/src/backend/optimizer/prep/prepsecurity.c
@@ -125,7 +125,8 @@ expand_security_quals(PlannerInfo *root, List *tlist)
 			rte->requiredPerms = 0;
 			rte->checkAsUser = InvalidOid;
 			rte->selectedCols = NULL;
-			rte->modifiedCols = NULL;
+			rte->insertedCols = NULL;
+			rte->updatedCols = NULL;
 
 			/*
 			 * For the most part, Vars referencing the original relation
@@ -224,7 +225,8 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
 			rte->requiredPerms = 0;
 			rte->checkAsUser = InvalidOid;
 			rte->selectedCols = NULL;
-			rte->modifiedCols = NULL;
+			rte->insertedCols = NULL;
+			rte->updatedCols = NULL;
 
 			/*
 			 * Now deal with any PlanRowMark on this RTE by requesting a lock
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b90fee3..9516244 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1367,14 +1367,16 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 		 * if this is the parent table, leave copyObject's result alone.
 		 *
 		 * Note: we need to do this even though the executor won't run any
-		 * permissions checks on the child RTE.  The modifiedCols bitmap may
-		 * be examined for trigger-firing purposes.
+		 * permissions checks on the child RTE.  The insertedCols/updatedCols
+		 * bitmaps may be examined for trigger-firing purposes.
 		 */
 		if (childOID != parentOID)
 		{
 			childrte->selectedCols = translate_col_privs(rte->selectedCols,
 												   appinfo->translated_vars);
-			childrte->modifiedCols = translate_col_privs(rte->modifiedCols,
+			childrte->insertedCols = translate_col_privs(rte->insertedCols,
+												   appinfo->translated_vars);
+			childrte->updatedCols = translate_col_privs(rte->updatedCols,
 												   appinfo->translated_vars);
 		}
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 1ad9492..168f970 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -735,7 +735,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 							  false);
 		qry->targetList = lappend(qry->targetList, tle);
 
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
+		rte->insertedCols = bms_add_member(rte->insertedCols,
 							  attr_num - FirstLowInvalidHeapAttributeNumber);
 
 		icols = lnext(icols);
@@ -2019,7 +2019,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 							  origTarget->location);
 
 		/* Mark the target column as requiring update permissions */
-		target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols,
+		target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
 								attrno - FirstLowInvalidHeapAttributeNumber);
 
 		origTargetList = lnext(origTargetList);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index f416fc2..c20698f 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1051,7 +1051,8 @@ addRangeTableEntry(ParseState *pstate,
 	rte->requiredPerms = ACL_SELECT;
 	rte->checkAsUser = InvalidOid;		/* not set-uid by default, either */
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1103,7 +1104,8 @@ addRangeTableEntryForRelation(ParseState *pstate,
 	rte->requiredPerms = ACL_SELECT;
 	rte->checkAsUser = InvalidOid;		/* not set-uid by default, either */
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1181,7 +1183,8 @@ addRangeTableEntryForSubquery(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1435,7 +1438,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1507,7 +1511,8 @@ addRangeTableEntryForValues(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1575,7 +1580,8 @@ addRangeTableEntryForJoin(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1675,7 +1681,8 @@ addRangeTableEntryForCTE(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 40458a0..86f1fc9 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1407,7 +1407,8 @@ ApplyRetrieveRule(Query *parsetree,
 			rte->requiredPerms = 0;
 			rte->checkAsUser = InvalidOid;
 			rte->selectedCols = NULL;
-			rte->modifiedCols = NULL;
+			rte->insertedCols = NULL;
+			rte->updatedCols = NULL;
 
 			/*
 			 * For the most part, Vars referencing the view should remain as
@@ -1470,12 +1471,14 @@ ApplyRetrieveRule(Query *parsetree,
 	subrte->requiredPerms = rte->requiredPerms;
 	subrte->checkAsUser = rte->checkAsUser;
 	subrte->selectedCols = rte->selectedCols;
-	subrte->modifiedCols = rte->modifiedCols;
+	subrte->insertedCols = rte->insertedCols;
+	subrte->updatedCols = rte->updatedCols;
 
 	rte->requiredPerms = 0;		/* no permission check on subquery itself */
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as
@@ -2588,9 +2591,9 @@ rewriteTargetView(Query *parsetree, Relation view)
 	/*
 	 * For INSERT/UPDATE the modified columns must all be updatable. Note that
 	 * we get the modified columns from the query's targetlist, not from the
-	 * result RTE's modifiedCols set, since rewriteTargetListIU may have added
-	 * additional targetlist entries for view defaults, and these must also be
-	 * updatable.
+	 * result RTE's insertedCols and/or updatedCols set, since
+	 * rewriteTargetListIU may have added additional targetlist entries for
+	 * view defaults, and these must also be updatable.
 	 */
 	if (parsetree->commandType != CMD_DELETE)
 	{
@@ -2727,26 +2730,31 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 *
 	 * Initially, new_rte contains selectedCols permission check bits for all
 	 * base-rel columns referenced by the view, but since the view is a SELECT
-	 * query its modifiedCols is empty.  We set modifiedCols to include all
-	 * the columns the outer query is trying to modify, adjusting the column
-	 * numbers as needed.  But we leave selectedCols as-is, so the view owner
-	 * must have read permission for all columns used in the view definition,
-	 * even if some of them are not read by the outer query.  We could try to
-	 * limit selectedCols to only columns used in the transformed query, but
-	 * that does not correspond to what happens in ordinary SELECT usage of a
-	 * view: all referenced columns must have read permission, even if
-	 * optimization finds that some of them can be discarded during query
-	 * transformation.  The flattening we're doing here is an optional
-	 * optimization, too.  (If you are unpersuaded and want to change this,
-	 * note that applying adjust_view_column_set to view_rte->selectedCols is
-	 * clearly *not* the right answer, since that neglects base-rel columns
-	 * used in the view's WHERE quals.)
+	 * query its insertedCols/updatedCols is empty.  We set insertedCols and
+	 * updatedCols to include all the columns the outer query is trying to
+	 * modify, adjusting the column numbers as needed.  But we leave
+	 * selectedCols as-is, so the view owner must have read permission for all
+	 * columns used in the view definition, even if some of them are not read
+	 * by the outer query.  We could try to limit selectedCols to only columns
+	 * used in the transformed query, but that does not correspond to what
+	 * happens in ordinary SELECT usage of a view: all referenced columns must
+	 * have read permission, even if optimization finds that some of them can
+	 * be discarded during query transformation.  The flattening we're doing
+	 * here is an optional optimization, too.  (If you are unpersuaded and want
+	 * to change this, note that applying adjust_view_column_set to
+	 * view_rte->selectedCols is clearly *not* the right answer, since that
+	 * neglects base-rel columns used in the view's WHERE quals.)
 	 *
 	 * This step needs the modified view targetlist, so we have to do things
 	 * in this order.
 	 */
-	Assert(bms_is_empty(new_rte->modifiedCols));
-	new_rte->modifiedCols = adjust_view_column_set(view_rte->modifiedCols,
+	Assert(bms_is_empty(new_rte->insertedCols) &&
+		   bms_is_empty(new_rte->updatedCols));
+
+	new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols,
+												   view_targetlist);
+
+	new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols,
 												   view_targetlist);
 
 	/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4252559..71309e1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -728,11 +728,12 @@ typedef struct XmlSerialize
  *	  For SELECT/INSERT/UPDATE permissions, if the user doesn't have
  *	  table-wide permissions then it is sufficient to have the permissions
  *	  on all columns identified in selectedCols (for SELECT) and/or
- *	  modifiedCols (for INSERT/UPDATE; we can tell which from the query type).
- *	  selectedCols and modifiedCols are bitmapsets, which cannot have negative
- *	  integer members, so we subtract FirstLowInvalidHeapAttributeNumber from
- *	  column numbers before storing them in these fields.  A whole-row Var
- *	  reference is represented by setting the bit for InvalidAttrNumber.
+ *	  insertedCols and/or updatedCols (INSERT with ON CONFLICT UPDATE may
+ *	  have all 3).  selectedCols, insertedCols and updatedCols are
+ *	  bitmapsets, which cannot have negative integer members, so we subtract
+ *	  FirstLowInvalidHeapAttributeNumber from column numbers before storing
+ *	  them in these fields.  A whole-row Var reference is represented by
+ *	  setting the bit for InvalidAttrNumber.
  *--------------------
  */
 typedef enum RTEKind
@@ -827,7 +828,8 @@ typedef struct RangeTblEntry
 	AclMode		requiredPerms;	/* bitmask of required access permissions */
 	Oid			checkAsUser;	/* if valid, check access as this role */
 	Bitmapset  *selectedCols;	/* columns needing SELECT permission */
-	Bitmapset  *modifiedCols;	/* columns needing INSERT/UPDATE permission */
+	Bitmapset  *insertedCols;	/* columns needing INSERT permission */
+	Bitmapset  *updatedCols;	/* columns needing UPDATE permission */
 	List	   *securityQuals;	/* any security barrier quals to apply */
 } RangeTblEntry;
 
-- 
1.9.1

0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patchtext/x-patch; charset=US-ASCII; name=0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patchDownload
From 2853f838751591c00934d4c8bb0568b5e529b5ac Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <peter.geoghegan86@gmail.com>
Date: Tue, 3 Mar 2015 19:44:42 -0800
Subject: [PATCH 1/4] Support INSERT ... ON CONFLICT IGNORE

This non-standard INSERT clause allows DML statement authors to specify
that in the event of each of any of the tuples being inserted
duplicating an existing tuple in terms of a value or set of values
constrained by a unique index, an alternative IGNORE path may be taken
(the tuple slot proposed for insertion is skipped without raising an
error).  The implementation loops until either an insert occurs, or a
conclusively committed conflicting tuple is determined to exist.

This is implemented using a new infrastructure called "speculative
insertion".  (The approach to "Value locking" presenting here follows
design #2, as described on the value locking Postgres Wiki page).

Users optionally specify a single unique index to take the alternative
path on, which is inferred from a set of user-supplied column names (or
expressions).

To support logical decoding, transaction commit reassembly now
consolidates speculative insertion related changes.  Using a
"peek-ahead", it is determined if a speculative insertion went on to be
super deleted.  If that is not the case, then it is reported as an
ordinary insertion to decoding plugins.
---
 contrib/pg_stat_statements/pg_stat_statements.c    |   3 +
 contrib/postgres_fdw/deparse.c                     |   7 +-
 contrib/postgres_fdw/expected/postgres_fdw.out     |   4 +
 contrib/postgres_fdw/postgres_fdw.c                |  10 +-
 contrib/postgres_fdw/postgres_fdw.h                |   2 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql          |   2 +
 doc/src/sgml/ddl.sgml                              |  18 ++
 doc/src/sgml/fdwhandler.sgml                       |   7 +
 doc/src/sgml/keywords.sgml                         |   7 +
 doc/src/sgml/postgres-fdw.sgml                     |   6 +
 doc/src/sgml/ref/create_rule.sgml                  |   7 +-
 doc/src/sgml/ref/create_table.sgml                 |   5 +-
 doc/src/sgml/ref/create_view.sgml                  |  26 +-
 doc/src/sgml/ref/insert.sgml                       | 190 +++++++++++-
 doc/src/sgml/ref/set_constraints.sgml              |   6 +-
 src/backend/access/heap/heapam.c                   | 107 +++++--
 src/backend/access/nbtree/nbtinsert.c              |  32 +-
 src/backend/catalog/index.c                        |  59 +++-
 src/backend/catalog/indexing.c                     |   2 +-
 src/backend/commands/constraint.c                  |   7 +-
 src/backend/commands/copy.c                        |   7 +-
 src/backend/commands/explain.c                     |   9 +
 src/backend/executor/README                        |  73 +++++
 src/backend/executor/execMain.c                    |  13 +-
 src/backend/executor/execUtils.c                   | 322 ++++++++++++++++++---
 src/backend/executor/nodeLockRows.c                |   9 +-
 src/backend/executor/nodeModifyTable.c             | 155 +++++++++-
 src/backend/nodes/copyfuncs.c                      |  36 +++
 src/backend/nodes/equalfuncs.c                     |  30 ++
 src/backend/nodes/nodeFuncs.c                      |  31 ++
 src/backend/nodes/outfuncs.c                       |   5 +
 src/backend/nodes/readfuncs.c                      |   3 +
 src/backend/optimizer/path/indxpath.c              |  57 ++++
 src/backend/optimizer/plan/createplan.c            |  13 +-
 src/backend/optimizer/plan/planner.c               |   2 +
 src/backend/optimizer/util/plancat.c               | 211 ++++++++++++++
 src/backend/parser/analyze.c                       |  29 +-
 src/backend/parser/gram.y                          |  48 ++-
 src/backend/parser/parse_clause.c                  | 160 ++++++++++
 src/backend/replication/logical/decode.c           |  11 +-
 src/backend/replication/logical/reorderbuffer.c    |  83 +++++-
 src/backend/rewrite/rewriteHandler.c               |  25 +-
 src/backend/storage/ipc/procarray.c                | 109 +++++++
 src/backend/storage/lmgr/lmgr.c                    |  81 ++++++
 src/backend/utils/adt/lockfuncs.c                  |   1 +
 src/backend/utils/time/tqual.c                     |  46 ++-
 src/include/access/heapam.h                        |   3 +-
 src/include/access/heapam_xlog.h                   |   2 +
 src/include/access/htup_details.h                  |  12 +
 src/include/catalog/index.h                        |   2 +
 src/include/executor/executor.h                    |  19 +-
 src/include/nodes/execnodes.h                      |   8 +
 src/include/nodes/nodes.h                          |  14 +
 src/include/nodes/parsenodes.h                     |  35 ++-
 src/include/nodes/plannodes.h                      |   2 +
 src/include/optimizer/paths.h                      |   1 +
 src/include/optimizer/plancat.h                    |   2 +
 src/include/optimizer/planmain.h                   |   2 +-
 src/include/parser/kwlist.h                        |   2 +
 src/include/parser/parse_clause.h                  |   2 +
 src/include/replication/reorderbuffer.h            |  11 +-
 src/include/storage/lmgr.h                         |   5 +
 src/include/storage/lock.h                         |  10 +
 src/include/storage/proc.h                         |  13 +
 src/include/storage/procarray.h                    |   7 +
 src/include/utils/snapshot.h                       |  11 +
 .../isolation/expected/insert-conflict-ignore.out  |  23 ++
 src/test/isolation/isolation_schedule              |   1 +
 .../isolation/specs/insert-conflict-ignore.spec    |  41 +++
 src/test/regress/expected/insert_conflict.out      |  55 ++++
 src/test/regress/expected/rules.out                |   9 +
 src/test/regress/expected/updatable_views.out      |   4 +
 src/test/regress/parallel_schedule                 |   1 +
 src/test/regress/serial_schedule                   |   1 +
 src/test/regress/sql/insert_conflict.sql           |  65 +++++
 src/test/regress/sql/rules.sql                     |   6 +
 src/test/regress/sql/updatable_views.sql           |   2 +
 77 files changed, 2311 insertions(+), 136 deletions(-)
 create mode 100644 src/test/isolation/expected/insert-conflict-ignore.out
 create mode 100644 src/test/isolation/specs/insert-conflict-ignore.spec
 create mode 100644 src/test/regress/expected/insert_conflict.out
 create mode 100644 src/test/regress/sql/insert_conflict.sql

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 95616b3..46f5189 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2198,6 +2198,9 @@ JumbleQuery(pgssJumbleState *jstate, Query *query)
 	JumbleRangeTable(jstate, query->rtable);
 	JumbleExpr(jstate, (Node *) query->jointree);
 	JumbleExpr(jstate, (Node *) query->targetList);
+	APP_JUMB(query->specClause);
+	JumbleExpr(jstate, (Node *) query->arbiterExpr);
+	JumbleExpr(jstate, query->arbiterWhere);
 	JumbleExpr(jstate, (Node *) query->returningList);
 	JumbleExpr(jstate, (Node *) query->groupClause);
 	JumbleExpr(jstate, query->havingQual);
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 59cb053..ca51586 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -847,8 +847,8 @@ appendWhereClause(StringInfo buf,
 void
 deparseInsertSql(StringInfo buf, PlannerInfo *root,
 				 Index rtindex, Relation rel,
-				 List *targetAttrs, List *returningList,
-				 List **retrieved_attrs)
+				 List *targetAttrs, bool ignore,
+				 List *returningList, List **retrieved_attrs)
 {
 	AttrNumber	pindex;
 	bool		first;
@@ -892,6 +892,9 @@ deparseInsertSql(StringInfo buf, PlannerInfo *root,
 	else
 		appendStringInfoString(buf, " DEFAULT VALUES");
 
+	if (ignore)
+		appendStringInfoString(buf, " ON CONFLICT IGNORE");
+
 	deparseReturningList(buf, root, rtindex, rel,
 					   rel->trigdesc && rel->trigdesc->trig_insert_after_row,
 						 returningList, retrieved_attrs);
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 583cce7..76d36e6 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -2327,6 +2327,10 @@ INSERT INTO ft1(c1, c2) VALUES(11, 12);  -- duplicate key
 ERROR:  duplicate key value violates unique constraint "t1_pkey"
 DETAIL:  Key ("C 1")=(11) already exists.
 CONTEXT:  Remote SQL command: INSERT INTO "S 1"."T 1"("C 1", c2, c3, c4, c5, c6, c7, c8) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
+ERROR:  relation "ft1" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 ERROR:  new row for relation "T 1" violates check constraint "c2positive"
 DETAIL:  Failing row contains (1111, -2, null, null, null, null, ft1       , null).
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 63f0577..0143fb4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1167,6 +1167,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	List	   *targetAttrs = NIL;
 	List	   *returningList = NIL;
 	List	   *retrieved_attrs = NIL;
+	bool		ignore = false;
 
 	initStringInfo(&sql);
 
@@ -1218,6 +1219,13 @@ postgresPlanForeignModify(PlannerInfo *root,
 	if (plan->returningLists)
 		returningList = (List *) list_nth(plan->returningLists, subplan_index);
 
+	if (root->parse->arbiterExpr)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("postgres_fdw does not support ON CONFLICT unique index inference")));
+	else if (plan->spec == SPEC_IGNORE)
+		ignore = true;
+
 	/*
 	 * Construct the SQL command string.
 	 */
@@ -1225,7 +1233,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	{
 		case CMD_INSERT:
 			deparseInsertSql(&sql, root, resultRelation, rel,
-							 targetAttrs, returningList,
+							 targetAttrs, ignore, returningList,
 							 &retrieved_attrs);
 			break;
 		case CMD_UPDATE:
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 950c6f7..3763a57 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -60,7 +60,7 @@ extern void appendWhereClause(StringInfo buf,
 				  List **params);
 extern void deparseInsertSql(StringInfo buf, PlannerInfo *root,
 				 Index rtindex, Relation rel,
-				 List *targetAttrs, List *returningList,
+				 List *targetAttrs, bool ignore, List *returningList,
 				 List **retrieved_attrs);
 extern void deparseUpdateSql(StringInfo buf, PlannerInfo *root,
 				 Index rtindex, Relation rel,
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 83e8fa7..0078747 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -372,6 +372,8 @@ UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *;
 ALTER TABLE "S 1"."T 1" ADD CONSTRAINT c2positive CHECK (c2 >= 0);
 
 INSERT INTO ft1(c1, c2) VALUES(11, 12);  -- duplicate key
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 UPDATE ft1 SET c2 = -c2 WHERE c1 = 1;  -- c2positive
 
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 570a003..dd3fc38 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2428,9 +2428,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
   </para>
 
   <para>
+   There is limited inheritance support for <command>INSERT</command>
+   commands with <literal>ON CONFLICT IGNORE</> clauses.  Tables with
+   children are not generally accepted as targets.  One notable
+   exception is that such tables are accepted as targets for
+   <command>INSERT</command> commands with <literal>ON CONFLICT
+   IGNORE</> clauses, provided a unique index inference clause was
+   omitted (which implies that there is no concern about
+   <emphasis>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 <command>INSERT</command>
+   with <literal>ON CONFLICT IGNORE</>.
+  </para>
+
+  <para>
    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, <command>INSERT</command> with <literal>ON CONFLICT</>
+   unique index inference considers only unique constraints/indexes
+   directly associated with the child
+   table.
   </para>
 
   <para>
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index c1daa4b..46b8db8 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1014,6 +1014,13 @@ GetForeignServerByName(const char *name, bool missing_ok);
      source provides.
     </para>
 
+    <para>
+     <command>INSERT</> with an <literal>ON CONFLICT</> clause is not
+     supported with a unique index inference specification.  When
+     planning an <command>INSERT</>, <function>PlanForeignModify</>
+     should reject these cases.
+    </para>
+
   </sect1>
 
  </chapter>
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 @@
     <entry></entry>
    </row>
    <row>
+    <entry><token>CONFLICT</token></entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
+   <row>
     <entry><token>CONNECT</token></entry>
     <entry></entry>
     <entry>reserved</entry>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 43adb61..81d4441 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -69,6 +69,12 @@
  </para>
 
  <para>
+  <filename>postgres_fdw</> supports <command>INSERT</command>
+  statements with an <literal>ON CONFLICT IGNORE</> clause, provided a
+  unique index inference specification is omitted.
+ </para>
+
+ <para>
   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 <filename>postgres_fdw</>
diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml
index 677766a..045f5d1 100644
--- a/doc/src/sgml/ref/create_rule.sgml
+++ b/doc/src/sgml/ref/create_rule.sgml
@@ -136,7 +136,12 @@ CREATE [ OR REPLACE ] RULE <replaceable class="parameter">name</replaceable> AS
      <para>
       The event is one of <literal>SELECT</literal>,
       <literal>INSERT</literal>, <literal>UPDATE</literal>, or
-      <literal>DELETE</literal>.
+      <literal>DELETE</literal>.  Note that an
+      <command>INSERT</command> containing an <literal>ON CONFLICT
+      IGNORE</literal> clause cannot be used on tables that have
+      either <literal>INSERT</literal> or <literal>UPDATE</literal>
+      rules.  Consider using an updatable view instead, which have
+      limited support for <literal>ON CONFLICT IGNORE</literal>.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..4733a6c 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
       <literal>EXCLUDE</>, and
       <literal>REFERENCES</> (foreign key) constraints accept this
       clause.  <literal>NOT NULL</> and <literal>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 <command>INSERT</command> statement
+      that includes an <literal>ON CONFLICT</> clause.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index 5dadab1..2b68121 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -286,8 +286,9 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
    <para>
     Simple views are automatically updatable: the system will allow
     <command>INSERT</>, <command>UPDATE</> and <command>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:
 
     <itemizedlist>
      <listitem>
@@ -383,6 +384,27 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     not need any permissions on the underlying base relations (see
     <xref linkend="rules-privileges">).
    </para>
+   <para>
+    <command>INSERT</command> with an <literal>ON CONFLICT IGNORE</>
+    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 <literal>IGNORE</> path
+    - then updatable views are not supported.  For example:
+   </para>
+   <para>
+<programlisting>
+-- Unsupported:
+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;
+</programlisting>
+   </para>
   </refsect2>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml
index a3cccb9..1395c48 100644
--- a/doc/src/sgml/ref/insert.sgml
+++ b/doc/src/sgml/ref/insert.sgml
@@ -24,6 +24,7 @@ PostgreSQL documentation
 [ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
 INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
     { DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) [, ...] | <replaceable class="PARAMETER">query</replaceable> }
+    [ ON CONFLICT [ ( { <replaceable class="parameter">column_name_index</replaceable> | ( <replaceable class="parameter">expression_index</replaceable> ) } [, ...] [ WHERE <replaceable class="PARAMETER">index_condition</replaceable> ] ) ] IGNORE]
     [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
@@ -32,9 +33,15 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
   <title>Description</title>
 
   <para>
-   <command>INSERT</command> 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.
+   <command>INSERT</command> 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
+   <literal>IGNORE</literal> path can optionally be specified, to be
+   taken in the event of detecting that proceeding with insertion
+   would result in a conflict (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.
   </para>
 
   <para>
@@ -59,6 +66,100 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
   </para>
 
   <para>
+   The optional <literal>ON CONFLICT</> clause specifies a path to
+   take as an alternative to raising a conflict related error.
+   <literal>ON CONFLICT IGNORE</> simply avoids inserting any
+   individual row when it is determined that a conflict related error
+   would otherwise need to be raised.
+  </para>
+
+  <para>
+   <literal>ON CONFLICT IGNORE</> optionally accepts a
+   <emphasis>unique index inference</emphasis> specification, which
+   consists of one or more <replaceable
+   class="PARAMETER">column_name_index</replaceable> columns and/or
+   <replaceable class="PARAMETER">expression_index</replaceable>
+   expressions on columns, appearing between parenthesis.  These are
+   used to infer a single unique index to limit pre-checking for
+   conflicts to (if no appropriate index is available, an error is
+   raised).  A subset of the table to limit the check for conflicts to
+   can optionally also be specified using <replaceable
+   class="PARAMETER">index_condition</replaceable>.  Note that any
+   available unique index must only cover at least that subset in
+   order to be arbitrate taking the alternative path;  it need not
+   match exactly, and so a non-partial unique index that otherwise
+   matches is applicable.  Note that omitting the specification
+   indicates a total indifference to where any conflict could occur,
+   which isn't always appropriate.  At times, it may be desirable for
+   <literal>ON CONFLICT IGNORE</> to <emphasis>not</emphasis> suppress
+   a conflict related error associated with an index where that isn't
+   explicitly anticipated.
+  </para>
+
+  <para>
+   Columns and/or expressions appearing in a unique index inference
+   specification must match all the columns/expressions of some
+   existing unique index on <replaceable
+   class="PARAMETER">table_name</replaceable> - there can be no
+   columns/expressions from the unique index that do not appear in the
+   inference specification, nor can there be any columns/expressions
+   appearing in the inference specification that do not appear in the
+   unique index definition.  However, the order of the
+   columns/expressions in the index definition, or whether or not the
+   index definition specified <literal>NULLS FIRST</> or
+   <literal>NULLS LAST</>, or the internal sort order of each column
+   (whether <literal>DESC</> or <literal>ASC</> were specified) are
+   all irrelevant.  Deferred unique constraints are not supported as
+   arbiters of whether an alternative <literal>ON CONFLICT</> path
+   should be taken.
+  </para>
+
+  <para>
+   The definition of a conflict for the purposes of <literal>ON
+   CONFLICT</> is somewhat subtle, although the exact definition is
+   seldom of great interest.  A conflict is either a unique violation
+   from a unique constraint (or unique index), or an exclusion
+   violation from an exclusion constraint.  Only unique indexes can be
+   inferred with a unique index inference specification.  In contrast
+   to the rules around certain other SQL clauses, like the
+   <literal>DISTINCT</literal> clause, the definition of a duplicate
+   (a conflict) is based on whatever unique indexes happen to be
+   defined on columns on the table.  This means that if a user-defined
+   type has multiple sort orders, and the "equals" operator of any of
+   those available sort orders happens to be inconsistent (which goes
+   against an unenforced convention of
+   <productname>PostgreSQL</productname>), the exact behavior depends
+   on the choice of operator class when the unique index was created
+   initially, and not any other consideration such as the default
+   operator class for the type of each indexed column.  If there are
+   multiple unique indexes available that seem like equally suitable
+   candidates, but with inconsistent definitions of "equals", then the
+   system chooses whatever it estimates to be the cheapest one to use
+   as an arbiter of taking the alternative
+   <command>UPDATE</command>/<literal>IGNORE</literal> path.
+  </para>
+
+  <para>
+   The optional <replaceable
+   class="PARAMETER">index_condition</replaceable> can be used to
+   allow the inference specification to infer that a partial unique
+   index can be used.  Any unique index that otherwise satisfies the
+   inference specification, while also covering at least all the rows
+   in the table covered by <replaceable
+   class="PARAMETER">index_condition</replaceable> may be used.  It is
+   recommended that the partial index predicate of the unique index
+   intended to be used as the arbiter of taking the alternative path
+   be matched exactly, but this is not required.  Note that an error
+   will be raised if an arbiter unique index is chosen that does not
+   cover the tuple or tuples ultimately proposed for insertion.
+   However, an overly specific <replaceable
+   class="PARAMETER">index_condition</replaceable> does not imply that
+   arbitrating conflicts will be limited to the subset of rows covered
+   by the inferred unique index corresponding to <replaceable
+   class="PARAMETER">index_condition</replaceable>.
+  </para>
+
+  <para>
    The optional <literal>RETURNING</> clause causes <command>INSERT</>
    to compute and return value(s) based on each row actually inserted.
    This is primarily useful for obtaining values that were supplied by
@@ -127,6 +228,49 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="PARAMETER">column_name_index</replaceable></term>
+    <listitem>
+     <para>
+      The name of a <replaceable
+      class="PARAMETER">table_name</replaceable> column (with several
+      columns potentially named).  These are used to infer a
+      particular unique index defined on <replaceable
+      class="PARAMETER">table_name</replaceable>.  This requires
+      <literal>ON CONFLICT UPDATE</> and <literal>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 <literal>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.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">expression_index</replaceable></term>
+    <listitem>
+     <para>
+      Equivalent to <replaceable
+      class="PARAMETER">column_name_index</replaceable>, but used to
+      infer a particular expressional index instead.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">index_condition</replaceable></term>
+    <listitem>
+     <para>
+      Used to allow inference of partial unique indexes.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>DEFAULT VALUES</literal></term>
     <listitem>
      <para>
@@ -171,8 +315,9 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
     <listitem>
      <para>
       An expression to be computed and returned by the <command>INSERT</>
-      command after each row is inserted.  The expression can use any
-      column names of the table named by <replaceable class="PARAMETER">table_name</replaceable>.
+      command after each row is inserted (not updated). The
+      expression can use any column names of the table named by
+      <replaceable class="PARAMETER">table_name</replaceable>.
       Write <literal>*</> to return all columns of the inserted row(s).
      </para>
     </listitem>
@@ -311,7 +456,37 @@ WITH upd AS (
     RETURNING *
 )
 INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
-</programlisting></para>
+</programlisting>
+  </para>
+  <para>
+   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.
+   Example assumes a unique index has been defined that constrains
+   values appearing in the <literal>did</literal> column (although
+   since the <literal>IGNORE</> variant was used, the specification of
+   columns to infer a unique index from is not mandatory):
+<programlisting>
+  INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH')
+  ON CONFLICT (did) IGNORE
+</programlisting>
+  </para>
+  <para>
+   Insert new distributor if possible;  otherwise
+   <literal>IGNORE</literal>.  Example assumes a unique index has been
+   defined that constrains values appearing in the
+   <literal>did</literal> column on a subset of rows where the
+   <literal>is_active</literal> boolean column evaluates to
+   <literal>true</literal>:
+<programlisting>
+  -- This statement could infer a partial unique index on did
+  -- with a predicate of WHERE is_active, but it could also
+  -- just use a regular unique constraint on did if that was
+  -- all that was available.
+  INSERT INTO distributors (did, dname) VALUES (9, 'Antwerp Design')
+  ON CONFLICT (did WHERE is_active) IGNORE
+</programlisting>
+  </para>
  </refsect1>
 
  <refsect1>
@@ -321,7 +496,8 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
    <command>INSERT</command> conforms to the SQL standard, except that
    the <literal>RETURNING</> clause is a
    <productname>PostgreSQL</productname> extension, as is the ability
-   to use <literal>WITH</> with <command>INSERT</>.
+   to use <literal>WITH</> with <command>INSERT</>, and the ability to
+   specify an alternative path with <literal>ON CONFLICT</>.
    Also, the case in
    which a column name list is omitted, but not all the columns are
    filled from the <literal>VALUES</> clause or <replaceable>query</>,
diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml
index 7c31871..5030f3c 100644
--- a/doc/src/sgml/ref/set_constraints.sgml
+++ b/doc/src/sgml/ref/set_constraints.sgml
@@ -69,7 +69,11 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
   <para>
    Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>,
    <literal>REFERENCES</> (foreign key), and <literal>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
+   <command>INSERT</command> statement that includes an <literal>ON
+   CONFLICT</> clause.
    <literal>NOT NULL</> and <literal>CHECK</> constraints are
    always checked immediately when a row is inserted or modified
    (<emphasis>not</> at the end of the statement).
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index cb6f8a3..22836a5 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2048,6 +2048,9 @@ FreeBulkInsertState(BulkInsertState bistate)
  * This causes rows to be frozen, which is an MVCC violation and
  * requires explicit options chosen by user.
  *
+ * If HEAP_INSERT_SPECULATIVE is specified, the MyProc->specInsert fields
+ * are filled.
+ *
  * Note that these options will be applied when inserting into the heap's
  * TOAST table, too, if the tuple requires any out-of-line data.
  *
@@ -2156,7 +2159,11 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 		}
 
 		xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
-		xlrec.flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
+		xlrec.flags = 0;
+		if (all_visible_cleared)
+			xlrec.flags |= XLOG_HEAP_ALL_VISIBLE_CLEARED;
+		if (options & HEAP_INSERT_SPECULATIVE)
+			xlrec.flags |= XLOG_HEAP_SPECULATIVE_TUPLE;
 		Assert(ItemPointerGetBlockNumber(&heaptup->t_self) == BufferGetBlockNumber(buffer));
 
 		/*
@@ -2196,6 +2203,13 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 
 	END_CRIT_SECTION();
 
+	/*
+	 * Let others know that we speculatively inserted this tuple, before
+	 * releasing the buffer lock.
+	 */
+	if (options & HEAP_INSERT_SPECULATIVE)
+		SetSpeculativeInsertionTid(relation->rd_node, &heaptup->t_self);
+
 	UnlockReleaseBuffer(buffer);
 	if (vmbuffer != InvalidBuffer)
 		ReleaseBuffer(vmbuffer);
@@ -2616,11 +2630,17 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
  * (the last only for HeapTupleSelfUpdated, since we
  * cannot obtain cmax from a combocid generated by another transaction).
  * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * If 'killspeculative' is true, caller requires that we "super-delete" a tuple
+ * we just inserted in the same command. Instead of the normal visibility
+ * checks, we check that the tuple was inserted by the current transaction and
+ * given command id.  Also, instead of setting its xmax, we set xmin to
+ * invalid, making it immediately appear as dead to everyone.
  */
 HTSU_Result
 heap_delete(Relation relation, ItemPointer tid,
 			CommandId cid, Snapshot crosscheck, bool wait,
-			HeapUpdateFailureData *hufd)
+			HeapUpdateFailureData *hufd, bool killspeculative)
 {
 	HTSU_Result result;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2678,7 +2698,18 @@ heap_delete(Relation relation, ItemPointer tid,
 	tp.t_self = *tid;
 
 l1:
-	result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+	if (!killspeculative)
+	{
+		result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+	}
+	else
+	{
+		if (tp.t_data->t_choice.t_heap.t_xmin != xid ||
+			tp.t_data->t_choice.t_heap.t_field3.t_cid != cid)
+			elog(ERROR, "attempted to super-delete a tuple from other CID");
+		result = HeapTupleMayBeUpdated;
+	}
+
 
 	if (result == HeapTupleInvisible)
 	{
@@ -2823,12 +2854,19 @@ l1:
 	 * using our own TransactionId below, since some other backend could
 	 * incorporate our XID into a MultiXact immediately afterwards.)
 	 */
-	MultiXactIdSetOldestMember();
+	if (!killspeculative)
+	{
+		MultiXactIdSetOldestMember();
 
-	compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(tp.t_data),
-							  tp.t_data->t_infomask, tp.t_data->t_infomask2,
-							  xid, LockTupleExclusive, true,
-							  &new_xmax, &new_infomask, &new_infomask2);
+		compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(tp.t_data),
+								  tp.t_data->t_infomask, tp.t_data->t_infomask2,
+								  xid, LockTupleExclusive, true,
+								  &new_xmax, &new_infomask, &new_infomask2);
+	}
+	else
+	{
+		new_xmax = new_infomask = new_infomask2 = 0;
+	}
 
 	START_CRIT_SECTION();
 
@@ -2855,8 +2893,23 @@ l1:
 	tp.t_data->t_infomask |= new_infomask;
 	tp.t_data->t_infomask2 |= new_infomask2;
 	HeapTupleHeaderClearHotUpdated(tp.t_data);
-	HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
-	HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
+	/*
+	 * When killing a speculatively-inserted tuple, we set xmin to invalid
+	 * instead of setting xmax, to make the tuple clearly invisible to
+	 * everyone. In particular, we want HeapTupleSatisfiesDirty() to regard
+	 * the tuple as dead, so that another backend inserting a duplicate key
+	 * value won't unnecessarily wait for our transaction to finish.
+	 */
+	if (!killspeculative)
+	{
+		HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
+		HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
+	}
+	else
+	{
+		HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+	}
+
 	/* Make sure there is no forward chain link in t_ctid */
 	tp.t_data->t_ctid = tp.t_self;
 
@@ -2872,7 +2925,11 @@ l1:
 		if (RelationIsAccessibleInLogicalDecoding(relation))
 			log_heap_new_cid(relation, &tp);
 
-		xlrec.flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
+		xlrec.flags = 0;
+		if (all_visible_cleared)
+			xlrec.flags |= XLOG_HEAP_ALL_VISIBLE_CLEARED;
+		if (killspeculative)
+			xlrec.flags |= XLOG_HEAP_SPECULATIVE_TUPLE;
 		xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
 											  tp.t_data->t_infomask2);
 		xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
@@ -2977,7 +3034,7 @@ simple_heap_delete(Relation relation, ItemPointer tid)
 	result = heap_delete(relation, tid,
 						 GetCurrentCommandId(true), InvalidSnapshot,
 						 true /* wait for commit */ ,
-						 &hufd);
+						 &hufd, false);
 	switch (result)
 	{
 		case HeapTupleSelfUpdated:
@@ -4070,14 +4127,16 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
  *
  * Function result may be:
  *	HeapTupleMayBeUpdated: lock was successfully acquired
+ *	HeapTupleInvisible: lock failed because tuple instantaneously invisible
  *	HeapTupleSelfUpdated: lock failed because tuple updated by self
  *	HeapTupleUpdated: lock failed because tuple updated by other xact
  *	HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
  *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
  * See comments for struct HeapUpdateFailureData for additional info.
  *
  * See README.tuplock for a thorough explanation of this mechanism.
@@ -4115,8 +4174,15 @@ l3:
 
 	if (result == HeapTupleInvisible)
 	{
-		UnlockReleaseBuffer(*buffer);
-		elog(ERROR, "attempted to lock invisible tuple");
+		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
+
+		/*
+		 * This is possible, but only when locking a tuple for speculative
+		 * insertion.  We return this value here rather than throwing an error
+		 * in order to give that case the opportunity to throw a more specific
+		 * error.
+		 */
+		return HeapTupleInvisible;
 	}
 	else if (result == HeapTupleBeingUpdated)
 	{
@@ -7326,7 +7392,10 @@ heap_xlog_delete(XLogReaderState *record)
 		HeapTupleHeaderClearHotUpdated(htup);
 		fix_infomask_from_infobits(xlrec->infobits_set,
 								   &htup->t_infomask, &htup->t_infomask2);
-		HeapTupleHeaderSetXmax(htup, xlrec->xmax);
+		if (!(xlrec->flags & XLOG_HEAP_SPECULATIVE_TUPLE))
+			HeapTupleHeaderSetXmax(htup, xlrec->xmax);
+		else
+			HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
 		HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
 
 		/* Mark the page as a candidate for pruning */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 932c6f7..1a4e18d 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -51,7 +51,8 @@ static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
 static TransactionId _bt_check_unique(Relation rel, IndexTuple itup,
 				 Relation heapRel, Buffer buf, OffsetNumber offset,
 				 ScanKey itup_scankey,
-				 IndexUniqueCheck checkUnique, bool *is_unique);
+				 IndexUniqueCheck checkUnique, bool *is_unique,
+				 uint32 *speculativeToken);
 static void _bt_findinsertloc(Relation rel,
 				  Buffer *bufptr,
 				  OffsetNumber *offsetptr,
@@ -159,17 +160,27 @@ top:
 	 */
 	if (checkUnique != UNIQUE_CHECK_NO)
 	{
-		TransactionId xwait;
+		TransactionId	xwait;
+		uint32			speculativeToken;
 
 		offset = _bt_binsrch(rel, buf, natts, itup_scankey, false);
 		xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey,
-								 checkUnique, &is_unique);
+								 checkUnique, &is_unique, &speculativeToken);
 
 		if (TransactionIdIsValid(xwait))
 		{
 			/* Have to wait for the other guy ... */
 			_bt_relbuf(rel, buf);
-			XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex);
+			/*
+			 * If it's a speculative insertion, wait for it to finish (ie.
+			 * to go ahead with the insertion, or kill the tuple). Otherwise
+			 * wait for the transaction to finish as usual.
+			 */
+			if (speculativeToken)
+				SpeculativeInsertionWait(xwait, speculativeToken);
+			else
+				XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex);
+
 			/* start over... */
 			_bt_freestack(stack);
 			goto top;
@@ -211,9 +222,12 @@ top:
  * also point to end-of-page, which means that the first tuple to check
  * is the first tuple on the next page.
  *
- * 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
- * conflict is detected, no return --- just ereport().
+ * 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
+ * conflict is detected, no return --- just ereport(). If an xact ID is
+ * returned, and the conflicting tuple still has a speculative insertion in
+ * progress, *speculativeToken is set to non-zero, and the caller can wait for
+ * the verdict on the insertion using SpeculativeInsertionWait().
  *
  * However, if checkUnique == UNIQUE_CHECK_PARTIAL, we always return
  * InvalidTransactionId because we don't want to wait.  In this case we
@@ -223,7 +237,8 @@ top:
 static TransactionId
 _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 				 Buffer buf, OffsetNumber offset, ScanKey itup_scankey,
-				 IndexUniqueCheck checkUnique, bool *is_unique)
+				 IndexUniqueCheck checkUnique, bool *is_unique,
+				 uint32 *speculativeToken)
 {
 	TupleDesc	itupdesc = RelationGetDescr(rel);
 	int			natts = rel->rd_rel->relnatts;
@@ -340,6 +355,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 						if (nbuf != InvalidBuffer)
 							_bt_relbuf(rel, nbuf);
 						/* Tell _bt_doinsert to wait... */
+						*speculativeToken = SnapshotDirty.speculativeToken;
 						return xwait;
 					}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f85ed93..e986d7e 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1662,6 +1662,10 @@ BuildIndexInfo(Relation index)
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
+	/* assume not doing speculative insertion for now */
+	ii->ii_UniqueOps = NULL;
+	ii->ii_UniqueProcs = NULL;
+	ii->ii_UniqueStrats = NULL;
 
 	/* initialize index-build state to default */
 	ii->ii_Concurrent = false;
@@ -1671,6 +1675,53 @@ BuildIndexInfo(Relation index)
 }
 
 /* ----------------
+ *		AddUniqueSpeculative
+ *			Append extra state to IndexInfo record
+ *
+ * For unique indexes, we usually don't want to add info to the IndexInfo for
+ * checking uniqueness, since the B-Tree AM handles that directly.  However, in
+ * the case of speculative insertion, external support is required.
+ *
+ * Do this processing here rather than in BuildIndexInfo() to save the common
+ * non-speculative cases the overhead they'd otherwise incur.
+ * ----------------
+ */
+void
+AddUniqueSpeculative(Relation index, IndexInfo *ii)
+{
+	int			ncols = index->rd_rel->relnatts;
+	int			i;
+
+	/*
+	 * fetch info for checking unique indexes
+	 */
+	Assert(ii->ii_Unique);
+
+	if (index->rd_rel->relam != BTREE_AM_OID)
+		elog(ERROR, "unexpected non-btree speculative unique index");
+
+	ii->ii_UniqueOps = (Oid *) palloc(sizeof(Oid) * ncols);
+	ii->ii_UniqueProcs = (Oid *) palloc(sizeof(Oid) * ncols);
+	ii->ii_UniqueStrats = (uint16 *) palloc(sizeof(uint16) * ncols);
+
+	/*
+	 * We have to look up the operator's strategy number.  This
+	 * provides a cross-check that the operator does match the index.
+	 */
+	/* We need the func OIDs and strategy numbers too */
+	for (i = 0; i < ncols; i++)
+	{
+		ii->ii_UniqueStrats[i] = BTEqualStrategyNumber;
+		ii->ii_UniqueOps[i] =
+			get_opfamily_member(index->rd_opfamily[i],
+								index->rd_opcintype[i],
+								index->rd_opcintype[i],
+								ii->ii_UniqueStrats[i]);
+		ii->ii_UniqueProcs[i] = get_opcode(ii->ii_UniqueOps[i]);
+	}
+}
+
+/* ----------------
  *		FormIndexDatum
  *			Construct values[] and isnull[] arrays for a new index tuple.
  *
@@ -2606,10 +2657,10 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * Check that this tuple has no conflicts.
 		 */
-		check_exclusion_constraint(heapRelation,
-								   indexRelation, indexInfo,
-								   &(heapTuple->t_self), values, isnull,
-								   estate, true, false);
+		check_exclusion_or_unique_constraint(heapRelation, indexRelation,
+											 indexInfo, &(heapTuple->t_self),
+											 values, isnull, estate, true,
+											 false, true, NULL);
 	}
 
 	heap_endscan(scan);
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index fe123ad..0231084 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -46,7 +46,7 @@ CatalogOpenIndexes(Relation heapRel)
 	resultRelInfo->ri_RelationDesc = heapRel;
 	resultRelInfo->ri_TrigDesc = NULL;	/* we don't fire triggers */
 
-	ExecOpenIndices(resultRelInfo);
+	ExecOpenIndices(resultRelInfo, false);
 
 	return resultRelInfo;
 }
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 561d8fa..d5ab12f 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -170,9 +170,10 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 		 * For exclusion constraints we just do the normal check, but now it's
 		 * okay to throw error.
 		 */
-		check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
-								   &(new_row->t_self), values, isnull,
-								   estate, false, false);
+		check_exclusion_or_unique_constraint(trigdata->tg_relation, indexRel,
+											 indexInfo, &(new_row->t_self),
+											 values, isnull, estate, false,
+											 false, true, NULL);
 	}
 
 	/*
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 92ff632..f85bd60 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2283,7 +2283,7 @@ CopyFrom(CopyState cstate)
 					  1,		/* dummy rangetable index */
 					  0);
 
-	ExecOpenIndices(resultRelInfo);
+	ExecOpenIndices(resultRelInfo, false);
 
 	estate->es_result_relations = resultRelInfo;
 	estate->es_num_result_relations = 1;
@@ -2438,7 +2438,8 @@ CopyFrom(CopyState cstate)
 
 				if (resultRelInfo->ri_NumIndices > 0)
 					recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-														   estate);
+														   estate, false,
+														   InvalidOid);
 
 				/* AFTER ROW INSERT Triggers */
 				ExecARInsertTriggers(estate, resultRelInfo, tuple,
@@ -2552,7 +2553,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
 			recheckIndexes =
 				ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
-									  estate);
+									  estate, false, InvalidOid);
 			ExecARInsertTriggers(estate, resultRelInfo,
 								 bufferedTuples[i],
 								 recheckIndexes);
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a951c55..8d2d0af 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2192,6 +2192,15 @@ static void
 ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
 {
 	ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
+
+	if (plan->arbiterIndex != InvalidOid)
+	{
+		char	   *indexname = get_rel_name(plan->arbiterIndex);
+
+		/* nothing to do for text format explains */
+		if (es->format != EXPLAIN_FORMAT_TEXT && indexname != NULL)
+			ExplainPropertyText("Arbiter Index", indexname, es);
+	}
 }
 
 /*
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 8afa1e3..862c312 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -200,3 +200,76 @@ is no explicit prohibition on SRFs in UPDATE, but the net effect will be
 that only the first result row of an SRF counts, because all subsequent
 rows will result in attempts to re-update an already updated target row.
 This is historical behavior and seems not worth changing.)
+
+Speculative insertion
+---------------------
+
+Speculative insertion is a process that the executor manages for the benefit of
+INSERT...ON CONFLICT IGNORE.  Supported indexes include nbtree unique
+indexes (nbtree is currently the only amcanunique index access method), or
+exclusion constraint indexes (exclusion constraints are considered a
+generalization of unique constraints).
+
+The primary user-visible goal for INSERT ... ON CONFLICT is to guarantee either
+an insert, or a conclusive determination that an insert cannot go ahead (due to
+a conclusively committed/visible conflict).  A would-be conflict (and the
+associated index) are the arbiters of whether or not the alternative (IGNORE)
+path is taken.  The implementation more or less tries to insert until one or
+the other of those two outcomes is reached.  There are some non-obvious hazards
+involved that are carefully avoided.  These hazards relate to concurrent
+activity causing conflicts for the implementation, which must be handled.
+
+The index is the authoritative source of truth for whether there is or is not a
+conflict, for unique index enforcement in general, and for speculative
+insertion in particular.  The heap must still be considered, though, not least
+since it alone has authoritative visibility information.  Through looping, we
+hope to overcome the disconnect between the heap and the arbiter index.  We
+must lock the row, and then verify that there is no conflict.  Only then do we
+UPDATE.  Theoretically, some individual session could loop forever, although
+under high concurrency one session always proceeds.
+
+The first step in the loop is to perform a pre-check.  The indexes are scanned
+for existing conflicting values.  At this point, we may have to wait until the
+end of another xact (or xact's promise token -- more on that later), iff it
+isn't immediately conclusive that there is or is not a conflict (when we finish
+the pre-check, there is a conclusion about there either being or
+not being a conflict).
+
+The second step (skipped when a conflict is found) is to insert a heap tuple
+and related index tuples opportunistically.  This uses the same mechanism as
+deferred unique indexes, and so we never wait for a possibly conflicting xact
+to commit or abort (unlike with conventional unique index insertion) -- we
+simply detect a possible conflict.
+
+When opportunistically inserting during the second step, we are not logically
+inserting a tuple as such.  Rather, the process is somewhat similar to the
+conventional unique index insertion steps taken within the nbtree AM, where we
+must briefly lock the *value* being inserted:  in that codepath, the value
+proposed for insertion is for an instant locked *in the abstract*, by way of a
+buffer lock on "the first leaf page the value could be on".  Then, having
+established the right to physically insert, do so (or throw an error).  For
+speculative insertion, if no conflict occurs during the insertion (which is
+usually the case, since it was just determined in the first step that there was
+no conflict), then we're done.  Otherwise, we must restart (and likely find the
+same conflict tuple during the first step of the new iteration). But a
+counter-intuitive step must be taken first (which is what makes this whole
+dance similar to conventional nbtree "value locking").
+
+We must "super delete" the tuple when the opportunistic insertion finds a
+conflict.  This means that it immediately becomes invisible to all snapshot
+types, and immediately becomes reclaimable by VACUUM.  Other backends
+(speculative inserters or ordinary inserters) know to not wait on our
+transaction end when they encounter an optimistically inserted "promise tuple".
+Rather, they wait on a corresponding promise token lock, which we hold only for
+as long as opportunistically inserting.  We release the lock when done
+opportunistically inserting (and after "super deleting", if that proved
+necessary), releasing our waiters (who will ordinarily re-find our promise
+tuple as a bona fide tuple, or occasionally will find that they can insert
+after all).  It's important that other xacts not wait on the end of our xact
+until we've established that we've successfully and conclusively inserted
+logically (or established that there was an insertion conflict, and cleaned up
+after it by "super deleting").  Otherwise, concurrent speculative inserters
+could be involved in "unprincipled deadlocks":  deadlocks where there is no
+user-visible mutual dependency, and yet an implementation related mutual
+dependency is unexpectedly introduced.  The user might be left with no
+reasonable way of avoiding these deadlocks, which would not be okay.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 33b172b..b69371e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2094,7 +2094,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * the latest version of the row was deleted, so we need do
 			 * nothing.  (Should be safe to examine xmin without getting
 			 * buffer's content lock, since xmin never changes in an existing
-			 * tuple.)
+			 * non-promise tuple.)
 			 */
 			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
 									 priorXmax))
@@ -2175,11 +2175,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 					 * case, so as to avoid the "Halloween problem" of
 					 * repeated update attempts.  In the latter case it might
 					 * be sensible to fetch the updated tuple instead, but
-					 * doing so would require changing heap_lock_tuple as well
-					 * as heap_update and heap_delete to not complain about
-					 * updating "invisible" tuples, which seems pretty scary.
-					 * So for now, treat the tuple as deleted and do not
-					 * process.
+					 * doing so would require changing heap_update and
+					 * heap_delete to not complain about updating "invisible"
+					 * tuples, which seems pretty scary (heap_lock_tuple will
+					 * not complain, but few callers expect HeapTupleInvisible,
+					 * and we're not one of them).  So for now, treat the tuple
+					 * as deleted and do not process.
 					 */
 					ReleaseBuffer(buffer);
 					return NULL;
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 022041b..ffe1d62 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -44,6 +44,7 @@
 
 #include "access/relscan.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "catalog/index.h"
 #include "executor/execdebug.h"
 #include "nodes/nodeFuncs.h"
@@ -885,7 +886,7 @@ ExecCloseScanRelation(Relation scanrel)
  * ----------------------------------------------------------------
  */
 void
-ExecOpenIndices(ResultRelInfo *resultRelInfo)
+ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
 {
 	Relation	resultRelation = resultRelInfo->ri_RelationDesc;
 	List	   *indexoidlist;
@@ -938,6 +939,13 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 		/* extract index key information from the index's pg_index info */
 		ii = BuildIndexInfo(indexDesc);
 
+		/*
+		 * Iff the indexes are to be used for speculative insertion, add extra
+		 * information required by unique index entries
+		 */
+		if (speculative && ii->ii_Unique)
+			AddUniqueSpeculative(indexDesc, ii);
+
 		relationDescs[i] = indexDesc;
 		indexInfoArray[i] = ii;
 		i++;
@@ -990,7 +998,8 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
  *
  *		This returns a list of index OIDs for any unique or exclusion
  *		constraints that are deferred and that had
- *		potential (unconfirmed) conflicts.
+ *		potential (unconfirmed) conflicts. (if noDupErr == true, the
+ *		same is done for non-deferred constraints)
  *
  *		CAUTION: this must not be called for a HOT update.
  *		We can't defend against that here for lack of info.
@@ -1000,7 +1009,9 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
 List *
 ExecInsertIndexTuples(TupleTableSlot *slot,
 					  ItemPointer tupleid,
-					  EState *estate)
+					  EState *estate,
+					  bool noDupErr,
+					  Oid arbiterIdx)
 {
 	List	   *result = NIL;
 	ResultRelInfo *resultRelInfo;
@@ -1070,7 +1081,17 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 
 			/* Skip this index-update if the predicate isn't satisfied */
 			if (!ExecQual(predicate, econtext, false))
+			{
+				if (arbiterIdx == indexRelation->rd_index->indexrelid)
+					ereport(ERROR,
+							(errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
+							 errmsg("partial arbiter unique index has predicate that does not cover tuple proposed for insertion"),
+							 errdetail("ON CONFLICT inference clause implies that the tuple proposed for insertion must be covered by predicate for partial index \"%s\".",
+									   RelationGetRelationName(indexRelation)),
+							 errtableconstraint(heapRelation,
+												RelationGetRelationName(indexRelation))));
 				continue;
+			}
 		}
 
 		/*
@@ -1092,9 +1113,16 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		 * 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.
+		 *
+		 * For a speculative insertion (used by INSERT ... ON CONFLICT), just
+		 * detect possible non-uniqueness, and tell the caller if it failed.
 		 */
 		if (!indexRelation->rd_index->indisunique)
 			checkUnique = UNIQUE_CHECK_NO;
+		else if (noDupErr && arbiterIdx == InvalidOid)
+			checkUnique = UNIQUE_CHECK_PARTIAL;
+		else if (noDupErr && arbiterIdx == indexRelation->rd_index->indexrelid)
+			checkUnique = UNIQUE_CHECK_PARTIAL;
 		else if (indexRelation->rd_index->indimmediate)
 			checkUnique = UNIQUE_CHECK_YES;
 		else
@@ -1112,8 +1140,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		 * If the index has an associated exclusion constraint, check that.
 		 * This is simpler than the process for uniqueness checks since we
 		 * always insert first and then check.  If the constraint is deferred,
-		 * we check now anyway, but don't throw error on violation; instead
-		 * we'll queue a recheck event.
+		 * we check now anyway, but don't throw error on violation or wait for
+		 * a conclusive outcome from a concurrent insertion; instead we'll
+		 * queue a recheck event.  Similarly, noDupErr callers (speculative
+		 * inserters) will recheck later, and wait for a conclusive outcome
+		 * then.
 		 *
 		 * An index for an exclusion constraint can't also be UNIQUE (not an
 		 * essential property, we just don't allow it in the grammar), so no
@@ -1121,13 +1152,15 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		 */
 		if (indexInfo->ii_ExclusionOps != NULL)
 		{
-			bool		errorOK = !indexRelation->rd_index->indimmediate;
+			bool		violationOK = (!indexRelation->rd_index->indimmediate ||
+									   noDupErr);
 
 			satisfiesConstraint =
-				check_exclusion_constraint(heapRelation,
-										   indexRelation, indexInfo,
-										   tupleid, values, isnull,
-										   estate, false, errorOK);
+				check_exclusion_or_unique_constraint(heapRelation,
+													 indexRelation, indexInfo,
+													 tupleid, values, isnull,
+													 estate, false,
+													 violationOK, false, NULL);
 		}
 
 		if ((checkUnique == UNIQUE_CHECK_PARTIAL ||
@@ -1135,7 +1168,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 			!satisfiesConstraint)
 		{
 			/*
-			 * The tuple potentially violates the uniqueness or exclusion
+			 * The tuple potentially violates the unique index or exclusion
 			 * constraint, so make a note of the index so that we can re-check
 			 * it later.
 			 */
@@ -1146,18 +1179,154 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 	return result;
 }
 
+/* ----------------------------------------------------------------
+ *		ExecCheckIndexConstraints
+ *
+ *		This routine checks if a tuple violates any unique or
+ *		exclusion constraints. If no conflict, returns true.
+ *		Otherwise returns false, and the TID of the conflicting
+ *		tuple is returned in *conflictTid
+ *
+ *		Note that this doesn't lock the values in any way, so it's
+ *		possible that a conflicting tuple is inserted immediately
+ *		after this returns, and a later insert with the same values
+ *		still conflicts. But this can be used for a pre-check before
+ *		insertion.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecCheckIndexConstraints(TupleTableSlot *slot,
+						  EState *estate, ItemPointer conflictTid,
+						  Oid arbiterIdx)
+{
+	ResultRelInfo *resultRelInfo;
+	int			i;
+	int			numIndices;
+	RelationPtr relationDescs;
+	Relation	heapRelation;
+	IndexInfo **indexInfoArray;
+	ExprContext *econtext;
+	Datum		values[INDEX_MAX_KEYS];
+	bool		isnull[INDEX_MAX_KEYS];
+	ItemPointerData invalidItemPtr;
+	bool		checkedIndex = false;
+
+	ItemPointerSetInvalid(conflictTid);
+	ItemPointerSetInvalid(&invalidItemPtr);
+
+	/*
+	 * Get information from the result relation info structure.
+	 */
+	resultRelInfo = estate->es_result_relation_info;
+	numIndices = resultRelInfo->ri_NumIndices;
+	relationDescs = resultRelInfo->ri_IndexRelationDescs;
+	indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
+	heapRelation = resultRelInfo->ri_RelationDesc;
+
+	/*
+	 * We will use the EState's per-tuple context for evaluating predicates
+	 * and index expressions (creating it if it's not already there).
+	 */
+	econtext = GetPerTupleExprContext(estate);
+
+	/* Arrange for econtext's scan tuple to be the tuple under test */
+	econtext->ecxt_scantuple = slot;
+
+	/*
+	 * for each index, form and insert the index tuple
+	 */
+	for (i = 0; i < numIndices; i++)
+	{
+		Relation	indexRelation = relationDescs[i];
+		IndexInfo  *indexInfo;
+		bool		satisfiesConstraint;
+
+		if (indexRelation == NULL)
+			continue;
+
+		indexInfo = indexInfoArray[i];
+
+		if (!indexInfo->ii_Unique && !indexInfo->ii_ExclusionOps)
+			continue;
+
+		/* If the index is marked as read-only, ignore it */
+		if (!indexInfo->ii_ReadyForInserts)
+			continue;
+
+		/* When specific arbiter index requested, only examine it */
+		if (arbiterIdx != InvalidOid &&
+			arbiterIdx != indexRelation->rd_index->indexrelid)
+			continue;
+
+		checkedIndex = true;
+
+		/* Check for partial index */
+		if (indexInfo->ii_Predicate != NIL)
+		{
+			List	   *predicate;
+
+			/*
+			 * If predicate state not set up yet, create it (in the estate's
+			 * per-query context)
+			 */
+			predicate = indexInfo->ii_PredicateState;
+			if (predicate == NIL)
+			{
+				predicate = (List *)
+					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
+									estate);
+				indexInfo->ii_PredicateState = predicate;
+			}
+
+			/* Skip this index-update if the predicate isn't satisfied */
+			if (!ExecQual(predicate, econtext, false))
+				continue;
+		}
+
+		/*
+		 * FormIndexDatum fills in its values and isnull parameters with the
+		 * appropriate values for the column(s) of the index.
+		 */
+		FormIndexDatum(indexInfo,
+					   slot,
+					   estate,
+					   values,
+					   isnull);
+
+		satisfiesConstraint =
+			check_exclusion_or_unique_constraint(heapRelation, indexRelation,
+												 indexInfo, &invalidItemPtr,
+												 values, isnull, estate, false,
+												 true, true, conflictTid);
+		if (!satisfiesConstraint)
+			return false;
+
+		/* If this was a user-specified arbiter index, we're done */
+		if (arbiterIdx == indexRelation->rd_index->indexrelid)
+			break;
+	}
+
+	if (arbiterIdx != InvalidOid && !checkedIndex)
+		elog(ERROR, "unexpected failure to find arbiter unique index");
+
+	return true;
+}
+
 /*
- * Check for violation of an exclusion constraint
+ * Check for violation of an exclusion or unique constraint
  *
  * heap: the table containing the new tuple
  * index: the index supporting the exclusion constraint
  * indexInfo: info about the index, including the exclusion properties
- * tupleid: heap TID of the new tuple we have just inserted
+ * tupleid: heap TID of the new tuple we have just inserted (invalid if we
+ *		haven't inserted a new tuple yet)
  * values, isnull: the *index* column values computed for the new tuple
  * estate: an EState we can do evaluation in
  * newIndex: if true, we are trying to build a new index (this affects
  *		only the wording of error messages)
  * errorOK: if true, don't throw error for violation
+ * wait: if true, wait for conflicting transaction to finish, even if !errorOK
+ * conflictTid: if not-NULL, the TID of conflicting tuple is returned here.
  *
  * Returns true if OK, false if actual or potential violation
  *
@@ -1167,16 +1336,25 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
  * is convenient for deferred exclusion checks; we need not bother queuing
  * a deferred event if there is definitely no conflict at insertion time.
  *
- * When errorOK is false, we'll throw error on violation, so a false result
+ * When violationOK is false, we'll throw error on violation, so a false result
  * is impossible.
+ *
+ * Note: The indexam is normally responsible for checking unique constraints,
+ * so this normally only needs to be used for exclusion constraints. But this
+ * function is also called when doing a "pre-check" for conflicts, for the
+ * benefit of speculative insertion.  Caller may request that conflict TID be
+ * set, to take further steps.
  */
 bool
-check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
-						   ItemPointer tupleid, Datum *values, bool *isnull,
-						   EState *estate, bool newIndex, bool errorOK)
+check_exclusion_or_unique_constraint(Relation heap, Relation index,
+									 IndexInfo *indexInfo, ItemPointer tupleid,
+									 Datum *values, bool *isnull,
+									 EState *estate, bool newIndex,
+									 bool violationOK, bool wait,
+									 ItemPointer conflictTid)
 {
-	Oid		   *constr_procs = indexInfo->ii_ExclusionProcs;
-	uint16	   *constr_strats = indexInfo->ii_ExclusionStrats;
+	Oid		   *constr_procs;
+	uint16	   *constr_strats;
 	Oid		   *index_collations = index->rd_indcollation;
 	int			index_natts = index->rd_index->indnatts;
 	IndexScanDesc index_scan;
@@ -1190,6 +1368,17 @@ check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
 	TupleTableSlot *existing_slot;
 	TupleTableSlot *save_scantuple;
 
+	if (indexInfo->ii_ExclusionOps)
+	{
+		constr_procs = indexInfo->ii_ExclusionProcs;
+		constr_strats = indexInfo->ii_ExclusionStrats;
+	}
+	else
+	{
+		constr_procs = indexInfo->ii_UniqueProcs;
+		constr_strats = indexInfo->ii_UniqueStrats;
+	}
+
 	/*
 	 * If any of the input values are NULL, the constraint check is assumed to
 	 * pass (i.e., we assume the operators are strict).
@@ -1254,7 +1443,8 @@ retry:
 		/*
 		 * Ignore the entry for the tuple we're trying to check.
 		 */
-		if (ItemPointerEquals(tupleid, &tup->t_self))
+		if (ItemPointerIsValid(tupleid) &&
+			ItemPointerEquals(tupleid, &tup->t_self))
 		{
 			if (found_self)		/* should not happen */
 				elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -1284,17 +1474,6 @@ retry:
 		}
 
 		/*
-		 * At this point we have either a conflict or a potential conflict. If
-		 * we're not supposed to raise error, just return the fact of the
-		 * potential conflict without waiting to see if it's real.
-		 */
-		if (errorOK)
-		{
-			conflict = true;
-			break;
-		}
-
-		/*
 		 * If an in-progress transaction is affecting the visibility of this
 		 * tuple, we need to wait for it to complete and then recheck.  For
 		 * simplicity we do rechecking by just restarting the whole scan ---
@@ -1305,18 +1484,90 @@ retry:
 		xwait = TransactionIdIsValid(DirtySnapshot.xmin) ?
 			DirtySnapshot.xmin : DirtySnapshot.xmax;
 
+		/*
+		 * At this point we have either a conflict or a potential conflict. If
+		 * we're not supposed to raise error, just return the fact of the
+		 * potential conflict without waiting to see if it's real.
+		 */
+		if (violationOK && !wait)
+		{
+			/*
+			 * For unique indexes, detecting conflict is coupled with physical
+			 * index tuple insertion, so we won't be called for recheck
+			 */
+			Assert(!indexInfo->ii_Unique);
+
+			conflict = true;
+			if (conflictTid)
+				*conflictTid = tup->t_self;
+
+			/*
+			 * Livelock insurance.
+			 *
+			 * When doing a speculative insertion pre-check, we cannot have an
+			 * "unprincipled deadlock" with another session, fundamentally
+			 * because there is no possible mutual dependency, since we only
+			 * hold a lock on our token, without attempting to lock anything
+			 * else (maybe this is not the first iteration, but no matter;
+			 * we'll have super deleted and released insertion token lock if
+			 * so, and all locks needed are already held.  Also, our XID lock
+			 * is irrelevant.)
+			 *
+			 * In the second phase, where there is a re-check for conflicts,
+			 * we can't deadlock either (we never lock another thing, since we
+			 * don't wait in that phase).  However, a theoretical livelock
+			 * hazard exists:  Two sessions could each see each other's
+			 * conflicting tuple, and each could go and delete, retrying
+			 * forever.
+			 *
+			 * To break the mutual dependency, we may wait on the other xact
+			 * here over our caller's request to not do so (in the second
+			 * phase).  This does not imply the risk of unprincipled deadlocks
+			 * either, because if we end up unexpectedly waiting, the other
+			 * session will super delete its own tuple *before* releasing its
+			 * token lock and freeing us, and without attempting to wait on us
+			 * to release our token lock.  We'll take another iteration here,
+			 * after waiting on the other session's token, not find a conflict
+			 * this time, and then proceed (assuming we're the oldest XID).
+			 *
+			 * N.B.:  Unprincipled deadlocks are still theoretically possible
+			 * with non-speculative insertion with exclusion constraints, but
+			 * this seems inconsequential, since an error was inevitable for
+			 * one of the sessions anyway.  We only worry about speculative
+			 * insertion's problems, since they're likely with idiomatic
+			 * usage.
+			 */
+			if (TransactionIdPrecedes(xwait, GetCurrentTransactionId()))
+				break;  /* go and super delete/restart speculative insertion */
+		}
+
 		if (TransactionIdIsValid(xwait))
 		{
 			ctid_wait = tup->t_data->t_ctid;
 			index_endscan(index_scan);
-			XactLockTableWait(xwait, heap, &ctid_wait,
-							  XLTW_RecheckExclusionConstr);
+			if (DirtySnapshot.speculativeToken)
+				SpeculativeInsertionWait(DirtySnapshot.xmin,
+										 DirtySnapshot.speculativeToken);
+			else if (violationOK)
+				XactLockTableWait(xwait, heap, &ctid_wait,
+								  XLTW_RecheckExclusionConstr);
+			else
+				XactLockTableWait(xwait, heap, &ctid_wait,
+								  XLTW_RecheckExclusionConstr);
 			goto retry;
 		}
 
 		/*
-		 * We have a definite conflict.  Report it.
+		 * We have a definite conflict.  Return it to caller, or report it.
 		 */
+		if (violationOK)
+		{
+			conflict = true;
+			if (conflictTid)
+				*conflictTid = tup->t_self;
+			break;
+		}
+
 		error_new = BuildIndexValueDescription(index, values, isnull);
 		error_existing = BuildIndexValueDescription(index, existing_values,
 													existing_isnull);
@@ -1352,6 +1603,9 @@ retry:
 	 * However, it is possible to define exclusion constraints for which that
 	 * wouldn't be true --- for instance, if the operator is <>. So we no
 	 * longer complain if found_self is still false.
+	 *
+	 * It would also not be true in the pre-check mode, when we haven't
+	 * inserted a tuple yet.
 	 */
 
 	econtext->ecxt_scantuple = save_scantuple;
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 48107d9..4699060 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -151,10 +151,11 @@ lnext:
 				 * case, so as to avoid the "Halloween problem" of repeated
 				 * update attempts.  In the latter case it might be sensible
 				 * to fetch the updated tuple instead, but doing so would
-				 * require changing heap_lock_tuple as well as heap_update and
-				 * heap_delete to not complain about updating "invisible"
-				 * tuples, which seems pretty scary.  So for now, treat the
-				 * tuple as deleted and do not process.
+				 * require changing heap_update and heap_delete to not complain
+				 * about updating "invisible" tuples, which seems pretty scary
+				 * (heap_lock_tuple will not complain, but few callers expect
+				 * HeapTupleInvisible, and we're not one of them).  So for now,
+				 * treat the tuple as deleted and do not process.
 				 */
 				goto lnext;
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index f96fb24..536536e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -46,6 +46,8 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/procarray.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
@@ -151,6 +153,35 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
 	return ExecProject(projectReturning, NULL);
 }
 
+/*
+ * ExecCheckHeapTupleVisible -- verify heap tuple is visible
+ *
+ * It would not be consistent with guarantees of the higher isolation levels to
+ * proceed with avoiding insertion (taking speculative insertion's alternative
+ * path) on the basis of another tuple that is not visible.  Check for the need
+ * to raise a serialization failure, and do so as necessary.
+ */
+static void
+ExecCheckHeapTupleVisible(EState *estate,
+						  ResultRelInfo *relinfo,
+						  ItemPointer tid)
+{
+	Relation	rel = relinfo->ri_RelationDesc;
+	Buffer		buffer;
+	HeapTupleData tuple;
+
+	if (!IsolationUsesXactSnapshot())
+		return;
+
+	tuple.t_self = *tid;
+	if (!heap_fetch(rel, estate->es_snapshot, &tuple, &buffer, false, NULL))
+		ereport(ERROR,
+				(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+				 errmsg("could not serialize access due to concurrent insert or update dictating alternative ON CONFLICT path")));
+
+	ReleaseBuffer(buffer);
+}
+
 /* ----------------------------------------------------------------
  *		ExecInsert
  *
@@ -163,6 +194,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
 static TupleTableSlot *
 ExecInsert(TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
+		   Oid arbiterIndex,
+		   SpecCmd spec,
 		   EState *estate,
 		   bool canSetTag)
 {
@@ -246,6 +279,8 @@ ExecInsert(TupleTableSlot *slot,
 	}
 	else
 	{
+		ItemPointerData conflictTid;
+
 		/*
 		 * Constraints might reference the tableoid column, so initialize
 		 * t_tableOid before evaluating them.
@@ -259,20 +294,122 @@ ExecInsert(TupleTableSlot *slot,
 			ExecConstraints(resultRelInfo, slot, estate);
 
 		/*
+		 * If we are performing speculative insertion, do a non-conclusive
+		 * check for conflicts.
+		 *
+		 * See the executor README for a full discussion of speculative
+		 * insertion.
+		 */
+vlock:
+		if (spec != SPEC_NONE && resultRelInfo->ri_NumIndices > 0)
+		{
+			/*
+			 * No need to check if running in bootstrap mode, since ON
+			 * CONFLICT with system catalogs forbidden generally.
+			 *
+			 * Check if it's required to proceed with the second phase
+			 * ("insertion proper") of speculative insertion in respect of the
+			 * slot.  If insertion ultimately does not proceed, no firing of
+			 * AFTER ROW INSERT triggers occurs.
+			 *
+			 * We don't suppress the effects (or, perhaps, side-effects) of
+			 * BEFORE ROW INSERT triggers.  This isn't ideal, but then we
+			 * cannot proceed with even considering uniqueness violations
+			 * until these triggers fire on the one hand, but on the other
+			 * hand they have the ability to execute arbitrary user-defined
+			 * code which may perform operations entirely outside the system's
+			 * ability to nullify.
+			 */
+			if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
+										   arbiterIndex))
+			{
+				/*
+				 * For the SPEC_IGNORE case, it's still often necessary to
+				 * verify that the tuple is visible to the executor's MVCC
+				 * snapshot.
+				 */
+				if (spec == SPEC_IGNORE)
+					ExecCheckHeapTupleVisible(estate, resultRelInfo, &conflictTid);
+
+				/*
+				 * The IGNORE path projects no tuples
+				 */
+				return NULL;
+			}
+
+			/*
+			 * Before we start insertion proper, acquire our "promise tuple
+			 * insertion lock". Others can use that (rather than an XID lock,
+			 * which is appropriate only for non-promise tuples) to wait for
+			 * us to decide if we're going to go ahead with the insertion.
+			 */
+			SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
+		}
+
+		/*
 		 * insert the tuple
 		 *
 		 * Note: heap_insert returns the tid (location) of the new tuple in
 		 * the t_self field.
 		 */
 		newId = heap_insert(resultRelationDesc, tuple,
-							estate->es_output_cid, 0, NULL);
+							estate->es_output_cid,
+							spec != SPEC_NONE ? HEAP_INSERT_SPECULATIVE : 0,
+							NULL);
 
 		/*
 		 * insert index entries for tuple
 		 */
 		if (resultRelInfo->ri_NumIndices > 0)
+		{
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate);
+												   estate, spec != SPEC_NONE,
+												   arbiterIndex);
+
+			if (spec != SPEC_NONE)
+			{
+				HeapUpdateFailureData hufd;
+
+				/*
+				 * Consider possible race:  concurrent insertion conflicts with
+				 * our speculative heap tuple.  Must then "super-delete" the
+				 * heap tuple and retry from the start.
+				 *
+				 * This is occasionally necessary so that "unprincipled
+				 * deadlocks" are avoided;  now that a conflict was found,
+				 * other sessions should not wait on our speculative token,
+				 * and they certainly shouldn't treat our
+				 * speculatively-inserted heap tuple as an ordinary tuple that
+				 * it must wait on the outcome of our xact to UPDATE/DELETE.
+				 * This makes heap tuples behave as conceptual "value locks"
+				 * of short duration, distinct from ordinary tuples that other
+				 * xacts must wait on xmin-xact-end of in the event of a
+				 * possible unique/exclusion violation (the violation that
+				 * arbitrates taking the alternative path).
+				 */
+				if (recheckIndexes)
+					heap_delete(resultRelationDesc, &(tuple->t_self),
+								estate->es_output_cid, InvalidSnapshot, false,
+								&hufd, true);
+
+				/*
+				 * Release speculative insertion lock.  Iff there was no
+				 * insertion conflict, and the tuple was therefore not super
+				 * deleted, this effectively make the promise tuple an ordinary
+				 * tuple
+				 */
+				SpeculativeInsertionLockRelease(GetCurrentTransactionId());
+				ClearSpeculativeInsertionState();
+
+				if (recheckIndexes)
+				{
+					list_free(recheckIndexes);
+					goto vlock;
+				}
+
+				/* since there was no insertion conflict, we're done */
+			}
+		}
 	}
 
 	if (canSetTag)
@@ -399,7 +536,8 @@ ldelete:;
 							 estate->es_output_cid,
 							 estate->es_crosscheck_snapshot,
 							 true /* wait for commit */ ,
-							 &hufd);
+							 &hufd,
+							 false);
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -768,7 +906,7 @@ lreplace:;
 		 */
 		if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate);
+												   estate, false, InvalidOid);
 	}
 
 	if (canSetTag)
@@ -852,6 +990,7 @@ ExecModifyTable(ModifyTableState *node)
 {
 	EState	   *estate = node->ps.state;
 	CmdType		operation = node->operation;
+	SpecCmd		spec = node->spec;
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
 	PlanState  *subplanstate;
@@ -1022,7 +1161,8 @@ ExecModifyTable(ModifyTableState *node)
 		switch (operation)
 		{
 			case CMD_INSERT:
-				slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
+				slot = ExecInsert(slot, planSlot, node->arbiterIndex, spec,
+								  estate, node->canSetTag);
 				break;
 			case CMD_UPDATE:
 				slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
@@ -1097,6 +1237,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
 	mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
 	mtstate->mt_nplans = nplans;
+	mtstate->spec = node->spec;
 
 	/* set up epqstate with dummy subplan data for the moment */
 	EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
@@ -1135,7 +1276,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
 			operation != CMD_DELETE &&
 			resultRelInfo->ri_IndexRelationDescs == NULL)
-			ExecOpenIndices(resultRelInfo);
+			ExecOpenIndices(resultRelInfo, mtstate->spec != SPEC_NONE);
+
+		mtstate->arbiterIndex = node->arbiterIndex;
 
 		/* Now init the plan for this result rel */
 		estate->es_result_relation_info = resultRelInfo;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9fe8008..393248a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -180,6 +180,8 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_NODE_FIELD(resultRelations);
 	COPY_SCALAR_FIELD(resultRelIndex);
 	COPY_NODE_FIELD(plans);
+	COPY_SCALAR_FIELD(spec);
+	COPY_SCALAR_FIELD(arbiterIndex);
 	COPY_NODE_FIELD(withCheckOptionLists);
 	COPY_NODE_FIELD(returningLists);
 	COPY_NODE_FIELD(fdwPrivLists);
@@ -2123,6 +2125,30 @@ _copyWithClause(const WithClause *from)
 	return newnode;
 }
 
+static InferClause *
+_copyInferClause(const InferClause *from)
+{
+	InferClause *newnode = makeNode(InferClause);
+
+	COPY_NODE_FIELD(indexElems);
+	COPY_NODE_FIELD(whereClause);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+static ConflictClause *
+_copyConflictClause(const ConflictClause *from)
+{
+	ConflictClause *newnode = makeNode(ConflictClause);
+
+	COPY_SCALAR_FIELD(specclause);
+	COPY_NODE_FIELD(infer);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 static CommonTableExpr *
 _copyCommonTableExpr(const CommonTableExpr *from)
 {
@@ -2528,6 +2554,9 @@ _copyQuery(const Query *from)
 	COPY_NODE_FIELD(jointree);
 	COPY_NODE_FIELD(targetList);
 	COPY_NODE_FIELD(withCheckOptions);
+	COPY_SCALAR_FIELD(specClause);
+	COPY_NODE_FIELD(arbiterExpr);
+	COPY_NODE_FIELD(arbiterWhere);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(groupClause);
 	COPY_NODE_FIELD(havingQual);
@@ -2551,6 +2580,7 @@ _copyInsertStmt(const InsertStmt *from)
 	COPY_NODE_FIELD(relation);
 	COPY_NODE_FIELD(cols);
 	COPY_NODE_FIELD(selectStmt);
+	COPY_NODE_FIELD(confClause);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(withClause);
 
@@ -4725,6 +4755,12 @@ copyObject(const void *from)
 		case T_WithClause:
 			retval = _copyWithClause(from);
 			break;
+		case T_InferClause:
+			retval = _copyInferClause(from);
+			break;
+		case T_ConflictClause:
+			retval = _copyConflictClause(from);
+			break;
 		case T_CommonTableExpr:
 			retval = _copyCommonTableExpr(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fe509b0..aa47887 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -865,6 +865,9 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_NODE_FIELD(jointree);
 	COMPARE_NODE_FIELD(targetList);
 	COMPARE_NODE_FIELD(withCheckOptions);
+	COMPARE_SCALAR_FIELD(specClause);
+	COMPARE_NODE_FIELD(arbiterExpr);
+	COMPARE_NODE_FIELD(arbiterWhere);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(groupClause);
 	COMPARE_NODE_FIELD(havingQual);
@@ -886,6 +889,7 @@ _equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
 	COMPARE_NODE_FIELD(relation);
 	COMPARE_NODE_FIELD(cols);
 	COMPARE_NODE_FIELD(selectStmt);
+	COMPARE_NODE_FIELD(confClause);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(withClause);
 
@@ -2428,6 +2432,26 @@ _equalWithClause(const WithClause *a, const WithClause *b)
 }
 
 static bool
+_equalInferClause(const InferClause *a, const InferClause *b)
+{
+	COMPARE_NODE_FIELD(indexElems);
+	COMPARE_NODE_FIELD(whereClause);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
+_equalConflictClause(const ConflictClause *a, const ConflictClause *b)
+{
+	COMPARE_SCALAR_FIELD(specclause);
+	COMPARE_NODE_FIELD(infer);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
 _equalCommonTableExpr(const CommonTableExpr *a, const CommonTableExpr *b)
 {
 	COMPARE_STRING_FIELD(ctename);
@@ -3150,6 +3174,12 @@ equal(const void *a, const void *b)
 		case T_WithClause:
 			retval = _equalWithClause(a, b);
 			break;
+		case T_InferClause:
+			retval = _equalInferClause(a, b);
+			break;
+		case T_ConflictClause:
+			retval = _equalConflictClause(a, b);
+			break;
 		case T_CommonTableExpr:
 			retval = _equalCommonTableExpr(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index d6f1f5b..44efc95 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1484,6 +1484,12 @@ exprLocation(const Node *expr)
 		case T_WithClause:
 			loc = ((const WithClause *) expr)->location;
 			break;
+		case T_InferClause:
+			loc = ((const InferClause *) expr)->location;
+			break;
+		case T_ConflictClause:
+			loc = ((const ConflictClause *) expr)->location;
+			break;
 		case T_CommonTableExpr:
 			loc = ((const CommonTableExpr *) expr)->location;
 			break;
@@ -1968,6 +1974,10 @@ query_tree_walker(Query *query,
 		return true;
 	if (walker((Node *) query->withCheckOptions, context))
 		return true;
+	if (walker((Node *) query->arbiterExpr, context))
+		return true;
+	if (walker(query->arbiterWhere, context))
+		return true;
 	if (walker((Node *) query->returningList, context))
 		return true;
 	if (walker((Node *) query->jointree, context))
@@ -2709,6 +2719,8 @@ query_tree_mutator(Query *query,
 
 	MUTATE(query->targetList, query->targetList, List *);
 	MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
+	MUTATE(query->arbiterExpr, query->arbiterExpr, List *);
+	MUTATE(query->arbiterWhere, query->arbiterWhere, Node *);
 	MUTATE(query->returningList, query->returningList, List *);
 	MUTATE(query->jointree, query->jointree, FromExpr *);
 	MUTATE(query->setOperations, query->setOperations, Node *);
@@ -2978,6 +2990,8 @@ raw_expression_tree_walker(Node *node,
 					return true;
 				if (walker(stmt->selectStmt, context))
 					return true;
+				if (walker(stmt->confClause, context))
+					return true;
 				if (walker(stmt->returningList, context))
 					return true;
 				if (walker(stmt->withClause, context))
@@ -3217,6 +3231,23 @@ raw_expression_tree_walker(Node *node,
 			break;
 		case T_WithClause:
 			return walker(((WithClause *) node)->ctes, context);
+
+		case T_InferClause:
+			{
+				InferClause *stmt = (InferClause *) node;
+
+				if (walker(stmt->indexElems, context))
+					return true;
+				if (walker(stmt->whereClause, context))
+					return true;
+			}
+		case T_ConflictClause:
+			{
+				ConflictClause *stmt = (ConflictClause *) node;
+
+				if (walker(stmt->infer, context))
+					return true;
+			}
 		case T_CommonTableExpr:
 			return walker(((CommonTableExpr *) node)->ctequery, context);
 		default:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 775f482..f92914a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -332,6 +332,8 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_NODE_FIELD(resultRelations);
 	WRITE_INT_FIELD(resultRelIndex);
 	WRITE_NODE_FIELD(plans);
+	WRITE_ENUM_FIELD(spec, SpecType);
+	WRITE_OID_FIELD(arbiterIndex);
 	WRITE_NODE_FIELD(withCheckOptionLists);
 	WRITE_NODE_FIELD(returningLists);
 	WRITE_NODE_FIELD(fdwPrivLists);
@@ -2308,6 +2310,9 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_NODE_FIELD(jointree);
 	WRITE_NODE_FIELD(targetList);
 	WRITE_NODE_FIELD(withCheckOptions);
+	WRITE_ENUM_FIELD(specClause, SpecType);
+	WRITE_NODE_FIELD(arbiterExpr);
+	WRITE_NODE_FIELD(arbiterWhere);
 	WRITE_NODE_FIELD(returningList);
 	WRITE_NODE_FIELD(groupClause);
 	WRITE_NODE_FIELD(havingQual);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 563209c..e492ef6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -214,6 +214,9 @@ _readQuery(void)
 	READ_NODE_FIELD(jointree);
 	READ_NODE_FIELD(targetList);
 	READ_NODE_FIELD(withCheckOptions);
+	READ_ENUM_FIELD(specClause, SpecCmd);
+	READ_NODE_FIELD(arbiterExpr);
+	READ_NODE_FIELD(arbiterWhere);
 	READ_NODE_FIELD(returningList);
 	READ_NODE_FIELD(groupClause);
 	READ_NODE_FIELD(havingQual);
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b86a3cd..f6c3d48 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -4013,3 +4013,60 @@ string_to_const(const char *str, Oid datatype)
 	return makeConst(datatype, -1, collation, constlen,
 					 conval, false, false);
 }
+
+/*
+ * plan_speculative_use_index
+ *		Use the planner to decide speculative insertion arbiter index
+ *
+ * Among indexes on target of INSERT ... ON CONFLICT,  decide which index to
+ * use to arbitrate taking alternative path.  This should be called
+ * infrequently in practice, because its unusual for more than one index to be
+ * available that can satisfy a user-specified unique index inference
+ * specification.
+ *
+ * Note: caller had better already hold some type of lock on the table.
+ */
+Oid
+plan_speculative_use_index(PlannerInfo *root, List *indexList)
+{
+	IndexOptInfo *indexInfo;
+	RelOptInfo *rel;
+	IndexPath  *cheapest;
+	IndexPath  *indexScanPath;
+	ListCell   *lc;
+
+	/* Set up RTE/RelOptInfo arrays if needed */
+	if (!root->simple_rel_array)
+		setup_simple_rel_arrays(root);
+
+	/* Build RelOptInfo */
+	rel = build_simple_rel(root, root->parse->resultRelation, RELOPT_BASEREL);
+
+	/* Locate cheapest IndexOptInfo for the target index */
+	cheapest = NULL;
+
+	foreach(lc, rel->indexlist)
+	{
+		indexInfo = (IndexOptInfo *) lfirst(lc);
+
+		if (!list_member_oid(indexList, indexInfo->indexoid))
+			continue;
+
+		/* Estimate the cost of index scan */
+		indexScanPath = create_index_path(root, indexInfo,
+										  NIL, NIL, NIL, NIL, NIL,
+										  ForwardScanDirection, false,
+										  NULL, 1.0);
+
+		if (!cheapest || compare_fractional_path_costs(&cheapest->path,
+													   &indexScanPath->path,
+													   DEFAULT_RANGE_INEQ_SEL) > 0)
+			cheapest = indexScanPath;
+
+	}
+
+	if (cheapest)
+		return cheapest->indexinfo->indexoid;
+
+	return InvalidOid;
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cb69c03..47fe29c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4815,7 +4815,7 @@ make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, int epqParam)
+				 List *rowMarks, SpecCmd spec, int epqParam)
 {
 	ModifyTable *node = makeNode(ModifyTable);
 	Plan	   *plan = &node->plan;
@@ -4865,6 +4865,8 @@ make_modifytable(PlannerInfo *root,
 	node->resultRelations = resultRelations;
 	node->resultRelIndex = -1;	/* will be set correctly in setrefs.c */
 	node->plans = subplans;
+	node->spec = spec;
+	node->arbiterIndex = InvalidOid;
 	node->withCheckOptionLists = withCheckOptionLists;
 	node->returningLists = returningLists;
 	node->rowMarks = rowMarks;
@@ -4917,6 +4919,15 @@ make_modifytable(PlannerInfo *root,
 	}
 	node->fdwPrivLists = fdw_private_list;
 
+	/*
+	 * If a set of unique index inference expressions was provided (for
+	 * INSERT...ON CONFLICT), then infer appropriate unique index (or throw an
+	 * error if none is available).  It's possible that there will be a costing
+	 * step in the event of having to choose between multiple alternatives.
+	 */
+	if (root->parse->arbiterExpr)
+		node->arbiterIndex = infer_unique_index(root);
+
 	return node;
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b02a107..b50cfe9 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -613,6 +613,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 											 withCheckOptionLists,
 											 returningLists,
 											 rowMarks,
+											 parse->specClause,
 											 SS_assign_special_param(root));
 		}
 	}
@@ -1073,6 +1074,7 @@ inheritance_planner(PlannerInfo *root)
 									 withCheckOptionLists,
 									 returningLists,
 									 rowMarks,
+									 parse->specClause,
 									 SS_assign_special_param(root));
 }
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 313a5c1..c6650ae 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -31,6 +31,7 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/predtest.h"
 #include "optimizer/prep.h"
@@ -394,6 +395,216 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 }
 
 /*
+ * infer_unique_index -
+ *	  Retrieves unique index to arbitrate speculative insertion.
+ *
+ * Uses user-supplied inference clause expressions and predicate to match a
+ * unique index from those defined and ready on the heap relation (target).  An
+ * exact match is required on columns/expressions (although they can appear in
+ * any order).  However, the predicate given by the user need only restrict
+ * insertion to a subset of some part of the table covered by some particular
+ * unique index (in particular, a partial unique index) in order to be
+ * inferred.
+ *
+ * The implementation does not consider which B-Tree operator class any
+ * particular available unique index uses.  In particular, there is no system
+ * dependency on the default operator class for the purposes of inference.
+ * This should be okay, since by convention non-default opclasses only
+ * introduce alternative sort orders, not alternative notions of equality
+ * (there are only trivial known exceptions to this convention, where "equals"
+ * operator of a type's opclasses do not match across opclasses, exceptions
+ * that exist precisely to discourage user code from using the divergent
+ * opclass).  Even if we assume that a type could usefully have multiple
+ * alternative concepts of equality, surely the definition actually implied by
+ * the operator class of actually indexed attributes is pertinent.  However,
+ * this is a bit of a wart, because strictly speaking there is leeway for a
+ * query to be interpreted in deference to available unique indexes, and
+ * indexes are traditionally only an implementation detail.  It hardly seems
+ * worth it to waste cycles on this corner case, though.
+ *
+ * This logic somewhat mirrors get_relation_info().  This process is not
+ * deferred to a get_relation_info() call while planning because there may not
+ * be any such call.
+ */
+Oid
+infer_unique_index(PlannerInfo *root)
+{
+	Query	   *parse = root->parse;
+	Relation	relation;
+	Oid			relationObjectId;
+	Bitmapset  *plainAttrs = NULL;
+	List	   *candidates = NIL;
+	ListCell   *l;
+	List	   *indexList;
+
+	Assert(parse->specClause == SPEC_IGNORE);
+
+	/*
+	 * We need not lock the relation since it was already locked, either by
+	 * the rewriter or when expand_inherited_rtentry() added it to the query's
+	 * rangetable.
+	 */
+	relationObjectId = rt_fetch(parse->resultRelation, parse->rtable)->relid;
+
+	relation = heap_open(relationObjectId, NoLock);
+
+	/*
+	 * Match expressions appearing in clause (if any) with index definition
+	 */
+	foreach(l, parse->arbiterExpr)
+	{
+		Expr	   *elem;
+		Var		   *var;
+		int			attno;
+
+		elem = (Expr *) lfirst(l);
+
+		/*
+		 * Parse analysis of inference elements performs full parse analysis
+		 * of Vars, even for non-expression indexes (in contrast with utility
+		 * command related use of IndexElem).  However, indexes are cataloged
+		 * with simple attribute numbers for non-expression indexes.
+		 * Therefore, we must build a compatible bms representation here.
+		 */
+		if (!IsA(elem, Var))
+			continue;
+
+		var = (Var *) elem;
+		attno = var->varattno;
+
+		if (attno < 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+					 errmsg("system columns may not appear in unique index inference specification")));
+		else if (attno == 0)
+			elog(ERROR, "whole row unique index inference specifications are not valid");
+
+		plainAttrs = bms_add_member(plainAttrs, attno);
+	}
+
+	indexList = RelationGetIndexList(relation);
+
+	/*
+	 * Using that representation, iterate through the list of indexes on the
+	 * target relation to try and find a match
+	 */
+	foreach(l, indexList)
+	{
+		Oid			indexoid = lfirst_oid(l);
+		Relation	idxRel;
+		Form_pg_index idxForm;
+		Bitmapset  *indexedPlainAttrs = NULL;
+		List	   *idxExprs;
+		List	   *predExprs;
+		List	   *whereExplicit;
+		AttrNumber	natt;
+		ListCell   *e;
+
+		/*
+		 * Extract info from the relation descriptor for the index.  We know
+		 * that this is a target, so get lock type it is known will ultimately
+		 * be required by the executor.
+		 *
+		 * Let executor complain about !indimmediate case directly.
+		 */
+		idxRel = index_open(indexoid, RowExclusiveLock);
+		idxForm = idxRel->rd_index;
+
+		if (!idxForm->indisunique ||
+			!IndexIsValid(idxForm))
+			goto next;
+
+		/*
+		 * If the index is valid, but cannot yet be used, ignore it. See
+		 * src/backend/access/heap/README.HOT for discussion.
+		 */
+		if (idxForm->indcheckxmin &&
+			!TransactionIdPrecedes(HeapTupleHeaderGetXmin(idxRel->rd_indextuple->t_data),
+								   TransactionXmin))
+			goto next;
+
+		/* Check in detail if the clause attributes/expressions match */
+		for (natt = 0; natt < idxForm->indnatts; natt++)
+		{
+			int			attno = idxRel->rd_index->indkey.values[natt];
+
+			if (attno < 0)
+				elog(ERROR, "system column in index");
+
+			if (attno != 0)
+				indexedPlainAttrs = bms_add_member(indexedPlainAttrs, attno);
+		}
+
+		/*
+		 * Since expressions were made unique during parse analysis, it's
+		 * evident that we cannot proceed with this index if the number of
+		 * attributes (plain or expression) does not match exactly.  This
+		 * precludes support for unique indexes created with redundantly
+		 * referenced columns (which are not forbidden by CREATE INDEX), but
+		 * this seems inconsequential.
+		 */
+		if (list_length(parse->arbiterExpr) != idxForm->indnatts)
+			goto next;
+
+		idxExprs = RelationGetIndexExpressions(idxRel);
+
+		/*
+		 * Match expressions appearing in clause (if any) with index
+		 * definition
+		 */
+		foreach(e, parse->arbiterExpr)
+		{
+			Expr	   *elem = (Expr *) lfirst(e);
+
+			/* Plain Vars were already separately accounted for */
+			if (IsA(elem, Var))
+				continue;
+
+			if (!list_member(idxExprs, elem))
+				goto next;
+		}
+
+		/* Non-expression attributes (if any) must match */
+		if (!bms_equal(indexedPlainAttrs, plainAttrs))
+			goto next;
+
+		/*
+		 * Any user-supplied ON CONFLICT unique index inference WHERE clause
+		 * need only be implied by the cataloged index definitions predicate
+		 */
+		predExprs = RelationGetIndexPredicate(idxRel);
+		whereExplicit = make_ands_implicit((Expr *) parse->arbiterWhere);
+
+		if (!predicate_implied_by(predExprs, whereExplicit))
+			goto next;
+
+		candidates = lappend_oid(candidates, idxForm->indexrelid);
+next:
+		index_close(idxRel, NoLock);
+	}
+
+	list_free(indexList);
+	heap_close(relation, NoLock);
+
+	/*
+	 * In the common case where there is only a single candidate unique index,
+	 * there is clearly no point in building index paths to determine which is
+	 * cheapest.
+	 */
+	if (list_length(candidates) == 1)
+		return linitial_oid(candidates);
+	else if (candidates == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT")));
+	else
+		/* Otherwise, deduce the least expensive unique index */
+		return plan_speculative_use_index(root, candidates);
+
+	return InvalidOid;			/* keep compiler quiet */
+}
+
+/*
  * estimate_rel_size - estimate # pages and # tuples in a table or index
  *
  * We also estimate the fraction of the pages that are marked all-visible in
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a68f2e8..1ad9492 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -387,6 +387,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	/* done building the range table and jointree */
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+	qry->specClause = SPEC_NONE;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -408,6 +409,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
 	SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
+	SpecCmd		spec = stmt->confClause ? stmt->confClause->specclause : SPEC_NONE;
 	List	   *exprList = NIL;
 	bool		isGeneralSelect;
 	List	   *sub_rtable;
@@ -741,12 +743,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	}
 
 	/*
-	 * If we have a RETURNING clause, we need to add the target relation to
-	 * the query namespace before processing it, so that Var references in
-	 * RETURNING will work.  Also, remove any namespace entries added in a
-	 * sub-SELECT or VALUES list.
+	 * If we have a RETURNING clause, or there are attributes used as the
+	 * condition on which to take an alternative ON CONFLICT path, we need to
+	 * add the target relation to the query namespace before processing it, so
+	 * that Var references in RETURNING/the alternative path key will work.
+	 * Also, remove any namespace entries added in a sub-SELECT or VALUES list.
 	 */
-	if (stmt->returningList)
+	if (stmt->returningList || stmt->confClause)
 	{
 		pstate->p_namespace = NIL;
 		addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
@@ -758,9 +761,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	/* done building the range table and jointree */
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
-
+	qry->specClause = spec;
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
+	if (stmt->confClause)
+	{
+		/*
+		 * Perform parse analysis of arbiter columns/expressions.  These are
+		 * later used to infer a unique index which arbitrates whether or not
+		 * to take the alternative ON CONFLICT path (i.e.  whether or not to
+		 * INSERT or take the alternative path in respect of each slot proposed
+		 * for insertion).
+		 */
+		transformConflictClause(pstate, stmt->confClause, &qry->arbiterExpr,
+								&qry->arbiterWhere);
+	}
+
 	assign_query_collations(pstate, qry);
 
 	return qry;
@@ -1006,6 +1022,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+	qry->specClause = SPEC_NONE;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 581f7a1..a76e038 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -215,6 +215,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	RangeVar			*range;
 	IntoClause			*into;
 	WithClause			*with;
+	InferClause			*infer;
+	ConflictClause			*conf;
 	A_Indices			*aind;
 	ResTarget			*target;
 	struct PrivTarget	*privtarget;
@@ -415,6 +417,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>	SeqOptElem
 
 %type <istmt>	insert_rest
+%type <infer>	opt_conf_expr
+%type <conf>	opt_on_conflict
 
 %type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest
 				 SetResetClause FunctionSetResetClause
@@ -551,8 +555,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
 	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
-	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
-	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
+	COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
+	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
 	CROSS CSV CURRENT_P
 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -572,7 +576,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
-	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P
+	IDENTITY_P IF_P IGNORE_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P
 	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
 	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
@@ -652,6 +656,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %nonassoc	OVERLAPS
 %nonassoc	BETWEEN
 %nonassoc	IN_P
+%nonassoc	DISTINCT
+%nonassoc	ON
 %left		POSTFIXOP		/* dummy for postfix Op rules */
 /*
  * To support target_el without AS, we must give IDENT an explicit priority
@@ -9407,10 +9413,12 @@ DeallocateStmt: DEALLOCATE name
  *****************************************************************************/
 
 InsertStmt:
-			opt_with_clause INSERT INTO qualified_name insert_rest returning_clause
+			opt_with_clause INSERT INTO qualified_name insert_rest
+			opt_on_conflict returning_clause
 				{
 					$5->relation = $4;
-					$5->returningList = $6;
+					$5->confClause = $6;
+					$5->returningList = $7;
 					$5->withClause = $1;
 					$$ = (Node *) $5;
 				}
@@ -9455,6 +9463,34 @@ insert_column_item:
 				}
 		;
 
+opt_on_conflict:
+			ON CONFLICT opt_conf_expr IGNORE_P
+				{
+					$$ = makeNode(ConflictClause);
+					$$->specclause = SPEC_IGNORE;
+					$$->infer = $3;
+					$$->location = @1;
+				}
+			| /*EMPTY*/
+				{
+					$$ = NULL;
+				}
+		;
+
+opt_conf_expr:
+			'(' index_params where_clause ')'
+				{
+					$$ = makeNode(InferClause);
+					$$->indexElems = $2;
+					$$->whereClause = $3;
+					$$->location = @1;
+				}
+			| /*EMPTY*/
+				{
+					$$ = NULL;
+				}
+		;
+
 returning_clause:
 			RETURNING target_list		{ $$ = $2; }
 			| /* EMPTY */				{ $$ = NIL; }
@@ -13221,6 +13257,7 @@ unreserved_keyword:
 			| COMMIT
 			| COMMITTED
 			| CONFIGURATION
+			| CONFLICT
 			| CONNECTION
 			| CONSTRAINTS
 			| CONTENT_P
@@ -13280,6 +13317,7 @@ unreserved_keyword:
 			| HOUR_P
 			| IDENTITY_P
 			| IF_P
+			| IGNORE_P
 			| IMMEDIATE
 			| IMMUTABLE
 			| IMPLICIT_P
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 8d90b50..029288b 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "catalog/catalog.h"
 #include "catalog/heap.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -75,6 +76,8 @@ static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
 						 List **tlist, ParseExprKind exprKind);
 static int get_matching_location(int sortgroupref,
 					  List *sortgrouprefs, List *exprs);
+static List *resolve_unique_index_expr(ParseState *pstate, InferClause * infer,
+						  Relation heapRel);
 static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle,
 					 List *grouplist, List *targetlist, int location,
 					 bool resolveUnknown);
@@ -2167,6 +2170,163 @@ get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs)
 }
 
 /*
+ * resolve_unique_index_expr
+ *		Infer a unique index from a list of indexElems, for ON
+ *		CONFLICT clause
+ *
+ * Perform parse analysis of expressions and columns appearing within ON
+ * CONFLICT clause.  During planning, the returned list of expressions is used
+ * to infer which unique index to use.
+ */
+static List *
+resolve_unique_index_expr(ParseState *pstate, InferClause *infer,
+						  Relation heapRel)
+{
+	List	   *clauseexprs = NIL;
+	ListCell   *l;
+
+	if (heapRel->rd_rel->relkind != RELKIND_RELATION)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("relation \"%s\" is not an ordinary table",
+						RelationGetRelationName(heapRel)),
+				 errhint("Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.")));
+
+	if (heapRel->rd_rel->relhassubclass)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("relation \"%s\" has inheritance children",
+						RelationGetRelationName(heapRel)),
+				 errhint("Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.")));
+
+	foreach(l, infer->indexElems)
+	{
+		IndexElem  *ielem = (IndexElem *) lfirst(l);
+		Node	   *trans;
+
+		/*
+		 * Raw grammar re-uses CREATE INDEX infrastructure for unique index
+		 * inference clause, and so will accept opclasses by name and so on.
+		 * Reject these here explicitly.
+		 */
+		if (ielem->ordering != SORTBY_DEFAULT ||
+			ielem->nulls_ordering != SORTBY_NULLS_DEFAULT)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+					 errmsg("ON CONFLICT does not accept ordering or NULLS FIRST/LAST specifications"),
+					 errhint("These factors do not affect uniqueness of indexed datums."),
+					 parser_errposition(pstate,
+										exprLocation((Node *) infer))));
+
+		if (ielem->collation != NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+					 errmsg("ON CONFLICT collation specification is unnecessary"),
+					 errhint("Collations do not affect uniqueness of collatable datums."),
+					 parser_errposition(pstate,
+										exprLocation((Node *) infer))));
+
+		if (ielem->opclass != NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("ON CONFLICT cannot accept non-default operator class specifications"),
+					 parser_errposition(pstate,
+										exprLocation((Node *) infer))));
+
+		if (!ielem->expr)
+		{
+			/* Simple index attribute */
+			ColumnRef  *n;
+
+			/*
+			 * Grammar won't have built raw expression for us in event of plain
+			 * column reference.  Create one directly, and perform expression
+			 * transformation, which seems better principled than simply
+			 * propagating catalog-style simple attribute numbers.  For
+			 * example, it means the Var is marked for SELECT privileges, which
+			 * speculative insertion requires.  Planner expects this, and
+			 * performs its own normalization for the purposes of matching
+			 * against pg_index.
+			 */
+			n = makeNode(ColumnRef);
+			n->fields = list_make1(makeString(ielem->name));
+			/* Location is approximately that of inference specification */
+			n->location = infer->location;
+			trans = (Node *) n;
+		}
+		else
+		{
+			/* Do parse transformation of the raw expression */
+			trans = (Node *) ielem->expr;
+		}
+
+		/*
+		 * transformExpr() should have already rejected subqueries,
+		 * aggregates, and window functions, based on the EXPR_KIND_ for an
+		 * index expression.  Expressions returning sets won't have been
+		 * rejected, but don't bother doing so here; there should be no
+		 * available expression unique index to match any such expression
+		 * against anyway.
+		 */
+		trans = transformExpr(pstate, trans, EXPR_KIND_INDEX_EXPRESSION);
+		/* Save in list of transformed expressions */
+		clauseexprs = list_append_unique(clauseexprs, trans);
+	}
+
+	return clauseexprs;
+}
+
+/*
+ * transformConflictClauseExpr -
+ *		transform expressions of ON CONFLICT.
+ *
+ * Transformed expressions used to infer one unique index relation to serve as
+ * an ON CONFLICT arbiter.  Partial unique indexes may be inferred using WHERE
+ * clause from inference specification clause.
+ */
+void
+transformConflictClause(ParseState *pstate, ConflictClause *confClause,
+						List **arbiterExpr, Node **arbiterWhere)
+{
+	InferClause *infer = confClause->infer;
+
+	/* This obviates the need for historic snapshot support */
+	if (IsCatalogRelation(pstate->p_target_relation))
+		elog(ERROR, "ON CONFLICT not supported with catalog relations");
+
+	/*
+	 * If there is no inference clause, this might be an updatable view, which
+	 * are supported by ON CONFLICT IGNORE (without columns/ expressions
+	 * specified to infer a unique index from).  It might also be a relation
+	 * with inheritance children, which would also make proceeding with
+	 * inference fail.
+	 */
+	if (infer)
+	{
+		*arbiterExpr = resolve_unique_index_expr(pstate, infer,
+												 pstate->p_target_relation);
+
+		/*
+		 * Handling inference WHERE clause (for partial unique index
+		 * inference)
+		 */
+		if (infer->whereClause)
+			*arbiterWhere = transformExpr(pstate, infer->whereClause,
+										  EXPR_KIND_INDEX_PREDICATE);
+	}
+
+	/*
+	 * It's convenient to form a list of expressions based on the
+	 * representation used by CREATE INDEX, since the same restrictions are
+	 * appropriate (on subqueries and so on).  However, from here on, the
+	 * handling of those expressions is identical to ordinary optimizable
+	 * statements.  In particular, assign_query_collations() can be trusted to
+	 * do the right thing with the post parse analysis query tree inference
+	 * clause representation.
+	 */
+}
+
+/*
  * addTargetToSortList
  *		If the given targetlist entry isn't already in the SortGroupClause
  *		list, add it to the end of the list, using the given sort ordering
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index e7614bd..3c05866 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -591,7 +591,11 @@ DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		return;
 
 	change = ReorderBufferGetChange(ctx->reorder);
-	change->action = REORDER_BUFFER_CHANGE_INSERT;
+	if (!(xlrec->flags & XLOG_HEAP_SPECULATIVE_TUPLE))
+		change->action = REORDER_BUFFER_CHANGE_INSERT;
+	else
+		change->action = REORDER_BUFFER_CHANGE_INTERNAL_INSERT;
+
 	memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
 
 	if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE)
@@ -682,7 +686,10 @@ DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		return;
 
 	change = ReorderBufferGetChange(ctx->reorder);
-	change->action = REORDER_BUFFER_CHANGE_DELETE;
+	if (!(xlrec->flags & XLOG_HEAP_SPECULATIVE_TUPLE))
+		change->action = REORDER_BUFFER_CHANGE_DELETE;
+	else
+		change->action = REORDER_BUFFER_CHANGE_INTERNAL_DELETE;
 
 	memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
 
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 20bb3b7..e8ad2ee 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -401,6 +401,8 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change)
 		case REORDER_BUFFER_CHANGE_INSERT:
 		case REORDER_BUFFER_CHANGE_UPDATE:
 		case REORDER_BUFFER_CHANGE_DELETE:
+		case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+		case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 			if (change->data.tp.newtuple)
 			{
 				ReorderBufferReturnTupleBuf(rb, change->data.tp.newtuple);
@@ -1314,6 +1316,7 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 	PG_TRY();
 	{
 		ReorderBufferChange *change;
+		ReorderBufferChange *peekchange = NULL;
 
 		if (using_subtxn)
 			BeginInternalSubTransaction("replay");
@@ -1323,16 +1326,26 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 		rb->begin(rb, txn);
 
 		iterstate = ReorderBufferIterTXNInit(rb, txn);
-		while ((change = ReorderBufferIterTXNNext(rb, iterstate)) != NULL)
+		while ((change = peekchange ? peekchange :
+				ReorderBufferIterTXNNext(rb, iterstate)) != NULL)
 		{
 			Relation	relation = NULL;
 			Oid			reloid;
 
+			/* Forget about previous peek ahead */
+			if (peekchange)
+				peekchange = NULL;
+			else
+				Assert(change->action !=
+					   REORDER_BUFFER_CHANGE_INTERNAL_DELETE);
+
 			switch (change->action)
 			{
 				case REORDER_BUFFER_CHANGE_INSERT:
 				case REORDER_BUFFER_CHANGE_UPDATE:
 				case REORDER_BUFFER_CHANGE_DELETE:
+				case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+				case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 					Assert(snapshot_now);
 
 					reloid = RelidByRelfilenode(change->data.tp.relnode.spcNode,
@@ -1374,7 +1387,65 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 						else if (!IsToastRelation(relation))
 						{
 							ReorderBufferToastReplace(rb, txn, relation, change);
-							rb->apply_change(rb, txn, relation, change);
+
+							/*
+							 * Kludge:  Speculative insertion occasionally
+							 * makes use of "super deletion" -- an
+							 * implementation defined delete of a speculatively
+							 * inserted tuple.  Neither the super deletion, nor
+							 * the insertion (which must be the prior record
+							 * type) are included in the final assembly when
+							 * the tuple was super-deleted.  Otherwise, an
+							 * ordinary insertion is assembled.
+							 */
+							if (change->action == REORDER_BUFFER_CHANGE_INTERNAL_INSERT)
+							{
+								/*
+								 * Need to ensure the memory used by promise
+								 * tuple isn't freed till we're done verifying
+								 * that there is no super deletion that
+								 * immediately follows.  Otherwise it could get
+								 * freed/reused while restoring spooled data
+								 * from disk.
+								 */
+								dlist_delete(&change->node);
+								peekchange = ReorderBufferIterTXNNext(rb, iterstate);
+								if (!peekchange || peekchange->action !=
+									REORDER_BUFFER_CHANGE_INTERNAL_DELETE)
+								{
+									/* Report as proper insert to client */
+									change->action = REORDER_BUFFER_CHANGE_INSERT;
+									rb->apply_change(rb, txn, relation,
+													 change);
+								}
+								else if (peekchange)
+								{
+									Assert(RelFileNodeEquals(change->data.tp.relnode,
+															 peekchange->data.tp.relnode));
+								}
+
+								ReorderBufferReturnChange(rb, change);
+							}
+							else if (change->action ==
+									 REORDER_BUFFER_CHANGE_INTERNAL_DELETE)
+							{
+								/*
+								 * The REORDER_BUFFER_CHANGE_INTERNAL_INSERT
+								 * case makes an assumption that
+								 * REORDER_BUFFER_CHANGE_INTERNAL_DELETE
+								 * changes immediately follows reliably iff a
+								 * speculatively inserted tuple was actually
+								 * super-deleted.
+								 */
+							}
+							else
+							{
+								/*
+								 * Handle non-speculative insertion related
+								 * changes
+								 */
+								rb->apply_change(rb, txn, relation, change);
+							}
 
 							/*
 							 * Only clear reassembled toast chunks if we're
@@ -2003,6 +2074,10 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
 		case REORDER_BUFFER_CHANGE_UPDATE:
 			/* fall through */
 		case REORDER_BUFFER_CHANGE_DELETE:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 			{
 				char	   *data;
 				ReorderBufferTupleBuf *oldtup,
@@ -2258,6 +2333,10 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
 		case REORDER_BUFFER_CHANGE_UPDATE:
 			/* fall through */
 		case REORDER_BUFFER_CHANGE_DELETE:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 			if (change->data.tp.newtuple)
 			{
 				Size		len = offsetof(ReorderBufferTupleBuf, t_data) +
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 9d2c280..40458a0 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -66,7 +66,7 @@ static void markQueryForLocking(Query *qry, Node *jtnode,
 					LockClauseStrength strength, LockWaitPolicy waitPolicy,
 					bool pushedDown);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
-		   int varno, Query *parsetree);
+		   int varno, Query *parsetree, bool *hasUpdate);
 static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
 			 bool forUpdatePushedDown);
 static bool view_has_instead_trigger(Relation view, CmdType event);
@@ -1288,7 +1288,8 @@ static List *
 matchLocks(CmdType event,
 		   RuleLock *rulelocks,
 		   int varno,
-		   Query *parsetree)
+		   Query *parsetree,
+		   bool *hasUpdate)
 {
 	List	   *matching_locks = NIL;
 	int			nlocks;
@@ -1309,6 +1310,9 @@ matchLocks(CmdType event,
 	{
 		RewriteRule *oneLock = rulelocks->rules[i];
 
+		if (oneLock->event == CMD_UPDATE)
+			*hasUpdate = true;
+
 		/*
 		 * Suppress ON INSERT/UPDATE/DELETE rules that are disabled or
 		 * configured to not fire during the current sessions replication
@@ -2953,6 +2957,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 	CmdType		event = parsetree->commandType;
 	bool		instead = false;
 	bool		returning = false;
+	bool		updatableview = false;
 	Query	   *qual_product = NULL;
 	List	   *rewritten = NIL;
 	ListCell   *lc1;
@@ -3035,6 +3040,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 		Relation	rt_entry_relation;
 		List	   *locks;
 		List	   *product_queries;
+		bool		hasUpdate = false;
 
 		result_relation = parsetree->resultRelation;
 		Assert(result_relation != 0);
@@ -3103,7 +3109,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 		 * Collect and apply the appropriate rules.
 		 */
 		locks = matchLocks(event, rt_entry_relation->rd_rules,
-						   result_relation, parsetree);
+						   result_relation, parsetree, &hasUpdate);
 
 		product_queries = fireRules(parsetree,
 									result_relation,
@@ -3152,6 +3158,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 			 */
 			instead = true;
 			returning = true;
+			updatableview = true;
 		}
 
 		/*
@@ -3232,6 +3239,18 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 			}
 		}
 
+		/*
+		 * Updatable views are supported on a limited basis by ON CONFLICT
+		 * IGNORE (if there is no unique index inference required, speculative
+		 * insertion proceeds).
+		 */
+		if (parsetree->specClause != SPEC_NONE &&
+			(product_queries != NIL || hasUpdate) &&
+			!updatableview)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("INSERT with ON CONFLICT clause may not target relation with INSERT or UPDATE rules")));
+
 		heap_close(rt_entry_relation, NoLock);
 	}
 
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 8eaec0c..f2d313e 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -418,6 +418,13 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 								  latestXid))
 			ShmemVariableCache->latestCompletedXid = latestXid;
 
+		/* Also clear any speculative insertion information */
+		MyProc->specInsertRel.spcNode = InvalidOid;
+		MyProc->specInsertRel.dbNode = InvalidOid;
+		MyProc->specInsertRel.relNode = InvalidOid;
+		ItemPointerSetInvalid(&MyProc->specInsertTid);
+		MyProc->specInsertToken = 0;
+
 		LWLockRelease(ProcArrayLock);
 	}
 	else
@@ -435,6 +442,11 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		pgxact->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
 		pgxact->delayChkpt = false;		/* be sure this is cleared in abort */
 		proc->recoveryConflictPending = false;
+		MyProc->specInsertRel.spcNode = InvalidOid;
+		MyProc->specInsertRel.dbNode = InvalidOid;
+		MyProc->specInsertRel.relNode = InvalidOid;
+		ItemPointerSetInvalid(&MyProc->specInsertTid);
+		MyProc->specInsertToken = 0;
 
 		Assert(pgxact->nxids == 0);
 		Assert(pgxact->overflowed == false);
@@ -473,6 +485,13 @@ ProcArrayClearTransaction(PGPROC *proc)
 	/* Clear the subtransaction-XID cache too */
 	pgxact->nxids = 0;
 	pgxact->overflowed = false;
+
+	/* these should be clear, but just in case.. */
+	MyProc->specInsertRel.spcNode = InvalidOid;
+	MyProc->specInsertRel.dbNode = InvalidOid;
+	MyProc->specInsertRel.relNode = InvalidOid;
+	ItemPointerSetInvalid(&MyProc->specInsertTid);
+	MyProc->specInsertToken = 0;
 }
 
 /*
@@ -1107,6 +1126,96 @@ TransactionIdIsActive(TransactionId xid)
 
 
 /*
+ * SetSpeculativeInsertionToken -- Set speculative token
+ *
+ * The backend local counter value is set, to allow waiters to differentiate
+ * individual speculative insertions.
+ */
+void
+SetSpeculativeInsertionToken(uint32 token)
+{
+	MyProc->specInsertToken = token;
+}
+
+/*
+ * SetSpeculativeInsertionTid -- Set TID for speculative relfilenode
+ */
+void
+SetSpeculativeInsertionTid(RelFileNode relnode, ItemPointer tid)
+{
+	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+	MyProc->specInsertRel = relnode;
+	ItemPointerCopy(tid, &MyProc->specInsertTid);
+	LWLockRelease(ProcArrayLock);
+}
+
+/*
+ * ClearSpeculativeInsertionState -- Clear token and TID for ourselves
+ */
+void
+ClearSpeculativeInsertionState(void)
+{
+	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+	MyProc->specInsertRel.spcNode = InvalidOid;
+	MyProc->specInsertRel.dbNode = InvalidOid;
+	MyProc->specInsertRel.relNode = InvalidOid;
+	ItemPointerSetInvalid(&MyProc->specInsertTid);
+	MyProc->specInsertToken = 0;
+	LWLockRelease(ProcArrayLock);
+}
+
+/*
+ * Returns a speculative insertion token for waiting for the insertion to
+ * finish
+ */
+uint32
+SpeculativeInsertionIsInProgress(TransactionId xid, RelFileNode rel,
+								 ItemPointer tid)
+{
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+	uint32		result = 0;
+
+	if (TransactionIdPrecedes(xid, RecentXmin))
+		return result;
+
+	/*
+	 * Get the top transaction id.
+	 *
+	 * XXX We could search the proc array first, like
+	 * TransactionIdIsInProgress() does, but this isn't performance-critical.
+	 */
+	xid = SubTransGetTopmostTransaction(xid);
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		int			pgprocno = arrayP->pgprocnos[index];
+		PGPROC	   *proc = &allProcs[pgprocno];
+		volatile PGXACT *pgxact = &allPgXact[pgprocno];
+
+		if (pgxact->xid == xid)
+		{
+			/*
+			 * Found the backend.  Is it doing a speculative insertion of the
+			 * given tuple?
+			 */
+			if (RelFileNodeEquals(proc->specInsertRel, rel) &&
+				ItemPointerEquals(tid, &proc->specInsertTid))
+				result = proc->specInsertToken;
+
+			break;
+		}
+	}
+
+	LWLockRelease(ProcArrayLock);
+
+	return result;
+}
+
+
+/*
  * GetOldestXmin -- returns oldest transaction that was running
  *					when any current transaction was started.
  *
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index d13a167..ed92f70 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -576,6 +576,81 @@ ConditionalXactLockTableWait(TransactionId xid)
 }
 
 /*
+ * Per-backend final disambiguator of an attempt to insert speculatively.
+ *
+ * This may wraparound, but since it is only a final disambiguator (speculative
+ * waiters also check TID and relfilenode), this is deemed to be acceptable.
+ * There is only a theoretical, vanishingly small chance of a backend
+ * spuriously considering that it must wait on another backend's
+ * end-of-speculative insertion (call to SpeculativeInsertionLockRelease())
+ * when that isn't strictly necessary, and even this is likely to be
+ * inconsequential.  At worst, unprincipled deadlocks are not entirely
+ * eliminated in extreme corner cases.
+ */
+static uint32 speculativeInsertionToken = 0;
+
+/*
+ *		SpeculativeInsertionLockAcquire
+ *
+ * Insert a lock showing that the given transaction ID is inserting a tuple,
+ * but hasn't yet decided whether it's going to keep it. The lock can then be
+ * used to wait for the decision to go ahead with the insertion, or aborting
+ * it.
+ *
+ * The token is used to distinguish multiple insertions by the same
+ * transaction. A counter will do, for example.
+ */
+void
+SpeculativeInsertionLockAcquire(TransactionId xid)
+{
+	LOCKTAG		tag;
+
+	speculativeInsertionToken++;
+	SetSpeculativeInsertionToken(speculativeInsertionToken);
+
+	SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, speculativeInsertionToken);
+
+	(void) LockAcquire(&tag, ExclusiveLock, false, false);
+}
+
+/*
+ *		SpeculativeInsertionLockRelease
+ *
+ * Delete the lock showing that the given transaction is speculatively
+ * inserting a tuple.
+ */
+void
+SpeculativeInsertionLockRelease(TransactionId xid)
+{
+	LOCKTAG		tag;
+
+	SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, speculativeInsertionToken);
+
+	LockRelease(&tag, ExclusiveLock, false);
+}
+
+/*
+ *		SpeculativeInsertionWait
+ *
+ * Wait for the specified transaction to finish or abort the insertion of a
+ * tuple.
+ */
+void
+SpeculativeInsertionWait(TransactionId xid, uint32 token)
+{
+	LOCKTAG		tag;
+
+	SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, token);
+
+	Assert(TransactionIdIsValid(xid));
+	Assert(token != 0);
+
+	(void) LockAcquire(&tag, ShareLock, false, false);
+	LockRelease(&tag, ShareLock, false);
+}
+
+
+/*
  * XactLockTableWaitErrorContextCb
  *		Error context callback for transaction lock waits.
  */
@@ -873,6 +948,12 @@ DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
 							 tag->locktag_field1,
 							 tag->locktag_field2);
 			break;
+		case LOCKTAG_PROMISE_TUPLE:
+			appendStringInfo(buf,
+							 _("promise tuple with token %u of transaction %u"),
+							 tag->locktag_field2,
+							 tag->locktag_field1);
+			break;
 		case LOCKTAG_OBJECT:
 			appendStringInfo(buf,
 							 _("object %u of class %u of database %u"),
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index a1967b69..1056e71 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -28,6 +28,7 @@ static const char *const LockTagTypeNames[] = {
 	"tuple",
 	"transactionid",
 	"virtualxid",
+	"promise tuple",
 	"object",
 	"userlock",
 	"advisory"
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 777f55c..68361e9 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -262,6 +262,9 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
 		}
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return false;
+
 	/* by here, the inserting transaction has committed */
 
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
@@ -360,6 +363,7 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
 
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
 
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
@@ -446,6 +450,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
 
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
 
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
@@ -726,6 +731,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
 	Assert(htup->t_tableOid != InvalidOid);
 
 	snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+	snapshot->speculativeToken = 0;
 
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
@@ -807,6 +813,26 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
 		}
 		else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
 		{
+			RelFileNode rnode;
+			ForkNumber	forkno;
+			BlockNumber blockno;
+
+			BufferGetTag(buffer, &rnode, &forkno, &blockno);
+
+			/* tuples can only be in the main fork */
+			Assert(forkno == MAIN_FORKNUM);
+			Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
+
+			/*
+			 * Set speculative token.  Caller can worry about xmax, since it
+			 * requires a conclusively locked row version, and a concurrent
+			 * update to this tuple is a conflict of its purposes.
+			 */
+			snapshot->speculativeToken =
+				SpeculativeInsertionIsInProgress(HeapTupleHeaderGetRawXmin(tuple),
+												 rnode,
+												 &htup->t_self);
+
 			snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
 			/* XXX shouldn't we fall through to look at xmax? */
 			return true;		/* in insertion by other */
@@ -823,6 +849,9 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
 		}
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return false;
+
 	/* by here, the inserting transaction has committed */
 
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
@@ -1022,6 +1051,9 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
 		}
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return false;
+
 	/*
 	 * By here, the inserting transaction has committed - have to check
 	 * when...
@@ -1218,6 +1250,9 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
 		 */
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return HEAPTUPLE_DEAD;
+
 	/*
 	 * Okay, the inserter committed, so it was good at some point.  Now what
 	 * about the deleting transaction?
@@ -1406,7 +1441,10 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
 	if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
 		return false;
 
-	/* Deleter committed, so tuple is dead if the XID is old enough. */
+	/*
+	 * Deleter committed, so tuple is dead if the XID is old enough.  This
+	 * handles super deleted tuples correctly.
+	 */
 	return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
 }
 
@@ -1539,6 +1577,8 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
 {
 	TransactionId xmax;
 
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
+
 	/* if there's no valid Xmax, then there's obviously no update either */
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)
 		return true;
@@ -1596,6 +1636,9 @@ TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
  * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
  * reading catalog pages which couldn't have been created in an older version.
  *
+ * We don't support speculative insertion into catalogs, and so there are no
+ * checks for super deleted tuples.
+ *
  * We don't set any hint bits in here as it seems unlikely to be beneficial as
  * those should already be set by normal access and it seems to be too
  * dangerous to do so as the semantics of doing so during timetravel are more
@@ -1611,6 +1654,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
 
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
 
 	/* inserting transaction aborted */
 	if (HeapTupleHeaderXminInvalid(tuple))
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 939d93d..62e760a 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -28,6 +28,7 @@
 #define HEAP_INSERT_SKIP_WAL	0x0001
 #define HEAP_INSERT_SKIP_FSM	0x0002
 #define HEAP_INSERT_FROZEN		0x0004
+#define HEAP_INSERT_SPECULATIVE 0x0008
 
 typedef struct BulkInsertStateData *BulkInsertState;
 
@@ -141,7 +142,7 @@ extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
 				  CommandId cid, int options, BulkInsertState bistate);
 extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
 			CommandId cid, Snapshot crosscheck, bool wait,
-			HeapUpdateFailureData *hufd);
+			HeapUpdateFailureData *hufd, bool killspeculative);
 extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
 			HeapTuple newtup,
 			CommandId cid, Snapshot crosscheck, bool wait,
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index f0f89de..568ec2c 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -73,6 +73,8 @@
 #define XLOG_HEAP_SUFFIX_FROM_OLD			(1<<6)
 /* last xl_heap_multi_insert record for one heap_multi_insert() call */
 #define XLOG_HEAP_LAST_MULTI_INSERT			(1<<7)
+/* reuse xl_heap_multi_insert-only bit for xl_heap_insert and xl_heap_delete */
+#define XLOG_HEAP_SPECULATIVE_TUPLE	XLOG_HEAP_LAST_MULTI_INSERT
 
 /* convenience macro for checking whether any form of old tuple was logged */
 #define XLOG_HEAP_CONTAINS_OLD						\
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 0a673cd..7f7ffaf 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -307,6 +307,18 @@ struct HeapTupleHeaderData
 )
 
 /*
+ * Was tuple "super deleted" following unsuccessful speculative insertion (i.e.
+ * conflict was detected at insertion time)?  Is is not sufficient to set
+ * HEAP_XMIN_INVALID to super delete because it is only a hint, and because it
+ * interacts with transaction commit status.  Speculative insertion decouples
+ * visibility from transaction duration for one special purpose.
+ */
+#define HeapTupleHeaderSuperDeleted(tup) \
+( \
+	(!TransactionIdIsValid(HeapTupleHeaderGetRawXmin(tup))) \
+)
+
+/*
  * HeapTupleHeaderGetRawXmax gets you the raw Xmax field.  To find out the Xid
  * that updated a tuple, you might need to resolve the MultiXactId if certain
  * bits are set.  HeapTupleHeaderGetUpdateXid checks those bits and takes care
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index e7cc7a0..42c10d4 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -80,6 +80,8 @@ extern void index_drop(Oid indexId, bool concurrent);
 
 extern IndexInfo *BuildIndexInfo(Relation index);
 
+extern void AddUniqueSpeculative(Relation index, IndexInfo *ii);
+
 extern void FormIndexDatum(IndexInfo *indexInfo,
 			   TupleTableSlot *slot,
 			   EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 40fde83..00f9c9c 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -352,16 +352,19 @@ extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
 extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags);
 extern void ExecCloseScanRelation(Relation scanrel);
 
-extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
+extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative);
 extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
 extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
-					  EState *estate);
-extern bool check_exclusion_constraint(Relation heap, Relation index,
-						   IndexInfo *indexInfo,
-						   ItemPointer tupleid,
-						   Datum *values, bool *isnull,
-						   EState *estate,
-						   bool newIndex, bool errorOK);
+					  EState *estate, bool noDupErr, Oid arbiterIdx);
+extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate,
+					  ItemPointer conflictTid, Oid arbiterIdx);
+extern bool check_exclusion_or_unique_constraint(Relation heap, Relation index,
+									   IndexInfo *indexInfo,
+									   ItemPointer tupleid,
+									   Datum *values, bool *isnull,
+									   EState *estate,
+									   bool newIndex, bool errorOK,
+									   bool wait, ItemPointer conflictTid);
 
 extern void RegisterExprContextCallback(ExprContext *econtext,
 							ExprContextCallbackFunction function,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 59b17f3..5ee93b9 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -41,6 +41,9 @@
  *		ExclusionOps		Per-column exclusion operators, or NULL if none
  *		ExclusionProcs		Underlying function OIDs for ExclusionOps
  *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
+ *		UniqueOps			Theses are like Exclusion*, but for unique indexes
+ *		UniqueProcs
+ *		UniqueStrats
  *		Unique				is it a unique index?
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
@@ -62,6 +65,9 @@ typedef struct IndexInfo
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
+	Oid		   *ii_UniqueOps;	/* array with one entry per column */
+	Oid		   *ii_UniqueProcs;		/* array with one entry per column */
+	uint16	   *ii_UniqueStrats;		/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
@@ -1089,6 +1095,8 @@ typedef struct ModifyTableState
 	int			mt_whichplan;	/* which one is being executed (0..n-1) */
 	ResultRelInfo *resultRelInfo;		/* per-subplan target relations */
 	List	  **mt_arowmarks;	/* per-subplan ExecAuxRowMark lists */
+	SpecCmd		spec;			/* reason for speculative insertion */
+	Oid			arbiterIndex;	/* unique index to arbitrate taking alt path */
 	EPQState	mt_epqstate;	/* for evaluating EvalPlanQual rechecks */
 	bool		fireBSTriggers; /* do we need to fire stmt triggers? */
 } ModifyTableState;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 97ef0fc..26c2f2a 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -412,6 +412,8 @@ typedef enum NodeTag
 	T_RowMarkClause,
 	T_XmlSerialize,
 	T_WithClause,
+	T_InferClause,
+	T_ConflictClause,
 	T_CommonTableExpr,
 
 	/*
@@ -624,4 +626,16 @@ typedef enum JoinType
 	   (1 << JOIN_RIGHT) | \
 	   (1 << JOIN_ANTI))) != 0)
 
+/*
+ * SpecCmd -
+ *	  "Speculative insertion" clause
+ *
+ * This is needed in both parsenodes.h and plannodes.h, so put it here...
+ */
+typedef enum
+{
+	SPEC_NONE,		/* Not involved in speculative insertion */
+	SPEC_IGNORE		/* INSERT of "ON CONFLICT IGNORE" */
+} SpecCmd;
+
 #endif   /* NODES_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac13302..4252559 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -132,6 +132,10 @@ typedef struct Query
 
 	List	   *withCheckOptions;		/* a list of WithCheckOption's */
 
+	SpecCmd		specClause;		/* speculative insertion clause */
+	List	   *arbiterExpr;	/* Unique index arbiter exprs */
+	Node	   *arbiterWhere;	/* Unique index arbiter WHERE clause */
+
 	List	   *returningList;	/* return-values list (of TargetEntry) */
 
 	List	   *groupClause;	/* a list of SortGroupClause's */
@@ -571,7 +575,7 @@ typedef enum TableLikeOption
 } TableLikeOption;
 
 /*
- * IndexElem - index parameters (used in CREATE INDEX)
+ * IndexElem - index parameters (used in CREATE INDEX, and in ON CONFLICT)
  *
  * For a plain index attribute, 'name' is the name of the table column to
  * index, and 'expr' is NULL.  For an index expression, 'name' is NULL and
@@ -1004,6 +1008,34 @@ typedef struct WithClause
 } WithClause;
 
 /*
+ * InferClause -
+ * 		ON CONFLICT unique index inference clause
+ *
+ * Note: InferClause does not propagate into the Query representation.
+ */
+typedef struct InferClause
+{
+	NodeTag		type;
+	List	   *indexElems;		/* IndexElems to infer unique index */
+	Node	   *whereClause;	/* qualification (partial-index predicate) */
+	int			location;		/* token location, or -1 if unknown */
+} InferClause;
+
+/*
+ * ConflictClause -
+ * 		representation of ON CONFLICT clause
+ *
+ * Note: ConflictClause does not propagate into the Query representation.
+ */
+typedef struct ConflictClause
+{
+	NodeTag			type;
+	SpecCmd			specclause;		/* Variant specified */
+	InferClause	   *infer;			/* Optional index inference clause */
+	int				location;		/* token location, or -1 if unknown */
+} ConflictClause;
+
+/*
  * CommonTableExpr -
  *	   representation of WITH list element
  *
@@ -1053,6 +1085,7 @@ typedef struct InsertStmt
 	RangeVar   *relation;		/* relation to insert into */
 	List	   *cols;			/* optional: names of the target columns */
 	Node	   *selectStmt;		/* the source SELECT/VALUES, or NULL */
+	ConflictClause  *confClause;	/* ON CONFLICT clause */
 	List	   *returningList;	/* list of expressions to return */
 	WithClause *withClause;		/* WITH clause */
 } InsertStmt;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index f6683f0..170084f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -178,6 +178,8 @@ typedef struct ModifyTable
 	List	   *resultRelations;	/* integer list of RT indexes */
 	int			resultRelIndex; /* index of first resultRel in plan's list */
 	List	   *plans;			/* plan(s) producing source data */
+	SpecCmd		spec;			/* speculative insertion specification */
+	Oid			arbiterIndex;	/* Oid of ON CONFLICT arbiter index */
 	List	   *withCheckOptionLists;	/* per-target-table WCO lists */
 	List	   *returningLists; /* per-target-table RETURNING tlists */
 	List	   *fdwPrivLists;	/* per-target-table FDW private data lists */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6cad92e..801effe 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -64,6 +64,7 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
 							int indexcol,
 							List **indexcolnos,
 							bool *var_on_left_p);
+extern Oid plan_speculative_use_index(PlannerInfo *root, List *indexList);
 
 /*
  * tidpath.h
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index 8eb2e57..878adfe 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -28,6 +28,8 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
 extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
 				  bool inhparent, RelOptInfo *rel);
 
+extern Oid infer_unique_index(PlannerInfo *root);
+
 extern void estimate_rel_size(Relation rel, int32 *attr_widths,
 				  BlockNumber *pages, double *tuples, double *allvisfrac);
 
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index fa72918..c3a0634 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -85,7 +85,7 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, int epqParam);
+				 List *rowMarks, SpecCmd spec, int epqParam);
 extern bool is_projection_capable_plan(Plan *plan);
 
 /*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7c243ec..cf501e6 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,7 @@ PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
 PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
 PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
+PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
 PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD)
 PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD)
@@ -180,6 +181,7 @@ PG_KEYWORD("hold", HOLD, UNRESERVED_KEYWORD)
 PG_KEYWORD("hour", HOUR_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("identity", IDENTITY_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("if", IF_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("ignore", IGNORE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("immediate", IMMEDIATE, UNRESERVED_KEYWORD)
 PG_KEYWORD("immutable", IMMUTABLE, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 6a4438f..d1d0d12 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -41,6 +41,8 @@ extern List *transformDistinctClause(ParseState *pstate,
 						List **targetlist, List *sortClause, bool is_agg);
 extern List *transformDistinctOnClause(ParseState *pstate, List *distinctlist,
 						  List **targetlist, List *sortClause);
+extern void transformConflictClause(ParseState *pstate, ConflictClause *confClause,
+									List **arbiterExpr, Node **arbiterWhere);
 
 extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 					List *sortlist, List *targetlist, SortBy *sortby,
diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h
index f1e0f57..d694981 100644
--- a/src/include/replication/reorderbuffer.h
+++ b/src/include/replication/reorderbuffer.h
@@ -43,6 +43,13 @@ typedef struct ReorderBufferTupleBuf
  * and ComboCids in the same list with the user visible INSERT/UPDATE/DELETE
  * changes. Users of the decoding facilities will never see changes with
  * *_INTERNAL_* actions.
+ *
+ * The REORDER_BUFFER_CHANGE_INTERNAL_INSERT and
+ * REORDER_BUFFER_CHANGE_INTERNAL_DELETE changes concern "super deletion",
+ * which is a mechanism that speculative insertion makes use of to handle
+ * conflicts.  At transaction reassembly these will be consolidated, and so
+ * decoding plugins will only ever handle REORDER_BUFFER_CHANGE_INSERT changes
+ * here too (in the common case where speculative insertion works out).
  */
 enum ReorderBufferChangeType
 {
@@ -51,7 +58,9 @@ enum ReorderBufferChangeType
 	REORDER_BUFFER_CHANGE_DELETE,
 	REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT,
 	REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID,
-	REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID
+	REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID,
+	REORDER_BUFFER_CHANGE_INTERNAL_INSERT,
+	REORDER_BUFFER_CHANGE_INTERNAL_DELETE
 };
 
 /*
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index f5d70e5..6bb95fc 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -76,6 +76,11 @@ extern bool ConditionalXactLockTableWait(TransactionId xid);
 extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode);
 extern void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode);
 
+/* Lock an XID for tuple insertion (used to wait for an insertion to finish) */
+extern void SpeculativeInsertionLockAcquire(TransactionId xid);
+extern void SpeculativeInsertionLockRelease(TransactionId xid);
+extern void SpeculativeInsertionWait(TransactionId xid, uint32 token);
+
 /* Lock a general object (other than a relation) of the current database */
 extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
 				   LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 1100923..7d49bb0 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -176,6 +176,8 @@ typedef enum LockTagType
 	/* ID info for a transaction is its TransactionId */
 	LOCKTAG_VIRTUALTRANSACTION, /* virtual transaction (ditto) */
 	/* ID info for a virtual transaction is its VirtualTransactionId */
+	LOCKTAG_PROMISE_TUPLE,		/* tuple insertion, keyed by Xid and token */
+	/* ID info for a transaction is its TransactionId */
 	LOCKTAG_OBJECT,				/* non-relation database object */
 	/* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */
 
@@ -261,6 +263,14 @@ typedef struct LOCKTAG
 	 (locktag).locktag_type = LOCKTAG_VIRTUALTRANSACTION, \
 	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
 
+#define SET_LOCKTAG_SPECULATIVE_INSERTION(locktag,xid,token) \
+	((locktag).locktag_field1 = (xid), \
+	 (locktag).locktag_field2 = (token),		\
+	 (locktag).locktag_field3 = 0, \
+	 (locktag).locktag_field4 = 0, \
+	 (locktag).locktag_type = LOCKTAG_PROMISE_TUPLE, \
+	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
+
 #define SET_LOCKTAG_OBJECT(locktag,dboid,classoid,objoid,objsubid) \
 	((locktag).locktag_field1 = (dboid), \
 	 (locktag).locktag_field2 = (classoid), \
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index e807a2e..c72f55b 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -16,9 +16,11 @@
 
 #include "access/xlogdefs.h"
 #include "lib/ilist.h"
+#include "storage/itemptr.h"
 #include "storage/latch.h"
 #include "storage/lock.h"
 #include "storage/pg_sema.h"
+#include "storage/relfilenode.h"
 
 /*
  * Each backend advertises up to PGPROC_MAX_CACHED_SUBXIDS TransactionIds
@@ -132,6 +134,17 @@ struct PGPROC
 	 */
 	SHM_QUEUE	myProcLocks[NUM_LOCK_PARTITIONS];
 
+	/*
+	 * Info to allow us to perform speculative insertion without "unprincipled
+	 * deadlocks". This state allows others to wait on the outcome of an
+	 * optimistically inserted speculative tuple for only the duration of the
+	 * insertion (not to the end of our xact) iff the insertion does not work
+	 * out (due to our detecting a conflict).
+	 */
+	RelFileNode	specInsertRel;		/* Relfilenode speculatively inserted into */
+	ItemPointerData specInsertTid;	/* TID within specInsertRel */
+	uint32		specInsertToken;	/* Final disambiguator of insertions */
+
 	struct XidCache subxids;	/* cache for subtransaction XIDs */
 
 	/* Per-backend LWLock.  Protects fields below. */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 97c6e93..ea2bba9 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -55,6 +55,13 @@ extern TransactionId GetOldestXmin(Relation rel, bool ignoreVacuum);
 extern TransactionId GetOldestActiveTransactionId(void);
 extern TransactionId GetOldestSafeDecodingTransactionId(void);
 
+extern void SetSpeculativeInsertionToken(uint32 token);
+extern void SetSpeculativeInsertionTid(RelFileNode relnode, ItemPointer tid);
+extern void ClearSpeculativeInsertionState(void);
+extern uint32 SpeculativeInsertionIsInProgress(TransactionId xid,
+											   RelFileNode rel,
+											   ItemPointer tid);
+
 extern VirtualTransactionId *GetVirtualXIDsDelayingChkpt(int *nvxids);
 extern bool HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids);
 
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 26fb257..cd5ad76 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -87,6 +87,17 @@ typedef struct SnapshotData
 	bool		copied;			/* false if it's a static snapshot */
 
 	/*
+	 * Snapshot's speculative token is value set by HeapTupleSatisfiesDirty,
+	 * indicating that the tuple is being inserted speculatively, and may yet
+	 * be "super-deleted" before EOX. The caller may use the value with
+	 * PromiseTupleInsertionWait to wait for the inserter to decide. It is only
+	 * set when a valid 'xmin' is set, too.  By convention, when
+	 * speculativeToken is zero, the caller must assume that is should wait on
+	 * a non-speculative tuple (i.e. wait for xmin/xmax to commit).
+	 */
+	uint32		speculativeToken;
+
+	/*
 	 * note: all ids in subxip[] are >= xmin, but we don't bother filtering
 	 * out any that are >= xmax
 	 */
diff --git a/src/test/isolation/expected/insert-conflict-ignore.out b/src/test/isolation/expected/insert-conflict-ignore.out
new file mode 100644
index 0000000..e6cc2a1
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-ignore.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: ignore1 ignore2 c1 select2 c2
+step ignore1: INSERT INTO ints(key, val) VALUES(1, 'ignore1') ON CONFLICT IGNORE;
+step ignore2: INSERT INTO ints(key, val) VALUES(1, 'ignore2') ON CONFLICT IGNORE; <waiting ...>
+step c1: COMMIT;
+step ignore2: <... completed>
+step select2: SELECT * FROM ints;
+key            val            
+
+1              ignore1        
+step c2: COMMIT;
+
+starting permutation: ignore1 ignore2 a1 select2 c2
+step ignore1: INSERT INTO ints(key, val) VALUES(1, 'ignore1') ON CONFLICT IGNORE;
+step ignore2: INSERT INTO ints(key, val) VALUES(1, 'ignore2') ON CONFLICT IGNORE; <waiting ...>
+step a1: ABORT;
+step ignore2: <... completed>
+step select2: SELECT * FROM ints;
+key            val            
+
+1              ignore2        
+step c2: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index c055a53..59d14e9 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -16,6 +16,7 @@ test: fk-deadlock2
 test: eval-plan-qual
 test: lock-update-delete
 test: lock-update-traversal
+test: insert-conflict-ignore
 test: delete-abort-savept
 test: delete-abort-savept-2
 test: aborted-keyrevoke
diff --git a/src/test/isolation/specs/insert-conflict-ignore.spec b/src/test/isolation/specs/insert-conflict-ignore.spec
new file mode 100644
index 0000000..fde43b3
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-ignore.spec
@@ -0,0 +1,41 @@
+# INSERT...ON CONFLICT IGNORE test
+#
+# This test tries to expose problems with the interaction between concurrent
+# sessions during INSERT...ON CONFLICT IGNORE.
+#
+# The convention here is that session 1 always ends up inserting, and session 2
+# always ends up ignoring.
+
+setup
+{
+  CREATE TABLE ints (key int primary key, val text);
+}
+
+teardown
+{
+  DROP TABLE ints;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "ignore1" { INSERT INTO ints(key, val) VALUES(1, 'ignore1') ON CONFLICT IGNORE; }
+step "c1" { COMMIT; }
+step "a1" { ABORT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "ignore2" { INSERT INTO ints(key, val) VALUES(1, 'ignore2') ON CONFLICT IGNORE; }
+step "select2" { SELECT * FROM ints; }
+step "c2" { COMMIT; }
+step "a2" { ABORT; }
+
+# Regular case where one session block-waits on another to determine if it
+# should proceed with an insert or ignore.
+permutation "ignore1" "ignore2" "c1" "select2" "c2"
+permutation "ignore1" "ignore2" "a1" "select2" "c2"
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
new file mode 100644
index 0000000..a34d857
--- /dev/null
+++ b/src/test/regress/expected/insert_conflict.out
@@ -0,0 +1,55 @@
+--
+-- insert...on conflict update unique index inference
+--
+create table insertconflicttest(key int4, fruit text);
+--
+-- Test partial unique index inference
+--
+create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
+-- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
+-- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
+ERROR:  partial arbiter unique index has predicate that does not cover tuple proposed for insertion
+DETAIL:  ON CONFLICT inference clause implies that the tuple proposed for insertion must be covered by predicate for partial index "partial_key_index".
+drop index partial_key_index;
+-- Cleanup
+drop table insertconflicttest;
+-- ******************************************************************
+-- *                                                                *
+-- * Test inheritance (example taken from tutorial)                 *
+-- *                                                                *
+-- ******************************************************************
+create table cities (
+	name		text,
+	population	float8,
+	altitude	int		-- (in ft)
+);
+create table capitals (
+	state		char(2)
+) inherits (cities);
+-- Create unique indexes.  Due to a general limitation of inheritance,
+-- uniqueness is only enforced per-relation
+create unique index cities_names_unique on cities (name);
+create unique index capitals_names_unique on capitals (name);
+-- prepopulate the tables.
+insert into cities values ('San Francisco', 7.24E+5, 63);
+insert into cities values ('Las Vegas', 2.583E+5, 2174);
+insert into cities values ('Mariposa', 1200, 1953);
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
+insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
+-- Tests proper for inheritance:
+-- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
+ERROR:  relation "cities" has inheritance children
+HINT:  Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.
+-- Succeeds:
+-- There is at least limited support for relations with children:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
+-- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
+-- clean up
+drop table capitals;
+drop table cities;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 26c60e4..e563514 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1123,6 +1123,10 @@ SELECT * FROM shoelace_log ORDER BY sl_name;
 	SELECT * FROM shoelace_obsolete WHERE sl_avail = 0;
 insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
 insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
+-- Unsupported (even though a similar updatable view construct is)
+insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0)
+  on conflict ignore;
+ERROR:  INSERT with ON CONFLICT clause may not target relation with INSERT or UPDATE rules
 SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm;
   sl_name   | sl_avail |  sl_color  | sl_len | sl_unit  | sl_len_cm 
 ------------+----------+------------+--------+----------+-----------
@@ -2351,6 +2355,11 @@ DETAIL:  Key (id3a, id3c)=(1, 13) is not present in table "rule_and_refint_t2".
 insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
 ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
 DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
+-- Ordinary table
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict ignore;
+ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
+DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
 			where (((rule_and_refint_t3.id3a = new.id3a)
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c49e769..b6af069 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -215,6 +215,10 @@ INSERT INTO rw_view15 VALUES (3, 'ROW 3'); -- should fail
 ERROR:  cannot insert into column "upper" of view "rw_view15"
 DETAIL:  View columns that are not columns of their base relation are not updatable.
 INSERT INTO rw_view15 (a) VALUES (3); -- should be OK
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT IGNORE; -- succeeds
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) IGNORE; -- fails, unsupported
+ERROR:  relation "rw_view15" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 ALTER VIEW rw_view15 ALTER COLUMN upper SET DEFAULT 'NOT SET';
 INSERT INTO rw_view15 (a) VALUES (4); -- should fail
 ERROR:  cannot insert into column "upper" of view "rw_view15"
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e0ae2f2..528d3b7 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -36,6 +36,7 @@ test: geometry horology regex oidjoins type_sanity opr_sanity
 # These four each depend on the previous one
 # ----------
 test: insert
+test: insert_conflict
 test: create_function_1
 test: create_type
 test: create_table
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 7f762bd..b7c8f53 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -50,6 +50,7 @@ test: oidjoins
 test: type_sanity
 test: opr_sanity
 test: insert
+test: insert_conflict
 test: create_function_1
 test: create_type
 test: create_table
diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql
new file mode 100644
index 0000000..e330ecd
--- /dev/null
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -0,0 +1,65 @@
+--
+-- insert...on conflict update unique index inference
+--
+create table insertconflicttest(key int4, fruit text);
+
+--
+-- Test partial unique index inference
+--
+create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
+
+-- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
+
+-- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
+insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
+
+drop index partial_key_index;
+
+-- Cleanup
+drop table insertconflicttest;
+
+-- ******************************************************************
+-- *                                                                *
+-- * Test inheritance (example taken from tutorial)                 *
+-- *                                                                *
+-- ******************************************************************
+create table cities (
+	name		text,
+	population	float8,
+	altitude	int		-- (in ft)
+);
+
+create table capitals (
+	state		char(2)
+) inherits (cities);
+
+-- Create unique indexes.  Due to a general limitation of inheritance,
+-- uniqueness is only enforced per-relation
+create unique index cities_names_unique on cities (name);
+create unique index capitals_names_unique on capitals (name);
+
+-- prepopulate the tables.
+insert into cities values ('San Francisco', 7.24E+5, 63);
+insert into cities values ('Las Vegas', 2.583E+5, 2174);
+insert into cities values ('Mariposa', 1200, 1953);
+
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
+insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
+
+-- Tests proper for inheritance:
+
+-- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
+
+-- Succeeds:
+
+-- There is at least limited support for relations with children:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
+-- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
+
+-- clean up
+drop table capitals;
+drop table cities;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index c385e41..5807331 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -680,6 +680,9 @@ SELECT * FROM shoelace_log ORDER BY sl_name;
 
 insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
 insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
+-- Unsupported (even though a similar updatable view construct is)
+insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0)
+  on conflict ignore;
 
 SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm;
 SELECT * FROM shoelace_candelete;
@@ -844,6 +847,9 @@ insert into rule_and_refint_t3 values (1, 12, 11, 'row3');
 insert into rule_and_refint_t3 values (1, 12, 12, 'row4');
 insert into rule_and_refint_t3 values (1, 11, 13, 'row5');
 insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
+-- Ordinary table
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict ignore;
 
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 60c7e29..48dd9a9 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -69,6 +69,8 @@ DELETE FROM rw_view14 WHERE a=3; -- should be OK
 -- Partially updatable view
 INSERT INTO rw_view15 VALUES (3, 'ROW 3'); -- should fail
 INSERT INTO rw_view15 (a) VALUES (3); -- should be OK
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT IGNORE; -- succeeds
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) IGNORE; -- fails, unsupported
 ALTER VIEW rw_view15 ALTER COLUMN upper SET DEFAULT 'NOT SET';
 INSERT INTO rw_view15 (a) VALUES (4); -- should fail
 UPDATE rw_view15 SET upper='ROW 3' WHERE a=3; -- should fail
-- 
1.9.1

#2Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#1)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 4, 2015 at 5:18 PM, Peter Geoghegan <pg@heroku.com> wrote:

Attached patch series forms what I'm calling V3.0 of the INSERT ... ON
CONFLICT IGNORE/UPDATE feature. (Sorry about all the threads. I feel
this development justifies a new thread, though.)

Bruce Momjian kindly made available a server for stress-testing [1]http://momjian.us/main/blogs/pgblog/2012.html#January_20_2012 -- Peter Geoghegan.
I'm using jjanes_upsert for this task (I stopped doing this for a
little while, and was in need of a new server).

At the very highest client count I'm testing (128), I see unprincipled
deadlocks for the exclusion constraint case very infrequently:

"""""
2015-03-05 14:09:36 EST [ 64987901 ]: ERROR: deadlock detected
2015-03-05 14:09:36 EST [ 64987901 ]: DETAIL: Process 7044 waits for
ShareLock on promise tuple with token 1 of transaction 64987589;
blocked by process 7200.
Process 7200 waits for ShareLock on transaction 64987901;
blocked by process 7044.
Process 7044: insert into upsert_race_test (index, count)
values ('541','-1') on conflict
update set count=TARGET.count + EXCLUDED.count
where TARGET.index = EXCLUDED.index
returning count
Process 7200: insert into upsert_race_test (index, count)
values ('541','-1') on conflict
update set count=TARGET.count + EXCLUDED.count
where TARGET.index = EXCLUDED.index
returning count
2015-03-05 14:09:36 EST [ 64987901 ]: HINT: See server log for query details.
2015-03-05 14:09:36 EST [ 64987901 ]: STATEMENT: insert into
upsert_race_test (index, count) values ('541','-1') on conflict
update set count=TARGET.count + EXCLUDED.count
where TARGET.index = EXCLUDED.index
returning count

"""""

(Reminder: Exclusion constraints doing UPSERTs, and not just IGNOREs,
are artificial; this has been enabled within the parser solely for the
benefit of this stress-testing. Also, the B-Tree AM does not have nor
require "livelock insurance").

This only happens after just over 30 minutes, while consistently doing
128 client exclusion constraint runs. This is pretty close to the most
stressful thing that you could throw at the implementation, so that's
really not too bad.

I believe that this regression occurred as a direct result of adding
"livelock insurance". Basically, we cannot be 100% sure that the
interleaving of WAL-logged things within and across transactions won't
be such that the "only", "oldest" session that gets to wait in the
second stage (the second possible call to
check_exclusion_or_unique_constraint(), from ExecInsertIndexTuples())
will really be the "oldest" XID. Another *older* xact could just get
in ahead of us, waiting on our promise tuple as we wait on its xact
end (maybe it updates some third tuple that we didn't see in that has
already committed...not 100% sure). Obviously XID assignment order
does not guarantee that things like heap insertion and index tuple
insertion occur in serial XID order, especially with confounding
factors like super deletion. And so, every once in a long while we
deadlock.

Now, the very fact that this happens at all actually demonstrates the
need for "livelock insurance", IMV. The fact that we reliably
terminate is *reassuring*, because livelocks are in general a
terrifying possibility. We cannot *truly* solve the unprincipled
deadlock problem without adding livelocks, I think. But what we have
here is very close to eliminating unprincipled deadlocks, while not
also adding any livelocks, AFAICT. I'd argue that that's good enough.

Of course, when I remove "livelock insurance", the problem ostensibly
goes away (I've waited on such a stress test session for a couple of
hours now, so this conclusion seems very likely to be correct). I
think that we should do nothing about this, other than document it as
possible in our comments on "livelock insurance". Again, it's very
unlikely to be a problem in the real world, especially since B-Trees
are unaffected.

Also, I should point out that one of those waiters doesn't look like
an insertion-related wait at all: "7200 waits for ShareLock on
transaction 64987901; blocked by process 7044". Looks like row locking
is an essential part of this deadlock, and ordinarily that isn't even
possible for exclusion constraints (unless the patch is hacked to make
the parser accept exclusion constraints for an UPSERT, as it has been
here). Not quite sure what exact XactLockTableWait() call did this,
but don't think it was any of them within
check_exclusion_or_unique_constraint(), based on the lack of details
(TID and so on are not shown).

[1]: http://momjian.us/main/blogs/pgblog/2012.html#January_20_2012 -- Peter Geoghegan
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Peter Geoghegan (#2)
1 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 5 March 2015 at 23:44, Peter Geoghegan <pg@heroku.com> wrote:

On Wed, Mar 4, 2015 at 5:18 PM, Peter Geoghegan <pg@heroku.com> wrote:

Attached patch series forms what I'm calling V3.0 of the INSERT ... ON
CONFLICT IGNORE/UPDATE feature. (Sorry about all the threads. I feel
this development justifies a new thread, though.)

Hi,

I had a play with this, mainly looking at the interaction with RLS.

(Note there is some bitrot in gram.y that prevents the first patch
from applying cleanly to HEAD)

I tested using the attached script, and one test didn't behave as I
expected. I believe the following should have been a valid upsert
(following the update path) but actually it failed:

INSERT INTO t1 VALUES (4, 0) ON CONFLICT (a) UPDATE SET b = 1;

AFAICT, it is applying a WITH CHECK OPTION with qual "b > 0 AND a % 2
= 0" to the about-to-be-updated tuple (a=4, b=0), which is wrong
because the "b > 0" check (policy p3) should only be applied to the
post-update tuple.

Possibly I'm missing something though.

Regards,
Dean

Attachments:

test.sqltext/x-sql; charset=US-ASCII; name=test.sqlDownload
#4Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#1)
1 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 03/05/2015 03:18 AM, Peter Geoghegan wrote:

Attached patch series forms what I'm calling V3.0 of the INSERT ... ON
CONFLICT IGNORE/UPDATE feature. (Sorry about all the threads. I feel
this development justifies a new thread, though.)

I'm still not sure the way the speculative locking works is the best
approach. Instead of clearing xmin on super-deletion, a new flag on the
heap tuple seems more straightforward. And we could put the speculative
insertion token in t_ctid, instead of stashing it in the PGPROC array.
That would again seem more straightforward.

A couple of quick random comments:

/*
* plan_speculative_use_index
* Use the planner to decide speculative insertion arbiter index
*
* Among indexes on target of INSERT ... ON CONFLICT, decide which index to
* use to arbitrate taking alternative path. This should be called
* infrequently in practice, because it's unusual for more than one index to
* be available that can satisfy a user-specified unique index inference
* specification.
*
* Note: caller had better already hold some type of lock on the table.
*/
Oid
plan_speculative_use_index(PlannerInfo *root, List *indexList)
{
...
/* Locate cheapest IndexOptInfo for the target index */

If I'm reading this correctly, if there are multiple indexes that match
the unique index inference specification, we pick the cheapest one.
Isn't that unstable? Two backends running the same INSERT ON CONFLICT
statement might pick different indexes, and the decision might change
over time as the table is analyzed. I think we should have a more robust
rule. Could we easily just use all matching indexes?

... Deferred unique constraints are not supported as
+   arbiters of whether an alternative <literal>ON CONFLICT</> path
+   should be taken.

We really need to find a shorter term for "arbiter of whether an
alternative path should be taken". Different variations of that term are
used a lot, and it's tedious to read.

* There is still an unresolved semantics issue with unique index
inference and non-default opclasses. I think it's sufficient that the
available/defined unique indexes dictate our idea of a unique
violation (that necessitates taking the alternative path). Even in a
world where there exists a non-default opclass with an "equals"
operator that does not match that of the default opclass (that is not
really the world we live in, because the only counter-example known is
made that way specifically to *discourage* its use by users), this
seems okay to me. It seems okay to me because surely the relevant
definition of equality is the one actually chosen for the available
unique index. If there exists an ambiguity for some strange reason
(i.e. there are two unique indexes, on the same attribute(s), but with
different "equals" operators), then its a costing issue, so the
behavior given is essentially non-predictable (it could end up being
either...but hey, those are the semantics). I have a very hard time
imagining how that could ever be the case, even when we have (say)
case insensitive opclasses for the text type. A case insensitive
opclass is stricter than a case sensitive opclass. Why would a user
ever want both on the same attribute(s) of the same table? Is the user
really more or less expecting to never get a unique violation on the
non-arbitrating unique index, despite all this?

If reviewers are absolutely insistent that this theoretical ambiguity
is a problem, we can add an optional CREATE INDEX style opclass
specification (I'm already using the IndexElems representation from
CREATE INDEX for the inference specification, actually, so that would
be easy enough). I really have a hard time believing that the ugliness
is a problem for those hypothetical users that eventually consider
"equals" operator ambiguity among opclasses among available unique
indexes to be a problem. I haven't just gone and implemented this
already because I didn't want to document that an opclass
specification will be accepted. Since there is literally no reason why
anyone would care today, I see no reason to add what IMV would really
just be cruft.

I've been thinking that it would be nice to be able to specify a
constraint name. Naming an index directly feels wrong, as in relational
and SQL philosophy, indexes are just an implementation detail, but
naming a constraint is a fair game. It would also be nice to be able to
specify "use the primary key".

Attached patch contains a few more things I saw at a quick read.

- Heikki

Attachments:

misc-upsert-issues.patchapplication/x-patch; name=misc-upsert-issues.patchDownload
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 46b8db8..d6fa98c 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1014,6 +1014,10 @@ GetForeignServerByName(const char *name, bool missing_ok);
      source provides.
     </para>
 
+    <!-- FIXME: If this is a hard limitation with the backend, the backend
+         should check and reject these cases. Otherwise, if it's possible
+         that a FDW would actually support this, this needs to be reworded
+      -->
     <para>
      <command>INSERT</> with an <literal>ON CONFLICT</> clause is not
      supported with a unique index inference specification.  When
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index 2b68121..24fb4b7 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -385,6 +385,7 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     <xref linkend="rules-privileges">).
    </para>
    <para>
+    <!-- FIXME: This paragraph needs to reworded, to make it more readable. -->
     <command>INSERT</command> with an <literal>ON CONFLICT IGNORE</>
     clause is only supported on updatable views under specific
     circumstances.  If a set of columns/expressions has been provided
diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml
index 1395c48..4f3878b 100644
--- a/doc/src/sgml/ref/insert.sgml
+++ b/doc/src/sgml/ref/insert.sgml
@@ -33,9 +33,12 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
   <title>Description</title>
 
   <para>
-   <command>INSERT</command> 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
+   <command>INSERT</command> 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.
+   <!-- FIXME: This needs to go somewhere later, not in the very first
+        introductory paragraph on INSERT in general -->
+   An alternative
    <literal>IGNORE</literal> path can optionally be specified, to be
    taken in the event of detecting that proceeding with insertion
    would result in a conflict (i.e. a conflicting tuple already
diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml
index 5030f3c..b1e8f9b 100644
--- a/doc/src/sgml/ref/set_constraints.sgml
+++ b/doc/src/sgml/ref/set_constraints.sgml
@@ -69,7 +69,11 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
   <para>
    Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>,
    <literal>REFERENCES</> (foreign key), and <literal>EXCLUDE</>
-   constraints are affected by this setting.  Note that constraints
+   constraints are affected by this setting.
+   <!-- FIXME: I take that this is meant to say that DEFERRED constraints
+        cannot be used as arbiters. e.g.  SET CONSTRAINTS ALL IMMEDIATE is
+        OK. -->
+   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
    <command>INSERT</command> statement that includes an <literal>ON
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index ffe1d62..0dabd16 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1548,6 +1548,7 @@ retry:
 			if (DirtySnapshot.speculativeToken)
 				SpeculativeInsertionWait(DirtySnapshot.xmin,
 										 DirtySnapshot.speculativeToken);
+			/* FIXME: both arms of this if do the same thing. Huh? */
 			else if (violationOK)
 				XactLockTableWait(xwait, heap, &ctid_wait,
 								  XLTW_RecheckExclusionConstr);
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 99c8d6a..d31db33 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -4118,10 +4118,10 @@ string_to_const(const char *str, Oid datatype)
  * plan_speculative_use_index
  *		Use the planner to decide speculative insertion arbiter index
  *
- * Among indexes on target of INSERT ... ON CONFLICT,  decide which index to
+ * Among indexes on target of INSERT ... ON CONFLICT, decide which index to
  * use to arbitrate taking alternative path.  This should be called
- * infrequently in practice, because its unusual for more than one index to be
- * available that can satisfy a user-specified unique index inference
+ * infrequently in practice, because it's unusual for more than one index to
+ * be available that can satisfy a user-specified unique index inference
  * specification.
  *
  * Note: caller had better already hold some type of lock on the table.
#5Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#2)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Mar 5, 2015 at 3:44 PM, Peter Geoghegan <pg@heroku.com> wrote:

Bruce Momjian kindly made available a server for stress-testing [1].
I'm using jjanes_upsert for this task (I stopped doing this for a
little while, and was in need of a new server).

BTW, this was run for about another week on Bruce's server, and no
further issues arose affecting either the B-Tree or exclusion
constraint implementations. I've stopped with it for now, because it
feels unlikely that I'm going to find any more bugs this way.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#4)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Mar 17, 2015 at 12:11 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I'm still not sure the way the speculative locking works is the best
approach. Instead of clearing xmin on super-deletion, a new flag on the heap
tuple seems more straightforward. And we could put the speculative insertion
token in t_ctid, instead of stashing it in the PGPROC array. That would
again seem more straightforward.

I see the appeal of that approach. What concerns me about that
approach is that it makes it the problem of the inserter to confirm
its insertion, even though overwhelmingly, speculative insertions
succeeds (the race window is small due to the pre-check). The current
speculative token is legitimately a very short lived thing, a thing
that I hesitate to write to disk at all. So our optimistic approach to
inserting speculatively loses its optimism, which I don't like, if you
know what I mean.

OTOH, apart from avoiding stashing things in PGPROC, I do see another
advantage to what you outline. Doing things this way (and making the
insertion and confirmation of a speculative insertion a two-phased
thing) unambiguously fixes all problems with logical decoding, without
adding unexpected cross-change-coordination tricks during transaction
reassembly, and really without much further thought. All we do is get
a new case to decode, that ultimately produces a
REORDER_BUFFER_CHANGE_INSERT change, which Andres more or less wanted
to do anyway.

Under this scheme with using t_ctid, heap_insert() would do minimal
WAL logging, necessary for the same reasons that some WAL logging is
required within heap_lock_tuple() (so the new record type is very
similar to the existing, simple xl_heap_lock record type). We'd use
one of the two remaining bits within t_infomask2 for this, so that
waiters will know to interpret t_ctid as a speculative insertion token
(and then wait on it). Then, after speculative insertion succeeded,
we'd WAL log something closer to an UPDATE to confirm that insertion
was in fact successful, which is where the contents of the new tuple
is actually finally WAL-logged (like UPDATEs used to be, but without a
new tuple being WAL-logged at all, since there of course is none).

Is that more or less what you have in mind?

A couple of quick random comments:

/*
* plan_speculative_use_index
* Use the planner to decide speculative insertion arbiter
index
*
* Among indexes on target of INSERT ... ON CONFLICT, decide which index
to
* use to arbitrate taking alternative path. This should be called
* infrequently in practice, because it's unusual for more than one index
to
* be available that can satisfy a user-specified unique index inference
* specification.
*
* Note: caller had better already hold some type of lock on the table.
*/
Oid
plan_speculative_use_index(PlannerInfo *root, List *indexList)
{
...
/* Locate cheapest IndexOptInfo for the target index */

If I'm reading this correctly, if there are multiple indexes that match the
unique index inference specification, we pick the cheapest one. Isn't that
unstable? Two backends running the same INSERT ON CONFLICT statement might
pick different indexes, and the decision might change over time as the table
is analyzed. I think we should have a more robust rule. Could we easily just
use all matching indexes?

Robert feel pretty strongly that there should be a costing aspect to
this. Initially I was skeptical of this, but just did what he said all
the same. But I've since come around to his view entirely because we
now support partial unique indexes.

You can add a predicate to a unique index inference specification, and
you're good, even if there is no partial index on those
attributes/expressions exactly matching the DML/inference predicate -
we use predicate_implied_by() for this, which works with an equivalent
non-partial unique index. This is because a non-partial unique index
covers those attributes sufficiently. There may be value in preferring
the cheap partial index, and then verifying that it actually did cover
the final inserted tuple version (which is how things work now). If we
cannot discriminate against one particular unique index, making sure
it covers the inserted heap tuple (by throwing a user-visible
ERRCODE_TRIGGERED_ACTION_EXCEPTION error if it doesn't, as I currently
do within ExecInsertIndexTuples()) is on shaky ground as a
user-visible behavior. I like that behavior, though - seems less
surprising than letting a failure to actually cover a selective
partial unique index predicate (that of the one and only arbiter
index) slide. You can only get this ERRCODE_TRIGGERED_ACTION_EXCEPTION
error when you actually had a predicate in your unique index inference
specification in the first place, so seems likely that you want the
error. But, I also like supporting unique indexes that are non-partial
even in the presence of a predicate in the unique index inference
specification clause, because, as I said, that's still correct - I can
see not doing this breaking queries when a partial unique index is
dropped due to a more general uniqueness requirement.

Besides, having a list of arbiter unique indexes that must all be
equivalent for the user's purpose anyway is very awkward indeed. It
seems very confusing. If they all have equivalent definitions anyway,
as they must, then it must not matter one bit which one you take
(except perhaps on cost grounds). It might be unstable, but it
couldn't possibly matter, so why bother with anything else? If it
*does* matter on semantics grounds, then we have bigger problems.

There is a tension between indexes as an implementation detail, and
indexes as the thing that define the semantics for the purposes of
this feature here (in a way that isn't baked into the DML statement,
but is only known from a cataloged definition of an index or its
physical structure). I have not fully resolved that tension here, but
I think I have a good practical balance between the two extremes. Does
that make sense?

... Deferred unique constraints are not supported as
+   arbiters of whether an alternative <literal>ON CONFLICT</> path
+   should be taken.

We really need to find a shorter term for "arbiter of whether an alternative
path should be taken". Different variations of that term are used a lot, and
it's tedious to read.

Okay. I'll think about that.

I've been thinking that it would be nice to be able to specify a constraint
name. Naming an index directly feels wrong, as in relational and SQL
philosophy, indexes are just an implementation detail, but naming a
constraint is a fair game. It would also be nice to be able to specify "use
the primary key".

Now that we have partial unique indexes covered, which never have a
constraint (I think - see my remarks above), this is probably okay.
But that doesn't really solve the highly theoretical (non-)issue with
"equals" operator ambiguity, which I called out as an open issue
recently [1]/messages/by-id/CAM3SWZS9DTFt1=sQT=WFdp5UFkOac2Qc1i5WE-Od4BXJZ=JsCw@mail.gmail.com -- Peter Geoghegan. That's still at thing we need to decide on, even if that
means that you agree with me that it doesn't matter (and since this
naming of constraints in DML is an escape hatch from that problem too,
that seems more likely than before). What do you think?

Attached patch contains a few more things I saw at a quick read.

This is mostly documentation stuff. I can certainly address this
feedback quickly and easily, and will do so soon.

[1]: /messages/by-id/CAM3SWZS9DTFt1=sQT=WFdp5UFkOac2Qc1i5WE-Od4BXJZ=JsCw@mail.gmail.com -- Peter Geoghegan
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Peter Geoghegan
pg@heroku.com
In reply to: Dean Rasheed (#3)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Mon, Mar 16, 2015 at 8:10 AM, Dean Rasheed <dean.a.rasheed@gmail.com> wrote:

(Note there is some bitrot in gram.y that prevents the first patch
from applying cleanly to HEAD)

That's trivially fixable. I'll have those fixes in the next revision,
once I firm some things up with Heikki.

I tested using the attached script, and one test didn't behave as I
expected. I believe the following should have been a valid upsert
(following the update path) but actually it failed:

INSERT INTO t1 VALUES (4, 0) ON CONFLICT (a) UPDATE SET b = 1;

AFAICT, it is applying a WITH CHECK OPTION with qual "b > 0 AND a % 2
= 0" to the about-to-be-updated tuple (a=4, b=0), which is wrong
because the "b > 0" check (policy p3) should only be applied to the
post-update tuple.

Possibly I'm missing something though.

I think that you may have. Did you read the commit message/docs of the
RLS commit 0004-*? You must consider the second point here, I believe:

""""
The 3 places that RLS policies are enforced are:

* Against row actually inserted, after insertion proceeds successfully
(INSERT-applicable policies only).

* Against row in target table that caused conflict. The implementation
is careful not to leak the contents of that row in diagnostic
messages (INSERT-applicable *and* UPDATE-applicable policies).

* Against the version of the row added by to the relation after
ExecUpdate() is called (INSERT-applicable *and* UPDATE-applicable
policies).

""""

You're seeing a failure that applies to the target tuple of the UPDATE
(the tuple that we can't leak the contents of). I felt it was best to
check all policies against the target/existing tuple, including both
WITH CHECK OPTIONS and USING quals (which are both enforced).

I can see why you might not like that behavior, but it is the intended
behavior. I thought that this whole intersection of RLS + UPSERT is
complex enough that it would be best to be almost as conservative as
possible in what fails and what succeeds. The one exception is when
the insert path is actually taken, since the statement is an INSERT
statement.
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Peter Geoghegan (#7)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 17 March 2015 at 23:25, Peter Geoghegan <pg@heroku.com> wrote:

Possibly I'm missing something though.

I think that you may have. Did you read the commit message/docs of the
RLS commit 0004-*? You must consider the second point here, I believe:

""""
The 3 places that RLS policies are enforced are:

* Against row actually inserted, after insertion proceeds successfully
(INSERT-applicable policies only).

* Against row in target table that caused conflict. The implementation
is careful not to leak the contents of that row in diagnostic
messages (INSERT-applicable *and* UPDATE-applicable policies).

* Against the version of the row added by to the relation after
ExecUpdate() is called (INSERT-applicable *and* UPDATE-applicable
policies).

""""

Yes, I read that, and I agree with the intention to not leak data
according to both the INSERT and UPDATE policies, however...

You're seeing a failure that applies to the target tuple of the UPDATE
(the tuple that we can't leak the contents of). I felt it was best to
check all policies against the target/existing tuple, including both
WITH CHECK OPTIONS and USING quals (which are both enforced).

I think that's an incorrect implementation of the RLS UPDATE policy.
The WITH CHECK quals of a RLS policy are intended to be applied to the
NEW data, not the existing data. This patch is applying the WITH CHECK
quals to both the existing and NEW tuples, which runs counter to the
way RLS polices are normally enforced, and I think that will just lead
to confusion.

I can see why you might not like that behavior, but it is the intended
behavior. I thought that this whole intersection of RLS + UPSERT is
complex enough that it would be best to be almost as conservative as
possible in what fails and what succeeds. The one exception is when
the insert path is actually taken, since the statement is an INSERT
statement.

The problem with that is that the user will see errors saying that the
data violates the RLS WITH CHECK policy, when they might quite
reasonably argue that it doesn't. That's not really being
conservative. I'd argue it's a bug.

Regards,
Dean

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#4)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Mar 17, 2015 at 3:11 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I've been thinking that it would be nice to be able to specify a constraint
name. Naming an index directly feels wrong, as in relational and SQL
philosophy, indexes are just an implementation detail, but naming a
constraint is a fair game. It would also be nice to be able to specify "use
the primary key".

Intuitively, I think you should specify an operator name, not a
constraint name. That's what we do for, e.g., exclusion constraints,
and it feels right. People sometimes create and drop indexes (and
thus, perhaps, the constraints that depend on them) for maintenance
reasons where a change in semantics will be unwelcome. But I don't
accept Peter's argument that it's OK to be indifferent to which
particular equality semantics are being used.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#6)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Mar 17, 2015 at 6:27 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Tue, Mar 17, 2015 at 12:11 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I'm still not sure the way the speculative locking works is the best
approach. Instead of clearing xmin on super-deletion, a new flag on the heap
tuple seems more straightforward. And we could put the speculative insertion
token in t_ctid, instead of stashing it in the PGPROC array. That would
again seem more straightforward.

I see the appeal of that approach. What concerns me about that
approach is that it makes it the problem of the inserter to confirm
its insertion, even though overwhelmingly, speculative insertions
succeeds (the race window is small due to the pre-check). The current
speculative token is legitimately a very short lived thing, a thing
that I hesitate to write to disk at all. So our optimistic approach to
inserting speculatively loses its optimism, which I don't like, if you
know what I mean.

Modifying a shared buffer is not the same as writing to disk.

If I'm reading this correctly, if there are multiple indexes that match the
unique index inference specification, we pick the cheapest one. Isn't that
unstable? Two backends running the same INSERT ON CONFLICT statement might
pick different indexes, and the decision might change over time as the table
is analyzed. I think we should have a more robust rule. Could we easily just
use all matching indexes?

Robert feel pretty strongly that there should be a costing aspect to
this. Initially I was skeptical of this, but just did what he said all
the same. But I've since come around to his view entirely because we
now support partial unique indexes.

I think Heikki's concern is something different, although I am not
altogether up to speed on this and may be confused. The issue is:
suppose that process A and process B are both furiously upserting into
the same table with the same key column but, because they have
different costing parameters, process A consistently chooses index X
and process B consistently chooses index Y. In that situation, will
we deadlock, livelock, error out, bloat, or get any other undesirable
behavior, or is that A-OK?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Robert Haas (#10)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 03/18/2015 06:23 PM, Robert Haas wrote:

On Tue, Mar 17, 2015 at 6:27 PM, Peter Geoghegan <pg@heroku.com> wrote:

On Tue, Mar 17, 2015 at 12:11 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I'm still not sure the way the speculative locking works is the best
approach. Instead of clearing xmin on super-deletion, a new flag on the heap
tuple seems more straightforward. And we could put the speculative insertion
token in t_ctid, instead of stashing it in the PGPROC array. That would
again seem more straightforward.

I see the appeal of that approach. What concerns me about that
approach is that it makes it the problem of the inserter to confirm
its insertion, even though overwhelmingly, speculative insertions
succeeds (the race window is small due to the pre-check). The current
speculative token is legitimately a very short lived thing, a thing
that I hesitate to write to disk at all. So our optimistic approach to
inserting speculatively loses its optimism, which I don't like, if you
know what I mean.

Modifying a shared buffer is not the same as writing to disk.

AFAICS, there is no need to go and clear the tag after the insert has
completed.

Here's what I had in mind: the inserter tags the tuple with the
speculative insertion token, by storing the token in the t_ctid field.
If the inserter needs to super-delete the tuple, it sets xmax like in a
regular deletion, but also sets another flag to indicate that it was a
super-deletion.

When another backend inserts, and notices that it has a potential
conflict with the first tuple, it tries to acquire a hw-lock on the
token. In most cases, the inserter has long since completed the
insertion, and the acquisition succeeds immediately but you have to
check because the token is not cleared on a completed insertion. It
would be slightly faster for the second backend if the inserter actually
removed the token after the insertion has completed: it wouldn't need to
check the lock if the tuple was not tagged in the first place. But
that'd be just a tiny optimization, for the rare case that there is a
conflict, and surely not worth it.

Hmm, I just realized a little fly in that ointment: if the inserter
inserts enough tuples to wrap around the token counter (4 billion?) in a
single transaction, another backend that inserts could confuse a new
speculative insertion with one that completed a long time ago. The risk
seems small enough that we could just accept it. I don't think anything
particularly bad would happen on a false positive here. Or we could also
use all 48 bits available in the t_ctid to push it truly in the realm of
ain't-gonna-happen. Or we could use (relfilenode, block, token) as the
lock tag for the SpeculativeInsertionLock.

Regarding the physical layout: We can use a magic OffsetNumber value
above MaxOffsetNumber to indicate that the t_ctid field stores a token
rather than a regular ctid value. And another magic t_ctid value to
indicate that a tuple has been super-deleted. The token and the
super-deletion flag are quite ephemeral, they are not needed after the
inserting transaction has completed, so it's nice to not consume the
valuable infomask bits for these things. Those states are conveniently
not possible on an updated tuple, when we would need the t_ctid field
for it's current purpose.

If I'm reading this correctly, if there are multiple indexes that match the
unique index inference specification, we pick the cheapest one. Isn't that
unstable? Two backends running the same INSERT ON CONFLICT statement might
pick different indexes, and the decision might change over time as the table
is analyzed. I think we should have a more robust rule. Could we easily just
use all matching indexes?

Robert feel pretty strongly that there should be a costing aspect to
this. Initially I was skeptical of this, but just did what he said all
the same. But I've since come around to his view entirely because we
now support partial unique indexes.

I think Heikki's concern is something different, although I am not
altogether up to speed on this and may be confused. The issue is:
suppose that process A and process B are both furiously upserting into
the same table with the same key column but, because they have
different costing parameters, process A consistently chooses index X
and process B consistently chooses index Y. In that situation, will
we deadlock, livelock, error out, bloat, or get any other undesirable
behavior, or is that A-OK?

Right, that's what I had in mind.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#11)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 18, 2015 at 11:41 AM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

AFAICS, there is no need to go and clear the tag after the insert has
completed.

Here's what I had in mind: the inserter tags the tuple with the speculative
insertion token, by storing the token in the t_ctid field. If the inserter
needs to super-delete the tuple, it sets xmax like in a regular deletion,
but also sets another flag to indicate that it was a super-deletion.

When another backend inserts, and notices that it has a potential conflict
with the first tuple, it tries to acquire a hw-lock on the token. In most
cases, the inserter has long since completed the insertion, and the
acquisition succeeds immediately but you have to check because the token is
not cleared on a completed insertion. It would be slightly faster for the
second backend if the inserter actually removed the token after the
insertion has completed: it wouldn't need to check the lock if the tuple was
not tagged in the first place. But that'd be just a tiny optimization, for
the rare case that there is a conflict, and surely not worth it.

Hmm, I just realized a little fly in that ointment: if the inserter inserts
enough tuples to wrap around the token counter (4 billion?) in a single
transaction, another backend that inserts could confuse a new speculative
insertion with one that completed a long time ago. The risk seems small
enough that we could just accept it. I don't think anything particularly bad
would happen on a false positive here. Or we could also use all 48 bits
available in the t_ctid to push it truly in the realm of ain't-gonna-happen.
Or we could use (relfilenode, block, token) as the lock tag for the
SpeculativeInsertionLock.

Maybe we'd use all 48-bits anyway, but it's not a major concern either
way. Speculative token locks (by design) are only held for an instant.
I think a spurious wait would be entirely benign, and very unlikely.

Regarding the physical layout: We can use a magic OffsetNumber value above
MaxOffsetNumber to indicate that the t_ctid field stores a token rather than
a regular ctid value. And another magic t_ctid value to indicate that a
tuple has been super-deleted. The token and the super-deletion flag are
quite ephemeral, they are not needed after the inserting transaction has
completed, so it's nice to not consume the valuable infomask bits for these
things. Those states are conveniently not possible on an updated tuple, when
we would need the t_ctid field for it's current purpose.

You seem pretty convinced that this is the way to go. I'll try and
produce a revision that works this way shortly. I don't think that it
will be all that hard to come up with something to prove the idea out.

I think Heikki's concern is something different, although I am not
altogether up to speed on this and may be confused. The issue is:
suppose that process A and process B are both furiously upserting into
the same table with the same key column but, because they have
different costing parameters, process A consistently chooses index X
and process B consistently chooses index Y. In that situation, will
we deadlock, livelock, error out, bloat, or get any other undesirable
behavior, or is that A-OK?

Right, that's what I had in mind.

Oh, I see. I totally failed to understand that that was the concern.

I think it'll be fine. The pre-check is going to look for a heap tuple
using one or the other of (say) a pair of equivalent indexes. We might
miss the heap tuple because we picked an index that had yet to have a
physical index tuple inserted, and then hit a conflict on optimistic
insertion (the second phase). But there is no reason to think that
that won't happen anyway. The ordering of operations isn't critical.

The one issue you might still have is a duplicate violation, because
you happened to infer the unique index that does not get to tolerate
unique violations as conflicts that can be recovered from (and there
was a race, which is unlikely). I don't really care about this,
though. You really are inferring one particular unique index, and for
reasons like this I think it would be a mistake to try to pretend that
the unique index is 100% an implementation detail. That's why I called
the new clause a unique index inference specification.

This hypothetical set of unique indexes will always have n-1 redundant
unique indexes - they must closely match. That's something that calls
into question why the user wants things this way to begin with. Also,
note that one unique index will consistently "win", since the
insertion order is stable (the relcache puts them in OID order). So it
will not be all over the map.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Peter Geoghegan
pg@heroku.com
In reply to: Robert Haas (#9)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 18, 2015 at 9:19 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Mar 17, 2015 at 3:11 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I've been thinking that it would be nice to be able to specify a constraint
name. Naming an index directly feels wrong, as in relational and SQL
philosophy, indexes are just an implementation detail, but naming a
constraint is a fair game. It would also be nice to be able to specify "use
the primary key".

Intuitively, I think you should specify an operator name, not a
constraint name. That's what we do for, e.g., exclusion constraints,
and it feels right. People sometimes create and drop indexes (and
thus, perhaps, the constraints that depend on them) for maintenance
reasons where a change in semantics will be unwelcome.

I think we should use a constraint name. That is the plain reality of
what we're doing, and is less ambiguous than an operator. 99% of the
time you'll be happy with an unspecified, across-the-board IGNORE, or
won't be using exclusion constraints anyway (so we can infer a unique
index).

A constraint name covers all reasonable cases, since partial unique
indexes are otherwise covered (partial unique indexes do not have a
pg_constraint entry). Oracle has a hint for ignoring particular, named
unique indexes (not constraints). I realize that Oracle hints are not
supposed to affect semantics, but this is actually true (Google it).
This is a bit ugly, but less ugly as the hint, since as Heikki points
out we're only naming a constraint. Naming a constraint reflects the
reality of how the feature needs to work, and has a sort of precedent
from other systems.

But I don't
accept Peter's argument that it's OK to be indifferent to which
particular equality semantics are being used.

I am not suggesting actual indifference makes sense. I am leaving it
up to the definition of available indexes. And there are no known
cases where it could matter anyway, unless you consider the ===
operator for tuples to be a counter example. And you need multiple
conflicting unique indexes on the exact same attributes/expressions on
attributes to begin with. Isn't that a highly esoteric thing to have
to worry about? Perhaps to the extent that literally no one will ever
have to care anyway? It's an oddball use-case, if ever I saw one.

Note: the issue of caring about equality semantics across B-Tree
opclasses of the same type, and the issue of naming unique indexes are
separate issues, AFAICT. No one should confuse them. The only
crossover is that the oddball use-case mentioned could use the named
constraint thing as an escape hatch.

As I've said, I think it's misguided to try to make unique indexes
100% an implementation detail. It's going to fall apart in edge cases,
like the one with multiple unique indexes that I mentioned in my last
e-mail. No one thinks of them that way, including users.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Robert Haas
robertmhaas@gmail.com
In reply to: Peter Geoghegan (#12)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 18, 2015 at 3:40 PM, Peter Geoghegan <pg@heroku.com> wrote:

I think Heikki's concern is something different, although I am not
altogether up to speed on this and may be confused. The issue is:
suppose that process A and process B are both furiously upserting into
the same table with the same key column but, because they have
different costing parameters, process A consistently chooses index X
and process B consistently chooses index Y. In that situation, will
we deadlock, livelock, error out, bloat, or get any other undesirable
behavior, or is that A-OK?

Right, that's what I had in mind.

Oh, I see. I totally failed to understand that that was the concern.

I think it'll be fine. The pre-check is going to look for a heap tuple
using one or the other of (say) a pair of equivalent indexes. We might
miss the heap tuple because we picked an index that had yet to have a
physical index tuple inserted, and then hit a conflict on optimistic
insertion (the second phase). But there is no reason to think that
that won't happen anyway. The ordering of operations isn't critical.

The one issue you might still have is a duplicate violation, because
you happened to infer the unique index that does not get to tolerate
unique violations as conflicts that can be recovered from (and there
was a race, which is unlikely). I don't really care about this,
though. You really are inferring one particular unique index, and for
reasons like this I think it would be a mistake to try to pretend that
the unique index is 100% an implementation detail. That's why I called
the new clause a unique index inference specification.

This hypothetical set of unique indexes will always have n-1 redundant
unique indexes - they must closely match. That's something that calls
into question why the user wants things this way to begin with. Also,
note that one unique index will consistently "win", since the
insertion order is stable (the relcache puts them in OID order). So it
will not be all over the map.

I think this is pretty lousy. The reasons why the user wants things
that way is because they created a UNIQUE index and it got bloated
somehow with lots of dead tuples. So they made a new UNIQUE index on
the same column and then they're planning to do a DROP INDEX
CONCURRENTLY on the old one, which is maybe even now in progress. And
now they start getting duplicate key failures, the avoidance of which
was their whole reason for using UPSERT in the first place. If I were
that user, I'd report that as a bug, and if someone told me that it
was intended behavior, I'd say "oh, so you deliberately designed this
feature to not work some of the time?".

ISTM that we need to (1) decide which operator we're using to compare
and then (2) tolerate conflicts in every index that uses that
operator. In most cases there will only be one, but if there are
more, so be it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Peter Geoghegan
pg@heroku.com
In reply to: Robert Haas (#14)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Mar 19, 2015 at 1:02 PM, Robert Haas <robertmhaas@gmail.com> wrote:

I think this is pretty lousy. The reasons why the user wants things
that way is because they created a UNIQUE index and it got bloated
somehow with lots of dead tuples. So they made a new UNIQUE index on
the same column and then they're planning to do a DROP INDEX
CONCURRENTLY on the old one, which is maybe even now in progress. And
now they start getting duplicate key failures, the avoidance of which
was their whole reason for using UPSERT in the first place. If I were
that user, I'd report that as a bug, and if someone told me that it
was intended behavior, I'd say "oh, so you deliberately designed this
feature to not work some of the time?".

ISTM that we need to (1) decide which operator we're using to compare
and then (2) tolerate conflicts in every index that uses that
operator. In most cases there will only be one, but if there are
more, so be it.

On reflection, I see your point. I'll try and do something about this too.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#11)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 18, 2015 at 2:41 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Here's what I had in mind: the inserter tags the tuple with the speculative
insertion token, by storing the token in the t_ctid field. If the inserter
needs to super-delete the tuple, it sets xmax like in a regular deletion,
but also sets another flag to indicate that it was a super-deletion.

I was able to quickly hack up a prototype of this in my hotel room at
pgConf.US. It works fine at first blush, passing the jjanes_upsert
stress tests and my own regression tests without a problem. Obviously
it needs more testing and clean-up before posting, but I was pleased
with how easy this was.

When another backend inserts, and notices that it has a potential conflict
with the first tuple, it tries to acquire a hw-lock on the token. In most
cases, the inserter has long since completed the insertion, and the
acquisition succeeds immediately but you have to check because the token is
not cleared on a completed insertion.

You don't even have to check/take a ShareLock on the token when the
other xact committed/aborted, because you know that if it is there,
then based on that (and based on the fact that it wasn't super
deleted) the tuple is visible/committed, or (in the event of
other-xact-abort) not visible/aborted. In other words, we continue to
only check for a speculative token when the inserting xact is in
flight - we just take the token from the heap now instead. Not much
needs to change, AFAICT.

Regarding the physical layout: We can use a magic OffsetNumber value above
MaxOffsetNumber to indicate that the t_ctid field stores a token rather than
a regular ctid value. And another magic t_ctid value to indicate that a
tuple has been super-deleted. The token and the super-deletion flag are
quite ephemeral, they are not needed after the inserting transaction has
completed, so it's nice to not consume the valuable infomask bits for these
things. Those states are conveniently not possible on an updated tuple, when
we would need the t_ctid field for it's current purpose.

Haven't done anything about this yet. I'm just using an infomask2 bit
for now. Although that was only because I forgot that you suggested
this before having a go at implementing this new t_ctid scheme!

My next revision will have a more polished version of this scheme. I'm
not going to immediately act on Robert's feedback elsewhere (although
I'd like to), owing to time constraints - no reason to deny you the
opportunity to review the entirely unrelated low-level speculative
locking mechanism due to that.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#16)
4 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 25, 2015 at 12:42 PM, Peter Geoghegan <pg@heroku.com> wrote:

My next revision will have a more polished version of this scheme. I'm
not going to immediately act on Robert's feedback elsewhere (although
I'd like to), owing to time constraints - no reason to deny you the
opportunity to review the entirely unrelated low-level speculative
locking mechanism due to that.

Attached revision, V3.1, implements this slightly different way of
figuring out if an insertion is speculative, as discussed. We reuse
t_ctid for speculatively inserted tuples. That's where the counter
goes. I think that this is a significant improvement, since there is
no longer any need to touch the proc array for any reason, without
there being any significant disadvantage that I'm aware of. I also
fixed some bitrot, and a bug with index costing (the details aren't
terribly interesting - tuple width wasn't being calculated correctly).

I have worked through Heikki's feedback on documentation and code
tweaks, too (they were mostly just documentation changes).

Notably, I have not:

* Added ON CONFLICT PRIMARY KEY (might get to this later)

* Otherwise altered the inference specification. I have not allowed it
to name a constraint, which Heikki and I favor (Robert wanted to name
an operator directly). Again, just haven't got around to it.

* Done anything new with logical decoding. My handling of conflicts
during transaction reassembly remains questionable. I hope this can be
worked out soon, possibly with input from Andres. He is sitting next
to me at pgConf.US, so maybe we can work something out in person.

* Addressed the concerns held by Heikki and Robert about multiple
equivalent unique indexes. I confirmed through testing that this can
cause unique violations that are arguably spurious. It isn't too bad,
though - with a couple of unique indexes, jjanes_upsert requires high
concurrency (i.e. 128 clients) in order to see one or two or three
such unique violations after a minute or so. But even still, let's fix
it.

As I mentioned, I am at a conference, and obviously there are other
time pressures, so I haven't put as much testing into this revision as
I'd like. I thought that under the circumstances it was preferable to
post what I have sooner rather than later, though.

--
Peter Geoghegan

Attachments:

0004-RLS-support-for-ON-CONFLICT-UPDATE.patchtext/x-patch; charset=US-ASCII; name=0004-RLS-support-for-ON-CONFLICT-UPDATE.patchDownload
From a33007b5748e62d539d32140f9116e3f29335c21 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@heroku.com>
Date: Tue, 6 Jan 2015 16:32:21 -0800
Subject: [PATCH 4/4] RLS support for ON CONFLICT UPDATE

Row-Level Security policies may apply to UPDATE commands or INSERT
commands only.  UPDATE RLS policies can have both USING() security
barrier quals, and CHECK options (INSERT RLS policies may only have
CHECK options, though).  It is necessary to carefully consider the
behavior of RLS policies in the context of INSERT with ON CONFLICT
UPDATE, since ON CONFLICT UPDATE is more or less a new top-level
command, conceptually quite different to two separate statements (an
INSERT and an UPDATE).

The approach taken is to "bunch together" both sets of policies, and to
enforce them in 3 different places against three different slots (3
different stages of query processing in the executor).

Note that UPDATE policy USING() barrier quals are always treated as
CHECK options.  It is thought that silently failing when USING() barrier
quals are not satisfied is a more surprising outcome, even if it is
closer to the existing behavior of UPDATE statements.  This is because
the user's intent to UPDATE one particular row based on simple criteria
is quite clear with ON CONFLICT UPDATE.

The 3 places that RLS policies are enforced are:

* Against row actually inserted, after insertion proceeds successfully
  (INSERT-applicable policies only).

* Against row in target table that caused conflict.  The implementation
  is careful not to leak the contents of that row in diagnostic
  messages (INSERT-applicable *and* UPDATE-applicable policies).

* Against the version of the row added by to the relation after
  ExecUpdate() is called (INSERT-applicable *and* UPDATE-applicable
  policies).
---
 doc/src/sgml/ref/alter_policy.sgml        |  7 ++-
 doc/src/sgml/ref/create_policy.sgml       | 37 ++++++++++--
 src/backend/executor/execMain.c           | 25 +++++---
 src/backend/executor/nodeModifyTable.c    | 53 ++++++++++++++++-
 src/backend/nodes/copyfuncs.c             |  1 +
 src/backend/nodes/equalfuncs.c            |  1 +
 src/backend/nodes/outfuncs.c              |  1 +
 src/backend/nodes/readfuncs.c             |  1 +
 src/backend/rewrite/rewriteHandler.c      |  2 +
 src/backend/rewrite/rowsecurity.c         | 94 ++++++++++++++++++++++++++-----
 src/include/executor/executor.h           |  3 +-
 src/include/nodes/parsenodes.h            |  1 +
 src/test/regress/expected/rowsecurity.out | 90 +++++++++++++++++++++++++++++
 src/test/regress/sql/rowsecurity.sql      | 73 ++++++++++++++++++++++++
 14 files changed, 357 insertions(+), 32 deletions(-)

diff --git a/doc/src/sgml/ref/alter_policy.sgml b/doc/src/sgml/ref/alter_policy.sgml
index 6d03db5..65cd85c 100644
--- a/doc/src/sgml/ref/alter_policy.sgml
+++ b/doc/src/sgml/ref/alter_policy.sgml
@@ -93,8 +93,11 @@ ALTER POLICY <replaceable class="parameter">name</replaceable> ON <replaceable c
       The USING expression for the policy.  This expression will be added as a
       security-barrier qualification to queries which use the table
       automatically.  If multiple policies are being applied for a given
-      table then they are all combined and added using OR.  The USING
-      expression applies to records which are being retrieved from the table.
+      table then they are all combined and added using OR (except as noted in
+      the <xref linkend="sql-createpolicy"> documentation for
+      <command>INSERT</command> with <literal> ON CONFLICT UPDATE</literal>).
+      The USING expression applies to records which are being retrieved from the
+      table.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_policy.sgml b/doc/src/sgml/ref/create_policy.sgml
index 868a6c1..f17192e 100644
--- a/doc/src/sgml/ref/create_policy.sgml
+++ b/doc/src/sgml/ref/create_policy.sgml
@@ -70,11 +70,12 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
    Policies can be applied for specific commands or for specific roles.  The
    default for newly created policies is that they apply for all commands and
    roles, unless otherwise specified.  If multiple policies apply to a given
-   query, they will be combined using OR.  Further, for commands which can have
-   both USING and WITH CHECK policies (ALL and UPDATE), if no WITH CHECK policy
-   is defined then the USING policy will be used for both what rows are visible
-   (normal USING case) and which rows will be allowed to be added (WITH CHECK
-   case).
+   query, they will be combined using OR (except as noted for
+   <command>INSERT</command> with <literal> ON CONFLICT UPDATE</literal>).
+   Further, for commands which can have both USING and WITH CHECK policies (ALL
+   and UPDATE), if no WITH CHECK policy is defined then the USING policy will
+   be used for both what rows are visible (normal USING case) and which rows
+   will be allowed to be added (WITH CHECK case).
   </para>
 
   <para>
@@ -255,6 +256,19 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
          as it only ever applies in cases where records are being added to the
          relation.
        </para>
+       <para>
+         Note that <literal>INSERT</literal> with <literal>ON CONFLICT
+         UPDATE</literal> requires that an <literal>INSERT</literal> policy WITH
+         CHECK expression also passes for both any existing tuple in the target
+         table that necessitates that the <literal>UPDATE</literal> path be
+         taken, and the final tuple added back into the relation.
+         <literal>INSERT</literal> policies are separately combined using
+         <literal>OR</literal>, and this distinct set of policy expressions must
+         always pass, regardless of whether any or all <literal>UPDATE</literal>
+         policies also pass (in the same tuple check).  However, successfully
+         inserted tuples are not subject to <literal>UPDATE</literal> policy
+         enforcement.
+       </para>
       </listitem>
      </varlistentry>
 
@@ -263,7 +277,9 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
       <listitem>
        <para>
          Using <literal>UPDATE</literal> for a policy means that it will apply
-         to <literal>UPDATE</literal> commands.  As <literal>UPDATE</literal>
+         to <literal>UPDATE</literal> commands (or auxiliary <literal>ON
+         CONFLICT UPDATE</literal> clauses of <literal>INSERT</literal>
+         commands).  As <literal>UPDATE</literal>
          involves pulling an existing record and then making changes to some
          portion (but possibly not all) of the record, the
          <literal>UPDATE</literal> policy accepts both a USING expression and
@@ -279,6 +295,15 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
          used for both <literal>USING</literal> and
          <literal>WITH CHECK</literal> cases.
        </para>
+       <para>
+         Note that <literal>INSERT</literal> with <literal>ON CONFLICT
+         UPDATE</literal> requires that an <literal>UPDATE</literal> policy
+         USING expression always be treated as a WITH CHECK
+         expression.  This <literal>UPDATE</literal> policy must
+         always pass, regardless of whether any
+         <literal>INSERT</literal> policy also passes in the same
+         tuple check.
+       </para>
       </listitem>
      </varlistentry>
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 9ef4179..6b6d4dd 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1699,7 +1699,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
  */
 void
 ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
-					 TupleTableSlot *slot, EState *estate)
+					 TupleTableSlot *slot, bool detail,
+					 bool onlyInsert, EState *estate)
 {
 	Relation	rel = resultRelInfo->ri_RelationDesc;
 	TupleDesc	tupdesc = RelationGetDescr(rel);
@@ -1724,6 +1725,15 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
 		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
 
 		/*
+		 * INSERT ... ON CONFLICT UPDATE callers may require that not all WITH
+		 * CHECK OPTIONs associated with resultRelInfo are enforced at all
+		 * stages of query processing. (UPDATE-related policies are not
+		 * enforced in respect of a successfully inserted tuple).
+		 */
+		if (onlyInsert && wco->commandType == CMD_UPDATE)
+			continue;
+
+		/*
 		 * WITH CHECK OPTION checks are intended to ensure that the new tuple
 		 * is visible (in the case of a view) or that it passes the
 		 * 'with-check' policy (in the case of row security).
@@ -1734,16 +1744,17 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
 		 */
 		if (!ExecQual((List *) wcoExpr, econtext, false))
 		{
-			char	   *val_desc;
+			char	   *val_desc = NULL;
 			Bitmapset  *modifiedCols;
 
 			modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
 			modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
-			val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
-													 slot,
-													 tupdesc,
-													 modifiedCols,
-													 64);
+			if (detail)
+				val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
+														 slot,
+														 tupdesc,
+														 modifiedCols,
+														 64);
 
 			ereport(ERROR,
 					(errcode(ERRCODE_WITH_CHECK_OPTION_VIOLATION),
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 5a2ac21..ac95eb9 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -455,7 +455,8 @@ vlock:
 
 	/* Check any WITH CHECK OPTION constraints */
 	if (resultRelInfo->ri_WithCheckOptions != NIL)
-		ExecWithCheckOptions(resultRelInfo, slot, estate);
+		ExecWithCheckOptions(resultRelInfo, slot, true, spec == SPEC_INSERT,
+							 estate);
 
 	/* Process RETURNING if present */
 	if (resultRelInfo->ri_projectReturning)
@@ -949,7 +950,7 @@ lreplace:;
 
 	/* Check any WITH CHECK OPTION constraints */
 	if (resultRelInfo->ri_WithCheckOptions != NIL)
-		ExecWithCheckOptions(resultRelInfo, slot, estate);
+		ExecWithCheckOptions(resultRelInfo, slot, true, false, estate);
 
 	/* Process RETURNING if present */
 	if (resultRelInfo->ri_projectReturning)
@@ -1135,6 +1136,54 @@ ExecLockUpdateTuple(ResultRelInfo *resultRelInfo,
 
 			slot = EvalPlanQualNext(&onConflict->mt_epqstate);
 
+			/*
+			 * For RLS with ON CONFLICT UPDATE, security quals are always
+			 * treated as WITH CHECK options, even when there were separate
+			 * security quals and explicit WITH CHECK options (ordinarily,
+			 * security quals are only treated as WITH CHECK options when there
+			 * are no explicit WITH CHECK options).  Also, CHECK OPTIONs
+			 * (originating either explicitly, or implicitly as security quals)
+			 * for both UPDATE and INSERT policies (or ALL policies) are
+			 * checked (as CHECK OPTIONs) at three different points for three
+			 * distinct but related tuples/slots in the context of ON CONFLICT
+			 * UPDATE.  There are three relevant ExecWithCheckOptions() calls:
+			 *
+			 * * After successful insertion, within ExecInsert(), against the
+			 * inserted tuple.  This only includes INSERT-applicable policies.
+			 *
+			 * * Here, after row locking but before calling ExecUpdate(), on
+			 * the existing tuple in the target relation (which we cannot leak
+			 * details of).  This is conceptually like a security barrier qual
+			 * for the purposes of the auxiliary update, although unlike
+			 * regular updates that require security barrier quals we prefer to
+			 * raise an error (by treating the security barrier quals as CHECK
+			 * OPTIONS) rather than silently not affect rows, because the
+			 * intent to update seems clear and unambiguous for ON CONFLICT
+			 * UPDATE.  This includes both INSERT-applicable and
+			 * UPDATE-applicable policies.
+			 *
+			 * * On the final tuple created by the update within ExecUpdate (if
+			 * any).  This is also subject to INSERT policy enforcement, unlike
+			 * conventional ExecUpdate() calls for UPDATE statements -- it
+			 * includes both INSERT-applicable and UPDATE-applicable policies.
+			 */
+			if (resultRelInfo->ri_WithCheckOptions != NIL)
+			{
+				TupleTableSlot *opts;
+
+				/* Construct temp slot for locked tuple from target */
+				opts = MakeSingleTupleTableSlot(slot->tts_tupleDescriptor);
+				ExecStoreTuple(copyTuple, opts, InvalidBuffer, false);
+
+				/*
+				 * Check, but without leaking contents of tuple;  user only
+				 * supplied one conflicting value or composition of values, and
+				 * not the entire tuple.
+				 */
+				ExecWithCheckOptions(resultRelInfo, opts, false, false,
+									 estate);
+			}
+
 			if (!TupIsNull(slot))
 				*returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, slot,
 										planSlot, &onConflict->mt_epqstate,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9368ab2..b05cd2b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2083,6 +2083,7 @@ _copyWithCheckOption(const WithCheckOption *from)
 
 	COPY_STRING_FIELD(viewname);
 	COPY_NODE_FIELD(qual);
+	COPY_SCALAR_FIELD(commandType);
 	COPY_SCALAR_FIELD(cascaded);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 30c164a..33129fb 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2379,6 +2379,7 @@ _equalWithCheckOption(const WithCheckOption *a, const WithCheckOption *b)
 {
 	COMPARE_STRING_FIELD(viewname);
 	COMPARE_NODE_FIELD(qual);
+	COMPARE_SCALAR_FIELD(commandType);
 	COMPARE_SCALAR_FIELD(cascaded);
 
 	return true;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index de08bb4..9d775d4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2349,6 +2349,7 @@ _outWithCheckOption(StringInfo str, const WithCheckOption *node)
 
 	WRITE_STRING_FIELD(viewname);
 	WRITE_NODE_FIELD(qual);
+	WRITE_ENUM_FIELD(commandType, CmdType);
 	WRITE_BOOL_FIELD(cascaded);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 319db56..f9910f3 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -272,6 +272,7 @@ _readWithCheckOption(void)
 
 	READ_STRING_FIELD(viewname);
 	READ_NODE_FIELD(qual);
+	READ_ENUM_FIELD(commandType, CmdType);
 	READ_BOOL_FIELD(cascaded);
 
 	READ_DONE();
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 8ef80d6..36d40cf 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1767,6 +1767,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 				List			   *quals = NIL;
 
 				wco = (WithCheckOption *) makeNode(WithCheckOption);
+				wco->commandType = parsetree->commandType;
 				quals = lcons(wco->qual, quals);
 
 				activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
@@ -2935,6 +2936,7 @@ rewriteTargetView(Query *parsetree, Relation view)
 			wco->viewname = pstrdup(RelationGetRelationName(view));
 			wco->qual = NULL;
 			wco->cascaded = cascaded;
+			wco->commandType = viewquery->commandType;
 
 			parsetree->withCheckOptions = lcons(wco,
 												parsetree->withCheckOptions);
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 7669130..09f1ac3 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -56,12 +56,14 @@
 #include "utils/syscache.h"
 #include "tcop/utility.h"
 
-static List *pull_row_security_policies(CmdType cmd, Relation relation,
-										Oid user_id);
+static List *pull_row_security_policies(CmdType cmd, bool onConflict,
+										Relation relation, Oid user_id);
 static void process_policies(List *policies, int rt_index,
 							 Expr **final_qual,
 							 Expr **final_with_check_qual,
-							 bool *hassublinks);
+							 bool *hassublinks,
+							 Expr **spec_with_check_eval,
+							 bool onConflict);
 static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id);
 
 /*
@@ -88,6 +90,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 	Expr			   *rowsec_with_check_expr = NULL;
 	Expr			   *hook_expr = NULL;
 	Expr			   *hook_with_check_expr = NULL;
+	Expr			   *hook_spec_with_check_expr = NULL;
 
 	List			   *rowsec_policies;
 	List			   *hook_policies = NIL;
@@ -149,8 +152,9 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 	/* Grab the built-in policies which should be applied to this relation. */
 	rel = heap_open(rte->relid, NoLock);
 
-	rowsec_policies = pull_row_security_policies(root->commandType, rel,
-												 user_id);
+	rowsec_policies = pull_row_security_policies(root->commandType,
+												 root->specClause == SPEC_INSERT,
+												 rel, user_id);
 
 	/*
 	 * Check if this is only the default-deny policy.
@@ -168,7 +172,9 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 
 	/* Now that we have our policies, build the expressions from them. */
 	process_policies(rowsec_policies, rt_index, &rowsec_expr,
-					 &rowsec_with_check_expr, &hassublinks);
+					 &rowsec_with_check_expr, &hassublinks,
+					 &hook_spec_with_check_expr,
+					 root->specClause == SPEC_INSERT);
 
 	/*
 	 * Also, allow extensions to add their own policies.
@@ -198,7 +204,9 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 
 		/* Build the expression from any policies returned. */
 		process_policies(hook_policies, rt_index, &hook_expr,
-						 &hook_with_check_expr, &hassublinks);
+						 &hook_with_check_expr, &hassublinks,
+						 &hook_spec_with_check_expr,
+						 root->specClause == SPEC_INSERT);
 	}
 
 	/*
@@ -230,6 +238,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 			wco->viewname = RelationGetRelationName(rel);
 			wco->qual = (Node *) rowsec_with_check_expr;
 			wco->cascaded = false;
+			wco->commandType = root->commandType;
 			root->withCheckOptions = lcons(wco, root->withCheckOptions);
 		}
 
@@ -244,6 +253,23 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
 			wco->viewname = RelationGetRelationName(rel);
 			wco->qual = (Node *) hook_with_check_expr;
 			wco->cascaded = false;
+			wco->commandType = root->commandType;
+			root->withCheckOptions = lcons(wco, root->withCheckOptions);
+		}
+
+		/*
+		 * Also add the expression, if any, returned from the extension that
+		 * applies to auxiliary UPDATE within ON CONFLICT UPDATE.
+		 */
+		if (hook_spec_with_check_expr)
+		{
+			WithCheckOption	   *wco;
+
+			wco = (WithCheckOption *) makeNode(WithCheckOption);
+			wco->viewname = RelationGetRelationName(rel);
+			wco->qual = (Node *) hook_spec_with_check_expr;
+			wco->cascaded = false;
+			wco->commandType = CMD_UPDATE;
 			root->withCheckOptions = lcons(wco, root->withCheckOptions);
 		}
 	}
@@ -288,7 +314,8 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
  *
  */
 static List *
-pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
+pull_row_security_policies(CmdType cmd, bool onConflict, Relation relation,
+						   Oid user_id)
 {
 	List			   *policies = NIL;
 	ListCell		   *item;
@@ -322,7 +349,9 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
 				if (policy->polcmd == ACL_INSERT_CHR
 					&& check_role_for_policy(policy->roles, user_id))
 					policies = lcons(policy, policies);
-				break;
+				if (!onConflict)
+					break;
+				/* FALL THRU */
 			case CMD_UPDATE:
 				if (policy->polcmd == ACL_UPDATE_CHR
 					&& check_role_for_policy(policy->roles, user_id))
@@ -384,26 +413,41 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
  */
 static void
 process_policies(List *policies, int rt_index, Expr **qual_eval,
-				 Expr **with_check_eval, bool *hassublinks)
+				 Expr **with_check_eval, bool *hassublinks,
+				 Expr **spec_with_check_eval, bool onConflict)
 {
 	ListCell		   *item;
 	List			   *quals = NIL;
 	List			   *with_check_quals = NIL;
+	List			   *conflict_update_quals = NIL;
 
 	/*
 	 * Extract the USING and WITH CHECK quals from each of the policies
-	 * and add them to our lists.
+	 * and add them to our lists.  CONFLICT UPDATE quals are always treated
+	 * as CHECK OPTIONS.
 	 */
 	foreach(item, policies)
 	{
 		RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
 
 		if (policy->qual != NULL)
-			quals = lcons(copyObject(policy->qual), quals);
+		{
+			if (!onConflict || policy->polcmd != ACL_UPDATE_CHR)
+				quals = lcons(copyObject(policy->qual), quals);
+			else
+				conflict_update_quals = lcons(copyObject(policy->qual), quals);
+		}
 
 		if (policy->with_check_qual != NULL)
-			with_check_quals = lcons(copyObject(policy->with_check_qual),
-									 with_check_quals);
+		{
+			if (!onConflict || policy->polcmd != ACL_UPDATE_CHR)
+				with_check_quals = lcons(copyObject(policy->with_check_qual),
+										 with_check_quals);
+			else
+				conflict_update_quals =
+					lcons(copyObject(policy->with_check_qual),
+						  conflict_update_quals);
+		}
 
 		if (policy->hassublinks)
 			*hassublinks = true;
@@ -420,6 +464,10 @@ process_policies(List *policies, int rt_index, Expr **qual_eval,
 	/*
 	 * If we end up with only USING quals, then use those as
 	 * WITH CHECK quals also.
+	 *
+	 * For the INSERT with ON CONFLICT UPDATE case, we always enforce that the
+	 * UPDATE's USING quals are treated like WITH CHECK quals, enforced against
+	 * the target relation's tuple in multiple places.
 	 */
 	if (with_check_quals == NIL)
 		with_check_quals = copyObject(quals);
@@ -453,6 +501,24 @@ process_policies(List *policies, int rt_index, Expr **qual_eval,
 	else
 		*with_check_eval = (Expr*) linitial(with_check_quals);
 
+	/*
+	 * For INSERT with ON CONFLICT UPDATE, *both* sets of WITH CHECK options
+	 * (from any INSERT policy and any UPDATE policy) are enforced.
+	 *
+	 * These are handled separately because enforcement of each type of WITH
+	 * CHECK option is based on the point in query processing of INSERT ... ON
+	 * CONFLICT UPDATE.  The INSERT path does not enforce UPDATE related CHECK
+	 * OPTIONs.
+	 */
+	if (conflict_update_quals != NIL)
+	{
+		if (list_length(conflict_update_quals) > 1)
+			*spec_with_check_eval = makeBoolExpr(AND_EXPR,
+												 conflict_update_quals, -1);
+		else
+			*spec_with_check_eval = (Expr*) linitial(conflict_update_quals);
+	}
+
 	return;
 }
 
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 8100bd8..91ba93e 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -194,7 +194,8 @@ extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
 				TupleTableSlot *slot, EState *estate);
 extern void ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
-					 TupleTableSlot *slot, EState *estate);
+					 TupleTableSlot *slot, bool detail, bool onlyInsert,
+					 EState *estate);
 extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti);
 extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
 extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fe4c69a..c23a8fe 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -886,6 +886,7 @@ typedef struct WithCheckOption
 	NodeTag		type;
 	char	   *viewname;		/* name of view that specified the WCO */
 	Node	   *qual;			/* constraint qual to check */
+	CmdType		commandType;	/* select|insert|update|delete */
 	bool		cascaded;		/* true = WITH CASCADED CHECK OPTION */
 } WithCheckOption;
 
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 44e8dab..eb234b5 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1193,6 +1193,96 @@ NOTICE:  f_leak => yyyyyy
 (3 rows)
 
 --
+-- INSERT ... ON CONFLICT UPDATE and Row-level security
+--
+-- Would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Can't insert new violating tuple, either:
+INSERT INTO document VALUES (22, 11, 2, 'rls_regress_user2', 'mediocre novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- INSERT path is taken here, so UPDATE targelist doesn't matter:
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+ did | cid | dlevel |      dauthor      |        dtitle        
+-----+-----+--------+-------------------+----------------------
+  33 |  22 |      1 | rls_regress_user1 | okay science fiction
+(1 row)
+
+-- Update path will now taken for same query, so UPDATE targelist now matters
+-- (this is the same query as the last, but now fails):
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+SET SESSION AUTHORIZATION rls_regress_user0;
+DROP POLICY p1 ON document;
+CREATE POLICY p1 ON document FOR SELECT USING (true);
+CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
+CREATE POLICY p3 ON document FOR UPDATE
+  USING (cid = (SELECT cid from category WHERE cname = 'novel'))
+  WITH CHECK (dauthor = current_user);
+SET SESSION AUTHORIZATION rls_regress_user1;
+-- Again, would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked, which is what
+-- failed to satisfy WITH CHECK options - not row proposed for insertion by
+-- user):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Again, can't insert new violating tuple, either (unsuccessfully inserted tuple
+-- values are reported here, though)
+--
+-- Violates actual CHECK OPTION within UPDATE:
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user2', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Violates USING qual for UPDATE policy p3, interpreted here as CHECK OPTION.
+--
+-- UPDATE path is taken, but UPDATE fails purely because *existing* row to be
+-- updated is not a "novel"/cid 11 (row is not leaked, even though we have
+-- SELECT privileges sufficient to see the row in this instance):
+INSERT INTO document VALUES (33, 11, 1, 'rls_regress_user1', 'Some novel, replaces sci-fi')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Fine (we UPDATE):
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user1', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
+ did | cid | dlevel |      dauthor      |     dtitle     
+-----+-----+--------+-------------------+----------------
+   2 |  11 |      2 | rls_regress_user1 | my first novel
+(1 row)
+
+-- Fine (we INSERT, so "cid = 33" isn't evaluated):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+ did | cid | dlevel |      dauthor      |      dtitle      
+-----+-----+--------+-------------------+------------------
+  78 |  11 |      1 | rls_regress_user1 | some other novel
+(1 row)
+
+-- Fail (same query, but we UPDATE, so "cid = 33" is evaluated at end of
+-- UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Fail (we UPDATE, so dauthor assignment is evaluated at end of UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = 'rls_regress_user2';
+ERROR:  new row violates WITH CHECK OPTION for "document"
+-- Don't fail because INSERT doesn't satisfy WITH CHECK option that originated
+-- as a barrier/USING() qual from the UPDATE.  Note that the UPDATE path
+-- *isn't* taken, and so UPDATE-related policy does not apply:
+INSERT INTO document VALUES (88, 33, 1, 'rls_regress_user1', 'technology book, can only insert')
+    ON CONFLICT (did) UPDATE SET dtitle = upper(EXCLUDED.dtitle) RETURNING *;
+ did | cid | dlevel |      dauthor      |              dtitle              
+-----+-----+--------+-------------------+----------------------------------
+  88 |  33 |      1 | rls_regress_user1 | technology book, can only insert
+(1 row)
+
+--
 -- ROLE/GROUP
 --
 SET SESSION AUTHORIZATION rls_regress_user0;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index ed7adbf..5c660d5 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -436,6 +436,79 @@ DELETE FROM only t1 WHERE f_leak(b) RETURNING oid, *, t1;
 DELETE FROM t1 WHERE f_leak(b) RETURNING oid, *, t1;
 
 --
+-- INSERT ... ON CONFLICT UPDATE and Row-level security
+--
+
+-- Would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+
+-- Can't insert new violating tuple, either:
+INSERT INTO document VALUES (22, 11, 2, 'rls_regress_user2', 'mediocre novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+
+-- INSERT path is taken here, so UPDATE targelist doesn't matter:
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+
+-- Update path will now taken for same query, so UPDATE targelist now matters
+-- (this is the same query as the last, but now fails):
+INSERT INTO document VALUES (33, 22, 1, 'rls_regress_user1', 'okay science fiction')
+    ON CONFLICT (did) UPDATE SET dauthor = 'rls_regress_user3' RETURNING *;
+
+SET SESSION AUTHORIZATION rls_regress_user0;
+DROP POLICY p1 ON document;
+
+CREATE POLICY p1 ON document FOR SELECT USING (true);
+CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
+CREATE POLICY p3 ON document FOR UPDATE
+  USING (cid = (SELECT cid from category WHERE cname = 'novel'))
+  WITH CHECK (dauthor = current_user);
+
+SET SESSION AUTHORIZATION rls_regress_user1;
+
+-- Again, would fail with unique violation, but with ON CONFLICT fails as row is
+-- locked for update (notably, existing/target row is not leaked, which is what
+-- failed to satisfy WITH CHECK options - not row proposed for insertion by
+-- user):
+INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my fourth manga')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+
+-- Again, can't insert new violating tuple, either (unsuccessfully inserted tuple
+-- values are reported here, though)
+--
+-- Violates actual CHECK OPTION within UPDATE:
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user2', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor;
+
+-- Violates USING qual for UPDATE policy p3, interpreted here as CHECK OPTION.
+--
+-- UPDATE path is taken, but UPDATE fails purely because *existing* row to be
+-- updated is not a "novel"/cid 11 (row is not leaked, even though we have
+-- SELECT privileges sufficient to see the row in this instance):
+INSERT INTO document VALUES (33, 11, 1, 'rls_regress_user1', 'Some novel, replaces sci-fi')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle;
+-- Fine (we UPDATE):
+INSERT INTO document VALUES (2, 11, 1, 'rls_regress_user1', 'my first novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
+-- Fine (we INSERT, so "cid = 33" isn't evaluated):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+-- Fail (same query, but we UPDATE, so "cid = 33" is evaluated at end of
+-- UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
+-- Fail (we UPDATE, so dauthor assignment is evaluated at end of UPDATE):
+INSERT INTO document VALUES (78, 11, 1, 'rls_regress_user1', 'some other novel')
+    ON CONFLICT (did) UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = 'rls_regress_user2';
+-- Don't fail because INSERT doesn't satisfy WITH CHECK option that originated
+-- as a barrier/USING() qual from the UPDATE.  Note that the UPDATE path
+-- *isn't* taken, and so UPDATE-related policy does not apply:
+INSERT INTO document VALUES (88, 33, 1, 'rls_regress_user1', 'technology book, can only insert')
+    ON CONFLICT (did) UPDATE SET dtitle = upper(EXCLUDED.dtitle) RETURNING *;
+
+--
 -- ROLE/GROUP
 --
 SET SESSION AUTHORIZATION rls_regress_user0;
-- 
1.9.1

0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patchtext/x-patch; charset=US-ASCII; name=0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patchDownload
From f1bbf5d020c8205bc44c18ca557a209e58133d21 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <peter.geoghegan86@gmail.com>
Date: Tue, 3 Mar 2015 21:43:21 -0800
Subject: [PATCH 3/4] Support INSERT ... ON CONFLICT UPDATE

This non-standard INSERT clause allows DML statement authors to specify
that in the event of each of any of the tuples being inserted
duplicating an existing tuple in terms of a value or set of values
constrained by a unique index, an alternative UPDATE path may be taken.
The implementation will go to UPDATE the existing tuple whose value is
duplicated by a value within one single tuple proposed for insertion.
The implementation loops until either an insert or an UPDATE occurs.  No
existing tuple may be affected more than once per INSERT - the statement
is "deterministic" (in the lexicon of the SQL standard's discussion of
MERGE).  Like INSERT ... ON CONFLICT IGNORE, the implementation uses the
speculative insertion infrastructure.

READ COMMITTED isolation level is permitted to UPDATE a tuple even where
no version is visible to the command's MVCC snapshot.  Similarly, any
query predicate associated with the UPDATE portion of the new statement
need only satisfy an already locked, conclusively committed and visible
conflict tuple.  When the predicate isn't satisfied, the tuple is still
locked, which implies that at READ COMMITTED, a tuple may be locked
without any version being visible to the command's MVCC snapshot.

The "unique index inference" clause is made mandatory for ON CONFLICT
UPDATE (unlike ON CONFLICT IGNORE), which should address concerns about
spuriously taking an incorrect alternative ON CONFLICT path (i.e. the
wrong unique index is used for arbitration of whether or not to take the
alternative path) due to there being more than one would-be unique
violation.

The auxiliary ModifyTable plan used by the UPDATE portion of the new
statement is not formally a subplan of its parent INSERT ModifyTable
plan.  Rather, it's an independently planned subquery, whose execution
is tightly driven by its parent.  Special auxiliary state pertaining to
the auxiliary UPDATE is tracked by its parent through all stages of
query execution.

The implementation imposes some restrictions on child auxiliary UPDATE
plans, which make the plans comport with their parent to the extent
required during the executor stage.  The parent and child share the same
target ResultRelation, for example.  One user-visible consequences of
this is that the special auxiliary UPDATE query cannot have subselects
within its targetlist or WHERE clause.  UPDATEs may not reference any
other table, and UPDATE FROM is disallowed.  INSERT's RETURNING clause
projects tuples successfully inserted and updated.  If an ON CONFLICT
UPDATE's WHERE clause is not satisfied in respect of some slot/tuple,
the post-update tuple is not projected (since an UPDATE didn't occur
- although the row is still locked).

To support referencing tuples proposed for insertion but found to have
forced the implementation to take the alternative (UPDATE) path, a new
primnode, ExcludedExpr is added.  This allows parse analysis to deal
with a distinct alias RTE. However, after rewriting, excluded tuples are
referenced through special ad-hoc ExcludedExpr Vars (the planner
therefore only has to generate a simple subquery with a sequential scan,
that is only actually ever executed using the EvalPlanQual()
infrastructure).  Note that pg_stat_statements does not fingerprint
ExludedExpr, because it cannot appear in the post-parse-analysis,
pre-rewrite Query tree.  (pg_stat_statements does not fingerprint every
primnode anyway, mostly because some are only expected in utility
statements).  Other existing Node handling sites that don't expect to
see primnodes that appear only after rewriting (ExcludedExpr may be in
its own subcategory here in that it is the only such non-utility related
Node) do not have an ExcludedExpr case added either.
---
 contrib/pg_stat_statements/pg_stat_statements.c    |   2 +
 contrib/postgres_fdw/expected/postgres_fdw.out     |   3 +
 contrib/postgres_fdw/postgres_fdw.c                |   1 +
 contrib/postgres_fdw/sql/postgres_fdw.sql          |   1 +
 doc/src/sgml/ddl.sgml                              |   9 +-
 doc/src/sgml/fdwhandler.sgml                       |   6 +-
 doc/src/sgml/mvcc.sgml                             |  24 ++
 doc/src/sgml/plpgsql.sgml                          |  14 +-
 doc/src/sgml/postgres-fdw.sgml                     |   8 +-
 doc/src/sgml/protocol.sgml                         |  13 +-
 doc/src/sgml/ref/create_rule.sgml                  |   6 +-
 doc/src/sgml/ref/create_table.sgml                 |   2 +-
 doc/src/sgml/ref/create_trigger.sgml               |   5 +-
 doc/src/sgml/ref/create_view.sgml                  |   9 +-
 doc/src/sgml/ref/insert.sgml                       | 246 +++++++++++++---
 doc/src/sgml/trigger.sgml                          |  49 +++-
 src/backend/catalog/sql_features.txt               |   2 +-
 src/backend/commands/explain.c                     |  78 ++++-
 src/backend/executor/README                        |  77 ++++-
 src/backend/executor/execMain.c                    |   3 +-
 src/backend/executor/execQual.c                    |  54 ++++
 src/backend/executor/nodeModifyTable.c             | 325 ++++++++++++++++++++-
 src/backend/nodes/copyfuncs.c                      |  19 ++
 src/backend/nodes/equalfuncs.c                     |  13 +
 src/backend/nodes/nodeFuncs.c                      |  43 +++
 src/backend/nodes/outfuncs.c                       |  13 +
 src/backend/nodes/readfuncs.c                      |  16 +
 src/backend/optimizer/path/tidpath.c               |   8 +-
 src/backend/optimizer/plan/createplan.c            |   4 +-
 src/backend/optimizer/plan/planner.c               |  51 ++++
 src/backend/optimizer/plan/setrefs.c               |  32 +-
 src/backend/optimizer/plan/subselect.c             |   6 +
 src/backend/optimizer/util/plancat.c               |   9 +-
 src/backend/parser/analyze.c                       |  60 +++-
 src/backend/parser/gram.y                          |  27 ++
 src/backend/parser/parse_clause.c                  | 103 +++++--
 src/backend/parser/parse_expr.c                    |   6 +-
 src/backend/parser/parse_node.c                    |   8 +-
 src/backend/rewrite/rewriteHandler.c               | 103 +++++++
 src/backend/tcop/pquery.c                          |  16 +-
 src/backend/utils/adt/ruleutils.c                  |  39 +++
 src/bin/psql/common.c                              |   5 +-
 src/include/nodes/execnodes.h                      |  11 +
 src/include/nodes/nodes.h                          |   6 +-
 src/include/nodes/parsenodes.h                     |   4 +-
 src/include/nodes/plannodes.h                      |   1 +
 src/include/nodes/primnodes.h                      |  47 +++
 src/include/optimizer/planmain.h                   |   3 +-
 src/include/parser/parse_node.h                    |   1 +
 .../expected/insert-conflict-update-2.out          |  23 ++
 .../expected/insert-conflict-update-3.out          |  26 ++
 .../isolation/expected/insert-conflict-update.out  |  23 ++
 src/test/isolation/isolation_schedule              |   3 +
 .../isolation/specs/insert-conflict-update-2.spec  |  41 +++
 .../isolation/specs/insert-conflict-update-3.spec  |  69 +++++
 .../isolation/specs/insert-conflict-update.spec    |  40 +++
 src/test/regress/expected/insert_conflict.out      | 186 ++++++++++++
 src/test/regress/expected/privileges.out           |   7 +-
 src/test/regress/expected/rules.out                |  12 +
 src/test/regress/expected/subselect.out            |  22 ++
 src/test/regress/expected/triggers.out             | 102 ++++++-
 src/test/regress/expected/update.out               |  27 ++
 src/test/regress/expected/with.out                 |  74 +++++
 src/test/regress/input/constraints.source          |   5 +
 src/test/regress/output/constraints.source         |  15 +-
 src/test/regress/sql/insert_conflict.sql           | 127 ++++++++
 src/test/regress/sql/privileges.sql                |   5 +-
 src/test/regress/sql/rules.sql                     |   8 +
 src/test/regress/sql/subselect.sql                 |  14 +
 src/test/regress/sql/triggers.sql                  |  69 ++++-
 src/test/regress/sql/update.sql                    |  14 +
 src/test/regress/sql/with.sql                      |  37 +++
 72 files changed, 2382 insertions(+), 158 deletions(-)
 create mode 100644 src/test/isolation/expected/insert-conflict-update-2.out
 create mode 100644 src/test/isolation/expected/insert-conflict-update-3.out
 create mode 100644 src/test/isolation/expected/insert-conflict-update.out
 create mode 100644 src/test/isolation/specs/insert-conflict-update-2.spec
 create mode 100644 src/test/isolation/specs/insert-conflict-update-3.spec
 create mode 100644 src/test/isolation/specs/insert-conflict-update.spec

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 46f5189..414ec83 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2201,6 +2201,8 @@ JumbleQuery(pgssJumbleState *jstate, Query *query)
 	APP_JUMB(query->specClause);
 	JumbleExpr(jstate, (Node *) query->arbiterExpr);
 	JumbleExpr(jstate, query->arbiterWhere);
+	if (query->onConflict)
+		JumbleQuery(jstate, (Query *) query->onConflict);
 	JumbleExpr(jstate, (Node *) query->returningList);
 	JumbleExpr(jstate, (Node *) query->groupClause);
 	JumbleExpr(jstate, query->havingQual);
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 47bb1ca..51e12ea 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -2331,6 +2331,9 @@ INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
 ERROR:  relation "ft1" is not an ordinary table
 HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) UPDATE SET c3 = 'ffg'; -- unsupported
+ERROR:  relation "ft1" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 ERROR:  new row for relation "T 1" violates check constraint "c2positive"
 DETAIL:  Failing row contains (1111, -2, null, null, null, null, ft1       , null).
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index df55cfd..2e90aec 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1229,6 +1229,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 				 errmsg("postgres_fdw does not support ON CONFLICT unique index inference")));
 	else if (plan->spec == SPEC_IGNORE)
 		ignore = true;
+	/* SPEC_INSERT case should be rejected in parser */
 	else if (plan->spec != SPEC_NONE)
 		elog(ERROR, "unexpected speculative specification: %d", (int) plan->spec);
 
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index aba373e..d6fb0f7 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -374,6 +374,7 @@ ALTER TABLE "S 1"."T 1" ADD CONSTRAINT c2positive CHECK (c2 >= 0);
 INSERT INTO ft1(c1, c2) VALUES(11, 12);  -- duplicate key
 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) UPDATE SET c3 = 'ffg'; -- unsupported
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 UPDATE ft1 SET c2 = -c2 WHERE c1 = 1;  -- c2positive
 
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 2b05e1d..366d25c 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2440,7 +2440,7 @@ VALUES ('Albany', NULL, NULL, 'NY');
 
   <para>
    There is limited inheritance support for <command>INSERT</command>
-   commands with <literal>ON CONFLICT IGNORE</> clauses.  Tables with
+   commands with <literal>ON CONFLICT</> clauses.  Tables with
    children are not generally accepted as targets.  One notable
    exception is that such tables are accepted as targets for
    <command>INSERT</command> commands with <literal>ON CONFLICT
@@ -2449,7 +2449,7 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <emphasis>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 <command>INSERT</command>
-   with <literal>ON CONFLICT IGNORE</>.
+   with <literal>ON CONFLICT</>.
   </para>
 
   <para>
@@ -2552,6 +2552,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
    not <literal>INSERT</literal> or <literal>ALTER TABLE ...
    RENAME</literal>) typically default to including child tables and
    support the <literal>ONLY</literal> notation to exclude them.
+   <literal>INSERT</literal> with an <literal>ON CONFLICT
+   UPDATE</literal> clause does not support the
+   <literal>ONLY</literal> notation, and so in effect tables with
+   inheritance children are not supported for the <literal>ON
+   CONFLICT</literal> variant.
    Commands that do database maintenance and tuning
    (e.g., <literal>REINDEX</literal>, <literal>VACUUM</literal>)
    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 0853078..792a250 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1015,8 +1015,10 @@ GetForeignServerByName(const char *name, bool missing_ok);
     </para>
 
     <para>
-     <command>INSERT</> with an <literal>ON CONFLICT</> clause is not
-     supported with a unique index inference specification.
+     <command>INSERT</> with an <literal>ON CONFLICT</> clause is not supported
+     with a unique index inference specification (this implies that <literal>ON
+     CONFLICT UPDATE</> is never supported, since the specification is
+     mandatory there).
     </para>
 
   </sect1>
diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml
index a0d6867..5e310d7 100644
--- a/doc/src/sgml/mvcc.sgml
+++ b/doc/src/sgml/mvcc.sgml
@@ -326,6 +326,30 @@
    </para>
 
    <para>
+    <command>INSERT</command> with an <literal>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 <command>INSERT</command>, the
+    <command>UPDATE</command> may affect that row, even though it may be the
+    case that <emphasis>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 <literal>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).
+   </para>
+
+   <para>
+    <command>INSERT</command> with an <literal>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 <command>INSERT</command>
+    snapshot.  Again, this is only the case in Read Committed mode.
+   </para>
+
+   <para>
     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 158d9d2..255c4bb 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -2608,7 +2608,11 @@ END;
     <para>
 
     This example uses exception handling to perform either
-    <command>UPDATE</> or <command>INSERT</>, as appropriate:
+    <command>UPDATE</> or <command>INSERT</>, as appropriate.  It is
+    recommended that applications use <command>INSERT</> with
+    <literal>ON CONFLICT UPDATE</> rather than actually emulating this
+    pattern.  This example serves only to illustrate use of
+    <application>PL/pgSQL</application> control flow structures:
 
 <programlisting>
 CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
@@ -3772,9 +3776,11 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
     <command>INSERT</> and <command>UPDATE</> operations, the return value
     should be <varname>NEW</>, which the trigger function may modify to
     support <command>INSERT RETURNING</> and <command>UPDATE RETURNING</>
-    (this will also affect the row value passed to any subsequent triggers).
-    For <command>DELETE</> operations, the return value should be
-    <varname>OLD</>.
+    (this will also affect the row value passed to any subsequent triggers,
+    or passed to a special <varname>EXCLUDED</> alias reference within
+    an <command>INSERT</> statement with an <literal>ON CONFLICT UPDATE</>
+    clause).  For <command>DELETE</> operations, the return
+    value should be <varname>OLD</>.
    </para>
 
    <para>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 81d4441..fa39661 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -69,9 +69,11 @@
  </para>
 
  <para>
-  <filename>postgres_fdw</> supports <command>INSERT</command>
-  statements with an <literal>ON CONFLICT IGNORE</> clause, provided a
-  unique index inference specification is omitted.
+  Note that <filename>postgres_fdw</> currently lacks support for
+  <command>INSERT</command> statements with an <literal>ON CONFLICT
+  UPDATE</> clause.  However, the <literal>ON CONFLICT IGNORE</>
+  clause is supported, provided a unique index inference specification
+  is omitted.
  </para>
 
  <para>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 3a753a0..ac13d32 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -2998,9 +2998,16 @@ CommandComplete (B)
         <literal>INSERT <replaceable>oid</replaceable>
         <replaceable>rows</replaceable></literal>, where
         <replaceable>rows</replaceable> is the number of rows
-        inserted. <replaceable>oid</replaceable> is the object ID
-        of the inserted row if <replaceable>rows</replaceable> is 1
-        and the target table has OIDs;
+        inserted. However, if and only if <literal>ON CONFLICT
+        UPDATE</> is specified, then the tag is <literal>UPSERT
+        <replaceable>oid</replaceable>
+        <replaceable>rows</replaceable></literal>, where
+        <replaceable>rows</replaceable> is the number of rows inserted
+        <emphasis>or updated</emphasis>.
+        <replaceable>oid</replaceable> is the object ID of the
+        inserted row if <replaceable>rows</replaceable> is 1 and the
+        target table has OIDs, and (for the <literal>UPSERT</literal>
+        tag), the row was actually inserted rather than updated;
         otherwise <replaceable>oid</replaceable> is 0.
        </para>
 
diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml
index 045f5d1..34a4ae1 100644
--- a/doc/src/sgml/ref/create_rule.sgml
+++ b/doc/src/sgml/ref/create_rule.sgml
@@ -137,11 +137,11 @@ CREATE [ OR REPLACE ] RULE <replaceable class="parameter">name</replaceable> AS
       The event is one of <literal>SELECT</literal>,
       <literal>INSERT</literal>, <literal>UPDATE</literal>, or
       <literal>DELETE</literal>.  Note that an
-      <command>INSERT</command> containing an <literal>ON CONFLICT
-      IGNORE</literal> clause cannot be used on tables that have
+      <command>INSERT</command> containing an <literal>ON
+      CONFLICT</literal> clause cannot be used on tables that have
       either <literal>INSERT</literal> or <literal>UPDATE</literal>
       rules.  Consider using an updatable view instead, which have
-      limited support for <literal>ON CONFLICT IGNORE</literal>.
+      limited support for <literal>ON CONFLICT IGNORE</literal> only.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 7f30fb1..758d0e9 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -720,7 +720,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
       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 <command>INSERT</command> statement
-      that includes an <literal>ON CONFLICT</> clause.
+      that includes an <literal>ON CONFLICT UPDATE</> clause.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_trigger.sgml b/doc/src/sgml/ref/create_trigger.sgml
index aae0b41..1b75b1a 100644
--- a/doc/src/sgml/ref/create_trigger.sgml
+++ b/doc/src/sgml/ref/create_trigger.sgml
@@ -76,7 +76,10 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
    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 <literal>FOR
-   EACH STATEMENT</literal> triggers).
+   EACH STATEMENT</literal> triggers).  Note that since
+   <command>INSERT</command> with an <literal>ON CONFLICT UPDATE</>
+   clause is considered an <command>INSERT</command> statement, no
+   <command>UPDATE</command> statement level trigger will be fired.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index b1b4b02..3ab8bec 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -388,11 +388,18 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     <command>INSERT</command> with an <literal>ON CONFLICT</> clause
     is only supported on updatable views under specific circumstances.
     If a <quote>unique index inference specifictation</quote> is
-    provided, then updatable views are not supported.  For example:
+    provided, then updatable views are not supported.
+    Since this specification is already mandatory for
+    <command>INSERT</command> with <literal>ON CONFLICT UPDATE</>,
+    this implies that only the <literal>ON CONFLICT IGNORE</> variant
+    is supported, and only when there is no such specification.  For
+    example:
    </para>
    <para>
 <programlisting>
 -- 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;
 
diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml
index f9d1436..5960bb2 100644
--- a/doc/src/sgml/ref/insert.sgml
+++ b/doc/src/sgml/ref/insert.sgml
@@ -24,7 +24,14 @@ PostgreSQL documentation
 [ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
 INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
     { DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) [, ...] | <replaceable class="PARAMETER">query</replaceable> }
-    [ ON CONFLICT [ ( { <replaceable class="parameter">column_name_index</replaceable> | ( <replaceable class="parameter">expression_index</replaceable> ) } [, ...] [ WHERE <replaceable class="PARAMETER">index_condition</replaceable> ] ) ] IGNORE]
+    [ ON CONFLICT [ ( { <replaceable class="parameter">column_name_index</replaceable> | ( <replaceable class="parameter">expression_index</replaceable> ) } [, ...] [ WHERE <replaceable class="PARAMETER">index_condition</replaceable> ] ) ]
+      { IGNORE | UPDATE
+        SET { <replaceable class="PARAMETER">column_name</replaceable> = { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } |
+              ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] )
+            } [, ...]
+        [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
+      }
+    ]
     [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
@@ -66,30 +73,109 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    for insertion;  it is taken (or not taken) once per row.
    <literal>ON CONFLICT IGNORE</> simply avoids inserting any
    individual row when it is determined that a conflict related error
-   would otherwise need to be raised.
+   would otherwise need to be raised.  <literal>ON CONFLICT UPDATE</>
+   has the system take an <command>UPDATE</command> path in respect of
+   such rows instead.  <literal>ON CONFLICT UPDATE</> guarantees an
+   atomic <command>INSERT</command> or <command>UPDATE</command>
+   outcome - provided there is no incidental error, one of those two
+   outcomes is guaranteed, even under high concurrency.
   </para>
 
   <para>
-   <literal>ON CONFLICT IGNORE</> optionally accepts a
-   <emphasis>unique index inference</emphasis> specification, which
-   consists of one or more <replaceable
-   class="PARAMETER">column_name_index</replaceable> columns and/or
-   <replaceable class="PARAMETER">expression_index</replaceable>
-   expressions on columns, appearing between parenthesis.  These are
-   used to infer a single unique index to limit pre-checking for
-   conflicts to (if no appropriate index is available, an error is
-   raised).  A subset of the table to limit the check for conflicts to
-   can optionally also be specified using <replaceable
+   <literal>ON CONFLICT UPDATE</> optionally accepts a
+   <literal>WHERE</> clause <replaceable>condition</>.  When provided,
+   the statement only proceeds with updating if the
+   <replaceable>condition</> is satisfied.  Otherwise, unlike a
+   conventional <command>UPDATE</command>, the row is still locked for
+   update.  Note that the <replaceable>condition</> is evaluated last,
+   after a conflict has been identified as a candidate to update.
+  </para>
+
+  <para>
+   <literal>ON CONFLICT UPDATE</> is effectively an auxiliary query of
+   its parent <command>INSERT</command>.  Two special aliases are
+   visible when <literal>ON CONFLICT UPDATE</> is specified -
+   <varname>TARGET</> and <varname>EXCLUDED</>.  The first alias is a
+   standard, generic alias for the target relation, while the second
+   alias refers to rows originally proposed for insertion.  Both
+   aliases can be used in the auxiliary query targetlist and
+   <literal>WHERE</> clause, while the <varname>TARGET</> alias can be
+   used anywhere within the entire statement (e.g., within the
+   <literal>RETURNING</> clause).  This allows expressions (in
+   particular, assignments) to reference rows originally proposed for
+   insertion.  Note that the effects of all per-row <literal>BEFORE
+   INSERT</> triggers are carried forward.  This is particularly
+   useful for multi-insert <literal>ON CONFLICT UPDATE</> statements;
+   when inserting or updating multiple rows, constants or parameter
+   values need only appear once.
+  </para>
+
+  <para>
+   There are several restrictions on the <literal>ON CONFLICT
+   UPDATE</> clause that do not apply to <command>UPDATE</command>
+   statements.  Subqueries may not appear in either the
+   <command>UPDATE</command> targetlist, nor its <literal>WHERE</>
+   clause (although simple multi-assignment expressions are
+   supported).  <literal>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.
+  </para>
+
+  <para>
+   <command>INSERT</command> with an <literal>ON CONFLICT UPDATE</>
+   clause is a <quote>deterministic</quote> statement.  This means
+   that the command will not be allowed to affect any single existing
+   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
+   <command>INSERT</command> statement with <literal>ON CONFLICT
+   UPDATE</literal>, rows proposed for insertion will never take the
+   alternative path (provided that a <literal>BEFORE ROW
+   INSERT</literal> 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 <literal>WHERE</> clause <replaceable>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).  However, the implementation checks for cardinality
+   violations after locking the row, and before updating (or
+   considering updating), so a cardinality violation may be raised
+   despite the fact that the row would not otherwise have gone on to
+   be updated if and only if the existing row was updated by the
+   <literal>ON CONFLICT UPDATE</literal> command at least once
+   already.
+  </para>
+
+  <para>
+   <literal>ON CONFLICT UPDATE</> requires a <emphasis>unique index
+   inference</emphasis> specification, which consists of one or more
+   <replaceable class="PARAMETER">column_name_index</replaceable>
+   columns and/or <replaceable
+   class="PARAMETER">expression_index</replaceable> expressions on
+   columns, appearing between parenthesis.  These are used to infer a
+   single unique index to limit pre-checking for conflicts to (if no
+   appropriate index is available, an error is raised).  A subset of
+   the table to limit the check for conflicts to can optionally also
+   be specified using <replaceable
    class="PARAMETER">index_condition</replaceable>.  Note that any
    available unique index must only cover at least that subset in
    order to be arbitrate taking the alternative path;  it need not
    match exactly, and so a non-partial unique index that otherwise
-   matches is applicable.  Note that omitting the specification
+   matches is applicable.  <literal>ON CONFLICT IGNORE</> makes an
+   inference specification optional;  omitting the specification
    indicates a total indifference to where any conflict could occur,
    which isn't always appropriate.  At times, it may be desirable for
    <literal>ON CONFLICT IGNORE</> to <emphasis>not</emphasis> suppress
    a conflict related error associated with an index where that isn't
-   explicitly anticipated.
+   explicitly anticipated.  Note that <literal>ON CONFLICT UPDATE</>
+   assignment may result in a uniqueness violation, just as with a
+   conventional <command>UPDATE</command>.
   </para>
 
   <para>
@@ -116,22 +202,24 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    seldom of great interest.  A conflict is either a unique violation
    from a unique constraint (or unique index), or an exclusion
    violation from an exclusion constraint.  Only unique indexes can be
-   inferred with a unique index inference specification.  In contrast
-   to the rules around certain other SQL clauses, like the
-   <literal>DISTINCT</literal> clause, the definition of a duplicate
-   (a conflict) is based on whatever unique indexes happen to be
-   defined on columns on the table.  This means that if a user-defined
-   type has multiple sort orders, and the "equals" operator of any of
-   those available sort orders happens to be inconsistent (which goes
-   against an unenforced convention of
-   <productname>PostgreSQL</productname>), the exact behavior depends
-   on the choice of operator class when the unique index was created
-   initially, and not any other consideration such as the default
-   operator class for the type of each indexed column.  If there are
-   multiple unique indexes available that seem like equally suitable
-   candidates, but with inconsistent definitions of "equals", then the
-   system chooses whatever it estimates to be the cheapest one to use
-   as an arbiter of taking the alternative
+   inferred with a unique index inference specification, which is
+   required for the <command>UPDATE</command> variant, so in effect
+   only unique constraints (and unique indexes) are supported by the
+   <command>UPDATE</command> variant.  In contrast to the rules around
+   certain other SQL clauses, like the <literal>DISTINCT</literal>
+   clause, the definition of a duplicate (a conflict) is based on
+   whatever unique indexes happen to be defined on columns on the
+   table.  This means that if a user-defined type has multiple sort
+   orders, and the "equals" operator of any of those available sort
+   orders happens to be inconsistent (which goes against an unenforced
+   convention of <productname>PostgreSQL</productname>), the exact
+   behavior depends on the choice of operator class when the unique
+   index was created initially, and not any other consideration such
+   as the default operator class for the type of each indexed column.
+   If there are multiple unique indexes available that seem like
+   equally suitable candidates, but with inconsistent definitions of
+   "equals", then the system chooses whatever it estimates to be the
+   cheapest one to use as an arbiter of taking the alternative
    <command>UPDATE</command>/<literal>IGNORE</literal> path.
   </para>
 
@@ -157,24 +245,40 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
 
   <para>
    The optional <literal>RETURNING</> clause causes <command>INSERT</>
-   to compute and return value(s) based on each row actually inserted.
+   to compute and return value(s) based on each row actually inserted
+   (or updated, if an <literal>ON CONFLICT UPDATE</> clause was used).
    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
    <literal>RETURNING</> list is identical to that of the output list
-   of <command>SELECT</>.
+   of <command>SELECT</>.  Only rows that were successfully inserted
+   or updated will be returned.  If a row was locked but not updated
+   because an <literal>ON CONFLICT UPDATE</> <literal>WHERE</> clause
+   did not pass, the row will not be returned.  Since
+   <literal>RETURNING</> is not part of the <command>UPDATE</>
+   auxiliary query, the special <literal>ON CONFLICT UPDATE</> aliases
+   (<varname>TARGET</> and <varname>EXCLUDED</>) may not be
+   referenced;  only the row as it exists after updating (or
+   inserting) is returned.
   </para>
 
   <para>
    You must have <literal>INSERT</literal> privilege on a table in
-   order to insert into it.  If a column list is specified, you only
-   need <literal>INSERT</literal> privilege on the listed columns.
-   Use of the <literal>RETURNING</> clause requires <literal>SELECT</>
-   privilege on all columns mentioned in <literal>RETURNING</>.
-   If you use the <replaceable
-   class="PARAMETER">query</replaceable> clause to insert rows from a
-   query, you of course need to have <literal>SELECT</literal> privilege on
-   any table or column used in the query.
+   order to insert into it, as well as <literal>UPDATE
+   privilege</literal> if and only if <literal>ON CONFLICT UPDATE</>
+   is specified.  If a column list is specified, you only need
+   <literal>INSERT</literal> privilege on the listed columns.
+   Similarly, when <literal>ON CONFLICT UPDATE</> is specified, you
+   only need <literal>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 <literal>ON CONFLICT UPDATE</>
+   expressions or <replaceable>condition</>.  Use of the
+   <literal>RETURNING</> clause requires <literal>SELECT</> privilege
+   on all columns mentioned in <literal>RETURNING</>.  If you use the
+   <replaceable class="PARAMETER">query</replaceable> clause to insert
+   rows from a query, you of course need to have
+   <literal>SELECT</literal> privilege on any table or column used in
+   the query.
   </para>
  </refsect1>
 
@@ -218,7 +322,12 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
       The name of a column in the table named by <replaceable class="PARAMETER">table_name</replaceable>.
       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 <literal>ON CONFLICT UPDATE</>, do not
+      include the table's name in the specification of a target
+      column.  For example, <literal>INSERT ... ON CONFLICT UPDATE tab
+      SET TARGET.col = 1</> is invalid (this follows the general
+      behavior for <command>UPDATE</>).
      </para>
     </listitem>
    </varlistentry>
@@ -307,6 +416,18 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="PARAMETER">condition</replaceable></term>
+    <listitem>
+     <para>
+      An expression that returns a value of type <type>boolean</type>.
+      Only rows for which this expression returns <literal>true</>
+      will be updated, although all rows will be locked when the
+      <literal>ON CONFLICT UPDATE</> path is taken.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+
     <term><replaceable class="PARAMETER">output_expression</replaceable></term>
     <listitem>
      <para>
@@ -339,20 +460,29 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
 <screen>
 INSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</replaceable>
 </screen>
+   However, in the event of an <literal>ON CONFLICT UPDATE</> clause
+   (but <emphasis>not</emphasis> in the event of an <literal>ON
+   CONFLICT IGNORE</> clause), the command tag reports the number of
+   rows inserted or updated together, of the form
+<screen>
+UPSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</replaceable>
+</screen>
    The <replaceable class="parameter">count</replaceable> is the number
    of rows inserted.  If <replaceable class="parameter">count</replaceable>
    is exactly one, and the target table has OIDs, then
    <replaceable class="parameter">oid</replaceable> is the
-   <acronym>OID</acronym> assigned to the inserted row.  Otherwise
-   <replaceable class="parameter">oid</replaceable> is zero.
+   <acronym>OID</acronym>
+   assigned to the inserted row (but not if there is only a single
+   updated row).  Otherwise <replaceable
+   class="parameter">oid</replaceable> is zero..
   </para>
 
   <para>
    If the <command>INSERT</> command contains a <literal>RETURNING</>
    clause, the result will be similar to that of a <command>SELECT</>
    statement containing the columns and values defined in the
-   <literal>RETURNING</> list, computed over the row(s) inserted by the
-   command.
+   <literal>RETURNING</> list, computed over the row(s) inserted or
+   updated by the command.
   </para>
  </refsect1>
 
@@ -455,6 +585,18 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
 </programlisting>
   </para>
   <para>
+   Insert or update new distributors as appropriate.  Assumes a unique
+   index has been defined that constrains values appearing in the
+   <literal>did</literal> column.  Note that an <varname>EXCLUDED</>
+   expression is used to reference values originally proposed for
+   insertion:
+<programlisting>
+  INSERT INTO distributors (did, dname)
+  VALUES (5, 'Gizmo transglobal'), (6, 'Associated Computing, inc')
+  ON CONFLICT (did) UPDATE SET dname = EXCLUDED.dname
+</programlisting>
+  </para>
+  <para>
    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.
@@ -468,6 +610,20 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
 </programlisting>
   </para>
   <para>
+   Insert or update new distributors as appropriate.  Example assumes
+   a unique index has been defined that constrains values appearing in
+   the <literal>did</literal> column.  <literal>WHERE</> clause is
+   used to limit the rows actually updated (any existing row not
+   updated will still be locked, though):
+<programlisting>
+  -- Don't update existing distributors based in a certain ZIP code
+  INSERT INTO distributors (did, dname) VALUES (8, 'Anvil Distribution')
+  ON CONFLICT (did) UPDATE
+  SET dname = EXCLUDED.dname || ' (formerly ' || TARGET.dname || ')'
+  WHERE TARGET.zipcode != '21201'
+</programlisting>
+  </para>
+  <para>
    Insert new distributor if possible;  otherwise
    <literal>IGNORE</literal>.  Example assumes a unique index has been
    defined that constrains values appearing in the
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index f94aea1..5141690 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -40,14 +40,17 @@
     On tables and foreign tables, triggers can be defined to execute either
     before or after any <command>INSERT</command>, <command>UPDATE</command>,
     or <command>DELETE</command> operation, either once per modified row,
-    or once per <acronym>SQL</acronym> statement.
-    <command>UPDATE</command> triggers can moreover be set to fire only if
-    certain columns are mentioned in the <literal>SET</literal> clause of the
-    <command>UPDATE</command> statement.
-    Triggers can also fire for <command>TRUNCATE</command> statements.
-    If a trigger event occurs, the trigger's function is called at the
-    appropriate time to handle the event.  Foreign tables do not support the
-    TRUNCATE statement at all.
+    or once per <acronym>SQL</acronym> statement.  If an
+    <command>INSERT</command> contains an <literal>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 a reference to
+    an <varname>EXCLUDED</> column appears.  <command>UPDATE</command>
+    triggers can moreover be set to fire only if certain columns are
+    mentioned in the <literal>SET</literal> clause of the
+    <command>UPDATE</command> statement.  Triggers can also fire for
+    <command>TRUNCATE</command> statements.  If a trigger event occurs,
+    the trigger's function is called at the appropriate time to handle the
+    event.  Foreign tables do not support the TRUNCATE statement at all.
    </para>
 
    <para>
@@ -119,6 +122,36 @@
    </para>
 
    <para>
+    If an <command>INSERT</command> contains an <literal>ON CONFLICT
+    UPDATE</> clause, it is possible that the effects of all row-level
+    <literal>BEFORE</> <command>INSERT</command> triggers and all
+    row-level BEFORE <command>UPDATE</command> triggers can both be
+    applied in a way that is apparent from the final state of the updated
+    row, if an <varname>EXCLUDED</> column is referenced.  There need not
+    be an <varname>EXCLUDED</> column reference for both sets of BEFORE
+    row-level triggers to execute, though.  The possibility of surprising
+    outcomes should be considered when there are both <literal>BEFORE</>
+    <command>INSERT</command> and <literal>BEFORE</>
+    <command>UPDATE</command> 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 <command>UPDATE</command>
+    triggers are executed when <literal>ON CONFLICT UPDATE</> is
+    specified, regardless of whether or not any rows were affected by
+    the <command>UPDATE</command>.  An <command>INSERT</command> with
+    an <literal>ON CONFLICT UPDATE</> clause will execute
+    statement-level <literal>BEFORE</> <command>INSERT</command>
+    triggers first, then statement-level <literal>BEFORE</>
+    <command>UPDATE</command> triggers, followed by statement-level
+    <literal>AFTER</> <command>UPDATE</command> triggers and finally
+    statement-level <literal>AFTER</> <command>INSERT</command>
+    triggers.  <literal>ON CONFLICT UPDATE</> is not supported on
+    views (Only <literal>ON CONFLICT IGNORE</> is supported on
+    updatable views); therefore, unpredictable interactions with
+    <literal>INSTEAD OF</> triggers are not possible.
+   </para>
+
+   <para>
     Trigger functions invoked by per-statement triggers should always
     return <symbol>NULL</symbol>. Trigger functions invoked by per-row
     triggers can return a table row (a value of
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 3329264..ca28928 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -229,7 +229,7 @@ F311	Schema definition statement	02	CREATE TABLE for persistent base tables	YES
 F311	Schema definition statement	03	CREATE VIEW	YES	
 F311	Schema definition statement	04	CREATE VIEW: WITH CHECK OPTION	YES	
 F311	Schema definition statement	05	GRANT statement	YES	
-F312	MERGE statement			NO	
+F312	MERGE statement			NO	Consider INSERT ... ON CONFLICT UPDATE
 F313	Enhanced MERGE statement			NO	
 F314	MERGE statement with DELETE branch			NO	
 F321	User authorization			YES	
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index d88c7fa..76ecfb1 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -103,7 +103,8 @@ static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
 static void ExplainScanTarget(Scan *plan, ExplainState *es);
 static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
-static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es);
+static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es,
+								  List *ancestors);
 static void ExplainMemberNodes(List *plans, PlanState **planstates,
 				   List *ancestors, ExplainState *es);
 static void ExplainSubPlans(List *plans, List *ancestors,
@@ -762,6 +763,9 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 			ExplainPreScanMemberNodes(((ModifyTable *) plan)->plans,
 								  ((ModifyTableState *) planstate)->mt_plans,
 									  rels_used);
+			if (((ModifyTable *) plan)->onConflictPlan)
+				ExplainPreScanNode(((ModifyTableState *) planstate)->onConflict,
+								   rels_used);
 			break;
 		case T_Append:
 			ExplainPreScanMemberNodes(((Append *) plan)->appendplans,
@@ -863,6 +867,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	const char *custom_name = NULL;
 	int			save_indent = es->indent;
 	bool		haschildren;
+	bool		suppresschildren = false;
+	ModifyTable *mtplan;
 
 	switch (nodeTag(plan))
 	{
@@ -871,13 +877,33 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			break;
 		case T_ModifyTable:
 			sname = "ModifyTable";
-			switch (((ModifyTable *) plan)->operation)
+			mtplan = (ModifyTable *) plan;
+			switch (mtplan->operation)
 			{
 				case CMD_INSERT:
 					pname = operation = "Insert";
 					break;
 				case CMD_UPDATE:
-					pname = operation = "Update";
+					if (mtplan->spec == SPEC_NONE)
+					{
+						pname = operation = "Update";
+					}
+					else
+					{
+						Assert(mtplan->spec == SPEC_UPDATE);
+
+						pname = operation = "Conflict Update";
+
+						/*
+						 * Do not display child sequential scan/result node.
+						 * Quals from child will be directly attributed to
+						 * ModifyTable node, since we prefer to avoid
+						 * displaying scan node to users, as it is merely an
+						 * implementation detail; it is never executed in the
+						 * conventional way.
+						 */
+						suppresschildren = true;
+					}
 					break;
 				case CMD_DELETE:
 					pname = operation = "Delete";
@@ -1457,7 +1483,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										   planstate, es);
 			break;
 		case T_ModifyTable:
-			show_modifytable_info((ModifyTableState *) planstate, es);
+			show_modifytable_info((ModifyTableState *) planstate, es,
+								  ancestors);
 			break;
 		case T_Hash:
 			show_hash_info((HashState *) planstate, es);
@@ -1585,7 +1612,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		planstate->subPlan;
 	if (haschildren)
 	{
-		ExplainOpenGroup("Plans", "Plans", false, es);
+		 if (!suppresschildren)
+			ExplainOpenGroup("Plans", "Plans", false, es);
 		/* Pass current PlanState as head of ancestors list for children */
 		ancestors = lcons(planstate, ancestors);
 	}
@@ -1608,9 +1636,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	switch (nodeTag(plan))
 	{
 		case T_ModifyTable:
-			ExplainMemberNodes(((ModifyTable *) plan)->plans,
-							   ((ModifyTableState *) planstate)->mt_plans,
-							   ancestors, es);
+			if (((ModifyTable *) plan)->spec != SPEC_UPDATE)
+				ExplainMemberNodes(((ModifyTable *) plan)->plans,
+								   ((ModifyTableState *) planstate)->mt_plans,
+								   ancestors, es);
+			if (((ModifyTable *) plan)->onConflictPlan)
+				ExplainNode(((ModifyTableState *) planstate)->onConflict,
+							ancestors, "Member", NULL, es);
 			break;
 		case T_Append:
 			ExplainMemberNodes(((Append *) plan)->appendplans,
@@ -1648,7 +1680,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	if (haschildren)
 	{
 		ancestors = list_delete_first(ancestors);
-		ExplainCloseGroup("Plans", "Plans", false, es);
+
+		if (!suppresschildren)
+			ExplainCloseGroup("Plans", "Plans", false, es);
 	}
 
 	/* in text format, undo whatever indentation we added */
@@ -2240,6 +2274,12 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 			if (es->verbose)
 				namespace = get_namespace_name(get_rel_namespace(rte->relid));
 			objecttag = "Relation Name";
+
+			/*
+			 * ON CONFLICT's "TARGET" alias will not appear in output for
+			 * auxiliary ModifyTable as its alias, because target
+			 * resultRelation is shared between parent and auxiliary queries
+			 */
 			break;
 		case T_FunctionScan:
 			{
@@ -2322,7 +2362,8 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
  * Second, give FDWs a chance to display extra info about foreign targets.
  */
 static void
-show_modifytable_info(ModifyTableState *mtstate, ExplainState *es)
+show_modifytable_info(ModifyTableState *mtstate, ExplainState *es,
+					  List *ancestors)
 {
 	ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
 	const char *operation;
@@ -2402,6 +2443,23 @@ show_modifytable_info(ModifyTableState *mtstate, ExplainState *es)
 											 j,
 											 es);
 		}
+		else if (mtstate->spec == SPEC_UPDATE)
+		{
+			PlanState *ps = (*mtstate->mt_plans);
+
+			/*
+			 * Seqscan node is always used, unless optimizer determined that
+			 * predicate precludes ever updating, in which case a simple Result
+			 * node is possible
+			 */
+			Assert(IsA(ps->plan, SeqScan) || IsA(ps->plan, Result));
+
+			/* Attribute child scan node's qual to ModifyTable node */
+			show_scan_qual(ps->plan->qual, "Filter", ps, ancestors, es);
+
+			if (ps->plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 1, ps, es);
+		}
 
 		if (labeltargets)
 		{
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 862c312..b5a5c33 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -205,17 +205,19 @@ Speculative insertion
 ---------------------
 
 Speculative insertion is a process that the executor manages for the benefit of
-INSERT...ON CONFLICT IGNORE.  Supported indexes include nbtree unique
+INSERT...ON CONFLICT UPDATE/IGNORE.  Supported indexes include nbtree unique
 indexes (nbtree is currently the only amcanunique index access method), or
 exclusion constraint indexes (exclusion constraints are considered a
-generalization of unique constraints).
-
-The primary user-visible goal for INSERT ... ON CONFLICT is to guarantee either
-an insert, or a conclusive determination that an insert cannot go ahead (due to
-a conclusively committed/visible conflict).  A would-be conflict (and the
-associated index) are the arbiters of whether or not the alternative (IGNORE)
-path is taken.  The implementation more or less tries to insert until one or
-the other of those two outcomes is reached.  There are some non-obvious hazards
+generalization of unique constraints).  Only ON CONFLICT IGNORE is supported
+with exclusion constraints.
+
+The primary user-visible goal for INSERT...ON CONFLICT UPDATE is to guarantee
+either an insert or update under normal operating conditions in READ COMMITTED
+mode (where serialization failures are just as unacceptable as they are with
+regular UPDATEs).  A would-be conflict (and the associated index) are the
+arbiters of whether or not the alternative (UPDATE/IGNORE) path is taken.  The
+implementation more or less tries to update or insert until one or the other of
+those two outcomes occurs successfully.  There are some non-obvious hazards
 involved that are carefully avoided.  These hazards relate to concurrent
 activity causing conflicts for the implementation, which must be handled.
 
@@ -228,12 +230,37 @@ must lock the row, and then verify that there is no conflict.  Only then do we
 UPDATE.  Theoretically, some individual session could loop forever, although
 under high concurrency one session always proceeds.
 
+There are 2 sources of conflicts for ON CONFLICT UPDATE:
+
+1. Conflicts from going to update (having found a conflict during the
+pre-check), and finding the tuple changed (which may or may not involve new,
+distinct constrained values in later tuple versions -- for simplicity, we don't
+bother with considering that).  This is not a conflict that the IGNORE variant
+considers.
+
+2. Conflicts from inserting a tuple (having not found a conflict during the
+pre-check), and only then finding a conflict at insertion time (when inserting
+index tuples, and finding a conflicting one when a buffer lock is held on an
+index page in the ordinary course of insertion).  This can happen if a
+concurrent insertion occurs after the pre-check, but before physical index
+tuple insertion.
+
 The first step in the loop is to perform a pre-check.  The indexes are scanned
 for existing conflicting values.  At this point, we may have to wait until the
 end of another xact (or xact's promise token -- more on that later), iff it
 isn't immediately conclusive that there is or is not a conflict (when we finish
-the pre-check, there is a conclusion about there either being or
-not being a conflict).
+the pre-check, there is a preliminary conclusion about there either being or
+not being a conflict -- but the conclusion only holds if there are no
+subsequent concurrent conflicts).  If a conclusively committed conflict tuple
+is detected during the first step, the executor goes to lock and update the row
+(for ON CONFLICT UPDATE -- otherwise, for ON CONFLICT IGNORE, we're done).  The
+TID to lock (and potentially UPDATE) can only be determined during the first
+step.  If locking the row finds a concurrent conflict (which may be from a
+concurrent UPDATE that hasn't even physically inspected the arbiter index yet)
+then we restart the loop from the very beginning.  We restart from scratch
+because all bets are off;  it's possible that the process will find no conflict
+the second time around, and will successfully insert, or will UPDATE another
+tuple that is not even part of the same UPDATE chain as first time around.
 
 The second step (skipped when a conflict is found) is to insert a heap tuple
 and related index tuples opportunistically.  This uses the same mechanism as
@@ -273,3 +300,31 @@ could be involved in "unprincipled deadlocks":  deadlocks where there is no
 user-visible mutual dependency, and yet an implementation related mutual
 dependency is unexpectedly introduced.  The user might be left with no
 reasonable way of avoiding these deadlocks, which would not be okay.
+
+Speculative insertion and EvalPlanQual()
+----------------------------------------
+
+Updating the tuple involves locking it first (to establish a definitive tuple
+to consider evaluating the additional UPDATE qual against).  The EvalPlanQual()
+mechanism (or, rather, some associated infrastructure) is reused for the
+benefit of auxiliary UPDATE expression evaluation.
+
+Locking first deviates from how conventional UPDATEs work, but allows the
+implementation to consider the possibility of conflicts first, and then, having
+reached a definitive conclusion, separately evaluate.
+
+ExecLockUpdateTuple() is somewhat similar to EvalPlanQual(), except it locks
+the TID reported as conflicting, and upon successfully locking, installs that
+into the UPDATE's EPQ slot.  There is no UPDATE chain to walk -- rather, new
+tuples to check the qual against come from continuous attempts at locking a
+tuple conclusively (avoiding conflicts).  The qual (if any) is then evaluated.
+Note that at READ COMMITTED, it's possible that *no* version of the tuple is
+visible, and yet it may still be updated.  Similarly,  since we do not walk the
+UPDATE chain, concurrent READ COMMITTED INSERT ... ON CONFLICT UPDATE sessions
+always attempt to lock the conclusively visible tuple, without regard to any
+other tuple version (repeatable read isolation level and up must consider MVCC
+visibility, though).  A further implication of this  is that the
+MVCC-snapshot-visible row version is denied the opportunity to prevent the
+UPDATE from taking place, should it not pass our qual (while a later version
+does pass it).  This is fundamentally similar to updating a tuple when no
+version is visible, though.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 43aa1a2..9ef4179 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2121,7 +2121,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * the latest version of the row was deleted, so we need do
 			 * nothing.  (Should be safe to examine xmin without getting
 			 * buffer's content lock, since xmin never changes in an existing
-			 * non-promise tuple.)
+			 * non-promise tuple, and there is no reason to lock a promise
+			 * tuple until it is clear that it has been fulfilled.)
 			 */
 			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
 									 priorXmax))
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d94fe58..6e29cf6 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -181,6 +181,9 @@ static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 						bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 					  bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalExcluded(ExcludedExprState *excludedExpr,
+				 ExprContext *econtext, bool *isNull,
+				 ExprDoneCond *isDone);
 
 
 /* ----------------------------------------------------------------
@@ -4333,6 +4336,33 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 	return 0;					/* keep compiler quiet */
 }
 
+/* ----------------------------------------------------------------
+ * ExecEvalExcluded
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalExcluded(ExcludedExprState *excludedExpr, ExprContext *econtext,
+				 bool *isNull, ExprDoneCond *isDone)
+{
+	/*
+	 * ExcludedExpr is essentially an expression that adapts its single Var
+	 * argument to refer to the expression context inner slot's tuple, which is
+	 * reserved for the purpose of referencing EXCLUDED.* tuples within ON
+	 * CONFLICT UPDATE auxiliary queries' EPQ expression context (ON CONFLICT
+	 * UPDATE makes special use of the EvalPlanQual() mechanism to update).
+	 *
+	 * nodeModifyTable.c assigns its own table slot in the auxiliary queries'
+	 * EPQ expression state (originating in the parent INSERT node) on the
+	 * assumption that it may only be used by ExcludedExpr, and on the
+	 * assumption that the inner slot is not otherwise useful.  This occurs in
+	 * advance of the expression evaluation for UPDATE (which calls here are
+	 * part of) once per slot proposed for insertion, and works because of
+	 * restrictions on the structure of ON CONFLICT UPDATE auxiliary queries.
+	 *
+	 * Just evaluate nested Var.
+	 */
+	return ExecEvalScalarVar(excludedExpr->arg, econtext, isNull, isDone);
+}
 
 /*
  * ExecEvalExprSwitchContext
@@ -5065,6 +5095,30 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			state = (ExprState *) makeNode(ExprState);
 			state->evalfunc = ExecEvalCurrentOfExpr;
 			break;
+		case T_ExcludedExpr:
+			{
+				ExcludedExpr *excludedexpr = (ExcludedExpr *) node;
+				ExcludedExprState *cstate = makeNode(ExcludedExprState);
+				Var		   *contained = (Var *) excludedexpr->arg;
+
+				/*
+				 * varno forced to INNER_VAR -- see remarks within
+				 * ExecLockUpdateTuple().
+				 *
+				 * We rely on the assumption that the only place that
+				 * ExcludedExpr may appear is where EXCLUDED Var references
+				 * originally appeared after parse analysis.  The rewriter
+				 * replaces these with ExcludedExpr that reference the
+				 * corresponding Var within the ON CONFLICT UPDATE target RTE.
+				 */
+				Assert(IsA(contained, Var));
+
+				contained->varno = INNER_VAR;
+				cstate->arg = ExecInitExpr((Expr *) contained, parent);
+				state = (ExprState *) cstate;
+				state->evalfunc = (ExprStateEvalFunc) ExecEvalExcluded;
+			}
+			break;
 		case T_TargetEntry:
 			{
 				TargetEntry *tle = (TargetEntry *) node;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 29b5b77..5a2ac21 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -53,6 +53,15 @@
 #include "utils/tqual.h"
 
 
+static bool ExecLockUpdateTuple(ResultRelInfo *resultRelInfo,
+					ItemPointer conflictTid,
+					TupleTableSlot *planSlot,
+					TupleTableSlot *insertSlot,
+					ModifyTableState *onConflict,
+					EState *estate,
+					bool canSetTag,
+					TupleTableSlot **returning);
+
 /*
  * Verify that the tuples to be produced by INSERT or UPDATE match the
  * target relation's rowtype
@@ -193,6 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
 static TupleTableSlot *
 ExecInsert(TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
+		   ModifyTableState *onConflict,
 		   Oid arbiterIndex,
 		   SpecCmd spec,
 		   EState *estate,
@@ -296,8 +306,9 @@ ExecInsert(TupleTableSlot *slot,
 		 * If we are performing speculative insertion, do a non-conclusive
 		 * check for conflicts.
 		 *
-		 * See the executor README for a full discussion of speculative
-		 * insertion.
+		 * Control returns here when there is 1) A row-locking conflict, or 2)
+		 * an insertion conflict.  See the executor README for a full
+		 * discussion of speculative insertion.
 		 */
 vlock:
 		if (spec != SPEC_NONE && resultRelInfo->ri_NumIndices > 0)
@@ -324,18 +335,35 @@ vlock:
 			if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
 										   arbiterIndex))
 			{
+				TupleTableSlot *returning = NULL;
+
 				/*
-				 * For the SPEC_IGNORE case, it's still often necessary to
-				 * verify that the tuple is visible to the executor's MVCC
-				 * snapshot.
+				 * Lock and consider updating in the SPEC_INSERT case.  For the
+				 * SPEC_IGNORE case, it's still often necessary to verify that
+				 * the tuple is visible to the executor's MVCC snapshot.
 				 */
-				if (spec == SPEC_IGNORE)
+				if (spec == SPEC_INSERT && !ExecLockUpdateTuple(resultRelInfo,
+																&conflictTid,
+																planSlot,
+																slot,
+																onConflict,
+																estate,
+																canSetTag,
+																&returning))
+					goto vlock;
+				else if (spec == SPEC_IGNORE)
 					ExecCheckHeapTupleVisible(estate, resultRelInfo, &conflictTid);
 
 				/*
-				 * The IGNORE path projects no tuples
+				 * RETURNING may have been processed already -- the target
+				 * ResultRelInfo might have made representation within
+				 * ExecUpdate() that this is required.  Inserted and updated
+				 * tuples are projected indifferently for ON CONFLICT UPDATE
+				 * with RETURNING.
+				 *
+				 * Since there was no row conflict, we're done.
 				 */
-				return NULL;
+				return returning;
 			}
 
 			/*
@@ -931,6 +959,240 @@ lreplace:;
 	return NULL;
 }
 
+/* ----------------------------------------------------------------
+ * Try to lock tuple for update as part of speculative insertion.  If
+ * a qual originating from ON CONFLICT UPDATE is satisfied, update
+ * (but still lock row, even though it may not satisfy estate's
+ * snapshot).
+ *
+ * Returns value indicating if we're done (with or without an
+ * update), or if the executor must start from scratch.
+ * ----------------------------------------------------------------
+ */
+static bool
+ExecLockUpdateTuple(ResultRelInfo *resultRelInfo,
+					ItemPointer conflictTid,
+					TupleTableSlot *planSlot,
+					TupleTableSlot *insertSlot,
+					ModifyTableState *onConflict,
+					EState *estate,
+					bool canSetTag,
+					TupleTableSlot **returning)
+{
+	Relation	relation = resultRelInfo->ri_RelationDesc;
+	HeapTupleData tuple;
+	HeapTuple	copyTuple = NULL;
+	HeapUpdateFailureData hufd;
+	HTSU_Result test;
+	Buffer		buffer;
+	TupleTableSlot *slot;
+	ExprContext *econtext;
+
+	/*
+	 * Lock tuple for update.
+	 *
+	 * Like EvalPlanQualFetch(), don't follow updates.  There is no actual
+	 * benefit to doing so, since as discussed below, a conflict invalidates
+	 * our previous conclusion that the tuple is the conclusively committed
+	 * conflicting tuple.
+	 */
+	tuple.t_self = *conflictTid;
+	test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
+						   LockTupleExclusive, LockWaitBlock, false, &buffer,
+						   &hufd);
+
+	if (test == HeapTupleMayBeUpdated)
+		copyTuple = heap_copytuple(&tuple);
+
+	switch (test)
+	{
+		case HeapTupleInvisible:
+
+			/*
+			 * This may occur when an instantaneously invisible tuple is
+			 * blamed as a conflict because multiple rows are inserted with
+			 * the same constrained values.
+			 *
+			 * We cannot proceed, because to do so would leave users open to
+			 * the risk that the same row will be updated a second time in the
+			 * same command;  allowing a second update affecting a single row
+			 * within the same command a second time would leave the update
+			 * order undefined.  It is the user's responsibility to resolve
+			 * these self-duplicates in advance of proposing for insertion a
+			 * set of tuples, but warn them.  These problems are why SQL-2003
+			 * similarly specifies that for SQL MERGE, an exception must be
+			 * raised in the event of an attempt to update the same row twice.
+			 *
+			 * XXX It might be preferable to do something similar when a row
+			 * is locked twice (and not updated twice) by the same speculative
+			 * insertion, as if to take each lock acquisition as a indication
+			 * of a discrete, unfulfilled intent to update (perhaps in some
+			 * later command of the same xact).  This does not seem feasible,
+			 * though.
+			 */
+			if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+				ereport(ERROR,
+						(errcode(ERRCODE_CARDINALITY_VIOLATION),
+						 errmsg("ON CONFLICT UPDATE command could not lock/update self-inserted tuple"),
+						 errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
+
+			/* This shouldn't happen */
+			elog(ERROR, "attempted to lock invisible tuple");
+			return false;		/* keep compiler quiet */
+		case HeapTupleSelfUpdated:
+
+			/*
+			 * XXX In practice this is dead code, since BEFORE triggers fire
+			 * prior to speculative insertion.  Since a dirty snapshot is used
+			 * to find possible conflict tuples, speculative insertion could
+			 * not have seen the old/MVCC-current row version at all (even if
+			 * it was only rendered old by this same command).
+			 */
+			elog(ERROR, "unexpected self-updated tuple");
+			return false;		/* keep compiler quiet */
+		case HeapTupleMayBeUpdated:
+
+			/*
+			 * Success -- we're done, as tuple is locked.  Verify that the
+			 * tuple is known to be visible to our snapshot under conventional
+			 * MVCC rules if the current isolation level mandates that.  In
+			 * READ COMMITTED mode, we can lock and update a tuple still in
+			 * progress according to our snapshot, but higher isolation levels
+			 * cannot avail of that, and must actively defend against doing
+			 * so. We might get a serialization failure within ExecUpdate()
+			 * anyway if this step was skipped, but this cannot be relied on,
+			 * for example because the auxiliary WHERE clause happened to not
+			 * be satisfied.
+			 */
+			ExecCheckHeapTupleVisible(estate, resultRelInfo, &tuple.t_data->t_ctid);
+
+			/*
+			 * This loosening of snapshot isolation for the benefit of READ
+			 * COMMITTED speculative insertions is used consistently:
+			 * speculative quals are only tested against already locked
+			 * tuples. It would be rather inconsistent to UPDATE when no tuple
+			 * version is MVCC-visible (which seems inevitable since we must
+			 * *do something* there, and "READ COMMITTED serialization
+			 * failures" are unappealing), while also avoiding updating here
+			 * entirely on the basis of a non-conclusive tuple version (the
+			 * version that happens to be visible to this command's MVCC
+			 * snapshot, or a subsequent non-conclusive version).
+			 *
+			 * In other words:  Only the final, conclusively locked tuple
+			 * (which must have the same value in the relevant constrained
+			 * attribute(s) as the value previously "value locked") matters.
+			 */
+
+			/* must provide our own instrumentation support */
+			if (onConflict->ps.instrument)
+				InstrStartNode(onConflict->ps.instrument);
+
+			/*
+			 * Conceptually, the parent ModifyTable is like a relation scan
+			 * node that uses a dirty snapshot, returning rows which the
+			 * auxiliary plan must operate on (if only to lock all such rows).
+			 * EvalPlanQual() is involved in the evaluation of their UPDATE,
+			 * regardless of whether or not the tuple is visible to the
+			 * command's MVCC Snapshot.
+			 */
+			EvalPlanQualBegin(&onConflict->mt_epqstate, onConflict->ps.state);
+
+			/*
+			 * Save EPQ expression context.  Auxiliary plan's scan node (which
+			 * would have been just initialized by EvalPlanQualBegin() on the
+			 * first time through here per query) cannot fail to provide one.
+			 */
+			econtext = onConflict->mt_epqstate.planstate->ps_ExprContext;
+
+			/*
+			 * UPDATE affects the same ResultRelation as INSERT in the context
+			 * of ON CONFLICT UPDATE, so parent's target rti is used
+			 */
+			EvalPlanQualSetTuple(&onConflict->mt_epqstate,
+								 resultRelInfo->ri_RangeTableIndex, copyTuple);
+
+			/*
+			 * Make available rejected tuple for referencing within UPDATE
+			 * expression (that is, make available a slot with the rejected
+			 * tuple, possibly already modified by BEFORE INSERT row
+			 * triggers).
+			 *
+			 * This is for the benefit of any ExcludedExpr that may appear
+			 * within UPDATE's targetlist or WHERE clause.  The EXCLUDED tuple
+			 * may be referenced as an ExcludedExpr, which exist purely for
+			 * our benefit.  The nested ExcludedExpr's Var will necessarily
+			 * have an INNER_VAR varno on the assumption that the inner slot
+			 * of the EPQ scan plan state's expression context will contain
+			 * the EXCLUDED heaptuple slot (that is, on the assumption that
+			 * during expression evaluation, the ecxt_innertuple will be
+			 * assigned the insertSlot by this codepath, in advance of
+			 * expression evaluation).
+			 *
+			 * See handling of ExcludedExpr within handleRewrite.c and
+			 * execQual.c.
+			 */
+			econtext->ecxt_innertuple = insertSlot;
+
+			slot = EvalPlanQualNext(&onConflict->mt_epqstate);
+
+			if (!TupIsNull(slot))
+				*returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, slot,
+										planSlot, &onConflict->mt_epqstate,
+										onConflict->ps.state, canSetTag);
+
+			ReleaseBuffer(buffer);
+
+			/*
+			 * As when executing an UPDATE's ModifyTable node in the
+			 * conventional manner, reset the per-output-tuple ExprContext
+			 */
+			ResetPerTupleExprContext(onConflict->ps.state);
+
+			/* must provide our own instrumentation support */
+			if (onConflict->ps.instrument)
+				InstrStopNode(onConflict->ps.instrument, *returning ? 1 : 0);
+
+			return true;
+		case HeapTupleUpdated:
+			if (IsolationUsesXactSnapshot())
+				ereport(ERROR,
+						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+						 errmsg("could not serialize access due to concurrent update")));
+
+			/*
+			 * Tell caller to try again from the very start.  We don't use the
+			 * usual EvalPlanQual() looping pattern here, fundamentally
+			 * because we don't have a useful qual to verify the next tuple
+			 * with.  Our "qual" is really any user-supplied qual AND the
+			 * unique constraint "col OP value" implied by a speculative
+			 * insertion conflict.  However, because of the selective
+			 * evaluation of the former "qual" (the interactions with MVCC and
+			 * row locking), this is an over-simplification.
+			 *
+			 * We might devise a means of verifying, by way of binary equality
+			 * in a similar manner to HOT codepaths, if any unique indexed
+			 * columns changed, but this would only serve to ameliorate the
+			 * fundamental problem.  It might well not be good enough, because
+			 * those columns could change too.  It seems unlikely that working
+			 * harder here is worthwhile.
+			 *
+			 * At this point, all bets are off -- it might actually turn out
+			 * to be okay to proceed with insertion instead of locking now
+			 * (the tuple we attempted to lock could have been deleted, for
+			 * example).  On the other hand, it might not be okay, but for an
+			 * entirely different reason, with an entirely separate TID to
+			 * blame and lock.  This TID may not even be part of the same
+			 * update chain.
+			 */
+			ReleaseBuffer(buffer);
+			return false;
+		default:
+			elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
+	}
+
+	return false;
+}
+
 
 /*
  * Process BEFORE EACH STATEMENT triggers
@@ -942,6 +1204,9 @@ fireBSTriggers(ModifyTableState *node)
 	{
 		case CMD_INSERT:
 			ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
+			if (node->spec == SPEC_INSERT)
+				ExecBSUpdateTriggers(node->onConflict->state,
+									 node->resultRelInfo);
 			break;
 		case CMD_UPDATE:
 			ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
@@ -964,6 +1229,9 @@ fireASTriggers(ModifyTableState *node)
 	switch (node->operation)
 	{
 		case CMD_INSERT:
+			if (node->spec == SPEC_INSERT)
+				ExecASUpdateTriggers(node->onConflict->state,
+									 node->resultRelInfo);
 			ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
 			break;
 		case CMD_UPDATE:
@@ -991,6 +1259,7 @@ ExecModifyTable(ModifyTableState *node)
 {
 	EState	   *estate = node->ps.state;
 	CmdType		operation = node->operation;
+	ModifyTableState *onConflict = (ModifyTableState *) node->onConflict;
 	SpecCmd		spec = node->spec;
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
@@ -1162,8 +1431,9 @@ ExecModifyTable(ModifyTableState *node)
 		switch (operation)
 		{
 			case CMD_INSERT:
-				slot = ExecInsert(slot, planSlot, node->arbiterIndex, spec,
-								  estate, node->canSetTag);
+				slot = ExecInsert(slot, planSlot, onConflict,
+								  node->arbiterIndex, spec, estate,
+								  node->canSetTag);
 				break;
 			case CMD_UPDATE:
 				slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
@@ -1211,6 +1481,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 {
 	ModifyTableState *mtstate;
 	CmdType		operation = node->operation;
+	Plan	   *onConflictPlan = node->onConflictPlan;
 	int			nplans = list_length(node->plans);
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
@@ -1279,6 +1550,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			resultRelInfo->ri_IndexRelationDescs == NULL)
 			ExecOpenIndices(resultRelInfo, mtstate->spec != SPEC_NONE);
 
+		/*
+		 * ON CONFLICT UPDATE variant must have unique index to arbitrate on
+		 * taking alternative path
+		 */
+		Assert(node->spec != SPEC_INSERT || node->arbiterIndex != InvalidOid);
+
 		mtstate->arbiterIndex = node->arbiterIndex;
 
 		/* Now init the plan for this result rel */
@@ -1451,6 +1728,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 				}
 				break;
 			case CMD_UPDATE:
+				junk_filter_needed = (node->spec == SPEC_NONE);
+				break;
 			case CMD_DELETE:
 				junk_filter_needed = true;
 				break;
@@ -1510,12 +1789,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		}
 		else
 		{
-			if (operation == CMD_INSERT)
+			if (operation == CMD_INSERT || operation == CMD_UPDATE)
 				ExecCheckPlanOutput(mtstate->resultRelInfo->ri_RelationDesc,
 									subplan->targetlist);
 		}
 	}
 
+	/* Initialize auxiliary ModifyTable node, for ON CONFLICT UPDATE */
+	if (onConflictPlan)
+	{
+		Assert(mtstate->spec == SPEC_INSERT);
+
+		/*
+		 * ExecModifyTable() is never called for auxiliary update
+		 * ModifyTableState.  Execution of the auxiliary plan is driven by its
+		 * parent in an ad-hoc fashion.
+		 */
+		mtstate->onConflict = ExecInitNode(onConflictPlan, estate, eflags);
+	}
+
 	/*
 	 * Set up a tuple table slot for use for trigger output tuples. In a plan
 	 * containing multiple ModifyTable nodes, all can share one such slot, so
@@ -1531,9 +1823,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 * ModifyTable node too, but there's no need.)  Note the use of lcons not
 	 * lappend: we need later-initialized ModifyTable nodes to be shut down
 	 * before earlier ones.  This ensures that we don't throw away RETURNING
-	 * rows that need to be seen by a later CTE subplan.
+	 * rows that need to be seen by a later CTE subplan.  Do not append an
+	 * auxiliary ON CONFLICT UPDATE node either, since it must have a parent
+	 * SPEC_INSERT ModifyTable node that it is auxiliary to that directly
+	 * drives execution of what is logically a single unified statement (*that*
+	 * plan will be appended here, though).  If it must project updated rows,
+	 * that will only ever be done through the parent.
 	 */
-	if (!mtstate->canSetTag)
+	if (!mtstate->canSetTag && mtstate->spec != SPEC_UPDATE)
 		estate->es_auxmodifytables = lcons(mtstate,
 										   estate->es_auxmodifytables);
 
@@ -1586,6 +1883,8 @@ ExecEndModifyTable(ModifyTableState *node)
 	 */
 	for (i = 0; i < node->mt_nplans; i++)
 		ExecEndNode(node->mt_plans[i]);
+
+	ExecEndNode(node->onConflict);
 }
 
 void
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 02b525e..9368ab2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -182,6 +182,7 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_NODE_FIELD(plans);
 	COPY_SCALAR_FIELD(spec);
 	COPY_SCALAR_FIELD(arbiterIndex);
+	COPY_NODE_FIELD(onConflictPlan);
 	COPY_NODE_FIELD(withCheckOptionLists);
 	COPY_NODE_FIELD(returningLists);
 	COPY_NODE_FIELD(fdwPrivLists);
@@ -1784,6 +1785,19 @@ _copyCurrentOfExpr(const CurrentOfExpr *from)
 }
 
 /*
+ * _copyExcludedExpr
+ */
+static ExcludedExpr *
+_copyExcludedExpr(const ExcludedExpr *from)
+{
+	ExcludedExpr *newnode = makeNode(ExcludedExpr);
+
+	COPY_NODE_FIELD(arg);
+
+	return newnode;
+}
+
+/*
  * _copyTargetEntry
  */
 static TargetEntry *
@@ -2150,6 +2164,7 @@ _copyConflictClause(const ConflictClause *from)
 
 	COPY_SCALAR_FIELD(specclause);
 	COPY_NODE_FIELD(infer);
+	COPY_NODE_FIELD(updatequery);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
@@ -2575,6 +2590,7 @@ _copyQuery(const Query *from)
 	COPY_SCALAR_FIELD(specClause);
 	COPY_NODE_FIELD(arbiterExpr);
 	COPY_NODE_FIELD(arbiterWhere);
+	COPY_NODE_FIELD(onConflict);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(groupClause);
 	COPY_NODE_FIELD(havingQual);
@@ -4293,6 +4309,9 @@ copyObject(const void *from)
 		case T_CurrentOfExpr:
 			retval = _copyCurrentOfExpr(from);
 			break;
+		case T_ExcludedExpr:
+			retval = _copyExcludedExpr(from);
+			break;
 		case T_TargetEntry:
 			retval = _copyTargetEntry(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e9dc0f9..30c164a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -683,6 +683,14 @@ _equalCurrentOfExpr(const CurrentOfExpr *a, const CurrentOfExpr *b)
 }
 
 static bool
+_equalExcludedExpr(const ExcludedExpr *a, const ExcludedExpr *b)
+{
+	COMPARE_NODE_FIELD(arg);
+
+	return true;
+}
+
+static bool
 _equalTargetEntry(const TargetEntry *a, const TargetEntry *b)
 {
 	COMPARE_NODE_FIELD(expr);
@@ -871,6 +879,7 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_SCALAR_FIELD(specClause);
 	COMPARE_NODE_FIELD(arbiterExpr);
 	COMPARE_NODE_FIELD(arbiterWhere);
+	COMPARE_NODE_FIELD(onConflict);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(groupClause);
 	COMPARE_NODE_FIELD(havingQual);
@@ -2439,6 +2448,7 @@ _equalConflictClause(const ConflictClause *a, const ConflictClause *b)
 {
 	COMPARE_SCALAR_FIELD(specclause);
 	COMPARE_NODE_FIELD(infer);
+	COMPARE_NODE_FIELD(updatequery);
 	COMPARE_LOCATION_FIELD(location);
 
 	return true;
@@ -2723,6 +2733,9 @@ equal(const void *a, const void *b)
 		case T_CurrentOfExpr:
 			retval = _equalCurrentOfExpr(a, b);
 			break;
+		case T_ExcludedExpr:
+			retval = _equalExcludedExpr(a, b);
+			break;
 		case T_TargetEntry:
 			retval = _equalTargetEntry(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 44efc95..c6e811a 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -235,6 +235,13 @@ exprType(const Node *expr)
 		case T_CurrentOfExpr:
 			type = BOOLOID;
 			break;
+		case T_ExcludedExpr:
+			{
+				const ExcludedExpr *n = (const ExcludedExpr *) expr;
+
+				type = exprType((Node *) n->arg);
+			}
+			break;
 		case T_PlaceHolderVar:
 			type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
@@ -469,6 +476,12 @@ exprTypmod(const Node *expr)
 			return ((const CoerceToDomainValue *) expr)->typeMod;
 		case T_SetToDefault:
 			return ((const SetToDefault *) expr)->typeMod;
+		case T_ExcludedExpr:
+			{
+				const ExcludedExpr *n = (const ExcludedExpr *) expr;
+
+				return ((const Var *) n->arg)->vartypmod;
+			}
 		case T_PlaceHolderVar:
 			return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 		default:
@@ -894,6 +907,9 @@ exprCollation(const Node *expr)
 		case T_CurrentOfExpr:
 			coll = InvalidOid;	/* result is always boolean */
 			break;
+		case T_ExcludedExpr:
+			coll = exprCollation((Node *) ((const ExcludedExpr *) expr)->arg);
+			break;
 		case T_PlaceHolderVar:
 			coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
@@ -1089,6 +1105,12 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_CurrentOfExpr:
 			Assert(!OidIsValid(collation));		/* result is always boolean */
 			break;
+		case T_ExcludedExpr:
+			{
+				Var *v = (Var *) ((ExcludedExpr *) expr)->arg;
+				v->varcollid = collation;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			break;
@@ -1497,6 +1519,10 @@ exprLocation(const Node *expr)
 			/* just use argument's location */
 			loc = exprLocation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_ExcludedExpr:
+			/* just use nested expr's location */
+			loc = exprLocation((Node *) ((const ExcludedExpr *) expr)->arg);
+			break;
 		default:
 			/* for any other node type it's just unknown... */
 			loc = -1;
@@ -1926,6 +1952,8 @@ expression_tree_walker(Node *node,
 			break;
 		case T_PlaceHolderVar:
 			return walker(((PlaceHolderVar *) node)->phexpr, context);
+		case T_ExcludedExpr:
+			return walker(((ExcludedExpr *) node)->arg, context);
 		case T_AppendRelInfo:
 			{
 				AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -1978,6 +2006,8 @@ query_tree_walker(Query *query,
 		return true;
 	if (walker(query->arbiterWhere, context))
 		return true;
+	if (walker(query->onConflict, context))
+		return true;
 	if (walker((Node *) query->returningList, context))
 		return true;
 	if (walker((Node *) query->jointree, context))
@@ -2640,6 +2670,16 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_ExcludedExpr:
+			{
+				ExcludedExpr *excludedexpr = (ExcludedExpr *) node;
+				ExcludedExpr *newnode;
+
+				FLATCOPY(newnode, excludedexpr, ExcludedExpr);
+				MUTATE(newnode->arg, newnode->arg, Node *);
+				return (Node *) newnode;
+			}
+			break;
 		case T_AppendRelInfo:
 			{
 				AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -2721,6 +2761,7 @@ query_tree_mutator(Query *query,
 	MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
 	MUTATE(query->arbiterExpr, query->arbiterExpr, List *);
 	MUTATE(query->arbiterWhere, query->arbiterWhere, Node *);
+	MUTATE(query->onConflict, query->onConflict, Node *);
 	MUTATE(query->returningList, query->returningList, List *);
 	MUTATE(query->jointree, query->jointree, FromExpr *);
 	MUTATE(query->setOperations, query->setOperations, Node *);
@@ -3247,6 +3288,8 @@ raw_expression_tree_walker(Node *node,
 
 				if (walker(stmt->infer, context))
 					return true;
+				if (walker(stmt->updatequery, context))
+					return true;
 			}
 		case T_CommonTableExpr:
 			return walker(((CommonTableExpr *) node)->ctequery, context);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 82d29fd..de08bb4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -334,6 +334,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_NODE_FIELD(plans);
 	WRITE_ENUM_FIELD(spec, SpecType);
 	WRITE_OID_FIELD(arbiterIndex);
+	WRITE_NODE_FIELD(onConflictPlan);
 	WRITE_NODE_FIELD(withCheckOptionLists);
 	WRITE_NODE_FIELD(returningLists);
 	WRITE_NODE_FIELD(fdwPrivLists);
@@ -1434,6 +1435,14 @@ _outCurrentOfExpr(StringInfo str, const CurrentOfExpr *node)
 }
 
 static void
+_outExcludedExpr(StringInfo str, const ExcludedExpr *node)
+{
+	WRITE_NODE_TYPE("EXCLUDED");
+
+	WRITE_NODE_FIELD(arg);
+}
+
+static void
 _outTargetEntry(StringInfo str, const TargetEntry *node)
 {
 	WRITE_NODE_TYPE("TARGETENTRY");
@@ -2319,6 +2328,7 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_ENUM_FIELD(specClause, SpecType);
 	WRITE_NODE_FIELD(arbiterExpr);
 	WRITE_NODE_FIELD(arbiterWhere);
+	WRITE_NODE_FIELD(onConflict);
 	WRITE_NODE_FIELD(returningList);
 	WRITE_NODE_FIELD(groupClause);
 	WRITE_NODE_FIELD(havingQual);
@@ -3111,6 +3121,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_CurrentOfExpr:
 				_outCurrentOfExpr(str, obj);
 				break;
+			case T_ExcludedExpr:
+				_outExcludedExpr(str, obj);
+				break;
 			case T_TargetEntry:
 				_outTargetEntry(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fc31573..319db56 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -217,6 +217,7 @@ _readQuery(void)
 	READ_ENUM_FIELD(specClause, SpecCmd);
 	READ_NODE_FIELD(arbiterExpr);
 	READ_NODE_FIELD(arbiterWhere);
+	READ_NODE_FIELD(onConflict);
 	READ_NODE_FIELD(returningList);
 	READ_NODE_FIELD(groupClause);
 	READ_NODE_FIELD(havingQual);
@@ -1133,6 +1134,19 @@ _readCurrentOfExpr(void)
 }
 
 /*
+ * _readExcludedExpr
+ */
+static ExcludedExpr *
+_readExcludedExpr(void)
+{
+	READ_LOCALS(ExcludedExpr);
+
+	READ_NODE_FIELD(arg);
+
+	READ_DONE();
+}
+
+/*
  * _readTargetEntry
  */
 static TargetEntry *
@@ -1397,6 +1411,8 @@ parseNodeString(void)
 		return_value = _readSetToDefault();
 	else if (MATCH("CURRENTOFEXPR", 13))
 		return_value = _readCurrentOfExpr();
+	else if (MATCH("EXCLUDED", 8))
+		return_value = _readExcludedExpr();
 	else if (MATCH("TARGETENTRY", 11))
 		return_value = _readTargetEntry();
 	else if (MATCH("RANGETBLREF", 11))
diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c
index 1258961..263ff5f 100644
--- a/src/backend/optimizer/path/tidpath.c
+++ b/src/backend/optimizer/path/tidpath.c
@@ -255,13 +255,17 @@ create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
 	/*
 	 * We don't support pushing join clauses into the quals of a tidscan, but
 	 * it could still have required parameterization due to LATERAL refs in
-	 * its tlist.
+	 * its tlist.  To be tidy, we disallow TID scans as the unexecuted scan
+	 * node of an ON CONFLICT UPDATE auxiliary query, even though there is no
+	 * reason to think that would be harmful;  the optimizer should always
+	 * prefer a SeqScan or Result node (actually, we assert that it's one of
+	 * those two in several places, so accepting TID scans would break those).
 	 */
 	required_outer = rel->lateral_relids;
 
 	tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
 
-	if (tidquals)
+	if (tidquals && root->parse->specClause != SPEC_UPDATE)
 		add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
 												   required_outer));
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 47fe29c..3973c37 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4815,7 +4815,8 @@ make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, SpecCmd spec, int epqParam)
+				 List *rowMarks, Plan *onConflictPlan, SpecCmd spec,
+				 int epqParam)
 {
 	ModifyTable *node = makeNode(ModifyTable);
 	Plan	   *plan = &node->plan;
@@ -4867,6 +4868,7 @@ make_modifytable(PlannerInfo *root,
 	node->plans = subplans;
 	node->spec = spec;
 	node->arbiterIndex = InvalidOid;
+	node->onConflictPlan = onConflictPlan;
 	node->withCheckOptionLists = withCheckOptionLists;
 	node->returningLists = returningLists;
 	node->rowMarks = rowMarks;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 6931e1e..55e871b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -612,8 +612,58 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 											 withCheckOptionLists,
 											 returningLists,
 											 rowMarks,
+											 NULL,
 											 parse->specClause,
 											 SS_assign_special_param(root));
+
+			if (parse->onConflict)
+			{
+				Query	   *conflictQry = (Query *) parse->onConflict;
+				ModifyTable *parent = (ModifyTable *) plan;
+
+				/*
+				 * An ON CONFLICT UPDATE query is a subquery of its parent
+				 * INSERT ModifyTable, but isn't formally a subplan -- it's an
+				 * "auxiliary" plan.
+				 *
+				 * During execution, the auxiliary plan state is used to
+				 * execute the UPDATE query in an ad-hoc manner, driven by the
+				 * parent.  The executor will only ever execute the auxiliary
+				 * plan through its parent.  onConflictPlan is "auxiliary" to
+				 * its parent in the sense that it's strictly encapsulated
+				 * from other code (for example, the executor does not
+				 * separately track it within estate as a plan that needs to
+				 * have execution finished when it appears within a
+				 * data-modifying CTE -- only the parent is specifically
+				 * tracked for that purpose).
+				 *
+				 * There is a fundamental nexus between parent and auxiliary
+				 * plans that makes a fully unified representation seem
+				 * compelling (a "CMD_UPSERT" ModifyTable plan and Query).
+				 * That would obviate the need to specially track auxiliary
+				 * state across all stages of execution just for this case;
+				 * the optimizer would then not have to generate a
+				 * fully-formed, independent UPDATE subquery plan (with a
+				 * scanstate only useful for EvalPlanQual() re-evaluation).
+				 * However, it's convenient to plan each ModifyTable
+				 * separately, as doing so maximizes code reuse.  The
+				 * alternative must be to introduce abstractions that (for
+				 * example) allow a single "CMD_UPSERT" ModifyTable to have
+				 * two distinct types of targetlist (that will need to be
+				 * processed differently during parsing and rewriting anyway).
+				 * The auxiliary UPDATE plan is a good trade-off between a
+				 * fully-fledged "CMD_UPSERT" representation, and the opposite
+				 * extreme of tracking two separate ModifyTable nodes, joined
+				 * by a contrived join type, with (for example) odd properties
+				 * around tuple visibility not well encapsulated.  A contrived
+				 * join based design would also necessitate teaching
+				 * ModifyTable nodes to support rescan just for the benefit of
+				 * ON CONFLICT UPDATE.
+				 */
+				parent->onConflictPlan = subquery_planner(glob, conflictQry,
+														  root, hasRecursion,
+														  0, NULL);
+			}
 		}
 	}
 
@@ -1073,6 +1123,7 @@ inheritance_planner(PlannerInfo *root)
 									 withCheckOptionLists,
 									 returningLists,
 									 rowMarks,
+									 NULL,
 									 parse->specClause,
 									 SS_assign_special_param(root));
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 5dd48f8..5e979a1 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -780,9 +780,35 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 				 * global list.
 				 */
 				splan->resultRelIndex = list_length(root->glob->resultRelations);
-				root->glob->resultRelations =
-					list_concat(root->glob->resultRelations,
-								list_copy(splan->resultRelations));
+
+				if (!splan->onConflictPlan)
+				{
+					/*
+					 * Only actually append result relation for non-auxiliary
+					 * ModifyTable plans
+					 */
+					root->glob->resultRelations =
+						list_concat(root->glob->resultRelations,
+									list_copy(splan->resultRelations));
+				}
+				else
+				{
+					/*
+					 * Adjust rtoffset passed to child, to compensate for
+					 * dummy RTE left by EXCLUDED.* alias in auxiliary plan.
+					 * Plan will have same resultRelation from flattened range
+					 * table as its parent.
+					 */
+					splan->onConflictPlan =
+						set_plan_refs(root, splan->onConflictPlan,
+									  rtoffset - PRS2_OLD_VARNO);
+
+					/*
+					 * Set up the visible plan targetlist as being the same as
+					 * the parent.  Again, this is for the use of EXPLAIN only.
+					 */
+					splan->onConflictPlan->targetlist = splan->plan.targetlist;
+				}
 			}
 			break;
 		case T_Append:
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index acfd0bc..40bcde9 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2349,6 +2349,12 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 													  valid_params,
 													  scan_params));
 				}
+
+				/*
+				 * No need to directly handle onConflictPlan here, since it
+				 * cannot have params (due to parse analysis enforced
+				 * restrictions prohibiting subqueries).
+				 */
 			}
 			break;
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a179b0c..762ee89 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -126,10 +126,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 	/*
 	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
-	 * Don't bother with indexes for an inheritance parent, either.
+	 * Don't bother with indexes for an inheritance parent or speculative
+	 * insertion UPDATE auxiliary queries, either.
 	 */
 	if (inhparent ||
-		(IgnoreSystemIndexes && IsSystemRelation(relation)))
+		(IgnoreSystemIndexes && IsSystemRelation(relation)) ||
+		root->parse->specClause == SPEC_UPDATE)
 		hasindex = false;
 	else
 		hasindex = relation->rd_rel->relhasindex;
@@ -437,7 +439,8 @@ infer_unique_index(PlannerInfo *root)
 	ListCell   *l;
 	List	   *indexList;
 
-	Assert(parse->specClause == SPEC_IGNORE);
+	Assert(parse->specClause == SPEC_INSERT ||
+		   parse->specClause == SPEC_IGNORE);
 
 	/*
 	 * We need not lock the relation since it was already locked, either by
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 8893e8b..2244f41 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -388,6 +388,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 	qry->specClause = SPEC_NONE;
+	qry->onConflict = NULL;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -427,6 +428,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
 	qry->commandType = CMD_INSERT;
 	pstate->p_is_insert = true;
+	pstate->p_is_speculative = spec != SPEC_NONE;
 
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
@@ -474,11 +476,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 		sub_namespace = NIL;
 	}
 
+	/* INSERT with an ON CONFLICT clause forces the "target" alias */
+	if (pstate->p_is_speculative)
+		stmt->relation->alias = makeAlias("target", NIL);
+
 	/*
 	 * Must get write lock on INSERT target table before scanning SELECT, else
 	 * we will grab the wrong kind of initial lock if the target table is also
 	 * mentioned in the SELECT part.  Note that the target table is not added
-	 * to the joinlist or namespace.
+	 * to the joinlist or namespace.  Note also that additional requiredPerms
+	 * may be added to the target RTE iff there is an auxiliary UPDATE.
 	 */
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
 										 false, false, ACL_INSERT);
@@ -763,10 +770,39 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 	qry->specClause = spec;
 	qry->hasSubLinks = pstate->p_hasSubLinks;
+	qry->onConflict = NULL;
 
 	if (stmt->confClause)
 	{
 		/*
+		 * ON CONFLICT UPDATE requires special parse analysis of auxiliary
+		 * update Query
+		 */
+		if (stmt->confClause->updatequery)
+		{
+			ParseState *sub_pstate = make_parsestate(pstate);
+			Query	   *uqry;
+
+			/*
+			 * The optimizer is not prepared to accept a subquery RTE for a
+			 * non-CMD_SELECT Query.  The CMD_UPDATE Query is tracked as
+			 * special auxiliary state, while there is more or less analogous
+			 * auxiliary state tracked in later stages of query execution.
+			 *
+			 * Parent canSetTag only ever actually consulted, so no need to
+			 * set that here.
+			 */
+			uqry = transformStmt(sub_pstate, stmt->confClause->updatequery);
+			Assert(uqry->commandType == CMD_UPDATE &&
+				   uqry->specClause == SPEC_UPDATE);
+
+			/* Save auxiliary query */
+			qry->onConflict = (Node *) uqry;
+
+			free_parsestate(sub_pstate);
+		}
+
+		/*
 		 * Perform parse analysis of arbiter columns/expressions.  These are
 		 * later used to infer a unique index which arbitrates whether or not
 		 * to take the alternative ON CONFLICT path (i.e.  whether or not to
@@ -1023,6 +1059,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 	qry->specClause = SPEC_NONE;
+	qry->onConflict = NULL;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -1920,10 +1957,14 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	Node	   *qual;
 	ListCell   *origTargetList;
 	ListCell   *tl;
+	bool InhOption;
 
 	qry->commandType = CMD_UPDATE;
 	pstate->p_is_update = true;
 
+	/* for auxiliary UPDATEs, visit parent INSERT to set target table */
+	pstate->p_is_speculative = (stmt->relation == NULL);
+
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
@@ -1932,8 +1973,22 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
 
+	if (!pstate->p_is_speculative)
+	{
+		InhOption = interpretInhOption(stmt->relation->inhOpt);
+
+		qry->specClause = SPEC_NONE;
+	}
+	else
+	{
+		/* auxiliary UPDATE does not accept ONLY */
+		InhOption = false;
+
+		qry->specClause = SPEC_UPDATE;
+	}
+
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
-								  interpretInhOption(stmt->relation->inhOpt),
+										 InhOption,
 										 true,
 										 ACL_UPDATE);
 
@@ -1964,6 +2019,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+	qry->onConflict = NULL;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5079562..adc3a43 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -520,6 +520,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>	cte_list
 
 %type <list>	within_group_clause
+%type <node>	UpdateInsertStmt
 %type <node>	filter_clause
 %type <list>	window_clause window_definition_list opt_partition_clause
 %type <windef>	window_definition over_clause window_specification
@@ -9406,11 +9407,21 @@ insert_column_item:
 		;
 
 opt_on_conflict:
+			ON CONFLICT opt_conf_expr UpdateInsertStmt
+				{
+					$$ = makeNode(ConflictClause);
+					$$->specclause = SPEC_INSERT;
+					$$->infer = $3;
+					$$->updatequery = $4;
+					$$->location = @1;
+				}
+			|
 			ON CONFLICT opt_conf_expr IGNORE_P
 				{
 					$$ = makeNode(ConflictClause);
 					$$->specclause = SPEC_IGNORE;
 					$$->infer = $3;
+					$$->updatequery = NULL;
 					$$->location = @1;
 				}
 			| /*EMPTY*/
@@ -9532,6 +9543,22 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
 				}
 		;
 
+UpdateInsertStmt: UPDATE
+			SET set_clause_list
+			where_clause
+				{
+					UpdateStmt *n = makeNode(UpdateStmt);
+					/* NULL relation conveys auxiliary */
+					n->relation = NULL;
+					n->targetList = $3;
+					n->fromClause = NULL;
+					n->whereClause = $4;
+					n->returningList = NULL;
+					n->withClause = NULL;
+					$$ = (Node *)n;
+				}
+		;
+
 set_clause_list:
 			set_clause							{ $$ = $1; }
 			| set_clause_list ',' set_clause	{ $$ = list_concat($1,$3); }
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 029288b..0dca575 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -148,7 +148,9 @@ transformFromClause(ParseState *pstate, List *frmList)
  *	  We also open the target relation and acquire a write lock on it.
  *	  This must be done before processing the FROM list, in case the target
  *	  is also mentioned as a source relation --- we want to be sure to grab
- *	  the write lock before any read lock.
+ *	  the write lock before any read lock.  Note that when called during
+ *	  the parse analysis of an auxiliary UPDATE query, relation may be
+ *	  NULL, and the details are acquired from the parent.
  *
  *	  If alsoSource is true, add the target to the query's joinlist and
  *	  namespace.  For INSERT, we don't want the target to be joined to;
@@ -175,19 +177,79 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 
 	/*
 	 * Open target rel and grab suitable lock (which we will hold till end of
-	 * transaction).
+	 * transaction), iff this is not an auxiliary ON CONFLICT UPDATE.
 	 *
 	 * free_parsestate() will eventually do the corresponding heap_close(),
-	 * but *not* release the lock.
+	 * but *not* release the lock (again, iff this is not an auxiliary ON
+	 * CONFLICT UPDATE).
 	 */
-	pstate->p_target_relation = parserOpenTable(pstate, relation,
-												RowExclusiveLock);
+	if (!pstate->p_is_speculative || pstate->p_is_insert)
+	{
+		pstate->p_target_relation = parserOpenTable(pstate, relation,
+													RowExclusiveLock);
+
+		/*
+		 * Now build an RTE.
+		 */
+		rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
+											relation->alias, inh, false);
+
+		/*
+		 * Override addRangeTableEntry's default ACL_SELECT permissions check,
+		 * and instead mark target table as requiring exactly the specified
+		 * permissions.
+		 *
+		 * If we find an explicit reference to the rel later during parse
+		 * analysis, we will add the ACL_SELECT bit back again; see
+		 * markVarForSelectPriv and its callers.
+		 */
+		rte->requiredPerms = requiredPerms;
+	}
+	else
+	{
+		RangeTblEntry *exclRte;
+
+		/* auxilary UPDATE (of ON CONFLICT UPDATE) */
+		Assert(pstate->p_is_update);
+		/* target shared with parent */
+		pstate->p_target_relation =
+			pstate->parentParseState->p_target_relation;
+		rte = pstate->parentParseState->p_target_rangetblentry;
+
+		/*
+		 * When called for auxiliary UPDATE, same target RTE is processed here
+		 * for a second time.  Just append requiredPerms.  There is no need to
+		 * override addRangeTableEntry's default ACL_SELECT permissions check
+		 * now.
+		 */
+		rte->requiredPerms |= requiredPerms;
+
+		/*
+		 * Build EXCLUDED alias for target relation.  This can be used to
+		 * reference the tuple originally proposed for insertion from within
+		 * the ON CONFLICT UPDATE auxiliary query.	This is not visible in the
+		 * parent INSERT.
+		 *
+		 * NOTE: 'EXCLUDED' will always have a varno equal to 1 (at least
+		 * until rewriting, where the RTE is effectively discarded -- its Vars
+		 * are replaced with a special-purpose primnode, ExcludedExpr).
+		 */
+		exclRte = addRangeTableEntryForRelation(pstate,
+												pstate->p_target_relation,
+												makeAlias("excluded", NIL),
+												false, false);
+
+		/*
+		 * Add EXCLUDED RTE to namespace.  It does not matter that the RTE is
+		 * not added to the Query joinlist, since its Vars are merely
+		 * placeholders for ExcludedExpr.
+		 */
+		addRTEtoQuery(pstate, exclRte, false, true, true);
+
+		/* Append parent/our target to Query rtable (should be last) */
+		pstate->p_rtable = lappend(pstate->p_rtable, rte);
+	}
 
-	/*
-	 * Now build an RTE.
-	 */
-	rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
-										relation->alias, inh, false);
 	pstate->p_target_rangetblentry = rte;
 
 	/* assume new rte is at end */
@@ -195,17 +257,6 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 
 	/*
-	 * Override addRangeTableEntry's default ACL_SELECT permissions check, and
-	 * instead mark target table as requiring exactly the specified
-	 * permissions.
-	 *
-	 * If we find an explicit reference to the rel later during parse
-	 * analysis, we will add the ACL_SELECT bit back again; see
-	 * markVarForSelectPriv and its callers.
-	 */
-	rte->requiredPerms = requiredPerms;
-
-	/*
 	 * If UPDATE/DELETE, add table to joinlist and namespace.
 	 *
 	 * Note: some callers know that they can find the new ParseNamespaceItem
@@ -2290,6 +2341,16 @@ transformConflictClause(ParseState *pstate, ConflictClause *confClause,
 {
 	InferClause *infer = confClause->infer;
 
+	if (confClause->specclause == SPEC_INSERT && !infer)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("ON CONFLICT with UPDATE must contain columns or expressions to infer a unique index from"),
+				 parser_errposition(pstate,
+									exprLocation((Node *) confClause))));
+
+	Assert(confClause->specclause != SPEC_INSERT ||
+		   confClause->updatequery != NULL);
+
 	/* This obviates the need for historic snapshot support */
 	if (IsCatalogRelation(pstate->p_target_relation))
 		elog(ERROR, "ON CONFLICT not supported with catalog relations");
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f759606..418ecea 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1651,7 +1651,8 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 	/*
 	 * Check to see if the sublink is in an invalid place within the query. We
 	 * allow sublinks everywhere in SELECT/INSERT/UPDATE/DELETE, but generally
-	 * not in utility statements.
+	 * not in utility statements.  They're also disallowed within auxiliary ON
+	 * CONFLICT UPDATE commands, which we check for here.
 	 */
 	err = NULL;
 	switch (pstate->p_expr_kind)
@@ -1718,6 +1719,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 			 * which is sane anyway.
 			 */
 	}
+
+	if (pstate->p_is_speculative && pstate->p_is_update)
+		 err = _("cannot use subquery in ON CONFLICT UPDATE");
 	if (err)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 4130cbf..9a94fa4 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -84,7 +84,13 @@ free_parsestate(ParseState *pstate)
 				 errmsg("target lists can have at most %d entries",
 						MaxTupleAttributeNumber)));
 
-	if (pstate->p_target_relation != NULL)
+	/*
+	 * Don't close target relation for auxiliary ON CONFLICT UPDATE, since it
+	 * is managed by parent INSERT directly
+	 */
+	if (pstate->p_target_relation != NULL &&
+		(!pstate->p_is_speculative ||
+		 pstate->p_is_insert))
 		heap_close(pstate->p_target_relation, NoLock);
 
 	pfree(pstate);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 86f1fc9..8ef80d6 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -43,6 +43,12 @@ typedef struct acquireLocksOnSubLinks_context
 	bool		for_execute;	/* AcquireRewriteLocks' forExecute param */
 } acquireLocksOnSubLinks_context;
 
+typedef struct excluded_replace_context
+{
+	int			varno;			/* varno of EXLCUDED.* Vars */
+	int			rvarno;			/* replace varno */
+}	excluded_replace_context;
+
 static bool acquireLocksOnSubLinks(Node *node,
 					   acquireLocksOnSubLinks_context *context);
 static Query *rewriteRuleAction(Query *parsetree,
@@ -71,6 +77,10 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
 			 bool forUpdatePushedDown);
 static bool view_has_instead_trigger(Relation view, CmdType event);
 static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
+static Node *excluded_replace_vars(Node *expr,
+					  excluded_replace_context * context);
+static Node *excluded_replace_vars_callback(Var *var,
+							   replace_rte_variables_context *context);
 
 
 /*
@@ -3100,6 +3110,50 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 				/* Process just the main targetlist */
 				rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
 			}
+
+			if (parsetree->specClause == SPEC_INSERT)
+			{
+				Query	   *qry;
+				excluded_replace_context context;
+
+				/*
+				 * While user-defined rules will never be applied in the
+				 * auxiliary update query, normalization of tlist is still
+				 * required
+				 */
+				qry = (Query *) parsetree->onConflict;
+				rewriteTargetListIU(qry, rt_entry_relation, NULL);
+
+				/*
+				 * Replace OLD Vars (associated with the EXCLUDED.* alias)
+				 * with first (and only) "real" relation RTE in rtable.  This
+				 * allows the implementation to treat EXCLUDED.* as an alias
+				 * for the target relation, which is useful during parse
+				 * analysis, while ultimately having those references
+				 * rewritten as special ExcludedExpr references to the
+				 * corresponding Var in the target RTE.
+				 *
+				 * This is necessary because while we want a join-like syntax
+				 * for aesthetic reasons, the resemblance is superficial.  In
+				 * fact, execution of the ModifyTable node (and its direct
+				 * child auxiliary query) manages tupleslot state directly,
+				 * and is directly tasked with making available the
+				 * appropriate tupleslot to the expression context.
+				 *
+				 * This is a kludge, but appears necessary, since the slot
+				 * made available for referencing via ExcludedExpr is in fact
+				 * the slot just excluded from insertion by speculative
+				 * insertion (with the effects of BEFORE ROW INSERT triggers
+				 * carried). An ad-hoc method for making the excluded tuple
+				 * available within the auxiliary expression context is
+				 * appropriate.
+				 */
+				context.varno = PRS2_OLD_VARNO;
+				context.rvarno = PRS2_OLD_VARNO + 1;
+
+				parsetree->onConflict =
+					excluded_replace_vars(parsetree->onConflict, &context);
+			}
 		}
 		else if (event == CMD_UPDATE)
 		{
@@ -3421,3 +3475,52 @@ QueryRewrite(Query *parsetree)
 
 	return results;
 }
+
+/*
+ * Apply pullup variable replacement throughout an expression tree
+ *
+ * Returns modified tree, with user-specified rvarno replaced with varno.
+ */
+static Node *
+excluded_replace_vars(Node *expr, excluded_replace_context *context)
+{
+	/*
+	 * Don't recurse into subqueries;  they're forbidden in auxiliary ON
+	 * CONFLICT query
+	 */
+	return replace_rte_variables(expr,
+								 context->varno, 0,
+								 excluded_replace_vars_callback,
+								 (void *) context,
+								 NULL);
+}
+
+static Node *
+excluded_replace_vars_callback(Var *var,
+							   replace_rte_variables_context *context)
+{
+	excluded_replace_context *rcon = (excluded_replace_context *) context->callback_arg;
+	ExcludedExpr *n = makeNode(ExcludedExpr);
+
+	/* Replace with an enclosing ExcludedExpr */
+	var->varno = rcon->rvarno;
+	n->arg = (Node *) var;
+
+	/*
+	 * Would have to adjust varlevelsup if referenced item is from higher
+	 * query (should not happen)
+	 */
+	Assert(var->varlevelsup == 0);
+
+	if (var->varattno < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("cannot reference system column using EXCLUDED.* alias")));
+
+	if (var->varattno == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("cannot reference whole-row using EXCLUDED.* alias")));
+
+	return (Node *) n;
+}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 9c14e8a..41c4191 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -189,7 +189,8 @@ ProcessQuery(PlannedStmt *plan,
 	 */
 	if (completionTag)
 	{
-		Oid			lastOid;
+		Oid					lastOid;
+		ModifyTableState   *pstate;
 
 		switch (queryDesc->operation)
 		{
@@ -198,12 +199,16 @@ ProcessQuery(PlannedStmt *plan,
 						 "SELECT %u", queryDesc->estate->es_processed);
 				break;
 			case CMD_INSERT:
+				pstate = (((ModifyTableState *) queryDesc->planstate));
+				Assert(IsA(pstate, ModifyTableState));
+
 				if (queryDesc->estate->es_processed == 1)
 					lastOid = queryDesc->estate->es_lastoid;
 				else
 					lastOid = InvalidOid;
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-				   "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
+				   "%s %u %u", pstate->spec == SPEC_INSERT? "UPSERT":"INSERT",
+				   lastOid, queryDesc->estate->es_processed);
 				break;
 			case CMD_UPDATE:
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
@@ -1356,7 +1361,10 @@ PortalRunMulti(Portal portal, bool isTopLevel,
 	 * 0" here because technically there is no query of the matching tag type,
 	 * and printing a non-zero count for a different query type seems wrong,
 	 * e.g.  an INSERT that does an UPDATE instead should not print "0 1" if
-	 * one row was updated.  See QueryRewrite(), step 3, for details.
+	 * one row was updated (unless the ON CONFLICT UPDATE, or "UPSERT" variant
+	 * of INSERT was used to update the row, where it's logically a direct
+	 * effect of the top level command).  See QueryRewrite(), step 3, for
+	 * details.
 	 */
 	if (completionTag && completionTag[0] == '\0')
 	{
@@ -1366,6 +1374,8 @@ PortalRunMulti(Portal portal, bool isTopLevel,
 			sprintf(completionTag, "SELECT 0 0");
 		else if (strcmp(completionTag, "INSERT") == 0)
 			strcpy(completionTag, "INSERT 0 0");
+		else if (strcmp(completionTag, "UPSERT") == 0)
+			strcpy(completionTag, "UPSERT 0 0");
 		else if (strcmp(completionTag, "UPDATE") == 0)
 			strcpy(completionTag, "UPDATE 0");
 		else if (strcmp(completionTag, "DELETE") == 0)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 28e1acf..c6118de 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5674,6 +5674,24 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
 		return NULL;
 	}
+	else if (var->varno == INNER_VAR)
+	{
+		/* Assume an EXCLUDED variable */
+		rte = rt_fetch(PRS2_OLD_VARNO, dpns->rtable);
+
+		/*
+		 * Sanity check:  EXCLUDED.* Vars should only appear in auxiliary ON
+		 * CONFLICT UPDATE queries.  Assert that rte and planstate are
+		 * consistent with that.
+		 */
+		Assert(rte->rtekind == RTE_RELATION);
+		Assert(IsA(dpns->planstate, SeqScanState) ||
+			   IsA(dpns->planstate, ResultState));
+
+		refname = "excluded";
+		colinfo = deparse_columns_fetch(PRS2_OLD_VARNO, dpns);
+		attnum = var->varattno;
+	}
 	else
 	{
 		elog(ERROR, "bogus varno: %d", var->varno);
@@ -6414,6 +6432,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 		case T_CoerceToDomainValue:
 		case T_SetToDefault:
 		case T_CurrentOfExpr:
+		case T_ExcludedExpr:
 			/* single words: always simple */
 			return true;
 
@@ -7639,6 +7658,26 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_ExcludedExpr:
+			{
+				ExcludedExpr *excludedexpr = (ExcludedExpr *) node;
+				Var		   *variable = (Var *) excludedexpr->arg;
+				bool		save_varprefix;
+
+				/*
+				 * Force parentheses because our caller probably assumed our
+				 * Var is a simple expression.
+				 */
+				appendStringInfoChar(buf, '(');
+				save_varprefix = context->varprefix;
+				/* Ensure EXCLUDED.* prefix is always visible */
+				context->varprefix = true;
+				get_rule_expr((Node *) variable, context, true);
+				context->varprefix = save_varprefix;
+				appendStringInfoChar(buf, ')');
+			}
+			break;
+
 		case T_List:
 			{
 				char	   *sep;
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 15488ff..06f7a96 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -894,9 +894,12 @@ PrintQueryResults(PGresult *results)
 				success = StoreQueryTuple(results);
 			else
 				success = PrintQueryTuples(results);
-			/* if it's INSERT/UPDATE/DELETE RETURNING, also print status */
+			/*
+			 * if it's INSERT/UPSERT/UPDATE/DELETE RETURNING, also print status
+			 */
 			cmdstatus = PQcmdStatus(results);
 			if (strncmp(cmdstatus, "INSERT", 6) == 0 ||
+				strncmp(cmdstatus, "UPSERT", 6) == 0 ||
 				strncmp(cmdstatus, "UPDATE", 6) == 0 ||
 				strncmp(cmdstatus, "DELETE", 6) == 0)
 				PrintQueryStatus(results);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e61a43a..37be480 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -977,6 +977,16 @@ typedef struct DomainConstraintState
 	ExprState  *check_expr;		/* for CHECK, a boolean expression */
 } DomainConstraintState;
 
+/* ----------------
+ *		ExcludedExprState node
+ * ----------------
+ */
+typedef struct ExcludedExprState
+{
+	ExprState	xprstate;
+	ExprState  *arg;			/* the argument */
+} ExcludedExprState;
+
 
 /* ----------------------------------------------------------------
  *				 Executor State Trees
@@ -1100,6 +1110,7 @@ typedef struct ModifyTableState
 	List	  **mt_arowmarks;	/* per-subplan ExecAuxRowMark lists */
 	SpecCmd		spec;			/* reason for speculative insertion */
 	Oid			arbiterIndex;	/* unique index to arbitrate taking alt path */
+	PlanState  *onConflict; /* associated OnConflict state */
 	EPQState	mt_epqstate;	/* for evaluating EvalPlanQual rechecks */
 	bool		fireBSTriggers; /* do we need to fire stmt triggers? */
 } ModifyTableState;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5b348fa..c3dd4ee 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -168,6 +168,7 @@ typedef enum NodeTag
 	T_CoerceToDomainValue,
 	T_SetToDefault,
 	T_CurrentOfExpr,
+	T_ExcludedExpr,
 	T_TargetEntry,
 	T_RangeTblRef,
 	T_JoinExpr,
@@ -207,6 +208,7 @@ typedef enum NodeTag
 	T_NullTestState,
 	T_CoerceToDomainState,
 	T_DomainConstraintState,
+	T_ExcludedExprState,
 
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
@@ -636,7 +638,9 @@ typedef enum JoinType
 typedef enum
 {
 	SPEC_NONE,		/* Not involved in speculative insertion */
-	SPEC_IGNORE		/* INSERT of "ON CONFLICT IGNORE" */
+	SPEC_IGNORE,	/* INSERT of "ON CONFLICT IGNORE" */
+	SPEC_INSERT,	/* INSERT of "ON CONFLICT UPDATE" */
+	SPEC_UPDATE		/* UPDATE of "ON CONFLICT UPDATE" */
 } SpecCmd;
 
 #endif   /* NODES_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0e6fe26..fe4c69a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -135,6 +135,7 @@ typedef struct Query
 	SpecCmd		specClause;		/* speculative insertion clause */
 	List	   *arbiterExpr;	/* Unique index arbiter exprs */
 	Node	   *arbiterWhere;	/* Unique index arbiter WHERE clause */
+	Node	   *onConflict;		/* ON CONFLICT Query */
 
 	List	   *returningList;	/* return-values list (of TargetEntry) */
 
@@ -1045,6 +1046,7 @@ typedef struct ConflictClause
 	NodeTag			type;
 	SpecCmd			specclause;		/* Variant specified */
 	InferClause	   *infer;			/* Optional index inference clause */
+	Node		   *updatequery;	/* Update parse stmt */
 	int				location;		/* token location, or -1 if unknown */
 } ConflictClause;
 
@@ -1124,7 +1126,7 @@ typedef struct DeleteStmt
 typedef struct UpdateStmt
 {
 	NodeTag		type;
-	RangeVar   *relation;		/* relation to update */
+	RangeVar   *relation;		/* relation to update (NULL for speculative) */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	Node	   *whereClause;	/* qualifications */
 	List	   *fromClause;		/* optional from clause for more tables */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 66027a3..7c6901a 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -180,6 +180,7 @@ typedef struct ModifyTable
 	List	   *plans;			/* plan(s) producing source data */
 	SpecCmd		spec;			/* speculative insertion specification */
 	Oid			arbiterIndex;	/* Oid of ON CONFLICT arbiter index */
+	Plan	   *onConflictPlan;	/* Plan for ON CONFLICT UPDATE auxiliary query */
 	List	   *withCheckOptionLists;	/* per-target-table WCO lists */
 	List	   *returningLists; /* per-target-table RETURNING tlists */
 	List	   *fdwPrivLists;	/* per-target-table FDW private data lists */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4f1d234..0219e54 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1143,6 +1143,53 @@ typedef struct CurrentOfExpr
 	int			cursor_param;	/* refcursor parameter number, or 0 */
 } CurrentOfExpr;
 
+/*
+ * ExcludedExpr - an EXCLUDED.* expression
+ *
+ * During parse analysis of ON CONFLICT UPDATE auxiliary queries, a dummy
+ * EXCLUDED range table entry is generated, which is actually just an alias for
+ * the target relation.  This is useful during parse analysis, allowing the
+ * parser to produce simple error messages, for example.  There is the
+ * appearance of a join within the auxiliary ON CONFLICT UPDATE, superficially
+ * similar to a join in an UPDATE ... FROM;  this is a limited, ad-hoc join
+ * though, as the executor needs to tightly control the referenced tuple/slot
+ * through which update evaluation references excluded values originally
+ * proposed for insertion.  Note that EXCLUDED.* values carry forward the
+ * effects of BEFORE ROW INSERT triggers.
+ *
+ * To implement a limited "join" for ON CONFLICT UPDATE auxiliary queries,
+ * during the rewrite stage, Vars referencing the alias EXCLUDED.* RTE are
+ * swapped with ExcludedExprs, which also contain Vars;  their Vars are
+ * equivalent, but reference the target instead.  The ExcludedExpr Var actually
+ * evaluates against varno INNER_VAR during expression evaluation (and not a
+ * varno INDEX_VAR associated with an entry in the flattened range table
+ * representing the target, which is necessarily being scanned whenever an
+ * ExcludedExpr is evaluated) while still being logically associated with the
+ * target.  The Var is only rigged to reference the inner slot during
+ * ExcludedExpr initialization.  The executor closely controls the evaluation
+ * expression, installing the EXCLUDED slot actually excluded from insertion
+ * into the inner slot of the child/auxiliary evaluation context in an ad-hoc
+ * fashion, which, after ExcludedExpr initialization, is expected (i.e. it is
+ * expected during ExcludedExpr evaluation that the parent insert will make
+ * each excluded tuple available in the inner slot in turn).  ExcludedExpr are
+ * only ever evaluated during special speculative insertion related EPQ
+ * expression evaluation, purely for the benefit of auxiliary UPDATE
+ * expressions.
+ *
+ * Aside from representing a logical choke point for this special expression
+ * evaluation, having a dedicated primnode also prevents the optimizer from
+ * considering various optimization that might otherwise be attempted.
+ * Obviously there is no useful join optimization possible within the auxiliary
+ * query, and an ExcludedExpr based post-rewrite query tree representation is a
+ * convenient way of preventing that, as well as related inapplicable
+ * optimizations concerning the equivalence of Vars.
+ */
+typedef struct ExcludedExpr
+{
+	Expr		xpr;
+	Node	   *arg;			/* argument (Var) */
+} ExcludedExpr;
+
 /*--------------------
  * TargetEntry -
  *	   a target entry (used in query target lists)
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index c3a0634..81a9058 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -85,7 +85,8 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, SpecCmd spec, int epqParam);
+				 List *rowMarks, Plan *onConflictPlan, SpecCmd spec,
+				 int epqParam);
 extern bool is_projection_capable_plan(Plan *plan);
 
 /*
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 3103b71..2b5804e 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -153,6 +153,7 @@ struct ParseState
 	bool		p_hasModifyingCTE;
 	bool		p_is_insert;
 	bool		p_is_update;
+	bool		p_is_speculative;
 	bool		p_locked_from_parent;
 	Relation	p_target_relation;
 	RangeTblEntry *p_target_rangetblentry;
diff --git a/src/test/isolation/expected/insert-conflict-update-2.out b/src/test/isolation/expected/insert-conflict-update-2.out
new file mode 100644
index 0000000..6a5ddfe
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-update-2.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: insert1 insert2 c1 select2 c2
+step insert1: INSERT INTO upsert(key, payload) VALUES('FooFoo', 'insert1') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, payload) VALUES('FOOFOO', 'insert2') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert2'; <waiting ...>
+step c1: COMMIT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            payload        
+
+FOOFOO         insert1 updated by insert2
+step c2: COMMIT;
+
+starting permutation: insert1 insert2 a1 select2 c2
+step insert1: INSERT INTO upsert(key, payload) VALUES('FooFoo', 'insert1') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, payload) VALUES('FOOFOO', 'insert2') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert2'; <waiting ...>
+step a1: ABORT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            payload        
+
+FOOFOO         insert2        
+step c2: COMMIT;
diff --git a/src/test/isolation/expected/insert-conflict-update-3.out b/src/test/isolation/expected/insert-conflict-update-3.out
new file mode 100644
index 0000000..29dd8b0
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-update-3.out
@@ -0,0 +1,26 @@
+Parsed test spec with 2 sessions
+
+starting permutation: update2 insert1 c2 select1surprise c1
+step update2: UPDATE colors SET is_active = true WHERE key = 1;
+step insert1: 
+    WITH t AS (
+        INSERT INTO colors(key, color, is_active)
+        VALUES(1, 'Brown', true), (2, 'Gray', true)
+        ON CONFLICT (key) UPDATE
+        SET color = EXCLUDED.color
+        WHERE TARGET.is_active)
+    SELECT * FROM colors ORDER BY key; <waiting ...>
+step c2: COMMIT;
+step insert1: <... completed>
+key            color          is_active      
+
+1              Red            f              
+2              Green          f              
+3              Blue           f              
+step select1surprise: SELECT * FROM colors ORDER BY key;
+key            color          is_active      
+
+1              Brown          t              
+2              Green          f              
+3              Blue           f              
+step c1: COMMIT;
diff --git a/src/test/isolation/expected/insert-conflict-update.out b/src/test/isolation/expected/insert-conflict-update.out
new file mode 100644
index 0000000..6976124
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-update.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: insert1 insert2 c1 select2 c2
+step insert1: INSERT INTO upsert(key, val) VALUES(1, 'insert1') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, val) VALUES(1, 'insert2') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert2'; <waiting ...>
+step c1: COMMIT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            val            
+
+1              insert1 updated by insert2
+step c2: COMMIT;
+
+starting permutation: insert1 insert2 a1 select2 c2
+step insert1: INSERT INTO upsert(key, val) VALUES(1, 'insert1') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert1';
+step insert2: INSERT INTO upsert(key, val) VALUES(1, 'insert2') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert2'; <waiting ...>
+step a1: ABORT;
+step insert2: <... completed>
+step select2: SELECT * FROM upsert;
+key            val            
+
+1              insert2        
+step c2: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 59d14e9..50948a2 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -17,6 +17,9 @@ test: eval-plan-qual
 test: lock-update-delete
 test: lock-update-traversal
 test: insert-conflict-ignore
+test: insert-conflict-update
+test: insert-conflict-update-2
+test: insert-conflict-update-3
 test: delete-abort-savept
 test: delete-abort-savept-2
 test: aborted-keyrevoke
diff --git a/src/test/isolation/specs/insert-conflict-update-2.spec b/src/test/isolation/specs/insert-conflict-update-2.spec
new file mode 100644
index 0000000..3e6e944
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-update-2.spec
@@ -0,0 +1,41 @@
+# INSERT...ON CONFLICT UPDATE test
+#
+# This test shows a plausible scenario in which the user might wish to UPDATE a
+# value that is also constrained by the unique index that is the arbiter of
+# whether the alternative path should be taken.
+
+setup
+{
+  CREATE TABLE upsert (key text not null, payload text);
+  CREATE UNIQUE INDEX ON upsert(lower(key));
+}
+
+teardown
+{
+  DROP TABLE upsert;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert1" { INSERT INTO upsert(key, payload) VALUES('FooFoo', 'insert1') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert1'; }
+step "c1" { COMMIT; }
+step "a1" { ABORT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert2" { INSERT INTO upsert(key, payload) VALUES('FOOFOO', 'insert2') ON CONFLICT (lower(key)) UPDATE set key = EXCLUDED.key, payload = TARGET.payload || ' updated by insert2'; }
+step "select2" { SELECT * FROM upsert; }
+step "c2" { COMMIT; }
+step "a2" { ABORT; }
+
+# One session (session 2) block-waits on another (session 1) to determine if it
+# should proceed with an insert or update.  The user can still usefully UPDATE
+# a column constrained by a unique index, as the example illustrates.
+permutation "insert1" "insert2" "c1" "select2" "c2"
+permutation "insert1" "insert2" "a1" "select2" "c2"
diff --git a/src/test/isolation/specs/insert-conflict-update-3.spec b/src/test/isolation/specs/insert-conflict-update-3.spec
new file mode 100644
index 0000000..94ae3df
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-update-3.spec
@@ -0,0 +1,69 @@
+# INSERT...ON CONFLICT UPDATE test
+#
+# Other INSERT...ON CONFLICT UPDATE isolation tests illustrate the "MVCC
+# violation" added to facilitate the feature, whereby a
+# not-visible-to-our-snapshot tuple can be updated by our command all the same.
+# This is generally needed to provide a guarantee of a successful INSERT or
+# UPDATE in READ COMMITTED mode.  This MVCC violation is quite distinct from
+# the putative "MVCC violation" that has existed in PostgreSQL for many years,
+# the EvalPlanQual() mechanism, because that mechanism always starts from a
+# tuple that is visible to the command's MVCC snapshot.  This test illustrates
+# a slightly distinct user-visible consequence of the same MVCC violation
+# generally associated with INSERT...ON CONFLICT UPDATE.  The impact of the
+# MVCC violation goes a little beyond updating MVCC-invisible tuples.
+#
+# With INSERT...ON CONFLICT UPDATE, the UPDATE predicate is only evaluated
+# once, on this conclusively-locked tuple, and not any other version of the
+# same tuple.  It is therefore possible (in READ COMMITTED mode) that the
+# predicate "fail to be satisfied" according to the command's MVCC snapshot.
+# It might simply be that there is no row version visible, but it's also
+# possible that there is some row version visible, but only as a version that
+# doesn't satisfy the predicate.  If, however, the conclusively-locked version
+# satisfies the predicate, that's good enough, and the tuple is updated.  The
+# MVCC-snapshot-visible row version is denied the opportunity to prevent the
+# UPDATE from taking place, because we don't walk the UPDATE chain in the usual
+# way.
+
+setup
+{
+  CREATE TABLE colors (key int4 PRIMARY KEY, color text, is_active boolean);
+  INSERT INTO colors (key, color, is_active) VALUES(1, 'Red', false);
+  INSERT INTO colors (key, color, is_active) VALUES(2, 'Green', false);
+  INSERT INTO colors (key, color, is_active) VALUES(3, 'Blue', false);
+}
+
+teardown
+{
+  DROP TABLE colors;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert1" {
+    WITH t AS (
+        INSERT INTO colors(key, color, is_active)
+        VALUES(1, 'Brown', true), (2, 'Gray', true)
+        ON CONFLICT (key) UPDATE
+        SET color = EXCLUDED.color
+        WHERE TARGET.is_active)
+    SELECT * FROM colors ORDER BY key;}
+step "select1surprise" { SELECT * FROM colors ORDER BY key; }
+step "c1" { COMMIT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "update2" { UPDATE colors SET is_active = true WHERE key = 1; }
+step "c2" { COMMIT; }
+
+# Perhaps surprisingly, the session 1 MVCC-snapshot-visible tuple (the tuple
+# with the pre-populated color 'Red') is denied the opportunity to prevent the
+# UPDATE from taking place -- only the conclusively-locked tuple version
+# matters, and so the tuple with key value 1 was updated to 'Brown' (but not
+# tuple with key value 2, since nothing changed there):
+permutation "update2" "insert1" "c2" "select1surprise" "c1"
diff --git a/src/test/isolation/specs/insert-conflict-update.spec b/src/test/isolation/specs/insert-conflict-update.spec
new file mode 100644
index 0000000..6529a0c
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-update.spec
@@ -0,0 +1,40 @@
+# INSERT...ON CONFLICT UPDATE test
+#
+# This test tries to expose problems with the interaction between concurrent
+# sessions.
+
+setup
+{
+  CREATE TABLE upsert (key int primary key, val text);
+}
+
+teardown
+{
+  DROP TABLE upsert;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert1" { INSERT INTO upsert(key, val) VALUES(1, 'insert1') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert1'; }
+step "c1" { COMMIT; }
+step "a1" { ABORT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "insert2" { INSERT INTO upsert(key, val) VALUES(1, 'insert2') ON CONFLICT (key) UPDATE set val = TARGET.val || ' updated by insert2'; }
+step "select2" { SELECT * FROM upsert; }
+step "c2" { COMMIT; }
+step "a2" { ABORT; }
+
+# One session (session 2) block-waits on another (session 1) to determine if it
+# should proceed with an insert or update.  Notably, this entails updating a
+# tuple while there is no version of that tuple visible to the updating
+# session's snapshot.  This is permitted only in READ COMMITTED mode.
+permutation "insert1" "insert2" "c1" "select2" "c2"
+permutation "insert1" "insert2" "a1" "select2" "c2"
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
index a34d857..c192bd3 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -3,14 +3,196 @@
 --
 create table insertconflicttest(key int4, fruit text);
 --
+-- Single key tests
+--
+create unique index key_index on insertconflicttest(key);
+--
+-- Explain tests
+--
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit;
+                     QUERY PLAN                     
+----------------------------------------------------
+ Insert on insertconflicttest target
+   ->  Result
+   ->  Conflict Update on insertconflicttest target
+(3 rows)
+
+-- Should display qual actually attributable to internal sequential scan:
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Cawesh';
+                     QUERY PLAN                     
+----------------------------------------------------
+ Insert on insertconflicttest target
+   ->  Result
+   ->  Conflict Update on insertconflicttest target
+         Filter: (fruit <> 'Cawesh'::text)
+(4 rows)
+
+-- With EXCLUDED.* expression in scan node:
+explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) update set fruit = excluded.fruit where excluded.fruit != 'Elderberry';
+                        QUERY PLAN                        
+----------------------------------------------------------
+ Insert on insertconflicttest target
+   ->  Result
+   ->  Conflict Update on insertconflicttest target
+         Filter: ((excluded.fruit) <> 'Elderberry'::text)
+(4 rows)
+
+-- Does the same, but JSON format shows "Arbiter Index":
+explain (costs off, format json) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Lime' returning *;
+                    QUERY PLAN                    
+--------------------------------------------------
+ [                                               +
+   {                                             +
+     "Plan": {                                   +
+       "Node Type": "ModifyTable",               +
+       "Operation": "Insert",                    +
+       "Relation Name": "insertconflicttest",    +
+       "Alias": "target",                        +
+       "Arbiter Index": "key_index",             +
+       "Plans": [                                +
+         {                                       +
+           "Node Type": "Result",                +
+           "Parent Relationship": "Member"       +
+         },                                      +
+         {                                       +
+           "Node Type": "ModifyTable",           +
+           "Operation": "Conflict Update",       +
+           "Parent Relationship": "Member",      +
+           "Relation Name": "insertconflicttest",+
+           "Alias": "target",                    +
+           "Filter": "(fruit <> 'Lime'::text)"   +
+         }                                       +
+       ]                                         +
+     }                                           +
+   }                                             +
+ ]
+(1 row)
+
+-- Fails (no unique index inference specification, required for update variant):
+insert into insertconflicttest values (1, 'Apple') on conflict update set fruit = excluded.fruit;
+ERROR:  ON CONFLICT with UPDATE must contain columns or expressions to infer a unique index from
+LINE 1: ...nsert into insertconflicttest values (1, 'Apple') on conflic...
+                                                             ^
+-- inference succeeds:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) update set fruit = excluded.fruit;
+-- Succeed, since multi-assignment does not involve subquery:
+INSERT INTO insertconflicttest
+VALUES (1, 'Apple'), (2, 'Orange')
+ON CONFLICT (key) UPDATE SET (fruit, key) = (EXCLUDED.fruit, EXCLUDED.key);
+-- Don't accept original table name -- only TARGET.* alias:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = insertconflicttest.fruit;
+ERROR:  invalid reference to FROM-clause entry for table "insertconflicttest"
+LINE 1: ...(1, 'Apple') on conflict (key) update set fruit = insertconf...
+                                                             ^
+HINT:  Perhaps you meant to reference the table alias "excluded".
+-- inference fails:
+insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (6, 'Passionfruit') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index key_index;
+--
+-- Composite key tests
+--
+create unique index comp_key_index on insertconflicttest(key, fruit);
+-- inference succeeds:
+insert into insertconflicttest values (7, 'Raspberry') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (8, 'Lime') on conflict (fruit, key) update set fruit = excluded.fruit;
+-- inference fails:
+insert into insertconflicttest values (9, 'Banana') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (10, 'Blueberry') on conflict (key, key, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (11, 'Cherry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (12, 'Date') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index comp_key_index;
+--
+-- Partial index tests, no inference predicate specificied
+--
+create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
+create unique index expr_part_comp_key_index on insertconflicttest(key, lower(fruit)) where key < 5;
+-- inference fails:
+insert into insertconflicttest values (13, 'Grape') on conflict (key, fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (14, 'Raisin') on conflict (fruit, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (15, 'Cranberry') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (16, 'Melon') on conflict (key, key, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (17, 'Mulberry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (18, 'Pineapple') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index part_comp_key_index;
+drop index expr_part_comp_key_index;
+--
+-- Expression index tests
+--
+create unique index expr_key_index on insertconflicttest(lower(fruit));
+-- inference succeeds:
+insert into insertconflicttest values (20, 'Quince') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (21, 'Pomegranate') on conflict (lower(fruit), lower(fruit)) update set fruit = excluded.fruit;
+-- inference fails:
+insert into insertconflicttest values (22, 'Apricot') on conflict (upper(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index expr_key_index;
+--
+-- Expression index tests (with regular column)
+--
+create unique index expr_comp_key_index on insertconflicttest(key, lower(fruit));
+-- inference succeeds:
+insert into insertconflicttest values (24, 'Plum') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (25, 'Peach') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (26, 'Fig') on conflict (lower(fruit), key, lower(fruit), key) update set fruit = excluded.fruit;
+-- inference fails:
+insert into insertconflicttest values (27, 'Prune') on conflict (key, upper(fruit)) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (28, 'Redcurrant') on conflict (fruit, key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (29, 'Nectarine') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+drop index expr_comp_key_index;
+--
+-- Non-spurious duplicate violation tests
+--
+create unique index key_index on insertconflicttest(key);
+create unique index fruit_index on insertconflicttest(fruit);
+-- succeeds, since UPDATE happens to update "fruit" to existing value:
+insert into insertconflicttest values (26, 'Fig') on conflict (key) update set fruit = excluded.fruit;
+-- fails, since UPDATE is to row with key value 26, and we're updating "fruit"
+-- to a value that happens to exist in another row ('peach'):
+insert into insertconflicttest values (26, 'Peach') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  duplicate key value violates unique constraint "fruit_index"
+DETAIL:  Key (fruit)=(Peach) already exists.
+-- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit"
+-- arbitrates that statement updates existing "Fig" row:
+insert into insertconflicttest values (25, 'Fig') on conflict (fruit) update set fruit = excluded.fruit;
+drop index key_index;
+drop index fruit_index;
+--
 -- Test partial unique index inference
 --
 create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
 -- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry') update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
 -- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key) update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
 ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit where fruit like '%berry') update set fruit = excluded.fruit;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
 insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
 ERROR:  partial arbiter unique index has predicate that does not cover tuple proposed for insertion
 DETAIL:  ON CONFLICT inference clause implies that the tuple proposed for insertion must be covered by predicate for partial index "partial_key_index".
@@ -42,6 +224,9 @@ insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
 insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
 -- Tests proper for inheritance:
 -- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) update set altitude = excluded.altitude;
+ERROR:  relation "cities" has inheritance children
+HINT:  Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
 ERROR:  relation "cities" has inheritance children
 HINT:  Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.
@@ -49,6 +234,7 @@ HINT:  Only heap relations without inheritance children are accepted as targets
 -- There is at least limited support for relations with children:
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
 -- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) update set altitude = excluded.altitude;
 insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
 -- clean up
 drop table capitals;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 0db1df3..6ac9868 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -269,7 +269,7 @@ SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying a
 ERROR:  permission denied for relation atest2
 -- Test column level permissions
 SET SESSION AUTHORIZATION regressuser1;
-CREATE TABLE atest5 (one int, two int, three int);
+CREATE TABLE atest5 (one int, two int unique, three int);
 CREATE TABLE atest6 (one int, two int, blue int);
 GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4;
 GRANT ALL (one) ON atest5 TO regressuser3;
@@ -367,6 +367,11 @@ UPDATE atest5 SET one = 8; -- fail
 ERROR:  permission denied for relation atest5
 UPDATE atest5 SET three = 5, one = 2; -- fail
 ERROR:  permission denied for relation atest5
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set three = 10; -- ok
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set one = 8; -- fails (due to UPDATE)
+ERROR:  permission denied for relation atest5
+INSERT INTO atest5(three) VALUES (4) ON CONFLICT (two) UPDATE set three = 10; -- fails (due to INSERT)
+ERROR:  permission denied for relation atest5
 SET SESSION AUTHORIZATION regressuser1;
 REVOKE ALL (one) ON atest5 FROM regressuser4;
 GRANT SELECT (one,two,blue) ON atest6 TO regressuser4;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 1d67331..b3e0777 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2357,6 +2357,18 @@ insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
   on conflict ignore;
 ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
 DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
+-- rule not fired, so fk violation
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict (id3a, id3b, id3c) update
+  set id3b = excluded.id3b;
+ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
+DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
+-- rule fired, so unsupported (only updatable views have limited support)
+insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0)
+  on conflict (id1a, id1b) update
+  set sl_avail = excluded.sl_avail;
+ERROR:  relation "shoelace" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
 			where (((rule_and_refint_t3.id3a = new.id3a)
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index b14410f..9ba3a44 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -639,6 +639,28 @@ from
 (0 rows)
 
 --
+-- Test case for subselect within UPDATE of INSERT...ON CONFLICT UPDATE
+--
+create temp table upsert(key int4 primary key, val text);
+insert into upsert values(1, 'val') on conflict (key) update set val = 'not seen';
+insert into upsert values(1, 'val') on conflict (key) update set val = 'unsupported ' || (select f1 from int4_tbl where f1 != 0 limit 1)::text;
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 1: ...conflict (key) update set val = 'unsupported ' || (select f1...
+                                                             ^
+select * from upsert;
+ key | val 
+-----+-----
+   1 | val
+(1 row)
+
+with aa as (select 'int4_tbl' u from int4_tbl limit 1)
+insert into upsert values (1, 'x'), (999, 'y')
+on conflict (key) update set val = (select u from aa)
+returning *;
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: on conflict (key) update set val = (select u from aa)
+                                           ^
+--
 -- Test case for cross-type partial matching in hashed subplan (bug #7597)
 --
 create temp table outer_7597 (f1 int4, f2 int4);
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index f1a5fde..77dfa06 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -274,7 +274,7 @@ drop sequence ttdummy_seq;
 -- tests for per-statement triggers
 --
 CREATE TABLE log_table (tstamp timestamp default timeofday()::timestamp);
-CREATE TABLE main_table (a int, b int);
+CREATE TABLE main_table (a int unique, b int);
 COPY main_table (a,b) FROM stdin;
 CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
 BEGIN
@@ -291,6 +291,14 @@ FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt');
 --
 CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table
 EXECUTE PROCEDURE trigger_func('after_upd_stmt');
+-- Both insert and update statement level triggers (before and after) should
+-- fire.  Doesn't fire UPDATE before trigger, but only because one isn't
+-- defined.
+INSERT INTO main_table (a, b) VALUES (5, 10) ON CONFLICT (a)
+  UPDATE SET b = EXCLUDED.b;
+NOTICE:  trigger_func(before_ins_stmt) called: action = INSERT, when = BEFORE, level = STATEMENT
+NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+NOTICE:  trigger_func(after_ins_stmt) called: action = INSERT, when = AFTER, level = STATEMENT
 CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table
 FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row');
 INSERT INTO main_table DEFAULT VALUES;
@@ -305,6 +313,8 @@ NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, lev
 -- UPDATE that effects zero rows should still call per-statement trigger
 UPDATE main_table SET a = a + 2 WHERE b > 100;
 NOTICE:  trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+-- constraint now unneeded
+ALTER TABLE main_table DROP CONSTRAINT main_table_a_key;
 -- COPY should fire per-row and per-statement INSERT triggers
 COPY main_table (a, b) FROM stdin;
 NOTICE:  trigger_func(before_ins_stmt) called: action = INSERT, when = BEFORE, level = STATEMENT
@@ -1731,3 +1741,93 @@ select * from self_ref_trigger;
 drop table self_ref_trigger;
 drop function self_ref_trigger_ins_func();
 drop function self_ref_trigger_del_func();
+--
+-- Verify behavior of before and after triggers with INSERT...ON CONFLICT
+-- UPDATE
+--
+create table upsert (key int4 primary key, color text);
+create function upsert_before_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'before update (old): %', old.*::text;
+    raise warning 'before update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'before insert (new): %', new.*::text;
+    if new.key % 2 = 0 then
+      new.key := new.key + 1;
+      new.color := new.color || ' trig modified';
+      raise warning 'before insert (new, modified): %', new.*::text;
+    end if;
+  end if;
+  return new;
+end;
+$$;
+create trigger upsert_before_trig before insert or update on upsert
+  for each row execute procedure upsert_before_func();
+create function upsert_after_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'after update (old): %', new.*::text;
+    raise warning 'after update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'after insert (new): %', new.*::text;
+  end if;
+  return null;
+end;
+$$;
+create trigger upsert_after_trig after insert or update on upsert
+  for each row execute procedure upsert_after_func();
+insert into upsert values(1, 'black') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (1,black)
+WARNING:  after insert (new): (1,black)
+insert into upsert values(2, 'red') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (2,red)
+WARNING:  before insert (new, modified): (3,"red trig modified")
+WARNING:  after insert (new): (3,"red trig modified")
+insert into upsert values(3, 'orange') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (3,orange)
+WARNING:  before update (old): (3,"red trig modified")
+WARNING:  before update (new): (3,"updated red trig modified")
+WARNING:  after update (old): (3,"updated red trig modified")
+WARNING:  after update (new): (3,"updated red trig modified")
+insert into upsert values(4, 'green') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (4,green)
+WARNING:  before insert (new, modified): (5,"green trig modified")
+WARNING:  after insert (new): (5,"green trig modified")
+insert into upsert values(5, 'purple') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (5,purple)
+WARNING:  before update (old): (5,"green trig modified")
+WARNING:  before update (new): (5,"updated green trig modified")
+WARNING:  after update (old): (5,"updated green trig modified")
+WARNING:  after update (new): (5,"updated green trig modified")
+insert into upsert values(6, 'white') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (6,white)
+WARNING:  before insert (new, modified): (7,"white trig modified")
+WARNING:  after insert (new): (7,"white trig modified")
+insert into upsert values(7, 'pink') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (7,pink)
+WARNING:  before update (old): (7,"white trig modified")
+WARNING:  before update (new): (7,"updated white trig modified")
+WARNING:  after update (old): (7,"updated white trig modified")
+WARNING:  after update (new): (7,"updated white trig modified")
+insert into upsert values(8, 'yellow') on conflict (key) update set color = 'updated ' || target.color;
+WARNING:  before insert (new): (8,yellow)
+WARNING:  before insert (new, modified): (9,"yellow trig modified")
+WARNING:  after insert (new): (9,"yellow trig modified")
+select * from upsert;
+ key |            color            
+-----+-----------------------------
+   1 | black
+   3 | updated red trig modified
+   5 | updated green trig modified
+   7 | updated white trig modified
+   9 | yellow trig modified
+(5 rows)
+
+drop table upsert;
+drop function upsert_before_func();
+drop function upsert_after_func();
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index 1de2a86..c646016 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -147,4 +147,31 @@ SELECT a, b, char_length(c) FROM update_test;
  42 |  12 |       10000
 (4 rows)
 
+ALTER TABLE update_test ADD constraint uuu UNIQUE(a);
+-- fail, update predicates are disallowed:
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a NOT IN (SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a NOT IN (SELECT a FROM update_test);
+                ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE EXISTS(SELECT b FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE EXISTS(SELECT b FROM update_test);
+              ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a IN (SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a IN (SELECT a FROM update_test);
+                ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ALL(SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a = ALL(SELECT a FROM update_test);
+                ^
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ANY(SELECT a FROM update_test);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 2: WHERE a = ANY(SELECT a FROM update_test);
+                ^
 DROP TABLE update_test;
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 6986f47..abea714 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -1806,6 +1806,80 @@ SELECT * FROM y;
   -400
 (22 rows)
 
+-- data-modifying WITH containing INSERT...ON CONFLICT UPDATE
+CREATE TABLE z AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
+ALTER TABLE z ADD UNIQUE (k);
+WITH t AS (
+    INSERT INTO z SELECT i, 'insert'
+    FROM generate_series(0, 16) i
+    ON CONFLICT (k) UPDATE SET v = TARGET.v || ', now update'
+    RETURNING *
+)
+SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k;
+ k |   v    | a 
+---+--------+---
+ 0 | insert | 0
+ 0 | insert | 0
+(2 rows)
+
+-- New query/snapshot demonstrates side-effects of previous query.
+SELECT * FROM z ORDER BY k;
+ k  |        v         
+----+------------------
+  0 | insert
+  1 | 1 v, now update
+  2 | insert
+  3 | insert
+  4 | 4 v, now update
+  5 | insert
+  6 | insert
+  7 | 7 v, now update
+  8 | insert
+  9 | insert
+ 10 | 10 v, now update
+ 11 | insert
+ 12 | insert
+ 13 | 13 v, now update
+ 14 | insert
+ 15 | insert
+ 16 | 16 v, now update
+(17 rows)
+
+--
+-- All these cases should fail, due to restrictions imposed upon the UPDATE
+-- portion of the query.
+--
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = ' update' WHERE target.k = (SELECT a FROM aa);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ...ICT (k) UPDATE SET v = ' update' WHERE target.k = (SELECT a ...
+                                                             ^
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 ))
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+ERROR:  cannot use subquery in ON CONFLICT UPDATE
+LINE 3: ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM a...
+                                       ^
+DROP TABLE z;
 -- check that run to completion happens in proper ordering
 TRUNCATE TABLE y;
 INSERT INTO y SELECT generate_series(1, 3);
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index c16f650..1bfc778 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -292,6 +292,11 @@ INSERT INTO UNIQUE_TBL VALUES (5, 'one');
 INSERT INTO UNIQUE_TBL (t) VALUES ('six');
 INSERT INTO UNIQUE_TBL (t) VALUES ('seven');
 
+INSERT INTO UNIQUE_TBL VALUES (5, 'five-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'five-upsert-update';
+INSERT INTO UNIQUE_TBL VALUES (6, 'six-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'six-upsert-update';
+-- should fail
+INSERT INTO UNIQUE_TBL VALUES (1, 'a'), (2, 'b'), (2, 'b') ON CONFLICT (i) UPDATE SET t = 'fails';
+
 SELECT '' AS five, * FROM UNIQUE_TBL;
 
 DROP TABLE UNIQUE_TBL;
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index d3ec233..6005ad62 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -421,16 +421,23 @@ INSERT INTO UNIQUE_TBL VALUES (4, 'four');
 INSERT INTO UNIQUE_TBL VALUES (5, 'one');
 INSERT INTO UNIQUE_TBL (t) VALUES ('six');
 INSERT INTO UNIQUE_TBL (t) VALUES ('seven');
+INSERT INTO UNIQUE_TBL VALUES (5, 'five-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'five-upsert-update';
+INSERT INTO UNIQUE_TBL VALUES (6, 'six-upsert-insert') ON CONFLICT (i) UPDATE SET t = 'six-upsert-update';
+-- should fail
+INSERT INTO UNIQUE_TBL VALUES (1, 'a'), (2, 'b'), (2, 'b') ON CONFLICT (i) UPDATE SET t = 'fails';
+ERROR:  ON CONFLICT UPDATE command could not lock/update self-inserted tuple
+HINT:  Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
 SELECT '' AS five, * FROM UNIQUE_TBL;
- five | i |   t   
-------+---+-------
+ five | i |         t          
+------+---+--------------------
       | 1 | one
       | 2 | two
       | 4 | four
-      | 5 | one
       |   | six
       |   | seven
-(6 rows)
+      | 5 | five-upsert-update
+      | 6 | six-upsert-insert
+(7 rows)
 
 DROP TABLE UNIQUE_TBL;
 CREATE TABLE UNIQUE_TBL (i int, t text,
diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql
index e330ecd..472d4ab 100644
--- a/src/test/regress/sql/insert_conflict.sql
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -4,15 +4,140 @@
 create table insertconflicttest(key int4, fruit text);
 
 --
+-- Single key tests
+--
+create unique index key_index on insertconflicttest(key);
+
+--
+-- Explain tests
+--
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit;
+-- Should display qual actually attributable to internal sequential scan:
+explain (costs off) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Cawesh';
+-- With EXCLUDED.* expression in scan node:
+explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key) update set fruit = excluded.fruit where excluded.fruit != 'Elderberry';
+-- Does the same, but JSON format shows "Arbiter Index":
+explain (costs off, format json) insert into insertconflicttest values (0, 'Bilberry') on conflict (key) update set fruit = excluded.fruit where target.fruit != 'Lime' returning *;
+
+-- Fails (no unique index inference specification, required for update variant):
+insert into insertconflicttest values (1, 'Apple') on conflict update set fruit = excluded.fruit;
+
+-- inference succeeds:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (2, 'Orange') on conflict (key, key, key) update set fruit = excluded.fruit;
+
+-- Succeed, since multi-assignment does not involve subquery:
+INSERT INTO insertconflicttest
+VALUES (1, 'Apple'), (2, 'Orange')
+ON CONFLICT (key) UPDATE SET (fruit, key) = (EXCLUDED.fruit, EXCLUDED.key);
+-- Don't accept original table name -- only TARGET.* alias:
+insert into insertconflicttest values (1, 'Apple') on conflict (key) update set fruit = insertconflicttest.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (3, 'Kiwi') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (4, 'Mango') on conflict (fruit, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (5, 'Lemon') on conflict (fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (6, 'Passionfruit') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+
+drop index key_index;
+
+--
+-- Composite key tests
+--
+create unique index comp_key_index on insertconflicttest(key, fruit);
+
+-- inference succeeds:
+insert into insertconflicttest values (7, 'Raspberry') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (8, 'Lime') on conflict (fruit, key) update set fruit = excluded.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (9, 'Banana') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (10, 'Blueberry') on conflict (key, key, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (11, 'Cherry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (12, 'Date') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+
+drop index comp_key_index;
+
+--
+-- Partial index tests, no inference predicate specificied
+--
+create unique index part_comp_key_index on insertconflicttest(key, fruit) where key < 5;
+create unique index expr_part_comp_key_index on insertconflicttest(key, lower(fruit)) where key < 5;
+
+-- inference fails:
+insert into insertconflicttest values (13, 'Grape') on conflict (key, fruit) update set fruit = excluded.fruit;
+insert into insertconflicttest values (14, 'Raisin') on conflict (fruit, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (15, 'Cranberry') on conflict (key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (16, 'Melon') on conflict (key, key, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (17, 'Mulberry') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (18, 'Pineapple') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+
+drop index part_comp_key_index;
+drop index expr_part_comp_key_index;
+
+--
+-- Expression index tests
+--
+create unique index expr_key_index on insertconflicttest(lower(fruit));
+
+-- inference succeeds:
+insert into insertconflicttest values (20, 'Quince') on conflict (lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (21, 'Pomegranate') on conflict (lower(fruit), lower(fruit)) update set fruit = excluded.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (22, 'Apricot') on conflict (upper(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit) update set fruit = excluded.fruit;
+
+drop index expr_key_index;
+
+--
+-- Expression index tests (with regular column)
+--
+create unique index expr_comp_key_index on insertconflicttest(key, lower(fruit));
+
+-- inference succeeds:
+insert into insertconflicttest values (24, 'Plum') on conflict (key, lower(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (25, 'Peach') on conflict (lower(fruit), key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (26, 'Fig') on conflict (lower(fruit), key, lower(fruit), key) update set fruit = excluded.fruit;
+
+-- inference fails:
+insert into insertconflicttest values (27, 'Prune') on conflict (key, upper(fruit)) update set fruit = excluded.fruit;
+insert into insertconflicttest values (28, 'Redcurrant') on conflict (fruit, key) update set fruit = excluded.fruit;
+insert into insertconflicttest values (29, 'Nectarine') on conflict (key) update set fruit = excluded.fruit;
+
+drop index expr_comp_key_index;
+
+--
+-- Non-spurious duplicate violation tests
+--
+create unique index key_index on insertconflicttest(key);
+create unique index fruit_index on insertconflicttest(fruit);
+
+-- succeeds, since UPDATE happens to update "fruit" to existing value:
+insert into insertconflicttest values (26, 'Fig') on conflict (key) update set fruit = excluded.fruit;
+-- fails, since UPDATE is to row with key value 26, and we're updating "fruit"
+-- to a value that happens to exist in another row ('peach'):
+insert into insertconflicttest values (26, 'Peach') on conflict (key) update set fruit = excluded.fruit;
+-- succeeds, since "key" isn't repeated/referenced in UPDATE, and "fruit"
+-- arbitrates that statement updates existing "Fig" row:
+insert into insertconflicttest values (25, 'Fig') on conflict (fruit) update set fruit = excluded.fruit;
+
+drop index key_index;
+drop index fruit_index;
+
+--
 -- Test partial unique index inference
 --
 create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
 
 -- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry') update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
 
 -- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key) update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
+insert into insertconflicttest values (23, 'Blackberry') on conflict (fruit where fruit like '%berry') update set fruit = excluded.fruit;
 insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
 
 drop index partial_key_index;
@@ -51,6 +176,7 @@ insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
 -- Tests proper for inheritance:
 
 -- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) update set altitude = excluded.altitude;
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
 
 -- Succeeds:
@@ -58,6 +184,7 @@ insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignor
 -- There is at least limited support for relations with children:
 insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
 -- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) update set altitude = excluded.altitude;
 insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
 
 -- clean up
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index f97a75a..861eac6 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -194,7 +194,7 @@ SELECT * FROM atestv2; -- fail (even though regressuser2 can access underlying a
 -- Test column level permissions
 
 SET SESSION AUTHORIZATION regressuser1;
-CREATE TABLE atest5 (one int, two int, three int);
+CREATE TABLE atest5 (one int, two int unique, three int);
 CREATE TABLE atest6 (one int, two int, blue int);
 GRANT SELECT (one), INSERT (two), UPDATE (three) ON atest5 TO regressuser4;
 GRANT ALL (one) ON atest5 TO regressuser3;
@@ -245,6 +245,9 @@ INSERT INTO atest5 VALUES (5,5,5); -- fail
 UPDATE atest5 SET three = 10; -- ok
 UPDATE atest5 SET one = 8; -- fail
 UPDATE atest5 SET three = 5, one = 2; -- fail
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set three = 10; -- ok
+INSERT INTO atest5(two) VALUES (6) ON CONFLICT (two) UPDATE set one = 8; -- fails (due to UPDATE)
+INSERT INTO atest5(three) VALUES (4) ON CONFLICT (two) UPDATE set three = 10; -- fails (due to INSERT)
 
 SET SESSION AUTHORIZATION regressuser1;
 REVOKE ALL (one) ON atest5 FROM regressuser4;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 5807331..31afee8 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -850,6 +850,14 @@ insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
 -- Ordinary table
 insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
   on conflict ignore;
+-- rule not fired, so fk violation
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict (id3a, id3b, id3c) update
+  set id3b = excluded.id3b;
+-- rule fired, so unsupported (only updatable views have limited support)
+insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0)
+  on conflict (id1a, id1b) update
+  set sl_avail = excluded.sl_avail;
 
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index 4be2e40..2be9cb7 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -374,6 +374,20 @@ from
   int4_tbl i4 on dummy = i4.f1;
 
 --
+-- Test case for subselect within UPDATE of INSERT...ON CONFLICT UPDATE
+--
+create temp table upsert(key int4 primary key, val text);
+insert into upsert values(1, 'val') on conflict (key) update set val = 'not seen';
+insert into upsert values(1, 'val') on conflict (key) update set val = 'unsupported ' || (select f1 from int4_tbl where f1 != 0 limit 1)::text;
+
+select * from upsert;
+
+with aa as (select 'int4_tbl' u from int4_tbl limit 1)
+insert into upsert values (1, 'x'), (999, 'y')
+on conflict (key) update set val = (select u from aa)
+returning *;
+
+--
 -- Test case for cross-type partial matching in hashed subplan (bug #7597)
 --
 
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index 0ea2c31..323ca1a 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -208,7 +208,7 @@ drop sequence ttdummy_seq;
 
 CREATE TABLE log_table (tstamp timestamp default timeofday()::timestamp);
 
-CREATE TABLE main_table (a int, b int);
+CREATE TABLE main_table (a int unique, b int);
 
 COPY main_table (a,b) FROM stdin;
 5	10
@@ -237,6 +237,12 @@ FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt');
 CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table
 EXECUTE PROCEDURE trigger_func('after_upd_stmt');
 
+-- Both insert and update statement level triggers (before and after) should
+-- fire.  Doesn't fire UPDATE before trigger, but only because one isn't
+-- defined.
+INSERT INTO main_table (a, b) VALUES (5, 10) ON CONFLICT (a)
+  UPDATE SET b = EXCLUDED.b;
+
 CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table
 FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row');
 
@@ -246,6 +252,9 @@ UPDATE main_table SET a = a + 1 WHERE b < 30;
 -- UPDATE that effects zero rows should still call per-statement trigger
 UPDATE main_table SET a = a + 2 WHERE b > 100;
 
+-- constraint now unneeded
+ALTER TABLE main_table DROP CONSTRAINT main_table_a_key;
+
 -- COPY should fire per-row and per-statement INSERT triggers
 COPY main_table (a, b) FROM stdin;
 30	40
@@ -1173,3 +1182,61 @@ select * from self_ref_trigger;
 drop table self_ref_trigger;
 drop function self_ref_trigger_ins_func();
 drop function self_ref_trigger_del_func();
+
+--
+-- Verify behavior of before and after triggers with INSERT...ON CONFLICT
+-- UPDATE
+--
+create table upsert (key int4 primary key, color text);
+
+create function upsert_before_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'before update (old): %', old.*::text;
+    raise warning 'before update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'before insert (new): %', new.*::text;
+    if new.key % 2 = 0 then
+      new.key := new.key + 1;
+      new.color := new.color || ' trig modified';
+      raise warning 'before insert (new, modified): %', new.*::text;
+    end if;
+  end if;
+  return new;
+end;
+$$;
+create trigger upsert_before_trig before insert or update on upsert
+  for each row execute procedure upsert_before_func();
+
+create function upsert_after_func()
+  returns trigger language plpgsql as
+$$
+begin
+  if (TG_OP = 'UPDATE') then
+    raise warning 'after update (old): %', new.*::text;
+    raise warning 'after update (new): %', new.*::text;
+  elsif (TG_OP = 'INSERT') then
+    raise warning 'after insert (new): %', new.*::text;
+  end if;
+  return null;
+end;
+$$;
+create trigger upsert_after_trig after insert or update on upsert
+  for each row execute procedure upsert_after_func();
+
+insert into upsert values(1, 'black') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(2, 'red') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(3, 'orange') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(4, 'green') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(5, 'purple') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(6, 'white') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(7, 'pink') on conflict (key) update set color = 'updated ' || target.color;
+insert into upsert values(8, 'yellow') on conflict (key) update set color = 'updated ' || target.color;
+
+select * from upsert;
+
+drop table upsert;
+drop function upsert_before_func();
+drop function upsert_after_func();
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index e71128c..903f3fb 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -74,4 +74,18 @@ UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10;
 UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car';
 SELECT a, b, char_length(c) FROM update_test;
 
+ALTER TABLE update_test ADD constraint uuu UNIQUE(a);
+
+-- fail, update predicates are disallowed:
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a NOT IN (SELECT a FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE EXISTS(SELECT b FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a IN (SELECT a FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ALL(SELECT a FROM update_test);
+INSERT INTO update_test VALUES(31, 77) ON CONFLICT (a) UPDATE SET b = 16
+WHERE a = ANY(SELECT a FROM update_test);
+
 DROP TABLE update_test;
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 1687c11..56478ca 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -795,6 +795,43 @@ SELECT * FROM t LIMIT 10;
 
 SELECT * FROM y;
 
+-- data-modifying WITH containing INSERT...ON CONFLICT UPDATE
+CREATE TABLE z AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
+ALTER TABLE z ADD UNIQUE (k);
+
+WITH t AS (
+    INSERT INTO z SELECT i, 'insert'
+    FROM generate_series(0, 16) i
+    ON CONFLICT (k) UPDATE SET v = TARGET.v || ', now update'
+    RETURNING *
+)
+SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k;
+
+-- New query/snapshot demonstrates side-effects of previous query.
+SELECT * FROM z ORDER BY k;
+
+--
+-- All these cases should fail, due to restrictions imposed upon the UPDATE
+-- portion of the query.
+--
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = ' update' WHERE target.k = (SELECT a FROM aa);
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b)
+INSERT INTO z VALUES(1, 'insert')
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1);
+WITH aa AS (SELECT 1 a, 2 b)
+INSERT INTO z VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 ))
+ON CONFLICT (k) UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
+
+DROP TABLE z;
+
 -- check that run to completion happens in proper ordering
 
 TRUNCATE TABLE y;
-- 
1.9.1

0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patchtext/x-patch; charset=US-ASCII; name=0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patchDownload
From 372f35427638f563cbb179d28af0ed3baa0730e4 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@heroku.com>
Date: Tue, 26 Aug 2014 21:28:40 -0700
Subject: [PATCH 2/4] Make UPDATE privileges distinct from INSERT privileges in
 RTEs

Previously, relation range table entries used a single Bitmapset field
representing which columns required either UPDATE or INSERT privileges,
despite the fact that INSERT and UPDATE privileges are separately
cataloged, and may be independently held.  This worked because
ExecCheckRTEPerms() was called with a ACL_INSERT or ACL_UPDATE
requiredPerms, and based on that it was evident which type of
optimizable statement was under consideration.  Since historically no
type of optimizable statement could directly INSERT and UPDATE at the
same time, there was no ambiguity as to which privileges were required.

This largely mechanical commit is required infrastructure for the
INSERT...ON CONFLICT UPDATE feature, which introduces an optimizable
statement that may be subject to both INSERT and UPDATE permissions
enforcement.  Tests follow in a later commit.

sepgsql is also affected by this commit.  Note that this commit
necessitates an initdb, since stored ACLs are broken.
---
 contrib/postgres_fdw/postgres_fdw.c       |   2 +-
 contrib/sepgsql/dml.c                     |  31 ++++++---
 src/backend/commands/copy.c               |   2 +-
 src/backend/commands/createas.c           |   2 +-
 src/backend/commands/trigger.c            |  22 +++---
 src/backend/executor/execMain.c           | 110 +++++++++++++++++++-----------
 src/backend/nodes/copyfuncs.c             |   3 +-
 src/backend/nodes/equalfuncs.c            |   3 +-
 src/backend/nodes/outfuncs.c              |   3 +-
 src/backend/nodes/readfuncs.c             |   3 +-
 src/backend/optimizer/plan/setrefs.c      |   6 +-
 src/backend/optimizer/prep/prepsecurity.c |   6 +-
 src/backend/optimizer/prep/prepunion.c    |   8 ++-
 src/backend/parser/analyze.c              |   4 +-
 src/backend/parser/parse_relation.c       |  21 ++++--
 src/backend/rewrite/rewriteHandler.c      |  52 ++++++++------
 src/include/nodes/parsenodes.h            |  14 ++--
 17 files changed, 177 insertions(+), 115 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 895274e..df55cfd 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1206,7 +1206,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 		int			col;
 
 		col = -1;
-		while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
+		while ((col = bms_next_member(rte->updatedCols, col)) >= 0)
 		{
 			/* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
 			AttrNumber	attno = col + FirstLowInvalidHeapAttributeNumber;
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index 36c6a37..4a71753 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -145,7 +145,8 @@ fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
 static bool
 check_relation_privileges(Oid relOid,
 						  Bitmapset *selected,
-						  Bitmapset *modified,
+						  Bitmapset *inserted,
+						  Bitmapset *updated,
 						  uint32 required,
 						  bool abort_on_violation)
 {
@@ -231,8 +232,9 @@ check_relation_privileges(Oid relOid,
 	 * Check permissions on the columns
 	 */
 	selected = fixup_whole_row_references(relOid, selected);
-	modified = fixup_whole_row_references(relOid, modified);
-	columns = bms_union(selected, modified);
+	inserted = fixup_whole_row_references(relOid, inserted);
+	updated = fixup_whole_row_references(relOid, updated);
+	columns = bms_union(selected, bms_union(inserted, updated));
 
 	while ((index = bms_first_member(columns)) >= 0)
 	{
@@ -241,13 +243,16 @@ check_relation_privileges(Oid relOid,
 
 		if (bms_is_member(index, selected))
 			column_perms |= SEPG_DB_COLUMN__SELECT;
-		if (bms_is_member(index, modified))
+		if (bms_is_member(index, inserted))
 		{
-			if (required & SEPG_DB_TABLE__UPDATE)
-				column_perms |= SEPG_DB_COLUMN__UPDATE;
 			if (required & SEPG_DB_TABLE__INSERT)
 				column_perms |= SEPG_DB_COLUMN__INSERT;
 		}
+		if (bms_is_member(index, updated))
+		{
+			if (required & SEPG_DB_TABLE__UPDATE)
+				column_perms |= SEPG_DB_COLUMN__UPDATE;
+		}
 		if (column_perms == 0)
 			continue;
 
@@ -304,7 +309,7 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 			required |= SEPG_DB_TABLE__INSERT;
 		if (rte->requiredPerms & ACL_UPDATE)
 		{
-			if (!bms_is_empty(rte->modifiedCols))
+			if (!bms_is_empty(rte->updatedCols))
 				required |= SEPG_DB_TABLE__UPDATE;
 			else
 				required |= SEPG_DB_TABLE__LOCK;
@@ -333,7 +338,8 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 		{
 			Oid			tableOid = lfirst_oid(li);
 			Bitmapset  *selectedCols;
-			Bitmapset  *modifiedCols;
+			Bitmapset  *insertedCols;
+			Bitmapset  *updatedCols;
 
 			/*
 			 * child table has different attribute numbers, so we need to fix
@@ -341,15 +347,18 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 			 */
 			selectedCols = fixup_inherited_columns(rte->relid, tableOid,
 												   rte->selectedCols);
-			modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
-												   rte->modifiedCols);
+			insertedCols = fixup_inherited_columns(rte->relid, tableOid,
+												   rte->insertedCols);
+			updatedCols = fixup_inherited_columns(rte->relid, tableOid,
+												  rte->updatedCols);
 
 			/*
 			 * check permissions on individual tables
 			 */
 			if (!check_relation_privileges(tableOid,
 										   selectedCols,
-										   modifiedCols,
+										   insertedCols,
+										   updatedCols,
 										   required, abort_on_violation))
 				return false;
 		}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f85bd60..2d45eb3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -847,7 +847,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
 			FirstLowInvalidHeapAttributeNumber;
 
 			if (is_from)
-				rte->modifiedCols = bms_add_member(rte->modifiedCols, attno);
+				rte->insertedCols = bms_add_member(rte->insertedCols, attno);
 			else
 				rte->selectedCols = bms_add_member(rte->selectedCols, attno);
 		}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 54b2f38..e8f0d79 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -433,7 +433,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	rte->requiredPerms = ACL_INSERT;
 
 	for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++)
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
+		rte->insertedCols = bms_add_member(rte->insertedCols,
 								attnum - FirstLowInvalidHeapAttributeNumber);
 
 	ExecCheckRTPerms(list_make1(rte), true);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index e491c5b..847dc5c 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -71,8 +71,8 @@ static int	MyTriggerDepth = 0;
  * it uses, so we let them be duplicated.  Be sure to update both if one needs
  * to be changed, however.
  */
-#define GetModifiedColumns(relinfo, estate) \
-	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
+#define GetUpdatedColumns(relinfo, estate) \
+	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
 
 /* Local function prototypes */
 static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
@@ -2347,7 +2347,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	TriggerDesc *trigdesc;
 	int			i;
 	TriggerData LocTriggerData;
-	Bitmapset  *modifiedCols;
+	Bitmapset  *updatedCols;
 
 	trigdesc = relinfo->ri_TrigDesc;
 
@@ -2356,7 +2356,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	if (!trigdesc->trig_update_before_statement)
 		return;
 
-	modifiedCols = GetModifiedColumns(relinfo, estate);
+	updatedCols = GetUpdatedColumns(relinfo, estate);
 
 	LocTriggerData.type = T_TriggerData;
 	LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
@@ -2377,7 +2377,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 								  TRIGGER_TYPE_UPDATE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							modifiedCols, NULL, NULL))
+							updatedCols, NULL, NULL))
 			continue;
 
 		LocTriggerData.tg_trigger = trigger;
@@ -2402,7 +2402,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 	if (trigdesc && trigdesc->trig_update_after_statement)
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
 							  false, NULL, NULL, NIL,
-							  GetModifiedColumns(relinfo, estate));
+							  GetUpdatedColumns(relinfo, estate));
 }
 
 TupleTableSlot *
@@ -2420,7 +2420,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	HeapTuple	oldtuple;
 	TupleTableSlot *newSlot;
 	int			i;
-	Bitmapset  *modifiedCols;
+	Bitmapset  *updatedCols;
 	Bitmapset  *keyCols;
 	LockTupleMode lockmode;
 
@@ -2429,10 +2429,10 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 	 * been modified, then we can use a weaker lock, allowing for better
 	 * concurrency.
 	 */
-	modifiedCols = GetModifiedColumns(relinfo, estate);
+	updatedCols = GetUpdatedColumns(relinfo, estate);
 	keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc,
 										 INDEX_ATTR_BITMAP_KEY);
-	if (bms_overlap(keyCols, modifiedCols))
+	if (bms_overlap(keyCols, updatedCols))
 		lockmode = LockTupleExclusive;
 	else
 		lockmode = LockTupleNoKeyExclusive;
@@ -2486,7 +2486,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
 								  TRIGGER_TYPE_UPDATE))
 			continue;
 		if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-							modifiedCols, trigtuple, newtuple))
+							updatedCols, trigtuple, newtuple))
 			continue;
 
 		LocTriggerData.tg_trigtuple = trigtuple;
@@ -2556,7 +2556,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 
 		AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
 							  true, trigtuple, newtuple, recheckIndexes,
-							  GetModifiedColumns(relinfo, estate));
+							  GetUpdatedColumns(relinfo, estate));
 		if (trigtuple != fdw_trigtuple)
 			heap_freetuple(trigtuple);
 	}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 0be893d..43aa1a2 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -82,6 +82,9 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
 			ScanDirection direction,
 			DestReceiver *dest);
 static bool ExecCheckRTEPerms(RangeTblEntry *rte);
+static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
+						  Bitmapset *modifiedCols,
+						  AclMode requiredPerms);
 static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
 static char *ExecBuildSlotValueDescription(Oid reloid,
 							  TupleTableSlot *slot,
@@ -97,8 +100,10 @@ static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
  * it uses, so we let them be duplicated.  Be sure to update both if one needs
  * to be changed, however.
  */
-#define GetModifiedColumns(relinfo, estate) \
-	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
+#define GetUpdatedColumns(relinfo, estate) \
+	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
+#define GetInsertedColumns(relinfo, estate) \
+	(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->insertedCols)
 
 /* end of local decls */
 
@@ -559,7 +564,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 	AclMode		remainingPerms;
 	Oid			relOid;
 	Oid			userid;
-	int			col;
 
 	/*
 	 * Only plain-relation RTEs need to be checked here.  Function RTEs are
@@ -597,6 +601,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 	remainingPerms = requiredPerms & ~relPerms;
 	if (remainingPerms != 0)
 	{
+		int			col = -1;
+
 		/*
 		 * If we lack any permissions that exist only as relation permissions,
 		 * we can fail straight away.
@@ -625,7 +631,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 					return false;
 			}
 
-			col = -1;
 			while ((col = bms_next_member(rte->selectedCols, col)) >= 0)
 			{
 				/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
@@ -648,43 +653,63 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 		}
 
 		/*
-		 * Basically the same for the mod columns, with either INSERT or
-		 * UPDATE privilege as specified by remainingPerms.
+		 * Basically the same for the mod columns, for both INSERT and UPDATE
+		 * privilege as specified by remainingPerms.
 		 */
-		remainingPerms &= ~ACL_SELECT;
-		if (remainingPerms != 0)
-		{
-			/*
-			 * When the query doesn't explicitly change any columns, allow the
-			 * query if we have permission on any column of the rel.  This is
-			 * to handle SELECT FOR UPDATE as well as possible corner cases in
-			 * INSERT and UPDATE.
-			 */
-			if (bms_is_empty(rte->modifiedCols))
-			{
-				if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms,
-											  ACLMASK_ANY) != ACLCHECK_OK)
-					return false;
-			}
+		if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid,
+																	  userid,
+																	  rte->insertedCols,
+																	  ACL_INSERT))
+			return false;
 
-			col = -1;
-			while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
-			{
-				/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
-				AttrNumber	attno = col + FirstLowInvalidHeapAttributeNumber;
+		if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid,
+																	  userid,
+																	  rte->updatedCols,
+																	  ACL_UPDATE))
+			return false;
+	}
+	return true;
+}
 
-				if (attno == InvalidAttrNumber)
-				{
-					/* whole-row reference can't happen here */
-					elog(ERROR, "whole-row update is not implemented");
-				}
-				else
-				{
-					if (pg_attribute_aclcheck(relOid, attno, userid,
-											  remainingPerms) != ACLCHECK_OK)
-						return false;
-				}
-			}
+/*
+ * ExecCheckRTEPermsModified
+ *		Check INSERT or UPDATE access permissions for a single RTE (these
+ *		are processed uniformly).
+ */
+static bool
+ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols,
+						  AclMode requiredPerms)
+{
+	int			col = -1;
+
+	/*
+	 * When the query doesn't explicitly update any columns, allow the
+	 * query if we have permission on any column of the rel.  This is
+	 * to handle SELECT FOR UPDATE as well as possible corner cases in
+	 * UPDATE.
+	 */
+	if (bms_is_empty(modifiedCols))
+	{
+		if (pg_attribute_aclcheck_all(relOid, userid, requiredPerms,
+									  ACLMASK_ANY) != ACLCHECK_OK)
+			return false;
+	}
+
+	while ((col = bms_next_member(modifiedCols, col)) >= 0)
+	{
+		/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
+		AttrNumber	attno = col + FirstLowInvalidHeapAttributeNumber;
+
+		if (attno == InvalidAttrNumber)
+		{
+			/* whole-row reference can't happen here */
+			elog(ERROR, "whole-row update is not implemented");
+		}
+		else
+		{
+			if (pg_attribute_aclcheck(relOid, attno, userid,
+									  requiredPerms) != ACLCHECK_OK)
+				return false;
 		}
 	}
 	return true;
@@ -1625,7 +1650,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				char	   *val_desc;
 				Bitmapset  *modifiedCols;
 
-				modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+				modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
+				modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
 				val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
 														 slot,
 														 tupdesc,
@@ -1651,7 +1677,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
 
-			modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+			modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
+			modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
 			val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
 													 slot,
 													 tupdesc,
@@ -1710,7 +1737,8 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo,
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
 
-			modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+			modifiedCols = GetUpdatedColumns(resultRelInfo, estate);
+			modifiedCols = bms_union(modifiedCols, GetInsertedColumns(resultRelInfo, estate));
 			val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
 													 slot,
 													 tupdesc,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f8a1514..02b525e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2039,7 +2039,8 @@ _copyRangeTblEntry(const RangeTblEntry *from)
 	COPY_SCALAR_FIELD(requiredPerms);
 	COPY_SCALAR_FIELD(checkAsUser);
 	COPY_BITMAPSET_FIELD(selectedCols);
-	COPY_BITMAPSET_FIELD(modifiedCols);
+	COPY_BITMAPSET_FIELD(insertedCols);
+	COPY_BITMAPSET_FIELD(updatedCols);
 	COPY_NODE_FIELD(securityQuals);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c86bafc..e9dc0f9 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2344,7 +2344,8 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
 	COMPARE_SCALAR_FIELD(requiredPerms);
 	COMPARE_SCALAR_FIELD(checkAsUser);
 	COMPARE_BITMAPSET_FIELD(selectedCols);
-	COMPARE_BITMAPSET_FIELD(modifiedCols);
+	COMPARE_BITMAPSET_FIELD(insertedCols);
+	COMPARE_BITMAPSET_FIELD(updatedCols);
 	COMPARE_NODE_FIELD(securityQuals);
 
 	return true;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 61b98c7..82d29fd 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2474,7 +2474,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_UINT_FIELD(requiredPerms);
 	WRITE_OID_FIELD(checkAsUser);
 	WRITE_BITMAPSET_FIELD(selectedCols);
-	WRITE_BITMAPSET_FIELD(modifiedCols);
+	WRITE_BITMAPSET_FIELD(insertedCols);
+	WRITE_BITMAPSET_FIELD(updatedCols);
 	WRITE_NODE_FIELD(securityQuals);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index e492ef6..fc31573 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1258,7 +1258,8 @@ _readRangeTblEntry(void)
 	READ_UINT_FIELD(requiredPerms);
 	READ_OID_FIELD(checkAsUser);
 	READ_BITMAPSET_FIELD(selectedCols);
-	READ_BITMAPSET_FIELD(modifiedCols);
+	READ_BITMAPSET_FIELD(insertedCols);
+	READ_BITMAPSET_FIELD(updatedCols);
 	READ_NODE_FIELD(securityQuals);
 
 	READ_DONE();
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index ec828cd..5dd48f8 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -367,9 +367,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob)
  *
  * In the flat rangetable, we zero out substructure pointers that are not
  * needed by the executor; this reduces the storage space and copying cost
- * for cached plans.  We keep only the alias and eref Alias fields, which
- * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
- * which are needed for executor-startup permissions checking and for
+ * for cached plans.  We keep only the alias and eref Alias fields, which are
+ * needed by EXPLAIN, and the selectedCols, insertedCols and updatedCols
+ * bitmaps, which are needed for executor-startup permissions checking and for
  * trigger event checking.
  */
 static void
diff --git a/src/backend/optimizer/prep/prepsecurity.c b/src/backend/optimizer/prep/prepsecurity.c
index 0be44c1..c4b61df 100644
--- a/src/backend/optimizer/prep/prepsecurity.c
+++ b/src/backend/optimizer/prep/prepsecurity.c
@@ -125,7 +125,8 @@ expand_security_quals(PlannerInfo *root, List *tlist)
 			rte->requiredPerms = 0;
 			rte->checkAsUser = InvalidOid;
 			rte->selectedCols = NULL;
-			rte->modifiedCols = NULL;
+			rte->insertedCols = NULL;
+			rte->updatedCols = NULL;
 
 			/*
 			 * For the most part, Vars referencing the original relation
@@ -224,7 +225,8 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
 			rte->requiredPerms = 0;
 			rte->checkAsUser = InvalidOid;
 			rte->selectedCols = NULL;
-			rte->modifiedCols = NULL;
+			rte->insertedCols = NULL;
+			rte->updatedCols = NULL;
 
 			/*
 			 * Now deal with any PlanRowMark on this RTE by requesting a lock
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 51b3da2..5859748 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1368,14 +1368,16 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 		 * if this is the parent table, leave copyObject's result alone.
 		 *
 		 * Note: we need to do this even though the executor won't run any
-		 * permissions checks on the child RTE.  The modifiedCols bitmap may
-		 * be examined for trigger-firing purposes.
+		 * permissions checks on the child RTE.  The insertedCols/updatedCols
+		 * bitmaps may be examined for trigger-firing purposes.
 		 */
 		if (childOID != parentOID)
 		{
 			childrte->selectedCols = translate_col_privs(rte->selectedCols,
 												   appinfo->translated_vars);
-			childrte->modifiedCols = translate_col_privs(rte->modifiedCols,
+			childrte->insertedCols = translate_col_privs(rte->insertedCols,
+												   appinfo->translated_vars);
+			childrte->updatedCols = translate_col_privs(rte->updatedCols,
 												   appinfo->translated_vars);
 		}
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a49d980..8893e8b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -735,7 +735,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 							  false);
 		qry->targetList = lappend(qry->targetList, tle);
 
-		rte->modifiedCols = bms_add_member(rte->modifiedCols,
+		rte->insertedCols = bms_add_member(rte->insertedCols,
 							  attr_num - FirstLowInvalidHeapAttributeNumber);
 
 		icols = lnext(icols);
@@ -2019,7 +2019,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 							  origTarget->location);
 
 		/* Mark the target column as requiring update permissions */
-		target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols,
+		target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
 								attrno - FirstLowInvalidHeapAttributeNumber);
 
 		origTargetList = lnext(origTargetList);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index ca560cc..562c2f5 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1218,7 +1218,8 @@ addRangeTableEntry(ParseState *pstate,
 	rte->requiredPerms = ACL_SELECT;
 	rte->checkAsUser = InvalidOid;		/* not set-uid by default, either */
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1272,7 +1273,8 @@ addRangeTableEntryForRelation(ParseState *pstate,
 	rte->requiredPerms = ACL_SELECT;
 	rte->checkAsUser = InvalidOid;		/* not set-uid by default, either */
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1351,7 +1353,8 @@ addRangeTableEntryForSubquery(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1606,7 +1609,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1679,7 +1683,8 @@ addRangeTableEntryForValues(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1748,7 +1753,8 @@ addRangeTableEntryForJoin(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
@@ -1849,7 +1855,8 @@ addRangeTableEntryForCTE(ParseState *pstate,
 	rte->requiredPerms = 0;
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * Add completed RTE to pstate's range table list, but not to join list
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 40458a0..86f1fc9 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1407,7 +1407,8 @@ ApplyRetrieveRule(Query *parsetree,
 			rte->requiredPerms = 0;
 			rte->checkAsUser = InvalidOid;
 			rte->selectedCols = NULL;
-			rte->modifiedCols = NULL;
+			rte->insertedCols = NULL;
+			rte->updatedCols = NULL;
 
 			/*
 			 * For the most part, Vars referencing the view should remain as
@@ -1470,12 +1471,14 @@ ApplyRetrieveRule(Query *parsetree,
 	subrte->requiredPerms = rte->requiredPerms;
 	subrte->checkAsUser = rte->checkAsUser;
 	subrte->selectedCols = rte->selectedCols;
-	subrte->modifiedCols = rte->modifiedCols;
+	subrte->insertedCols = rte->insertedCols;
+	subrte->updatedCols = rte->updatedCols;
 
 	rte->requiredPerms = 0;		/* no permission check on subquery itself */
 	rte->checkAsUser = InvalidOid;
 	rte->selectedCols = NULL;
-	rte->modifiedCols = NULL;
+	rte->insertedCols = NULL;
+	rte->updatedCols = NULL;
 
 	/*
 	 * If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as
@@ -2588,9 +2591,9 @@ rewriteTargetView(Query *parsetree, Relation view)
 	/*
 	 * For INSERT/UPDATE the modified columns must all be updatable. Note that
 	 * we get the modified columns from the query's targetlist, not from the
-	 * result RTE's modifiedCols set, since rewriteTargetListIU may have added
-	 * additional targetlist entries for view defaults, and these must also be
-	 * updatable.
+	 * result RTE's insertedCols and/or updatedCols set, since
+	 * rewriteTargetListIU may have added additional targetlist entries for
+	 * view defaults, and these must also be updatable.
 	 */
 	if (parsetree->commandType != CMD_DELETE)
 	{
@@ -2727,26 +2730,31 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 *
 	 * Initially, new_rte contains selectedCols permission check bits for all
 	 * base-rel columns referenced by the view, but since the view is a SELECT
-	 * query its modifiedCols is empty.  We set modifiedCols to include all
-	 * the columns the outer query is trying to modify, adjusting the column
-	 * numbers as needed.  But we leave selectedCols as-is, so the view owner
-	 * must have read permission for all columns used in the view definition,
-	 * even if some of them are not read by the outer query.  We could try to
-	 * limit selectedCols to only columns used in the transformed query, but
-	 * that does not correspond to what happens in ordinary SELECT usage of a
-	 * view: all referenced columns must have read permission, even if
-	 * optimization finds that some of them can be discarded during query
-	 * transformation.  The flattening we're doing here is an optional
-	 * optimization, too.  (If you are unpersuaded and want to change this,
-	 * note that applying adjust_view_column_set to view_rte->selectedCols is
-	 * clearly *not* the right answer, since that neglects base-rel columns
-	 * used in the view's WHERE quals.)
+	 * query its insertedCols/updatedCols is empty.  We set insertedCols and
+	 * updatedCols to include all the columns the outer query is trying to
+	 * modify, adjusting the column numbers as needed.  But we leave
+	 * selectedCols as-is, so the view owner must have read permission for all
+	 * columns used in the view definition, even if some of them are not read
+	 * by the outer query.  We could try to limit selectedCols to only columns
+	 * used in the transformed query, but that does not correspond to what
+	 * happens in ordinary SELECT usage of a view: all referenced columns must
+	 * have read permission, even if optimization finds that some of them can
+	 * be discarded during query transformation.  The flattening we're doing
+	 * here is an optional optimization, too.  (If you are unpersuaded and want
+	 * to change this, note that applying adjust_view_column_set to
+	 * view_rte->selectedCols is clearly *not* the right answer, since that
+	 * neglects base-rel columns used in the view's WHERE quals.)
 	 *
 	 * This step needs the modified view targetlist, so we have to do things
 	 * in this order.
 	 */
-	Assert(bms_is_empty(new_rte->modifiedCols));
-	new_rte->modifiedCols = adjust_view_column_set(view_rte->modifiedCols,
+	Assert(bms_is_empty(new_rte->insertedCols) &&
+		   bms_is_empty(new_rte->updatedCols));
+
+	new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols,
+												   view_targetlist);
+
+	new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols,
 												   view_targetlist);
 
 	/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 75e33ea..0e6fe26 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -739,11 +739,12 @@ typedef struct XmlSerialize
  *	  For SELECT/INSERT/UPDATE permissions, if the user doesn't have
  *	  table-wide permissions then it is sufficient to have the permissions
  *	  on all columns identified in selectedCols (for SELECT) and/or
- *	  modifiedCols (for INSERT/UPDATE; we can tell which from the query type).
- *	  selectedCols and modifiedCols are bitmapsets, which cannot have negative
- *	  integer members, so we subtract FirstLowInvalidHeapAttributeNumber from
- *	  column numbers before storing them in these fields.  A whole-row Var
- *	  reference is represented by setting the bit for InvalidAttrNumber.
+ *	  insertedCols and/or updatedCols (INSERT with ON CONFLICT UPDATE may
+ *	  have all 3).  selectedCols, insertedCols and updatedCols are
+ *	  bitmapsets, which cannot have negative integer members, so we subtract
+ *	  FirstLowInvalidHeapAttributeNumber from column numbers before storing
+ *	  them in these fields.  A whole-row Var reference is represented by
+ *	  setting the bit for InvalidAttrNumber.
  *--------------------
  */
 typedef enum RTEKind
@@ -838,7 +839,8 @@ typedef struct RangeTblEntry
 	AclMode		requiredPerms;	/* bitmask of required access permissions */
 	Oid			checkAsUser;	/* if valid, check access as this role */
 	Bitmapset  *selectedCols;	/* columns needing SELECT permission */
-	Bitmapset  *modifiedCols;	/* columns needing INSERT/UPDATE permission */
+	Bitmapset  *insertedCols;	/* columns needing INSERT permission */
+	Bitmapset  *updatedCols;	/* columns needing UPDATE permission */
 	List	   *securityQuals;	/* any security barrier quals to apply */
 } RangeTblEntry;
 
-- 
1.9.1

0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patchtext/x-patch; charset=US-ASCII; name=0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patchDownload
From c5929a90c07e855e66714489eded24f185d3ab8c Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <peter.geoghegan86@gmail.com>
Date: Tue, 3 Mar 2015 19:44:42 -0800
Subject: [PATCH 1/4] Support INSERT ... ON CONFLICT IGNORE

This non-standard INSERT clause allows DML statement authors to specify
that in the event of each of any of the tuples being inserted
duplicating an existing tuple in terms of a value or set of values
constrained by a unique index, an alternative IGNORE path may be taken
(the tuple slot proposed for insertion is skipped without raising an
error).  The implementation loops until either an insert occurs, or a
conclusively committed conflicting tuple is determined to exist.

This is implemented using a new infrastructure called "speculative
insertion".  (The approach to "Value locking" presenting here follows
design #2, as described on the value locking Postgres Wiki page).

Users optionally specify a single unique index to take the alternative
path on, which is inferred from a set of user-supplied column names (or
expressions).

To support logical decoding, transaction commit reassembly now
consolidates speculative insertion related changes.  Using a
"peek-ahead", it is determined if a speculative insertion went on to be
super deleted.  If that is not the case, then it is reported as an
ordinary insertion to decoding plugins.
---
 contrib/pg_stat_statements/pg_stat_statements.c    |   3 +
 contrib/postgres_fdw/deparse.c                     |   7 +-
 contrib/postgres_fdw/expected/postgres_fdw.out     |   4 +
 contrib/postgres_fdw/postgres_fdw.c                |  12 +-
 contrib/postgres_fdw/postgres_fdw.h                |   2 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql          |   2 +
 doc/src/sgml/ddl.sgml                              |  18 ++
 doc/src/sgml/fdwhandler.sgml                       |   5 +
 doc/src/sgml/keywords.sgml                         |   7 +
 doc/src/sgml/postgres-fdw.sgml                     |   6 +
 doc/src/sgml/ref/create_rule.sgml                  |   7 +-
 doc/src/sgml/ref/create_table.sgml                 |   5 +-
 doc/src/sgml/ref/create_view.sgml                  |  22 +-
 doc/src/sgml/ref/insert.sgml                       | 180 +++++++++++-
 doc/src/sgml/ref/set_constraints.sgml              |   5 +-
 src/backend/access/heap/heapam.c                   | 110 +++++--
 src/backend/access/nbtree/nbtinsert.c              |  32 ++-
 src/backend/catalog/index.c                        |  59 +++-
 src/backend/catalog/indexing.c                     |   2 +-
 src/backend/commands/constraint.c                  |   7 +-
 src/backend/commands/copy.c                        |   7 +-
 src/backend/commands/explain.c                     |   9 +
 src/backend/executor/README                        |  73 +++++
 src/backend/executor/execMain.c                    |  16 +-
 src/backend/executor/execUtils.c                   | 319 ++++++++++++++++++---
 src/backend/executor/nodeLockRows.c                |   9 +-
 src/backend/executor/nodeModifyTable.c             | 156 +++++++++-
 src/backend/nodes/copyfuncs.c                      |  36 +++
 src/backend/nodes/equalfuncs.c                     |  30 ++
 src/backend/nodes/nodeFuncs.c                      |  31 ++
 src/backend/nodes/outfuncs.c                       |   5 +
 src/backend/nodes/readfuncs.c                      |   3 +
 src/backend/optimizer/path/indxpath.c              |  66 +++++
 src/backend/optimizer/plan/createplan.c            |  13 +-
 src/backend/optimizer/plan/planner.c               |   2 +
 src/backend/optimizer/util/plancat.c               | 219 ++++++++++++++
 src/backend/parser/analyze.c                       |  29 +-
 src/backend/parser/gram.y                          |  48 +++-
 src/backend/parser/parse_clause.c                  | 160 +++++++++++
 src/backend/replication/logical/decode.c           |  11 +-
 src/backend/replication/logical/reorderbuffer.c    |  83 +++++-
 src/backend/rewrite/rewriteHandler.c               |  25 +-
 src/backend/storage/lmgr/lmgr.c                    |  88 ++++++
 src/backend/utils/adt/lockfuncs.c                  |   1 +
 src/backend/utils/time/tqual.c                     |  45 ++-
 src/include/access/heapam.h                        |   3 +-
 src/include/access/heapam_xlog.h                   |   2 +
 src/include/access/htup_details.h                  |  29 ++
 src/include/catalog/index.h                        |   2 +
 src/include/executor/executor.h                    |  19 +-
 src/include/nodes/execnodes.h                      |   8 +
 src/include/nodes/nodes.h                          |  14 +
 src/include/nodes/parsenodes.h                     |  35 ++-
 src/include/nodes/plannodes.h                      |   2 +
 src/include/optimizer/paths.h                      |   2 +
 src/include/optimizer/plancat.h                    |   2 +
 src/include/optimizer/planmain.h                   |   2 +-
 src/include/parser/kwlist.h                        |   2 +
 src/include/parser/parse_clause.h                  |   2 +
 src/include/replication/reorderbuffer.h            |  11 +-
 src/include/storage/lmgr.h                         |   5 +
 src/include/storage/lock.h                         |  10 +
 src/include/utils/snapshot.h                       |  11 +
 .../isolation/expected/insert-conflict-ignore.out  |  23 ++
 src/test/isolation/isolation_schedule              |   1 +
 .../isolation/specs/insert-conflict-ignore.spec    |  41 +++
 src/test/regress/expected/insert_conflict.out      |  55 ++++
 src/test/regress/expected/rules.out                |   9 +
 src/test/regress/expected/updatable_views.out      |   4 +
 src/test/regress/parallel_schedule                 |   1 +
 src/test/regress/serial_schedule                   |   1 +
 src/test/regress/sql/insert_conflict.sql           |  65 +++++
 src/test/regress/sql/rules.sql                     |   6 +
 src/test/regress/sql/updatable_views.sql           |   2 +
 74 files changed, 2215 insertions(+), 133 deletions(-)
 create mode 100644 src/test/isolation/expected/insert-conflict-ignore.out
 create mode 100644 src/test/isolation/specs/insert-conflict-ignore.spec
 create mode 100644 src/test/regress/expected/insert_conflict.out
 create mode 100644 src/test/regress/sql/insert_conflict.sql

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 95616b3..46f5189 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2198,6 +2198,9 @@ JumbleQuery(pgssJumbleState *jstate, Query *query)
 	JumbleRangeTable(jstate, query->rtable);
 	JumbleExpr(jstate, (Node *) query->jointree);
 	JumbleExpr(jstate, (Node *) query->targetList);
+	APP_JUMB(query->specClause);
+	JumbleExpr(jstate, (Node *) query->arbiterExpr);
+	JumbleExpr(jstate, query->arbiterWhere);
 	JumbleExpr(jstate, (Node *) query->returningList);
 	JumbleExpr(jstate, (Node *) query->groupClause);
 	JumbleExpr(jstate, query->havingQual);
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 59cb053..ca51586 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -847,8 +847,8 @@ appendWhereClause(StringInfo buf,
 void
 deparseInsertSql(StringInfo buf, PlannerInfo *root,
 				 Index rtindex, Relation rel,
-				 List *targetAttrs, List *returningList,
-				 List **retrieved_attrs)
+				 List *targetAttrs, bool ignore,
+				 List *returningList, List **retrieved_attrs)
 {
 	AttrNumber	pindex;
 	bool		first;
@@ -892,6 +892,9 @@ deparseInsertSql(StringInfo buf, PlannerInfo *root,
 	else
 		appendStringInfoString(buf, " DEFAULT VALUES");
 
+	if (ignore)
+		appendStringInfoString(buf, " ON CONFLICT IGNORE");
+
 	deparseReturningList(buf, root, rtindex, rel,
 					   rel->trigdesc && rel->trigdesc->trig_insert_after_row,
 						 returningList, retrieved_attrs);
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 4207fb3..47bb1ca 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -2327,6 +2327,10 @@ INSERT INTO ft1(c1, c2) VALUES(11, 12);  -- duplicate key
 ERROR:  duplicate key value violates unique constraint "t1_pkey"
 DETAIL:  Key ("C 1")=(11) already exists.
 CONTEXT:  Remote SQL command: INSERT INTO "S 1"."T 1"("C 1", c2, c3, c4, c5, c6, c7, c8) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
+ERROR:  relation "ft1" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 ERROR:  new row for relation "T 1" violates check constraint "c2positive"
 DETAIL:  Failing row contains (1111, -2, null, null, null, null, ft1       , null).
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 478e124..895274e 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1171,6 +1171,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	List	   *targetAttrs = NIL;
 	List	   *returningList = NIL;
 	List	   *retrieved_attrs = NIL;
+	bool		ignore = false;
 
 	initStringInfo(&sql);
 
@@ -1222,6 +1223,15 @@ postgresPlanForeignModify(PlannerInfo *root,
 	if (plan->returningLists)
 		returningList = (List *) list_nth(plan->returningLists, subplan_index);
 
+	if (root->parse->arbiterExpr)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("postgres_fdw does not support ON CONFLICT unique index inference")));
+	else if (plan->spec == SPEC_IGNORE)
+		ignore = true;
+	else if (plan->spec != SPEC_NONE)
+		elog(ERROR, "unexpected speculative specification: %d", (int) plan->spec);
+
 	/*
 	 * Construct the SQL command string.
 	 */
@@ -1229,7 +1239,7 @@ postgresPlanForeignModify(PlannerInfo *root,
 	{
 		case CMD_INSERT:
 			deparseInsertSql(&sql, root, resultRelation, rel,
-							 targetAttrs, returningList,
+							 targetAttrs, ignore, returningList,
 							 &retrieved_attrs);
 			break;
 		case CMD_UPDATE:
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 950c6f7..3763a57 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -60,7 +60,7 @@ extern void appendWhereClause(StringInfo buf,
 				  List **params);
 extern void deparseInsertSql(StringInfo buf, PlannerInfo *root,
 				 Index rtindex, Relation rel,
-				 List *targetAttrs, List *returningList,
+				 List *targetAttrs, bool ignore, List *returningList,
 				 List **retrieved_attrs);
 extern void deparseUpdateSql(StringInfo buf, PlannerInfo *root,
 				 Index rtindex, Relation rel,
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 4a23457..aba373e 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -372,6 +372,8 @@ UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *;
 ALTER TABLE "S 1"."T 1" ADD CONSTRAINT c2positive CHECK (c2 >= 0);
 
 INSERT INTO ft1(c1, c2) VALUES(11, 12);  -- duplicate key
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT IGNORE; -- works
+INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) IGNORE; -- unsupported
 INSERT INTO ft1(c1, c2) VALUES(1111, -2);  -- c2positive
 UPDATE ft1 SET c2 = -c2 WHERE c1 = 1;  -- c2positive
 
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 1c56f16..2b05e1d 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2439,9 +2439,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
   </para>
 
   <para>
+   There is limited inheritance support for <command>INSERT</command>
+   commands with <literal>ON CONFLICT IGNORE</> clauses.  Tables with
+   children are not generally accepted as targets.  One notable
+   exception is that such tables are accepted as targets for
+   <command>INSERT</command> commands with <literal>ON CONFLICT
+   IGNORE</> clauses, provided a unique index inference clause was
+   omitted (which implies that there is no concern about
+   <emphasis>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 <command>INSERT</command>
+   with <literal>ON CONFLICT IGNORE</>.
+  </para>
+
+  <para>
    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, <command>INSERT</command> with <literal>ON CONFLICT</>
+   unique index inference considers only unique constraints/indexes
+   directly associated with the child
+   table.
   </para>
 
   <para>
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index c1daa4b..0853078 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1014,6 +1014,11 @@ GetForeignServerByName(const char *name, bool missing_ok);
      source provides.
     </para>
 
+    <para>
+     <command>INSERT</> with an <literal>ON CONFLICT</> clause is not
+     supported with a unique index inference specification.
+    </para>
+
   </sect1>
 
  </chapter>
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 @@
     <entry></entry>
    </row>
    <row>
+    <entry><token>CONFLICT</token></entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
+   <row>
     <entry><token>CONNECT</token></entry>
     <entry></entry>
     <entry>reserved</entry>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 43adb61..81d4441 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -69,6 +69,12 @@
  </para>
 
  <para>
+  <filename>postgres_fdw</> supports <command>INSERT</command>
+  statements with an <literal>ON CONFLICT IGNORE</> clause, provided a
+  unique index inference specification is omitted.
+ </para>
+
+ <para>
   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 <filename>postgres_fdw</>
diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml
index 677766a..045f5d1 100644
--- a/doc/src/sgml/ref/create_rule.sgml
+++ b/doc/src/sgml/ref/create_rule.sgml
@@ -136,7 +136,12 @@ CREATE [ OR REPLACE ] RULE <replaceable class="parameter">name</replaceable> AS
      <para>
       The event is one of <literal>SELECT</literal>,
       <literal>INSERT</literal>, <literal>UPDATE</literal>, or
-      <literal>DELETE</literal>.
+      <literal>DELETE</literal>.  Note that an
+      <command>INSERT</command> containing an <literal>ON CONFLICT
+      IGNORE</literal> clause cannot be used on tables that have
+      either <literal>INSERT</literal> or <literal>UPDATE</literal>
+      rules.  Consider using an updatable view instead, which have
+      limited support for <literal>ON CONFLICT IGNORE</literal>.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 324d593..7f30fb1 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -717,7 +717,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
       <literal>EXCLUDE</>, and
       <literal>REFERENCES</> (foreign key) constraints accept this
       clause.  <literal>NOT NULL</> and <literal>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 <command>INSERT</command> statement
+      that includes an <literal>ON CONFLICT</> clause.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index 5dadab1..b1b4b02 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -286,8 +286,9 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
    <para>
     Simple views are automatically updatable: the system will allow
     <command>INSERT</>, <command>UPDATE</> and <command>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:
 
     <itemizedlist>
      <listitem>
@@ -383,6 +384,23 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     not need any permissions on the underlying base relations (see
     <xref linkend="rules-privileges">).
    </para>
+   <para>
+    <command>INSERT</command> with an <literal>ON CONFLICT</> clause
+    is only supported on updatable views under specific circumstances.
+    If a <quote>unique index inference specifictation</quote> is
+    provided, then updatable views are not supported.  For example:
+   </para>
+   <para>
+<programlisting>
+-- Unsupported:
+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;
+</programlisting>
+   </para>
   </refsect2>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml
index a3cccb9..f9d1436 100644
--- a/doc/src/sgml/ref/insert.sgml
+++ b/doc/src/sgml/ref/insert.sgml
@@ -24,6 +24,7 @@ PostgreSQL documentation
 [ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
 INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
     { DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) [, ...] | <replaceable class="PARAMETER">query</replaceable> }
+    [ ON CONFLICT [ ( { <replaceable class="parameter">column_name_index</replaceable> | ( <replaceable class="parameter">expression_index</replaceable> ) } [, ...] [ WHERE <replaceable class="PARAMETER">index_condition</replaceable> ] ) ] IGNORE]
     [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
@@ -59,6 +60,102 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
   </para>
 
   <para>
+   The optional <literal>ON CONFLICT</> clause specifies a path to
+   take as an alternative to raising a conflict related error.  The
+   alternative path is considered individually for each row proposed
+   for insertion;  it is taken (or not taken) once per row.
+   <literal>ON CONFLICT IGNORE</> simply avoids inserting any
+   individual row when it is determined that a conflict related error
+   would otherwise need to be raised.
+  </para>
+
+  <para>
+   <literal>ON CONFLICT IGNORE</> optionally accepts a
+   <emphasis>unique index inference</emphasis> specification, which
+   consists of one or more <replaceable
+   class="PARAMETER">column_name_index</replaceable> columns and/or
+   <replaceable class="PARAMETER">expression_index</replaceable>
+   expressions on columns, appearing between parenthesis.  These are
+   used to infer a single unique index to limit pre-checking for
+   conflicts to (if no appropriate index is available, an error is
+   raised).  A subset of the table to limit the check for conflicts to
+   can optionally also be specified using <replaceable
+   class="PARAMETER">index_condition</replaceable>.  Note that any
+   available unique index must only cover at least that subset in
+   order to be arbitrate taking the alternative path;  it need not
+   match exactly, and so a non-partial unique index that otherwise
+   matches is applicable.  Note that omitting the specification
+   indicates a total indifference to where any conflict could occur,
+   which isn't always appropriate.  At times, it may be desirable for
+   <literal>ON CONFLICT IGNORE</> to <emphasis>not</emphasis> suppress
+   a conflict related error associated with an index where that isn't
+   explicitly anticipated.
+  </para>
+
+  <para>
+   Columns and/or expressions appearing in a unique index inference
+   specification must match all the columns/expressions of some
+   existing unique index on <replaceable
+   class="PARAMETER">table_name</replaceable> - there can be no
+   columns/expressions from the unique index that do not appear in the
+   inference specification, nor can there be any columns/expressions
+   appearing in the inference specification that do not appear in the
+   unique index definition.  However, the order of the
+   columns/expressions in the index definition, or whether or not the
+   index definition specified <literal>NULLS FIRST</> or
+   <literal>NULLS LAST</>, or the internal sort order of each column
+   (whether <literal>DESC</> or <literal>ASC</> were specified) are
+   all irrelevant.  Deferred unique constraints are not supported as
+   arbiters of whether an alternative <literal>ON CONFLICT</> path
+   should be taken.
+  </para>
+
+  <para>
+   The definition of a conflict for the purposes of <literal>ON
+   CONFLICT</> is somewhat subtle, although the exact definition is
+   seldom of great interest.  A conflict is either a unique violation
+   from a unique constraint (or unique index), or an exclusion
+   violation from an exclusion constraint.  Only unique indexes can be
+   inferred with a unique index inference specification.  In contrast
+   to the rules around certain other SQL clauses, like the
+   <literal>DISTINCT</literal> clause, the definition of a duplicate
+   (a conflict) is based on whatever unique indexes happen to be
+   defined on columns on the table.  This means that if a user-defined
+   type has multiple sort orders, and the "equals" operator of any of
+   those available sort orders happens to be inconsistent (which goes
+   against an unenforced convention of
+   <productname>PostgreSQL</productname>), the exact behavior depends
+   on the choice of operator class when the unique index was created
+   initially, and not any other consideration such as the default
+   operator class for the type of each indexed column.  If there are
+   multiple unique indexes available that seem like equally suitable
+   candidates, but with inconsistent definitions of "equals", then the
+   system chooses whatever it estimates to be the cheapest one to use
+   as an arbiter of taking the alternative
+   <command>UPDATE</command>/<literal>IGNORE</literal> path.
+  </para>
+
+  <para>
+   The optional <replaceable
+   class="PARAMETER">index_condition</replaceable> can be used to
+   allow the inference specification to infer that a partial unique
+   index can be used.  Any unique index that otherwise satisfies the
+   inference specification, while also covering at least all the rows
+   in the table covered by <replaceable
+   class="PARAMETER">index_condition</replaceable> may be used.  It is
+   recommended that the partial index predicate of the unique index
+   intended to be used as the arbiter of taking the alternative path
+   be matched exactly, but this is not required.  Note that an error
+   will be raised if an arbiter unique index is chosen that does not
+   cover the tuple or tuples ultimately proposed for insertion.
+   However, an overly specific <replaceable
+   class="PARAMETER">index_condition</replaceable> does not imply that
+   arbitrating conflicts will be limited to the subset of rows covered
+   by the inferred unique index corresponding to <replaceable
+   class="PARAMETER">index_condition</replaceable>.
+  </para>
+
+  <para>
    The optional <literal>RETURNING</> clause causes <command>INSERT</>
    to compute and return value(s) based on each row actually inserted.
    This is primarily useful for obtaining values that were supplied by
@@ -127,6 +224,49 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="PARAMETER">column_name_index</replaceable></term>
+    <listitem>
+     <para>
+      The name of a <replaceable
+      class="PARAMETER">table_name</replaceable> column (with several
+      columns potentially named).  These are used to infer a
+      particular unique index defined on <replaceable
+      class="PARAMETER">table_name</replaceable>.  This requires
+      <literal>ON CONFLICT UPDATE</> and <literal>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 <literal>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.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">expression_index</replaceable></term>
+    <listitem>
+     <para>
+      Equivalent to <replaceable
+      class="PARAMETER">column_name_index</replaceable>, but used to
+      infer a particular expressional index instead.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">index_condition</replaceable></term>
+    <listitem>
+     <para>
+      Used to allow inference of partial unique indexes.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>DEFAULT VALUES</literal></term>
     <listitem>
      <para>
@@ -171,8 +311,9 @@ INSERT INTO <replaceable class="PARAMETER">table_name</replaceable> [ ( <replace
     <listitem>
      <para>
       An expression to be computed and returned by the <command>INSERT</>
-      command after each row is inserted.  The expression can use any
-      column names of the table named by <replaceable class="PARAMETER">table_name</replaceable>.
+      command after each row is inserted (not updated). The
+      expression can use any column names of the table named by
+      <replaceable class="PARAMETER">table_name</replaceable>.
       Write <literal>*</> to return all columns of the inserted row(s).
      </para>
     </listitem>
@@ -311,7 +452,37 @@ WITH upd AS (
     RETURNING *
 )
 INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
-</programlisting></para>
+</programlisting>
+  </para>
+  <para>
+   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.
+   Example assumes a unique index has been defined that constrains
+   values appearing in the <literal>did</literal> column (although
+   since the <literal>IGNORE</> variant was used, the specification of
+   columns to infer a unique index from is not mandatory):
+<programlisting>
+  INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH')
+  ON CONFLICT (did) IGNORE
+</programlisting>
+  </para>
+  <para>
+   Insert new distributor if possible;  otherwise
+   <literal>IGNORE</literal>.  Example assumes a unique index has been
+   defined that constrains values appearing in the
+   <literal>did</literal> column on a subset of rows where the
+   <literal>is_active</literal> boolean column evaluates to
+   <literal>true</literal>:
+<programlisting>
+  -- This statement could infer a partial unique index on did
+  -- with a predicate of WHERE is_active, but it could also
+  -- just use a regular unique constraint on did if that was
+  -- all that was available.
+  INSERT INTO distributors (did, dname) VALUES (9, 'Antwerp Design')
+  ON CONFLICT (did WHERE is_active) IGNORE
+</programlisting>
+  </para>
  </refsect1>
 
  <refsect1>
@@ -321,7 +492,8 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
    <command>INSERT</command> conforms to the SQL standard, except that
    the <literal>RETURNING</> clause is a
    <productname>PostgreSQL</productname> extension, as is the ability
-   to use <literal>WITH</> with <command>INSERT</>.
+   to use <literal>WITH</> with <command>INSERT</>, and the ability to
+   specify an alternative path with <literal>ON CONFLICT</>.
    Also, the case in
    which a column name list is omitted, but not all the columns are
    filled from the <literal>VALUES</> clause or <replaceable>query</>,
diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml
index 7c31871..ba2b5ba 100644
--- a/doc/src/sgml/ref/set_constraints.sgml
+++ b/doc/src/sgml/ref/set_constraints.sgml
@@ -69,7 +69,10 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
   <para>
    Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>,
    <literal>REFERENCES</> (foreign key), and <literal>EXCLUDE</>
-   constraints are affected by this setting.
+   constraints are affected by this setting.  Note that constraints
+   that are <literal>DEFERRED</literal> cannot be used as arbiters by
+   the <literal>ON CONFLICT</> clause that <command>INSERT</>
+   supports.
    <literal>NOT NULL</> and <literal>CHECK</> constraints are
    always checked immediately when a row is inserted or modified
    (<emphasis>not</> at the end of the statement).
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index cb6f8a3..5152e08 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1948,9 +1948,17 @@ heap_get_latest_tid(Relation relation,
 			*tid = ctid;
 
 		/*
+		 * Should not have followed a t_ctid chain and found a speculative
+		 * tuple that way
+		 */
+		Assert(!HeapTupleHeaderIsSpeculative(tp.t_data) ||
+			   !TransactionIdIsValid(priorXmax));
+
+		/*
 		 * If there's a valid t_ctid link, follow it, else we're done.
 		 */
 		if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsSpeculative(tp.t_data) ||
 			HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
 			ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
 		{
@@ -2156,7 +2164,11 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 		}
 
 		xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
-		xlrec.flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
+		xlrec.flags = 0;
+		if (all_visible_cleared)
+			xlrec.flags |= XLOG_HEAP_ALL_VISIBLE_CLEARED;
+		if (options & HEAP_INSERT_SPECULATIVE)
+			xlrec.flags |= XLOG_HEAP_SPECULATIVE_TUPLE;
 		Assert(ItemPointerGetBlockNumber(&heaptup->t_self) == BufferGetBlockNumber(buffer));
 
 		/*
@@ -2262,6 +2274,10 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
 	tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
 	tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	HeapTupleHeaderSetXmin(tup->t_data, xid);
+
+	if (options & HEAP_INSERT_SPECULATIVE)
+		HeapTupleHeaderSetSpeculative(tup->t_data);
+
 	if (options & HEAP_INSERT_FROZEN)
 		HeapTupleHeaderSetXminFrozen(tup->t_data);
 
@@ -2616,11 +2632,17 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
  * (the last only for HeapTupleSelfUpdated, since we
  * cannot obtain cmax from a combocid generated by another transaction).
  * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * If 'speculative' is true, caller requires that we "super-delete" a tuple we
+ * just inserted in the same command.  Instead of the normal visibility checks,
+ * we check that the tuple was inserted by the current transaction and given
+ * command id.  Also, instead of setting its xmax, we set xmin to invalid,
+ * making it immediately appear as dead to everyone.
  */
 HTSU_Result
 heap_delete(Relation relation, ItemPointer tid,
 			CommandId cid, Snapshot crosscheck, bool wait,
-			HeapUpdateFailureData *hufd)
+			HeapUpdateFailureData *hufd, bool speculative)
 {
 	HTSU_Result result;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2678,7 +2700,18 @@ heap_delete(Relation relation, ItemPointer tid,
 	tp.t_self = *tid;
 
 l1:
-	result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+	if (!speculative)
+	{
+		result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+	}
+	else
+	{
+		if (tp.t_data->t_choice.t_heap.t_xmin != xid ||
+			tp.t_data->t_choice.t_heap.t_field3.t_cid != cid)
+			elog(ERROR, "attempted to super-delete a tuple from other CID");
+		result = HeapTupleMayBeUpdated;
+	}
+
 
 	if (result == HeapTupleInvisible)
 	{
@@ -2823,12 +2856,19 @@ l1:
 	 * using our own TransactionId below, since some other backend could
 	 * incorporate our XID into a MultiXact immediately afterwards.)
 	 */
-	MultiXactIdSetOldestMember();
+	if (!speculative)
+	{
+		MultiXactIdSetOldestMember();
 
-	compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(tp.t_data),
-							  tp.t_data->t_infomask, tp.t_data->t_infomask2,
-							  xid, LockTupleExclusive, true,
-							  &new_xmax, &new_infomask, &new_infomask2);
+		compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(tp.t_data),
+								  tp.t_data->t_infomask, tp.t_data->t_infomask2,
+								  xid, LockTupleExclusive, true,
+								  &new_xmax, &new_infomask, &new_infomask2);
+	}
+	else
+	{
+		new_xmax = new_infomask = new_infomask2 = 0;
+	}
 
 	START_CRIT_SECTION();
 
@@ -2855,8 +2895,23 @@ l1:
 	tp.t_data->t_infomask |= new_infomask;
 	tp.t_data->t_infomask2 |= new_infomask2;
 	HeapTupleHeaderClearHotUpdated(tp.t_data);
-	HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
-	HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
+	/*
+	 * When killing a speculatively-inserted tuple, we set xmin to invalid
+	 * instead of setting xmax, to make the tuple clearly invisible to
+	 * everyone. In particular, we want HeapTupleSatisfiesDirty() to regard
+	 * the tuple as dead, so that another backend inserting a duplicate key
+	 * value won't unnecessarily wait for our transaction to finish.
+	 */
+	if (!speculative)
+	{
+		HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
+		HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
+	}
+	else
+	{
+		HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+	}
+
 	/* Make sure there is no forward chain link in t_ctid */
 	tp.t_data->t_ctid = tp.t_self;
 
@@ -2872,7 +2927,11 @@ l1:
 		if (RelationIsAccessibleInLogicalDecoding(relation))
 			log_heap_new_cid(relation, &tp);
 
-		xlrec.flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
+		xlrec.flags = 0;
+		if (all_visible_cleared)
+			xlrec.flags |= XLOG_HEAP_ALL_VISIBLE_CLEARED;
+		if (speculative)
+			xlrec.flags |= XLOG_HEAP_SPECULATIVE_TUPLE;
 		xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
 											  tp.t_data->t_infomask2);
 		xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
@@ -2977,7 +3036,7 @@ simple_heap_delete(Relation relation, ItemPointer tid)
 	result = heap_delete(relation, tid,
 						 GetCurrentCommandId(true), InvalidSnapshot,
 						 true /* wait for commit */ ,
-						 &hufd);
+						 &hufd, false);
 	switch (result)
 	{
 		case HeapTupleSelfUpdated:
@@ -3686,6 +3745,7 @@ l2:
 		/* Clear obsolete visibility flags ... */
 		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
 		oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+		oldtup.t_data->t_infomask2 &= ~HEAP_SPECULATIVE;
 		/* ... and store info about transaction updating this tuple */
 		Assert(TransactionIdIsValid(xmax_old_tuple));
 		HeapTupleHeaderSetXmax(oldtup.t_data, xmax_old_tuple);
@@ -4070,14 +4130,16 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
  *
  * Function result may be:
  *	HeapTupleMayBeUpdated: lock was successfully acquired
+ *	HeapTupleInvisible: lock failed because tuple instantaneously invisible
  *	HeapTupleSelfUpdated: lock failed because tuple updated by self
  *	HeapTupleUpdated: lock failed because tuple updated by other xact
  *	HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
  *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
  * See comments for struct HeapUpdateFailureData for additional info.
  *
  * See README.tuplock for a thorough explanation of this mechanism.
@@ -4115,8 +4177,15 @@ l3:
 
 	if (result == HeapTupleInvisible)
 	{
-		UnlockReleaseBuffer(*buffer);
-		elog(ERROR, "attempted to lock invisible tuple");
+		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
+
+		/*
+		 * This is possible, but only when locking a tuple for speculative
+		 * insertion.  We return this value here rather than throwing an error
+		 * in order to give that case the opportunity to throw a more specific
+		 * error.
+		 */
+		return HeapTupleInvisible;
 	}
 	else if (result == HeapTupleBeingUpdated)
 	{
@@ -7326,7 +7395,10 @@ heap_xlog_delete(XLogReaderState *record)
 		HeapTupleHeaderClearHotUpdated(htup);
 		fix_infomask_from_infobits(xlrec->infobits_set,
 								   &htup->t_infomask, &htup->t_infomask2);
-		HeapTupleHeaderSetXmax(htup, xlrec->xmax);
+		if (!(xlrec->flags & XLOG_HEAP_SPECULATIVE_TUPLE))
+			HeapTupleHeaderSetXmax(htup, xlrec->xmax);
+		else
+			HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
 		HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
 
 		/* Mark the page as a candidate for pruning */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 932c6f7..1a4e18d 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -51,7 +51,8 @@ static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
 static TransactionId _bt_check_unique(Relation rel, IndexTuple itup,
 				 Relation heapRel, Buffer buf, OffsetNumber offset,
 				 ScanKey itup_scankey,
-				 IndexUniqueCheck checkUnique, bool *is_unique);
+				 IndexUniqueCheck checkUnique, bool *is_unique,
+				 uint32 *speculativeToken);
 static void _bt_findinsertloc(Relation rel,
 				  Buffer *bufptr,
 				  OffsetNumber *offsetptr,
@@ -159,17 +160,27 @@ top:
 	 */
 	if (checkUnique != UNIQUE_CHECK_NO)
 	{
-		TransactionId xwait;
+		TransactionId	xwait;
+		uint32			speculativeToken;
 
 		offset = _bt_binsrch(rel, buf, natts, itup_scankey, false);
 		xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey,
-								 checkUnique, &is_unique);
+								 checkUnique, &is_unique, &speculativeToken);
 
 		if (TransactionIdIsValid(xwait))
 		{
 			/* Have to wait for the other guy ... */
 			_bt_relbuf(rel, buf);
-			XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex);
+			/*
+			 * If it's a speculative insertion, wait for it to finish (ie.
+			 * to go ahead with the insertion, or kill the tuple). Otherwise
+			 * wait for the transaction to finish as usual.
+			 */
+			if (speculativeToken)
+				SpeculativeInsertionWait(xwait, speculativeToken);
+			else
+				XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex);
+
 			/* start over... */
 			_bt_freestack(stack);
 			goto top;
@@ -211,9 +222,12 @@ top:
  * also point to end-of-page, which means that the first tuple to check
  * is the first tuple on the next page.
  *
- * 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
- * conflict is detected, no return --- just ereport().
+ * 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
+ * conflict is detected, no return --- just ereport(). If an xact ID is
+ * returned, and the conflicting tuple still has a speculative insertion in
+ * progress, *speculativeToken is set to non-zero, and the caller can wait for
+ * the verdict on the insertion using SpeculativeInsertionWait().
  *
  * However, if checkUnique == UNIQUE_CHECK_PARTIAL, we always return
  * InvalidTransactionId because we don't want to wait.  In this case we
@@ -223,7 +237,8 @@ top:
 static TransactionId
 _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 				 Buffer buf, OffsetNumber offset, ScanKey itup_scankey,
-				 IndexUniqueCheck checkUnique, bool *is_unique)
+				 IndexUniqueCheck checkUnique, bool *is_unique,
+				 uint32 *speculativeToken)
 {
 	TupleDesc	itupdesc = RelationGetDescr(rel);
 	int			natts = rel->rd_rel->relnatts;
@@ -340,6 +355,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 						if (nbuf != InvalidBuffer)
 							_bt_relbuf(rel, nbuf);
 						/* Tell _bt_doinsert to wait... */
+						*speculativeToken = SnapshotDirty.speculativeToken;
 						return xwait;
 					}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f85ed93..e986d7e 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1662,6 +1662,10 @@ BuildIndexInfo(Relation index)
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
+	/* assume not doing speculative insertion for now */
+	ii->ii_UniqueOps = NULL;
+	ii->ii_UniqueProcs = NULL;
+	ii->ii_UniqueStrats = NULL;
 
 	/* initialize index-build state to default */
 	ii->ii_Concurrent = false;
@@ -1671,6 +1675,53 @@ BuildIndexInfo(Relation index)
 }
 
 /* ----------------
+ *		AddUniqueSpeculative
+ *			Append extra state to IndexInfo record
+ *
+ * For unique indexes, we usually don't want to add info to the IndexInfo for
+ * checking uniqueness, since the B-Tree AM handles that directly.  However, in
+ * the case of speculative insertion, external support is required.
+ *
+ * Do this processing here rather than in BuildIndexInfo() to save the common
+ * non-speculative cases the overhead they'd otherwise incur.
+ * ----------------
+ */
+void
+AddUniqueSpeculative(Relation index, IndexInfo *ii)
+{
+	int			ncols = index->rd_rel->relnatts;
+	int			i;
+
+	/*
+	 * fetch info for checking unique indexes
+	 */
+	Assert(ii->ii_Unique);
+
+	if (index->rd_rel->relam != BTREE_AM_OID)
+		elog(ERROR, "unexpected non-btree speculative unique index");
+
+	ii->ii_UniqueOps = (Oid *) palloc(sizeof(Oid) * ncols);
+	ii->ii_UniqueProcs = (Oid *) palloc(sizeof(Oid) * ncols);
+	ii->ii_UniqueStrats = (uint16 *) palloc(sizeof(uint16) * ncols);
+
+	/*
+	 * We have to look up the operator's strategy number.  This
+	 * provides a cross-check that the operator does match the index.
+	 */
+	/* We need the func OIDs and strategy numbers too */
+	for (i = 0; i < ncols; i++)
+	{
+		ii->ii_UniqueStrats[i] = BTEqualStrategyNumber;
+		ii->ii_UniqueOps[i] =
+			get_opfamily_member(index->rd_opfamily[i],
+								index->rd_opcintype[i],
+								index->rd_opcintype[i],
+								ii->ii_UniqueStrats[i]);
+		ii->ii_UniqueProcs[i] = get_opcode(ii->ii_UniqueOps[i]);
+	}
+}
+
+/* ----------------
  *		FormIndexDatum
  *			Construct values[] and isnull[] arrays for a new index tuple.
  *
@@ -2606,10 +2657,10 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * Check that this tuple has no conflicts.
 		 */
-		check_exclusion_constraint(heapRelation,
-								   indexRelation, indexInfo,
-								   &(heapTuple->t_self), values, isnull,
-								   estate, true, false);
+		check_exclusion_or_unique_constraint(heapRelation, indexRelation,
+											 indexInfo, &(heapTuple->t_self),
+											 values, isnull, estate, true,
+											 false, true, NULL);
 	}
 
 	heap_endscan(scan);
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index fe123ad..0231084 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -46,7 +46,7 @@ CatalogOpenIndexes(Relation heapRel)
 	resultRelInfo->ri_RelationDesc = heapRel;
 	resultRelInfo->ri_TrigDesc = NULL;	/* we don't fire triggers */
 
-	ExecOpenIndices(resultRelInfo);
+	ExecOpenIndices(resultRelInfo, false);
 
 	return resultRelInfo;
 }
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 561d8fa..d5ab12f 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -170,9 +170,10 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 		 * For exclusion constraints we just do the normal check, but now it's
 		 * okay to throw error.
 		 */
-		check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
-								   &(new_row->t_self), values, isnull,
-								   estate, false, false);
+		check_exclusion_or_unique_constraint(trigdata->tg_relation, indexRel,
+											 indexInfo, &(new_row->t_self),
+											 values, isnull, estate, false,
+											 false, true, NULL);
 	}
 
 	/*
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 92ff632..f85bd60 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2283,7 +2283,7 @@ CopyFrom(CopyState cstate)
 					  1,		/* dummy rangetable index */
 					  0);
 
-	ExecOpenIndices(resultRelInfo);
+	ExecOpenIndices(resultRelInfo, false);
 
 	estate->es_result_relations = resultRelInfo;
 	estate->es_num_result_relations = 1;
@@ -2438,7 +2438,8 @@ CopyFrom(CopyState cstate)
 
 				if (resultRelInfo->ri_NumIndices > 0)
 					recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-														   estate);
+														   estate, false,
+														   InvalidOid);
 
 				/* AFTER ROW INSERT Triggers */
 				ExecARInsertTriggers(estate, resultRelInfo, tuple,
@@ -2552,7 +2553,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
 			ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
 			recheckIndexes =
 				ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
-									  estate);
+									  estate, false, InvalidOid);
 			ExecARInsertTriggers(estate, resultRelInfo,
 								 bufferedTuples[i],
 								 recheckIndexes);
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 315a528..d88c7fa 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2196,6 +2196,15 @@ static void
 ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
 {
 	ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
+
+	if (plan->arbiterIndex != InvalidOid)
+	{
+		char	   *indexname = get_rel_name(plan->arbiterIndex);
+
+		/* nothing to do for text format explains */
+		if (es->format != EXPLAIN_FORMAT_TEXT && indexname != NULL)
+			ExplainPropertyText("Arbiter Index", indexname, es);
+	}
 }
 
 /*
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 8afa1e3..862c312 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -200,3 +200,76 @@ is no explicit prohibition on SRFs in UPDATE, but the net effect will be
 that only the first result row of an SRF counts, because all subsequent
 rows will result in attempts to re-update an already updated target row.
 This is historical behavior and seems not worth changing.)
+
+Speculative insertion
+---------------------
+
+Speculative insertion is a process that the executor manages for the benefit of
+INSERT...ON CONFLICT IGNORE.  Supported indexes include nbtree unique
+indexes (nbtree is currently the only amcanunique index access method), or
+exclusion constraint indexes (exclusion constraints are considered a
+generalization of unique constraints).
+
+The primary user-visible goal for INSERT ... ON CONFLICT is to guarantee either
+an insert, or a conclusive determination that an insert cannot go ahead (due to
+a conclusively committed/visible conflict).  A would-be conflict (and the
+associated index) are the arbiters of whether or not the alternative (IGNORE)
+path is taken.  The implementation more or less tries to insert until one or
+the other of those two outcomes is reached.  There are some non-obvious hazards
+involved that are carefully avoided.  These hazards relate to concurrent
+activity causing conflicts for the implementation, which must be handled.
+
+The index is the authoritative source of truth for whether there is or is not a
+conflict, for unique index enforcement in general, and for speculative
+insertion in particular.  The heap must still be considered, though, not least
+since it alone has authoritative visibility information.  Through looping, we
+hope to overcome the disconnect between the heap and the arbiter index.  We
+must lock the row, and then verify that there is no conflict.  Only then do we
+UPDATE.  Theoretically, some individual session could loop forever, although
+under high concurrency one session always proceeds.
+
+The first step in the loop is to perform a pre-check.  The indexes are scanned
+for existing conflicting values.  At this point, we may have to wait until the
+end of another xact (or xact's promise token -- more on that later), iff it
+isn't immediately conclusive that there is or is not a conflict (when we finish
+the pre-check, there is a conclusion about there either being or
+not being a conflict).
+
+The second step (skipped when a conflict is found) is to insert a heap tuple
+and related index tuples opportunistically.  This uses the same mechanism as
+deferred unique indexes, and so we never wait for a possibly conflicting xact
+to commit or abort (unlike with conventional unique index insertion) -- we
+simply detect a possible conflict.
+
+When opportunistically inserting during the second step, we are not logically
+inserting a tuple as such.  Rather, the process is somewhat similar to the
+conventional unique index insertion steps taken within the nbtree AM, where we
+must briefly lock the *value* being inserted:  in that codepath, the value
+proposed for insertion is for an instant locked *in the abstract*, by way of a
+buffer lock on "the first leaf page the value could be on".  Then, having
+established the right to physically insert, do so (or throw an error).  For
+speculative insertion, if no conflict occurs during the insertion (which is
+usually the case, since it was just determined in the first step that there was
+no conflict), then we're done.  Otherwise, we must restart (and likely find the
+same conflict tuple during the first step of the new iteration). But a
+counter-intuitive step must be taken first (which is what makes this whole
+dance similar to conventional nbtree "value locking").
+
+We must "super delete" the tuple when the opportunistic insertion finds a
+conflict.  This means that it immediately becomes invisible to all snapshot
+types, and immediately becomes reclaimable by VACUUM.  Other backends
+(speculative inserters or ordinary inserters) know to not wait on our
+transaction end when they encounter an optimistically inserted "promise tuple".
+Rather, they wait on a corresponding promise token lock, which we hold only for
+as long as opportunistically inserting.  We release the lock when done
+opportunistically inserting (and after "super deleting", if that proved
+necessary), releasing our waiters (who will ordinarily re-find our promise
+tuple as a bona fide tuple, or occasionally will find that they can insert
+after all).  It's important that other xacts not wait on the end of our xact
+until we've established that we've successfully and conclusively inserted
+logically (or established that there was an insertion conflict, and cleaned up
+after it by "super deleting").  Otherwise, concurrent speculative inserters
+could be involved in "unprincipled deadlocks":  deadlocks where there is no
+user-visible mutual dependency, and yet an implementation related mutual
+dependency is unexpectedly introduced.  The user might be left with no
+reasonable way of avoiding these deadlocks, which would not be okay.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ad7e207..0be893d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2093,7 +2093,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 			 * the latest version of the row was deleted, so we need do
 			 * nothing.  (Should be safe to examine xmin without getting
 			 * buffer's content lock, since xmin never changes in an existing
-			 * tuple.)
+			 * non-promise tuple.)
 			 */
 			if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
 									 priorXmax))
@@ -2174,11 +2174,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 					 * case, so as to avoid the "Halloween problem" of
 					 * repeated update attempts.  In the latter case it might
 					 * be sensible to fetch the updated tuple instead, but
-					 * doing so would require changing heap_lock_tuple as well
-					 * as heap_update and heap_delete to not complain about
-					 * updating "invisible" tuples, which seems pretty scary.
-					 * So for now, treat the tuple as deleted and do not
-					 * process.
+					 * doing so would require changing heap_update and
+					 * heap_delete to not complain about updating "invisible"
+					 * tuples, which seems pretty scary (heap_lock_tuple will
+					 * not complain, but few callers expect HeapTupleInvisible,
+					 * and we're not one of them).  So for now, treat the tuple
+					 * as deleted and do not process.
 					 */
 					ReleaseBuffer(buffer);
 					return NULL;
@@ -2193,6 +2194,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 						ereport(ERROR,
 								(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 								 errmsg("could not serialize access due to concurrent update")));
+
+					/* Should not encounter speculative tuple on recheck */
+					Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
 					if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
 					{
 						/* it was updated, so look at the updated version */
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 022041b..0ab018b 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -44,6 +44,7 @@
 
 #include "access/relscan.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "catalog/index.h"
 #include "executor/execdebug.h"
 #include "nodes/nodeFuncs.h"
@@ -885,7 +886,7 @@ ExecCloseScanRelation(Relation scanrel)
  * ----------------------------------------------------------------
  */
 void
-ExecOpenIndices(ResultRelInfo *resultRelInfo)
+ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
 {
 	Relation	resultRelation = resultRelInfo->ri_RelationDesc;
 	List	   *indexoidlist;
@@ -938,6 +939,13 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 		/* extract index key information from the index's pg_index info */
 		ii = BuildIndexInfo(indexDesc);
 
+		/*
+		 * Iff the indexes are to be used for speculative insertion, add extra
+		 * information required by unique index entries
+		 */
+		if (speculative && ii->ii_Unique)
+			AddUniqueSpeculative(indexDesc, ii);
+
 		relationDescs[i] = indexDesc;
 		indexInfoArray[i] = ii;
 		i++;
@@ -990,7 +998,8 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
  *
  *		This returns a list of index OIDs for any unique or exclusion
  *		constraints that are deferred and that had
- *		potential (unconfirmed) conflicts.
+ *		potential (unconfirmed) conflicts. (if noDupErr == true, the
+ *		same is done for non-deferred constraints)
  *
  *		CAUTION: this must not be called for a HOT update.
  *		We can't defend against that here for lack of info.
@@ -1000,7 +1009,9 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
 List *
 ExecInsertIndexTuples(TupleTableSlot *slot,
 					  ItemPointer tupleid,
-					  EState *estate)
+					  EState *estate,
+					  bool noDupErr,
+					  Oid arbiterIdx)
 {
 	List	   *result = NIL;
 	ResultRelInfo *resultRelInfo;
@@ -1070,7 +1081,17 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 
 			/* Skip this index-update if the predicate isn't satisfied */
 			if (!ExecQual(predicate, econtext, false))
+			{
+				if (arbiterIdx == indexRelation->rd_index->indexrelid)
+					ereport(ERROR,
+							(errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
+							 errmsg("partial arbiter unique index has predicate that does not cover tuple proposed for insertion"),
+							 errdetail("ON CONFLICT inference clause implies that the tuple proposed for insertion must be covered by predicate for partial index \"%s\".",
+									   RelationGetRelationName(indexRelation)),
+							 errtableconstraint(heapRelation,
+												RelationGetRelationName(indexRelation))));
 				continue;
+			}
 		}
 
 		/*
@@ -1092,9 +1113,16 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		 * 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.
+		 *
+		 * For a speculative insertion (used by INSERT ... ON CONFLICT), just
+		 * detect possible non-uniqueness, and tell the caller if it failed.
 		 */
 		if (!indexRelation->rd_index->indisunique)
 			checkUnique = UNIQUE_CHECK_NO;
+		else if (noDupErr && arbiterIdx == InvalidOid)
+			checkUnique = UNIQUE_CHECK_PARTIAL;
+		else if (noDupErr && arbiterIdx == indexRelation->rd_index->indexrelid)
+			checkUnique = UNIQUE_CHECK_PARTIAL;
 		else if (indexRelation->rd_index->indimmediate)
 			checkUnique = UNIQUE_CHECK_YES;
 		else
@@ -1112,8 +1140,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		 * If the index has an associated exclusion constraint, check that.
 		 * This is simpler than the process for uniqueness checks since we
 		 * always insert first and then check.  If the constraint is deferred,
-		 * we check now anyway, but don't throw error on violation; instead
-		 * we'll queue a recheck event.
+		 * we check now anyway, but don't throw error on violation or wait for
+		 * a conclusive outcome from a concurrent insertion; instead we'll
+		 * queue a recheck event.  Similarly, noDupErr callers (speculative
+		 * inserters) will recheck later, and wait for a conclusive outcome
+		 * then.
 		 *
 		 * An index for an exclusion constraint can't also be UNIQUE (not an
 		 * essential property, we just don't allow it in the grammar), so no
@@ -1121,13 +1152,15 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		 */
 		if (indexInfo->ii_ExclusionOps != NULL)
 		{
-			bool		errorOK = !indexRelation->rd_index->indimmediate;
+			bool		violationOK = (!indexRelation->rd_index->indimmediate ||
+									   noDupErr);
 
 			satisfiesConstraint =
-				check_exclusion_constraint(heapRelation,
-										   indexRelation, indexInfo,
-										   tupleid, values, isnull,
-										   estate, false, errorOK);
+				check_exclusion_or_unique_constraint(heapRelation,
+													 indexRelation, indexInfo,
+													 tupleid, values, isnull,
+													 estate, false,
+													 violationOK, false, NULL);
 		}
 
 		if ((checkUnique == UNIQUE_CHECK_PARTIAL ||
@@ -1135,7 +1168,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 			!satisfiesConstraint)
 		{
 			/*
-			 * The tuple potentially violates the uniqueness or exclusion
+			 * The tuple potentially violates the unique index or exclusion
 			 * constraint, so make a note of the index so that we can re-check
 			 * it later.
 			 */
@@ -1146,18 +1179,154 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 	return result;
 }
 
+/* ----------------------------------------------------------------
+ *		ExecCheckIndexConstraints
+ *
+ *		This routine checks if a tuple violates any unique or
+ *		exclusion constraints. If no conflict, returns true.
+ *		Otherwise returns false, and the TID of the conflicting
+ *		tuple is returned in *conflictTid
+ *
+ *		Note that this doesn't lock the values in any way, so it's
+ *		possible that a conflicting tuple is inserted immediately
+ *		after this returns, and a later insert with the same values
+ *		still conflicts. But this can be used for a pre-check before
+ *		insertion.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecCheckIndexConstraints(TupleTableSlot *slot,
+						  EState *estate, ItemPointer conflictTid,
+						  Oid arbiterIdx)
+{
+	ResultRelInfo *resultRelInfo;
+	int			i;
+	int			numIndices;
+	RelationPtr relationDescs;
+	Relation	heapRelation;
+	IndexInfo **indexInfoArray;
+	ExprContext *econtext;
+	Datum		values[INDEX_MAX_KEYS];
+	bool		isnull[INDEX_MAX_KEYS];
+	ItemPointerData invalidItemPtr;
+	bool		checkedIndex = false;
+
+	ItemPointerSetInvalid(conflictTid);
+	ItemPointerSetInvalid(&invalidItemPtr);
+
+	/*
+	 * Get information from the result relation info structure.
+	 */
+	resultRelInfo = estate->es_result_relation_info;
+	numIndices = resultRelInfo->ri_NumIndices;
+	relationDescs = resultRelInfo->ri_IndexRelationDescs;
+	indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
+	heapRelation = resultRelInfo->ri_RelationDesc;
+
+	/*
+	 * We will use the EState's per-tuple context for evaluating predicates
+	 * and index expressions (creating it if it's not already there).
+	 */
+	econtext = GetPerTupleExprContext(estate);
+
+	/* Arrange for econtext's scan tuple to be the tuple under test */
+	econtext->ecxt_scantuple = slot;
+
+	/*
+	 * for each index, form and insert the index tuple
+	 */
+	for (i = 0; i < numIndices; i++)
+	{
+		Relation	indexRelation = relationDescs[i];
+		IndexInfo  *indexInfo;
+		bool		satisfiesConstraint;
+
+		if (indexRelation == NULL)
+			continue;
+
+		indexInfo = indexInfoArray[i];
+
+		if (!indexInfo->ii_Unique && !indexInfo->ii_ExclusionOps)
+			continue;
+
+		/* If the index is marked as read-only, ignore it */
+		if (!indexInfo->ii_ReadyForInserts)
+			continue;
+
+		/* When specific arbiter index requested, only examine it */
+		if (arbiterIdx != InvalidOid &&
+			arbiterIdx != indexRelation->rd_index->indexrelid)
+			continue;
+
+		checkedIndex = true;
+
+		/* Check for partial index */
+		if (indexInfo->ii_Predicate != NIL)
+		{
+			List	   *predicate;
+
+			/*
+			 * If predicate state not set up yet, create it (in the estate's
+			 * per-query context)
+			 */
+			predicate = indexInfo->ii_PredicateState;
+			if (predicate == NIL)
+			{
+				predicate = (List *)
+					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
+									estate);
+				indexInfo->ii_PredicateState = predicate;
+			}
+
+			/* Skip this index-update if the predicate isn't satisfied */
+			if (!ExecQual(predicate, econtext, false))
+				continue;
+		}
+
+		/*
+		 * FormIndexDatum fills in its values and isnull parameters with the
+		 * appropriate values for the column(s) of the index.
+		 */
+		FormIndexDatum(indexInfo,
+					   slot,
+					   estate,
+					   values,
+					   isnull);
+
+		satisfiesConstraint =
+			check_exclusion_or_unique_constraint(heapRelation, indexRelation,
+												 indexInfo, &invalidItemPtr,
+												 values, isnull, estate, false,
+												 true, true, conflictTid);
+		if (!satisfiesConstraint)
+			return false;
+
+		/* If this was a user-specified arbiter index, we're done */
+		if (arbiterIdx == indexRelation->rd_index->indexrelid)
+			break;
+	}
+
+	if (arbiterIdx != InvalidOid && !checkedIndex)
+		elog(ERROR, "unexpected failure to find arbiter unique index");
+
+	return true;
+}
+
 /*
- * Check for violation of an exclusion constraint
+ * Check for violation of an exclusion or unique constraint
  *
  * heap: the table containing the new tuple
  * index: the index supporting the exclusion constraint
  * indexInfo: info about the index, including the exclusion properties
- * tupleid: heap TID of the new tuple we have just inserted
+ * tupleid: heap TID of the new tuple we have just inserted (invalid if we
+ *		haven't inserted a new tuple yet)
  * values, isnull: the *index* column values computed for the new tuple
  * estate: an EState we can do evaluation in
  * newIndex: if true, we are trying to build a new index (this affects
  *		only the wording of error messages)
  * errorOK: if true, don't throw error for violation
+ * wait: if true, wait for conflicting transaction to finish, even if !errorOK
+ * conflictTid: if not-NULL, the TID of conflicting tuple is returned here.
  *
  * Returns true if OK, false if actual or potential violation
  *
@@ -1167,16 +1336,25 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
  * is convenient for deferred exclusion checks; we need not bother queuing
  * a deferred event if there is definitely no conflict at insertion time.
  *
- * When errorOK is false, we'll throw error on violation, so a false result
+ * When violationOK is false, we'll throw error on violation, so a false result
  * is impossible.
+ *
+ * Note: The indexam is normally responsible for checking unique constraints,
+ * so this normally only needs to be used for exclusion constraints. But this
+ * function is also called when doing a "pre-check" for conflicts, for the
+ * benefit of speculative insertion.  Caller may request that conflict TID be
+ * set, to take further steps.
  */
 bool
-check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
-						   ItemPointer tupleid, Datum *values, bool *isnull,
-						   EState *estate, bool newIndex, bool errorOK)
+check_exclusion_or_unique_constraint(Relation heap, Relation index,
+									 IndexInfo *indexInfo, ItemPointer tupleid,
+									 Datum *values, bool *isnull,
+									 EState *estate, bool newIndex,
+									 bool violationOK, bool wait,
+									 ItemPointer conflictTid)
 {
-	Oid		   *constr_procs = indexInfo->ii_ExclusionProcs;
-	uint16	   *constr_strats = indexInfo->ii_ExclusionStrats;
+	Oid		   *constr_procs;
+	uint16	   *constr_strats;
 	Oid		   *index_collations = index->rd_indcollation;
 	int			index_natts = index->rd_index->indnatts;
 	IndexScanDesc index_scan;
@@ -1190,6 +1368,17 @@ check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
 	TupleTableSlot *existing_slot;
 	TupleTableSlot *save_scantuple;
 
+	if (indexInfo->ii_ExclusionOps)
+	{
+		constr_procs = indexInfo->ii_ExclusionProcs;
+		constr_strats = indexInfo->ii_ExclusionStrats;
+	}
+	else
+	{
+		constr_procs = indexInfo->ii_UniqueProcs;
+		constr_strats = indexInfo->ii_UniqueStrats;
+	}
+
 	/*
 	 * If any of the input values are NULL, the constraint check is assumed to
 	 * pass (i.e., we assume the operators are strict).
@@ -1254,7 +1443,8 @@ retry:
 		/*
 		 * Ignore the entry for the tuple we're trying to check.
 		 */
-		if (ItemPointerEquals(tupleid, &tup->t_self))
+		if (ItemPointerIsValid(tupleid) &&
+			ItemPointerEquals(tupleid, &tup->t_self))
 		{
 			if (found_self)		/* should not happen */
 				elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -1284,17 +1474,6 @@ retry:
 		}
 
 		/*
-		 * At this point we have either a conflict or a potential conflict. If
-		 * we're not supposed to raise error, just return the fact of the
-		 * potential conflict without waiting to see if it's real.
-		 */
-		if (errorOK)
-		{
-			conflict = true;
-			break;
-		}
-
-		/*
 		 * If an in-progress transaction is affecting the visibility of this
 		 * tuple, we need to wait for it to complete and then recheck.  For
 		 * simplicity we do rechecking by just restarting the whole scan ---
@@ -1305,18 +1484,87 @@ retry:
 		xwait = TransactionIdIsValid(DirtySnapshot.xmin) ?
 			DirtySnapshot.xmin : DirtySnapshot.xmax;
 
+		/*
+		 * At this point we have either a conflict or a potential conflict. If
+		 * we're not supposed to raise error, just return the fact of the
+		 * potential conflict without waiting to see if it's real.
+		 */
+		if (violationOK && !wait)
+		{
+			/*
+			 * For unique indexes, detecting conflict is coupled with physical
+			 * index tuple insertion, so we won't be called for recheck
+			 */
+			Assert(!indexInfo->ii_Unique);
+
+			conflict = true;
+			if (conflictTid)
+				*conflictTid = tup->t_self;
+
+			/*
+			 * Livelock insurance.
+			 *
+			 * When doing a speculative insertion pre-check, we cannot have an
+			 * "unprincipled deadlock" with another session, fundamentally
+			 * because there is no possible mutual dependency, since we only
+			 * hold a lock on our token, without attempting to lock anything
+			 * else (maybe this is not the first iteration, but no matter;
+			 * we'll have super deleted and released insertion token lock if
+			 * so, and all locks needed are already held.  Also, our XID lock
+			 * is irrelevant.)
+			 *
+			 * In the second phase, where there is a re-check for conflicts,
+			 * we can't deadlock either (we never lock another thing, since we
+			 * don't wait in that phase).  However, a theoretical livelock
+			 * hazard exists:  Two sessions could each see each other's
+			 * conflicting tuple, and each could go and delete, retrying
+			 * forever.
+			 *
+			 * To break the mutual dependency, we may wait on the other xact
+			 * here over our caller's request to not do so (in the second
+			 * phase).  This does not imply the risk of unprincipled deadlocks
+			 * either, because if we end up unexpectedly waiting, the other
+			 * session will super delete its own tuple *before* releasing its
+			 * token lock and freeing us, and without attempting to wait on us
+			 * to release our token lock.  We'll take another iteration here,
+			 * after waiting on the other session's token, not find a conflict
+			 * this time, and then proceed (assuming we're the oldest XID).
+			 *
+			 * N.B.:  Unprincipled deadlocks are still theoretically possible
+			 * with non-speculative insertion with exclusion constraints, but
+			 * this seems inconsequential, since an error was inevitable for
+			 * one of the sessions anyway.  We only worry about speculative
+			 * insertion's problems, since they're likely with idiomatic
+			 * usage.
+			 */
+			if (TransactionIdPrecedes(xwait, GetCurrentTransactionId()))
+				break;  /* go and super delete/restart speculative insertion */
+		}
+
 		if (TransactionIdIsValid(xwait))
 		{
 			ctid_wait = tup->t_data->t_ctid;
 			index_endscan(index_scan);
-			XactLockTableWait(xwait, heap, &ctid_wait,
-							  XLTW_RecheckExclusionConstr);
+			if (DirtySnapshot.speculativeToken)
+				SpeculativeInsertionWait(DirtySnapshot.xmin,
+										 DirtySnapshot.speculativeToken);
+			else
+				XactLockTableWait(xwait, heap, &ctid_wait,
+								  XLTW_RecheckExclusionConstr);
 			goto retry;
 		}
 
 		/*
-		 * We have a definite conflict.  Report it.
+		 * We have a definite conflict.  Return it to caller, or report it.
 		 */
+		if (violationOK)
+		{
+			conflict = true;
+			if (conflictTid)
+				*conflictTid = tup->t_self;
+			break;
+		}
+
 		error_new = BuildIndexValueDescription(index, values, isnull);
 		error_existing = BuildIndexValueDescription(index, existing_values,
 													existing_isnull);
@@ -1352,6 +1600,9 @@ retry:
 	 * However, it is possible to define exclusion constraints for which that
 	 * wouldn't be true --- for instance, if the operator is <>. So we no
 	 * longer complain if found_self is still false.
+	 *
+	 * It would also not be true in the pre-check mode, when we haven't
+	 * inserted a tuple yet.
 	 */
 
 	econtext->ecxt_scantuple = save_scantuple;
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index bb6df47..56da0f5 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -152,10 +152,11 @@ lnext:
 				 * case, so as to avoid the "Halloween problem" of repeated
 				 * update attempts.  In the latter case it might be sensible
 				 * to fetch the updated tuple instead, but doing so would
-				 * require changing heap_lock_tuple as well as heap_update and
-				 * heap_delete to not complain about updating "invisible"
-				 * tuples, which seems pretty scary.  So for now, treat the
-				 * tuple as deleted and do not process.
+				 * require changing heap_update and heap_delete to not complain
+				 * about updating "invisible" tuples, which seems pretty scary
+				 * (heap_lock_tuple will not complain, but few callers expect
+				 * HeapTupleInvisible, and we're not one of them).  So for now,
+				 * treat the tuple as deleted and do not process.
 				 */
 				goto lnext;
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index f96fb24..29b5b77 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -46,6 +46,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "storage/bufmgr.h"
+#include "storage/lmgr.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
@@ -151,6 +152,35 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
 	return ExecProject(projectReturning, NULL);
 }
 
+/*
+ * ExecCheckHeapTupleVisible -- verify heap tuple is visible
+ *
+ * It would not be consistent with guarantees of the higher isolation levels to
+ * proceed with avoiding insertion (taking speculative insertion's alternative
+ * path) on the basis of another tuple that is not visible.  Check for the need
+ * to raise a serialization failure, and do so as necessary.
+ */
+static void
+ExecCheckHeapTupleVisible(EState *estate,
+						  ResultRelInfo *relinfo,
+						  ItemPointer tid)
+{
+	Relation	rel = relinfo->ri_RelationDesc;
+	Buffer		buffer;
+	HeapTupleData tuple;
+
+	if (!IsolationUsesXactSnapshot())
+		return;
+
+	tuple.t_self = *tid;
+	if (!heap_fetch(rel, estate->es_snapshot, &tuple, &buffer, false, NULL))
+		ereport(ERROR,
+				(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+				 errmsg("could not serialize access due to concurrent insert or update dictating alternative ON CONFLICT path")));
+
+	ReleaseBuffer(buffer);
+}
+
 /* ----------------------------------------------------------------
  *		ExecInsert
  *
@@ -163,6 +193,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
 static TupleTableSlot *
 ExecInsert(TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
+		   Oid arbiterIndex,
+		   SpecCmd spec,
 		   EState *estate,
 		   bool canSetTag)
 {
@@ -246,6 +278,8 @@ ExecInsert(TupleTableSlot *slot,
 	}
 	else
 	{
+		ItemPointerData conflictTid;
+
 		/*
 		 * Constraints might reference the tableoid column, so initialize
 		 * t_tableOid before evaluating them.
@@ -259,20 +293,124 @@ ExecInsert(TupleTableSlot *slot,
 			ExecConstraints(resultRelInfo, slot, estate);
 
 		/*
+		 * If we are performing speculative insertion, do a non-conclusive
+		 * check for conflicts.
+		 *
+		 * See the executor README for a full discussion of speculative
+		 * insertion.
+		 */
+vlock:
+		if (spec != SPEC_NONE && resultRelInfo->ri_NumIndices > 0)
+		{
+			uint32		specToken;
+
+			/*
+			 * No need to check if running in bootstrap mode, since ON
+			 * CONFLICT with system catalogs forbidden generally.
+			 *
+			 * Check if it's required to proceed with the second phase
+			 * ("insertion proper") of speculative insertion in respect of the
+			 * slot.  If insertion ultimately does not proceed, no firing of
+			 * AFTER ROW INSERT triggers occurs.
+			 *
+			 * We don't suppress the effects (or, perhaps, side-effects) of
+			 * BEFORE ROW INSERT triggers.  This isn't ideal, but then we
+			 * cannot proceed with even considering uniqueness violations
+			 * until these triggers fire on the one hand, but on the other
+			 * hand they have the ability to execute arbitrary user-defined
+			 * code which may perform operations entirely outside the system's
+			 * ability to nullify.
+			 */
+			if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
+										   arbiterIndex))
+			{
+				/*
+				 * For the SPEC_IGNORE case, it's still often necessary to
+				 * verify that the tuple is visible to the executor's MVCC
+				 * snapshot.
+				 */
+				if (spec == SPEC_IGNORE)
+					ExecCheckHeapTupleVisible(estate, resultRelInfo, &conflictTid);
+
+				/*
+				 * The IGNORE path projects no tuples
+				 */
+				return NULL;
+			}
+
+			/*
+			 * Before we start insertion proper, acquire our "promise tuple
+			 * insertion lock".  Others can use that (rather than an XID lock,
+			 * which is appropriate only for non-promise tuples) to wait for us
+			 * to decide if we're going to go ahead with the insertion.
+			 */
+			specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
+			ItemPointerSetBlockNumber(&tuple->t_data->t_ctid, specToken);
+		}
+
+		/*
 		 * insert the tuple
 		 *
 		 * Note: heap_insert returns the tid (location) of the new tuple in
 		 * the t_self field.
 		 */
 		newId = heap_insert(resultRelationDesc, tuple,
-							estate->es_output_cid, 0, NULL);
+							estate->es_output_cid,
+							spec != SPEC_NONE ? HEAP_INSERT_SPECULATIVE : 0,
+							NULL);
 
 		/*
 		 * insert index entries for tuple
 		 */
 		if (resultRelInfo->ri_NumIndices > 0)
+		{
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate);
+												   estate, spec != SPEC_NONE,
+												   arbiterIndex);
+
+			if (spec != SPEC_NONE)
+			{
+				HeapUpdateFailureData hufd;
+
+				/*
+				 * Consider possible race:  concurrent insertion conflicts with
+				 * our speculative heap tuple.  Must then "super-delete" the
+				 * heap tuple and retry from the start.
+				 *
+				 * This is occasionally necessary so that "unprincipled
+				 * deadlocks" are avoided;  now that a conflict was found,
+				 * other sessions should not wait on our speculative token,
+				 * and they certainly shouldn't treat our
+				 * speculatively-inserted heap tuple as an ordinary tuple that
+				 * it must wait on the outcome of our xact to UPDATE/DELETE.
+				 * This makes heap tuples behave as conceptual "value locks"
+				 * of short duration, distinct from ordinary tuples that other
+				 * xacts must wait on xmin-xact-end of in the event of a
+				 * possible unique/exclusion violation (the violation that
+				 * arbitrates taking the alternative path).
+				 */
+				if (recheckIndexes)
+					heap_delete(resultRelationDesc, &(tuple->t_self),
+								estate->es_output_cid, InvalidSnapshot, false,
+								&hufd, true);
+
+				/*
+				 * Release speculative insertion lock.  Iff there was no
+				 * insertion conflict, and the tuple was therefore not super
+				 * deleted, this effectively make the promise tuple an ordinary
+				 * tuple
+				 */
+				SpeculativeInsertionLockRelease(GetCurrentTransactionId());
+
+				if (recheckIndexes)
+				{
+					list_free(recheckIndexes);
+					goto vlock;
+				}
+
+				/* since there was no insertion conflict, we're done */
+			}
+		}
 	}
 
 	if (canSetTag)
@@ -399,7 +537,8 @@ ldelete:;
 							 estate->es_output_cid,
 							 estate->es_crosscheck_snapshot,
 							 true /* wait for commit */ ,
-							 &hufd);
+							 &hufd,
+							 false);
 		switch (result)
 		{
 			case HeapTupleSelfUpdated:
@@ -768,7 +907,7 @@ lreplace:;
 		 */
 		if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
-												   estate);
+												   estate, false, InvalidOid);
 	}
 
 	if (canSetTag)
@@ -852,6 +991,7 @@ ExecModifyTable(ModifyTableState *node)
 {
 	EState	   *estate = node->ps.state;
 	CmdType		operation = node->operation;
+	SpecCmd		spec = node->spec;
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
 	PlanState  *subplanstate;
@@ -1022,7 +1162,8 @@ ExecModifyTable(ModifyTableState *node)
 		switch (operation)
 		{
 			case CMD_INSERT:
-				slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
+				slot = ExecInsert(slot, planSlot, node->arbiterIndex, spec,
+								  estate, node->canSetTag);
 				break;
 			case CMD_UPDATE:
 				slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
@@ -1097,6 +1238,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
 	mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
 	mtstate->mt_nplans = nplans;
+	mtstate->spec = node->spec;
 
 	/* set up epqstate with dummy subplan data for the moment */
 	EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
@@ -1135,7 +1277,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
 			operation != CMD_DELETE &&
 			resultRelInfo->ri_IndexRelationDescs == NULL)
-			ExecOpenIndices(resultRelInfo);
+			ExecOpenIndices(resultRelInfo, mtstate->spec != SPEC_NONE);
+
+		mtstate->arbiterIndex = node->arbiterIndex;
 
 		/* Now init the plan for this result rel */
 		estate->es_result_relation_info = resultRelInfo;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 029761e..f8a1514 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -180,6 +180,8 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_NODE_FIELD(resultRelations);
 	COPY_SCALAR_FIELD(resultRelIndex);
 	COPY_NODE_FIELD(plans);
+	COPY_SCALAR_FIELD(spec);
+	COPY_SCALAR_FIELD(arbiterIndex);
 	COPY_NODE_FIELD(withCheckOptionLists);
 	COPY_NODE_FIELD(returningLists);
 	COPY_NODE_FIELD(fdwPrivLists);
@@ -2128,6 +2130,30 @@ _copyWithClause(const WithClause *from)
 	return newnode;
 }
 
+static InferClause *
+_copyInferClause(const InferClause *from)
+{
+	InferClause *newnode = makeNode(InferClause);
+
+	COPY_NODE_FIELD(indexElems);
+	COPY_NODE_FIELD(whereClause);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+static ConflictClause *
+_copyConflictClause(const ConflictClause *from)
+{
+	ConflictClause *newnode = makeNode(ConflictClause);
+
+	COPY_SCALAR_FIELD(specclause);
+	COPY_NODE_FIELD(infer);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 static CommonTableExpr *
 _copyCommonTableExpr(const CommonTableExpr *from)
 {
@@ -2545,6 +2571,9 @@ _copyQuery(const Query *from)
 	COPY_NODE_FIELD(jointree);
 	COPY_NODE_FIELD(targetList);
 	COPY_NODE_FIELD(withCheckOptions);
+	COPY_SCALAR_FIELD(specClause);
+	COPY_NODE_FIELD(arbiterExpr);
+	COPY_NODE_FIELD(arbiterWhere);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(groupClause);
 	COPY_NODE_FIELD(havingQual);
@@ -2568,6 +2597,7 @@ _copyInsertStmt(const InsertStmt *from)
 	COPY_NODE_FIELD(relation);
 	COPY_NODE_FIELD(cols);
 	COPY_NODE_FIELD(selectStmt);
+	COPY_NODE_FIELD(confClause);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(withClause);
 
@@ -4729,6 +4759,12 @@ copyObject(const void *from)
 		case T_WithClause:
 			retval = _copyWithClause(from);
 			break;
+		case T_InferClause:
+			retval = _copyInferClause(from);
+			break;
+		case T_ConflictClause:
+			retval = _copyConflictClause(from);
+			break;
 		case T_CommonTableExpr:
 			retval = _copyCommonTableExpr(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 190e50a..c86bafc 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -868,6 +868,9 @@ _equalQuery(const Query *a, const Query *b)
 	COMPARE_NODE_FIELD(jointree);
 	COMPARE_NODE_FIELD(targetList);
 	COMPARE_NODE_FIELD(withCheckOptions);
+	COMPARE_SCALAR_FIELD(specClause);
+	COMPARE_NODE_FIELD(arbiterExpr);
+	COMPARE_NODE_FIELD(arbiterWhere);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(groupClause);
 	COMPARE_NODE_FIELD(havingQual);
@@ -889,6 +892,7 @@ _equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
 	COMPARE_NODE_FIELD(relation);
 	COMPARE_NODE_FIELD(cols);
 	COMPARE_NODE_FIELD(selectStmt);
+	COMPARE_NODE_FIELD(confClause);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(withClause);
 
@@ -2420,6 +2424,26 @@ _equalWithClause(const WithClause *a, const WithClause *b)
 }
 
 static bool
+_equalInferClause(const InferClause *a, const InferClause *b)
+{
+	COMPARE_NODE_FIELD(indexElems);
+	COMPARE_NODE_FIELD(whereClause);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
+_equalConflictClause(const ConflictClause *a, const ConflictClause *b)
+{
+	COMPARE_SCALAR_FIELD(specclause);
+	COMPARE_NODE_FIELD(infer);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
 _equalCommonTableExpr(const CommonTableExpr *a, const CommonTableExpr *b)
 {
 	COMPARE_STRING_FIELD(ctename);
@@ -3152,6 +3176,12 @@ equal(const void *a, const void *b)
 		case T_WithClause:
 			retval = _equalWithClause(a, b);
 			break;
+		case T_InferClause:
+			retval = _equalInferClause(a, b);
+			break;
+		case T_ConflictClause:
+			retval = _equalConflictClause(a, b);
+			break;
 		case T_CommonTableExpr:
 			retval = _equalCommonTableExpr(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index d6f1f5b..44efc95 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1484,6 +1484,12 @@ exprLocation(const Node *expr)
 		case T_WithClause:
 			loc = ((const WithClause *) expr)->location;
 			break;
+		case T_InferClause:
+			loc = ((const InferClause *) expr)->location;
+			break;
+		case T_ConflictClause:
+			loc = ((const ConflictClause *) expr)->location;
+			break;
 		case T_CommonTableExpr:
 			loc = ((const CommonTableExpr *) expr)->location;
 			break;
@@ -1968,6 +1974,10 @@ query_tree_walker(Query *query,
 		return true;
 	if (walker((Node *) query->withCheckOptions, context))
 		return true;
+	if (walker((Node *) query->arbiterExpr, context))
+		return true;
+	if (walker(query->arbiterWhere, context))
+		return true;
 	if (walker((Node *) query->returningList, context))
 		return true;
 	if (walker((Node *) query->jointree, context))
@@ -2709,6 +2719,8 @@ query_tree_mutator(Query *query,
 
 	MUTATE(query->targetList, query->targetList, List *);
 	MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
+	MUTATE(query->arbiterExpr, query->arbiterExpr, List *);
+	MUTATE(query->arbiterWhere, query->arbiterWhere, Node *);
 	MUTATE(query->returningList, query->returningList, List *);
 	MUTATE(query->jointree, query->jointree, FromExpr *);
 	MUTATE(query->setOperations, query->setOperations, Node *);
@@ -2978,6 +2990,8 @@ raw_expression_tree_walker(Node *node,
 					return true;
 				if (walker(stmt->selectStmt, context))
 					return true;
+				if (walker(stmt->confClause, context))
+					return true;
 				if (walker(stmt->returningList, context))
 					return true;
 				if (walker(stmt->withClause, context))
@@ -3217,6 +3231,23 @@ raw_expression_tree_walker(Node *node,
 			break;
 		case T_WithClause:
 			return walker(((WithClause *) node)->ctes, context);
+
+		case T_InferClause:
+			{
+				InferClause *stmt = (InferClause *) node;
+
+				if (walker(stmt->indexElems, context))
+					return true;
+				if (walker(stmt->whereClause, context))
+					return true;
+			}
+		case T_ConflictClause:
+			{
+				ConflictClause *stmt = (ConflictClause *) node;
+
+				if (walker(stmt->infer, context))
+					return true;
+			}
 		case T_CommonTableExpr:
 			return walker(((CommonTableExpr *) node)->ctequery, context);
 		default:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 385b289..61b98c7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -332,6 +332,8 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_NODE_FIELD(resultRelations);
 	WRITE_INT_FIELD(resultRelIndex);
 	WRITE_NODE_FIELD(plans);
+	WRITE_ENUM_FIELD(spec, SpecType);
+	WRITE_OID_FIELD(arbiterIndex);
 	WRITE_NODE_FIELD(withCheckOptionLists);
 	WRITE_NODE_FIELD(returningLists);
 	WRITE_NODE_FIELD(fdwPrivLists);
@@ -2314,6 +2316,9 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_NODE_FIELD(jointree);
 	WRITE_NODE_FIELD(targetList);
 	WRITE_NODE_FIELD(withCheckOptions);
+	WRITE_ENUM_FIELD(specClause, SpecType);
+	WRITE_NODE_FIELD(arbiterExpr);
+	WRITE_NODE_FIELD(arbiterWhere);
 	WRITE_NODE_FIELD(returningList);
 	WRITE_NODE_FIELD(groupClause);
 	WRITE_NODE_FIELD(havingQual);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 563209c..e492ef6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -214,6 +214,9 @@ _readQuery(void)
 	READ_NODE_FIELD(jointree);
 	READ_NODE_FIELD(targetList);
 	READ_NODE_FIELD(withCheckOptions);
+	READ_ENUM_FIELD(specClause, SpecCmd);
+	READ_NODE_FIELD(arbiterExpr);
+	READ_NODE_FIELD(arbiterWhere);
 	READ_NODE_FIELD(returningList);
 	READ_NODE_FIELD(groupClause);
 	READ_NODE_FIELD(havingQual);
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 49ab366..d7ea575 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -4113,3 +4113,69 @@ string_to_const(const char *str, Oid datatype)
 	return makeConst(datatype, -1, collation, constlen,
 					 conval, false, false);
 }
+
+/*
+ * plan_speculative_use_index
+ *		Use the planner to decide speculative insertion arbiter index
+ *
+ * Among indexes on target of INSERT ... ON CONFLICT, decide which index to use
+ * to arbitrate taking alternative path.  This should be called infrequently in
+ * practice, because its unusual for more than one index to be available that
+ * can satisfy a user-specified unique index inference specification.
+ *
+ * Note: caller had better already hold some type of lock on the table.
+ */
+Oid
+plan_speculative_use_index(PlannerInfo *root, int widthTarget, List *indexList)
+{
+	IndexOptInfo *indexInfo;
+	RelOptInfo *rel;
+	IndexPath  *cheapest;
+	IndexPath  *indexScanPath;
+	ListCell   *lc;
+
+	/* Set up RTE/RelOptInfo arrays if needed */
+	if (!root->simple_rel_array)
+		setup_simple_rel_arrays(root);
+
+	/* Build RelOptInfo */
+	rel = build_simple_rel(root, root->parse->resultRelation, RELOPT_BASEREL);
+
+	/*
+	 * Rather than doing all the pushups that would be needed to use
+	 * set_baserel_size_estimates, just do a quick hack for rows.  Caller
+	 * provides width.
+	 */
+	rel->rows = rel->tuples;
+	rel->width = widthTarget;
+
+	root->total_table_pages = rel->pages;
+
+	/* Locate cheapest IndexOptInfo for the target index */
+	cheapest = NULL;
+
+	foreach(lc, rel->indexlist)
+	{
+		indexInfo = (IndexOptInfo *) lfirst(lc);
+
+		if (!list_member_oid(indexList, indexInfo->indexoid))
+			continue;
+
+		/* Estimate the cost of index scan */
+		indexScanPath = create_index_path(root, indexInfo,
+										  NIL, NIL, NIL, NIL, NIL,
+										  ForwardScanDirection, false,
+										  NULL, 1.0);
+
+		if (!cheapest || compare_fractional_path_costs(&cheapest->path,
+													   &indexScanPath->path,
+													   DEFAULT_RANGE_INEQ_SEL) > 0)
+			cheapest = indexScanPath;
+
+	}
+
+	if (cheapest)
+		return cheapest->indexinfo->indexoid;
+
+	return InvalidOid;
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cb69c03..47fe29c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4815,7 +4815,7 @@ make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, int epqParam)
+				 List *rowMarks, SpecCmd spec, int epqParam)
 {
 	ModifyTable *node = makeNode(ModifyTable);
 	Plan	   *plan = &node->plan;
@@ -4865,6 +4865,8 @@ make_modifytable(PlannerInfo *root,
 	node->resultRelations = resultRelations;
 	node->resultRelIndex = -1;	/* will be set correctly in setrefs.c */
 	node->plans = subplans;
+	node->spec = spec;
+	node->arbiterIndex = InvalidOid;
 	node->withCheckOptionLists = withCheckOptionLists;
 	node->returningLists = returningLists;
 	node->rowMarks = rowMarks;
@@ -4917,6 +4919,15 @@ make_modifytable(PlannerInfo *root,
 	}
 	node->fdwPrivLists = fdw_private_list;
 
+	/*
+	 * If a set of unique index inference expressions was provided (for
+	 * INSERT...ON CONFLICT), then infer appropriate unique index (or throw an
+	 * error if none is available).  It's possible that there will be a costing
+	 * step in the event of having to choose between multiple alternatives.
+	 */
+	if (root->parse->arbiterExpr)
+		node->arbiterIndex = infer_unique_index(root);
+
 	return node;
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 876a87f..6931e1e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -612,6 +612,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 											 withCheckOptionLists,
 											 returningLists,
 											 rowMarks,
+											 parse->specClause,
 											 SS_assign_special_param(root));
 		}
 	}
@@ -1072,6 +1073,7 @@ inheritance_planner(PlannerInfo *root)
 									 withCheckOptionLists,
 									 returningLists,
 									 rowMarks,
+									 parse->specClause,
 									 SS_assign_special_param(root));
 }
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 313a5c1..a179b0c 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -31,6 +31,7 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/predtest.h"
 #include "optimizer/prep.h"
@@ -394,6 +395,224 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 }
 
 /*
+ * infer_unique_index -
+ *	  Retrieves unique index to arbitrate speculative insertion.
+ *
+ * Uses user-supplied inference clause expressions and predicate to match a
+ * unique index from those defined and ready on the heap relation (target).  An
+ * exact match is required on columns/expressions (although they can appear in
+ * any order).  However, the predicate given by the user need only restrict
+ * insertion to a subset of some part of the table covered by some particular
+ * unique index (in particular, a partial unique index) in order to be
+ * inferred.
+ *
+ * The implementation does not consider which B-Tree operator class any
+ * particular available unique index uses.  In particular, there is no system
+ * dependency on the default operator class for the purposes of inference.
+ * This should be okay, since by convention non-default opclasses only
+ * introduce alternative sort orders, not alternative notions of equality
+ * (there are only trivial known exceptions to this convention, where "equals"
+ * operator of a type's opclasses do not match across opclasses, exceptions
+ * that exist precisely to discourage user code from using the divergent
+ * opclass).  Even if we assume that a type could usefully have multiple
+ * alternative concepts of equality, surely the definition actually implied by
+ * the operator class of actually indexed attributes is pertinent.  However,
+ * this is a bit of a wart, because strictly speaking there is leeway for a
+ * query to be interpreted in deference to available unique indexes, and
+ * indexes are traditionally only an implementation detail.  It hardly seems
+ * worth it to waste cycles on this corner case, though.
+ *
+ * This logic somewhat mirrors get_relation_info().  This process is not
+ * deferred to a get_relation_info() call while planning because there may not
+ * be any such call.
+ */
+Oid
+infer_unique_index(PlannerInfo *root)
+{
+	Query	   *parse = root->parse;
+	Relation	relation;
+	Oid			relationObjectId;
+	Bitmapset  *plainAttrs = NULL;
+	List	   *candidates = NIL;
+	ListCell   *l;
+	List	   *indexList;
+
+	Assert(parse->specClause == SPEC_IGNORE);
+
+	/*
+	 * We need not lock the relation since it was already locked, either by
+	 * the rewriter or when expand_inherited_rtentry() added it to the query's
+	 * rangetable.
+	 */
+	relationObjectId = rt_fetch(parse->resultRelation, parse->rtable)->relid;
+
+	relation = heap_open(relationObjectId, NoLock);
+
+	/*
+	 * Match expressions appearing in clause (if any) with index definition
+	 */
+	foreach(l, parse->arbiterExpr)
+	{
+		Expr	   *elem;
+		Var		   *var;
+		int			attno;
+
+		elem = (Expr *) lfirst(l);
+
+		/*
+		 * Parse analysis of inference elements performs full parse analysis
+		 * of Vars, even for non-expression indexes (in contrast with utility
+		 * command related use of IndexElem).  However, indexes are cataloged
+		 * with simple attribute numbers for non-expression indexes.
+		 * Therefore, we must build a compatible bms representation here.
+		 */
+		if (!IsA(elem, Var))
+			continue;
+
+		var = (Var *) elem;
+		attno = var->varattno;
+
+		if (attno < 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+					 errmsg("system columns may not appear in unique index inference specification")));
+		else if (attno == 0)
+			elog(ERROR, "whole row unique index inference specifications are not valid");
+
+		plainAttrs = bms_add_member(plainAttrs, attno);
+	}
+
+	indexList = RelationGetIndexList(relation);
+
+	/*
+	 * Using that representation, iterate through the list of indexes on the
+	 * target relation to try and find a match
+	 */
+	foreach(l, indexList)
+	{
+		Oid			indexoid = lfirst_oid(l);
+		Relation	idxRel;
+		Form_pg_index idxForm;
+		Bitmapset  *indexedPlainAttrs = NULL;
+		List	   *idxExprs;
+		List	   *predExprs;
+		List	   *whereExplicit;
+		AttrNumber	natt;
+		ListCell   *e;
+
+		/*
+		 * Extract info from the relation descriptor for the index.  We know
+		 * that this is a target, so get lock type it is known will ultimately
+		 * be required by the executor.
+		 *
+		 * Let executor complain about !indimmediate case directly.
+		 */
+		idxRel = index_open(indexoid, RowExclusiveLock);
+		idxForm = idxRel->rd_index;
+
+		if (!idxForm->indisunique ||
+			!IndexIsValid(idxForm))
+			goto next;
+
+		/*
+		 * If the index is valid, but cannot yet be used, ignore it. See
+		 * src/backend/access/heap/README.HOT for discussion.
+		 */
+		if (idxForm->indcheckxmin &&
+			!TransactionIdPrecedes(HeapTupleHeaderGetXmin(idxRel->rd_indextuple->t_data),
+								   TransactionXmin))
+			goto next;
+
+		/* Check in detail if the clause attributes/expressions match */
+		for (natt = 0; natt < idxForm->indnatts; natt++)
+		{
+			int			attno = idxRel->rd_index->indkey.values[natt];
+
+			if (attno < 0)
+				elog(ERROR, "system column in index");
+
+			if (attno != 0)
+				indexedPlainAttrs = bms_add_member(indexedPlainAttrs, attno);
+		}
+
+		/*
+		 * Since expressions were made unique during parse analysis, it's
+		 * evident that we cannot proceed with this index if the number of
+		 * attributes (plain or expression) does not match exactly.  This
+		 * precludes support for unique indexes created with redundantly
+		 * referenced columns (which are not forbidden by CREATE INDEX), but
+		 * this seems inconsequential.
+		 */
+		if (list_length(parse->arbiterExpr) != idxForm->indnatts)
+			goto next;
+
+		idxExprs = RelationGetIndexExpressions(idxRel);
+
+		/*
+		 * Match expressions appearing in clause (if any) with index
+		 * definition
+		 */
+		foreach(e, parse->arbiterExpr)
+		{
+			Expr	   *elem = (Expr *) lfirst(e);
+
+			/* Plain Vars were already separately accounted for */
+			if (IsA(elem, Var))
+				continue;
+
+			if (!list_member(idxExprs, elem))
+				goto next;
+		}
+
+		/* Non-expression attributes (if any) must match */
+		if (!bms_equal(indexedPlainAttrs, plainAttrs))
+			goto next;
+
+		/*
+		 * Any user-supplied ON CONFLICT unique index inference WHERE clause
+		 * need only be implied by the cataloged index definitions predicate
+		 */
+		predExprs = RelationGetIndexPredicate(idxRel);
+		whereExplicit = make_ands_implicit((Expr *) parse->arbiterWhere);
+
+		if (!predicate_implied_by(predExprs, whereExplicit))
+			goto next;
+
+		candidates = lappend_oid(candidates, idxForm->indexrelid);
+next:
+		index_close(idxRel, NoLock);
+	}
+
+	list_free(indexList);
+	heap_close(relation, NoLock);
+
+	/*
+	 * In the common case where there is only a single candidate unique index,
+	 * there is clearly no point in building index paths to determine which is
+	 * cheapest
+	 */
+	if (list_length(candidates) == 1)
+	{
+		return linitial_oid(candidates);
+	}
+	else if (candidates == NIL)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT")));
+	}
+	else
+	{
+		int		width = get_relation_data_width(relationObjectId, NULL);
+
+		/* Deduce the least expensive unique index through costing */
+		return plan_speculative_use_index(root, width, candidates);
+	}
+
+	return InvalidOid;			/* keep compiler quiet */
+}
+
+/*
  * estimate_rel_size - estimate # pages and # tuples in a table or index
  *
  * We also estimate the fraction of the pages that are marked all-visible in
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 4a5a520..a49d980 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -387,6 +387,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	/* done building the range table and jointree */
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+	qry->specClause = SPEC_NONE;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -408,6 +409,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
 	SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
+	SpecCmd		spec = stmt->confClause ? stmt->confClause->specclause : SPEC_NONE;
 	List	   *exprList = NIL;
 	bool		isGeneralSelect;
 	List	   *sub_rtable;
@@ -741,12 +743,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	}
 
 	/*
-	 * If we have a RETURNING clause, we need to add the target relation to
-	 * the query namespace before processing it, so that Var references in
-	 * RETURNING will work.  Also, remove any namespace entries added in a
-	 * sub-SELECT or VALUES list.
+	 * If we have a RETURNING clause, or there are attributes used as the
+	 * condition on which to take an alternative ON CONFLICT path, we need to
+	 * add the target relation to the query namespace before processing it, so
+	 * that Var references in RETURNING/the alternative path key will work.
+	 * Also, remove any namespace entries added in a sub-SELECT or VALUES list.
 	 */
-	if (stmt->returningList)
+	if (stmt->returningList || stmt->confClause)
 	{
 		pstate->p_namespace = NIL;
 		addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
@@ -758,9 +761,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	/* done building the range table and jointree */
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
-
+	qry->specClause = spec;
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
+	if (stmt->confClause)
+	{
+		/*
+		 * Perform parse analysis of arbiter columns/expressions.  These are
+		 * later used to infer a unique index which arbitrates whether or not
+		 * to take the alternative ON CONFLICT path (i.e.  whether or not to
+		 * INSERT or take the alternative path in respect of each slot proposed
+		 * for insertion).
+		 */
+		transformConflictClause(pstate, stmt->confClause, &qry->arbiterExpr,
+								&qry->arbiterWhere);
+	}
+
 	assign_query_collations(pstate, qry);
 
 	return qry;
@@ -1006,6 +1022,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+	qry->specClause = SPEC_NONE;
 
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 873ca79..5079562 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -217,6 +217,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	RangeVar			*range;
 	IntoClause			*into;
 	WithClause			*with;
+	InferClause			*infer;
+	ConflictClause			*conf;
 	A_Indices			*aind;
 	ResTarget			*target;
 	struct PrivTarget	*privtarget;
@@ -416,6 +418,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>	SeqOptElem
 
 %type <istmt>	insert_rest
+%type <infer>	opt_conf_expr
+%type <conf>	opt_on_conflict
 
 %type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest
 				 SetResetClause FunctionSetResetClause
@@ -555,8 +559,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
 	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
-	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
-	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
+	COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
+	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
 	CROSS CSV CURRENT_P
 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -576,7 +580,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
-	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P
+	IDENTITY_P IF_P IGNORE_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P
 	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
 	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
@@ -657,6 +661,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %nonassoc	IS ISNULL NOTNULL	/* IS sets precedence for IS NULL, etc */
 %nonassoc	'<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
 %nonassoc	BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA
+%nonassoc	DISTINCT
+%nonassoc	ON
 %nonassoc	ESCAPE			/* ESCAPE must be just above LIKE/ILIKE/SIMILAR */
 %nonassoc	OVERLAPS
 %left		POSTFIXOP		/* dummy for postfix Op rules */
@@ -9349,10 +9355,12 @@ DeallocateStmt: DEALLOCATE name
  *****************************************************************************/
 
 InsertStmt:
-			opt_with_clause INSERT INTO qualified_name insert_rest returning_clause
+			opt_with_clause INSERT INTO qualified_name insert_rest
+			opt_on_conflict returning_clause
 				{
 					$5->relation = $4;
-					$5->returningList = $6;
+					$5->confClause = $6;
+					$5->returningList = $7;
 					$5->withClause = $1;
 					$$ = (Node *) $5;
 				}
@@ -9397,6 +9405,34 @@ insert_column_item:
 				}
 		;
 
+opt_on_conflict:
+			ON CONFLICT opt_conf_expr IGNORE_P
+				{
+					$$ = makeNode(ConflictClause);
+					$$->specclause = SPEC_IGNORE;
+					$$->infer = $3;
+					$$->location = @1;
+				}
+			| /*EMPTY*/
+				{
+					$$ = NULL;
+				}
+		;
+
+opt_conf_expr:
+			'(' index_params where_clause ')'
+				{
+					$$ = makeNode(InferClause);
+					$$->indexElems = $2;
+					$$->whereClause = $3;
+					$$->location = @1;
+				}
+			| /*EMPTY*/
+				{
+					$$ = NULL;
+				}
+		;
+
 returning_clause:
 			RETURNING target_list		{ $$ = $2; }
 			| /* EMPTY */				{ $$ = NIL; }
@@ -13280,6 +13316,7 @@ unreserved_keyword:
 			| COMMIT
 			| COMMITTED
 			| CONFIGURATION
+			| CONFLICT
 			| CONNECTION
 			| CONSTRAINTS
 			| CONTENT_P
@@ -13339,6 +13376,7 @@ unreserved_keyword:
 			| HOUR_P
 			| IDENTITY_P
 			| IF_P
+			| IGNORE_P
 			| IMMEDIATE
 			| IMMUTABLE
 			| IMPLICIT_P
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 8d90b50..029288b 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "catalog/catalog.h"
 #include "catalog/heap.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -75,6 +76,8 @@ static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
 						 List **tlist, ParseExprKind exprKind);
 static int get_matching_location(int sortgroupref,
 					  List *sortgrouprefs, List *exprs);
+static List *resolve_unique_index_expr(ParseState *pstate, InferClause * infer,
+						  Relation heapRel);
 static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle,
 					 List *grouplist, List *targetlist, int location,
 					 bool resolveUnknown);
@@ -2167,6 +2170,163 @@ get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs)
 }
 
 /*
+ * resolve_unique_index_expr
+ *		Infer a unique index from a list of indexElems, for ON
+ *		CONFLICT clause
+ *
+ * Perform parse analysis of expressions and columns appearing within ON
+ * CONFLICT clause.  During planning, the returned list of expressions is used
+ * to infer which unique index to use.
+ */
+static List *
+resolve_unique_index_expr(ParseState *pstate, InferClause *infer,
+						  Relation heapRel)
+{
+	List	   *clauseexprs = NIL;
+	ListCell   *l;
+
+	if (heapRel->rd_rel->relkind != RELKIND_RELATION)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("relation \"%s\" is not an ordinary table",
+						RelationGetRelationName(heapRel)),
+				 errhint("Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.")));
+
+	if (heapRel->rd_rel->relhassubclass)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("relation \"%s\" has inheritance children",
+						RelationGetRelationName(heapRel)),
+				 errhint("Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.")));
+
+	foreach(l, infer->indexElems)
+	{
+		IndexElem  *ielem = (IndexElem *) lfirst(l);
+		Node	   *trans;
+
+		/*
+		 * Raw grammar re-uses CREATE INDEX infrastructure for unique index
+		 * inference clause, and so will accept opclasses by name and so on.
+		 * Reject these here explicitly.
+		 */
+		if (ielem->ordering != SORTBY_DEFAULT ||
+			ielem->nulls_ordering != SORTBY_NULLS_DEFAULT)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+					 errmsg("ON CONFLICT does not accept ordering or NULLS FIRST/LAST specifications"),
+					 errhint("These factors do not affect uniqueness of indexed datums."),
+					 parser_errposition(pstate,
+										exprLocation((Node *) infer))));
+
+		if (ielem->collation != NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+					 errmsg("ON CONFLICT collation specification is unnecessary"),
+					 errhint("Collations do not affect uniqueness of collatable datums."),
+					 parser_errposition(pstate,
+										exprLocation((Node *) infer))));
+
+		if (ielem->opclass != NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("ON CONFLICT cannot accept non-default operator class specifications"),
+					 parser_errposition(pstate,
+										exprLocation((Node *) infer))));
+
+		if (!ielem->expr)
+		{
+			/* Simple index attribute */
+			ColumnRef  *n;
+
+			/*
+			 * Grammar won't have built raw expression for us in event of plain
+			 * column reference.  Create one directly, and perform expression
+			 * transformation, which seems better principled than simply
+			 * propagating catalog-style simple attribute numbers.  For
+			 * example, it means the Var is marked for SELECT privileges, which
+			 * speculative insertion requires.  Planner expects this, and
+			 * performs its own normalization for the purposes of matching
+			 * against pg_index.
+			 */
+			n = makeNode(ColumnRef);
+			n->fields = list_make1(makeString(ielem->name));
+			/* Location is approximately that of inference specification */
+			n->location = infer->location;
+			trans = (Node *) n;
+		}
+		else
+		{
+			/* Do parse transformation of the raw expression */
+			trans = (Node *) ielem->expr;
+		}
+
+		/*
+		 * transformExpr() should have already rejected subqueries,
+		 * aggregates, and window functions, based on the EXPR_KIND_ for an
+		 * index expression.  Expressions returning sets won't have been
+		 * rejected, but don't bother doing so here; there should be no
+		 * available expression unique index to match any such expression
+		 * against anyway.
+		 */
+		trans = transformExpr(pstate, trans, EXPR_KIND_INDEX_EXPRESSION);
+		/* Save in list of transformed expressions */
+		clauseexprs = list_append_unique(clauseexprs, trans);
+	}
+
+	return clauseexprs;
+}
+
+/*
+ * transformConflictClauseExpr -
+ *		transform expressions of ON CONFLICT.
+ *
+ * Transformed expressions used to infer one unique index relation to serve as
+ * an ON CONFLICT arbiter.  Partial unique indexes may be inferred using WHERE
+ * clause from inference specification clause.
+ */
+void
+transformConflictClause(ParseState *pstate, ConflictClause *confClause,
+						List **arbiterExpr, Node **arbiterWhere)
+{
+	InferClause *infer = confClause->infer;
+
+	/* This obviates the need for historic snapshot support */
+	if (IsCatalogRelation(pstate->p_target_relation))
+		elog(ERROR, "ON CONFLICT not supported with catalog relations");
+
+	/*
+	 * If there is no inference clause, this might be an updatable view, which
+	 * are supported by ON CONFLICT IGNORE (without columns/ expressions
+	 * specified to infer a unique index from).  It might also be a relation
+	 * with inheritance children, which would also make proceeding with
+	 * inference fail.
+	 */
+	if (infer)
+	{
+		*arbiterExpr = resolve_unique_index_expr(pstate, infer,
+												 pstate->p_target_relation);
+
+		/*
+		 * Handling inference WHERE clause (for partial unique index
+		 * inference)
+		 */
+		if (infer->whereClause)
+			*arbiterWhere = transformExpr(pstate, infer->whereClause,
+										  EXPR_KIND_INDEX_PREDICATE);
+	}
+
+	/*
+	 * It's convenient to form a list of expressions based on the
+	 * representation used by CREATE INDEX, since the same restrictions are
+	 * appropriate (on subqueries and so on).  However, from here on, the
+	 * handling of those expressions is identical to ordinary optimizable
+	 * statements.  In particular, assign_query_collations() can be trusted to
+	 * do the right thing with the post parse analysis query tree inference
+	 * clause representation.
+	 */
+}
+
+/*
  * addTargetToSortList
  *		If the given targetlist entry isn't already in the SortGroupClause
  *		list, add it to the end of the list, using the given sort ordering
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index eb7293f..e2c1a87 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -538,7 +538,11 @@ DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		return;
 
 	change = ReorderBufferGetChange(ctx->reorder);
-	change->action = REORDER_BUFFER_CHANGE_INSERT;
+	if (!(xlrec->flags & XLOG_HEAP_SPECULATIVE_TUPLE))
+		change->action = REORDER_BUFFER_CHANGE_INSERT;
+	else
+		change->action = REORDER_BUFFER_CHANGE_INTERNAL_INSERT;
+
 	memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
 
 	if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE)
@@ -629,7 +633,10 @@ DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 		return;
 
 	change = ReorderBufferGetChange(ctx->reorder);
-	change->action = REORDER_BUFFER_CHANGE_DELETE;
+	if (!(xlrec->flags & XLOG_HEAP_SPECULATIVE_TUPLE))
+		change->action = REORDER_BUFFER_CHANGE_DELETE;
+	else
+		change->action = REORDER_BUFFER_CHANGE_INTERNAL_DELETE;
 
 	memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
 
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 20bb3b7..e8ad2ee 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -401,6 +401,8 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change)
 		case REORDER_BUFFER_CHANGE_INSERT:
 		case REORDER_BUFFER_CHANGE_UPDATE:
 		case REORDER_BUFFER_CHANGE_DELETE:
+		case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+		case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 			if (change->data.tp.newtuple)
 			{
 				ReorderBufferReturnTupleBuf(rb, change->data.tp.newtuple);
@@ -1314,6 +1316,7 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 	PG_TRY();
 	{
 		ReorderBufferChange *change;
+		ReorderBufferChange *peekchange = NULL;
 
 		if (using_subtxn)
 			BeginInternalSubTransaction("replay");
@@ -1323,16 +1326,26 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 		rb->begin(rb, txn);
 
 		iterstate = ReorderBufferIterTXNInit(rb, txn);
-		while ((change = ReorderBufferIterTXNNext(rb, iterstate)) != NULL)
+		while ((change = peekchange ? peekchange :
+				ReorderBufferIterTXNNext(rb, iterstate)) != NULL)
 		{
 			Relation	relation = NULL;
 			Oid			reloid;
 
+			/* Forget about previous peek ahead */
+			if (peekchange)
+				peekchange = NULL;
+			else
+				Assert(change->action !=
+					   REORDER_BUFFER_CHANGE_INTERNAL_DELETE);
+
 			switch (change->action)
 			{
 				case REORDER_BUFFER_CHANGE_INSERT:
 				case REORDER_BUFFER_CHANGE_UPDATE:
 				case REORDER_BUFFER_CHANGE_DELETE:
+				case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+				case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 					Assert(snapshot_now);
 
 					reloid = RelidByRelfilenode(change->data.tp.relnode.spcNode,
@@ -1374,7 +1387,65 @@ ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 						else if (!IsToastRelation(relation))
 						{
 							ReorderBufferToastReplace(rb, txn, relation, change);
-							rb->apply_change(rb, txn, relation, change);
+
+							/*
+							 * Kludge:  Speculative insertion occasionally
+							 * makes use of "super deletion" -- an
+							 * implementation defined delete of a speculatively
+							 * inserted tuple.  Neither the super deletion, nor
+							 * the insertion (which must be the prior record
+							 * type) are included in the final assembly when
+							 * the tuple was super-deleted.  Otherwise, an
+							 * ordinary insertion is assembled.
+							 */
+							if (change->action == REORDER_BUFFER_CHANGE_INTERNAL_INSERT)
+							{
+								/*
+								 * Need to ensure the memory used by promise
+								 * tuple isn't freed till we're done verifying
+								 * that there is no super deletion that
+								 * immediately follows.  Otherwise it could get
+								 * freed/reused while restoring spooled data
+								 * from disk.
+								 */
+								dlist_delete(&change->node);
+								peekchange = ReorderBufferIterTXNNext(rb, iterstate);
+								if (!peekchange || peekchange->action !=
+									REORDER_BUFFER_CHANGE_INTERNAL_DELETE)
+								{
+									/* Report as proper insert to client */
+									change->action = REORDER_BUFFER_CHANGE_INSERT;
+									rb->apply_change(rb, txn, relation,
+													 change);
+								}
+								else if (peekchange)
+								{
+									Assert(RelFileNodeEquals(change->data.tp.relnode,
+															 peekchange->data.tp.relnode));
+								}
+
+								ReorderBufferReturnChange(rb, change);
+							}
+							else if (change->action ==
+									 REORDER_BUFFER_CHANGE_INTERNAL_DELETE)
+							{
+								/*
+								 * The REORDER_BUFFER_CHANGE_INTERNAL_INSERT
+								 * case makes an assumption that
+								 * REORDER_BUFFER_CHANGE_INTERNAL_DELETE
+								 * changes immediately follows reliably iff a
+								 * speculatively inserted tuple was actually
+								 * super-deleted.
+								 */
+							}
+							else
+							{
+								/*
+								 * Handle non-speculative insertion related
+								 * changes
+								 */
+								rb->apply_change(rb, txn, relation, change);
+							}
 
 							/*
 							 * Only clear reassembled toast chunks if we're
@@ -2003,6 +2074,10 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
 		case REORDER_BUFFER_CHANGE_UPDATE:
 			/* fall through */
 		case REORDER_BUFFER_CHANGE_DELETE:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 			{
 				char	   *data;
 				ReorderBufferTupleBuf *oldtup,
@@ -2258,6 +2333,10 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
 		case REORDER_BUFFER_CHANGE_UPDATE:
 			/* fall through */
 		case REORDER_BUFFER_CHANGE_DELETE:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_INSERT:
+			/* fall through */
+		case REORDER_BUFFER_CHANGE_INTERNAL_DELETE:
 			if (change->data.tp.newtuple)
 			{
 				Size		len = offsetof(ReorderBufferTupleBuf, t_data) +
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 9d2c280..40458a0 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -66,7 +66,7 @@ static void markQueryForLocking(Query *qry, Node *jtnode,
 					LockClauseStrength strength, LockWaitPolicy waitPolicy,
 					bool pushedDown);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
-		   int varno, Query *parsetree);
+		   int varno, Query *parsetree, bool *hasUpdate);
 static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
 			 bool forUpdatePushedDown);
 static bool view_has_instead_trigger(Relation view, CmdType event);
@@ -1288,7 +1288,8 @@ static List *
 matchLocks(CmdType event,
 		   RuleLock *rulelocks,
 		   int varno,
-		   Query *parsetree)
+		   Query *parsetree,
+		   bool *hasUpdate)
 {
 	List	   *matching_locks = NIL;
 	int			nlocks;
@@ -1309,6 +1310,9 @@ matchLocks(CmdType event,
 	{
 		RewriteRule *oneLock = rulelocks->rules[i];
 
+		if (oneLock->event == CMD_UPDATE)
+			*hasUpdate = true;
+
 		/*
 		 * Suppress ON INSERT/UPDATE/DELETE rules that are disabled or
 		 * configured to not fire during the current sessions replication
@@ -2953,6 +2957,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 	CmdType		event = parsetree->commandType;
 	bool		instead = false;
 	bool		returning = false;
+	bool		updatableview = false;
 	Query	   *qual_product = NULL;
 	List	   *rewritten = NIL;
 	ListCell   *lc1;
@@ -3035,6 +3040,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 		Relation	rt_entry_relation;
 		List	   *locks;
 		List	   *product_queries;
+		bool		hasUpdate = false;
 
 		result_relation = parsetree->resultRelation;
 		Assert(result_relation != 0);
@@ -3103,7 +3109,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 		 * Collect and apply the appropriate rules.
 		 */
 		locks = matchLocks(event, rt_entry_relation->rd_rules,
-						   result_relation, parsetree);
+						   result_relation, parsetree, &hasUpdate);
 
 		product_queries = fireRules(parsetree,
 									result_relation,
@@ -3152,6 +3158,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 			 */
 			instead = true;
 			returning = true;
+			updatableview = true;
 		}
 
 		/*
@@ -3232,6 +3239,18 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
 			}
 		}
 
+		/*
+		 * Updatable views are supported on a limited basis by ON CONFLICT
+		 * IGNORE (if there is no unique index inference required, speculative
+		 * insertion proceeds).
+		 */
+		if (parsetree->specClause != SPEC_NONE &&
+			(product_queries != NIL || hasUpdate) &&
+			!updatableview)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("INSERT with ON CONFLICT clause may not target relation with INSERT or UPDATE rules")));
+
 		heap_close(rt_entry_relation, NoLock);
 	}
 
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index d13a167..65da516 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -576,6 +576,88 @@ ConditionalXactLockTableWait(TransactionId xid)
 }
 
 /*
+ * Per-backend final disambiguator of an attempt to insert speculatively.
+ *
+ * This may wraparound, but since it is only a final disambiguator (speculative
+ * waiters also check TID and relfilenode), this is deemed to be acceptable.
+ * There is only a theoretical, vanishingly small chance of a backend
+ * spuriously considering that it must wait on another backend's
+ * end-of-speculative insertion (call to SpeculativeInsertionLockRelease())
+ * when that isn't strictly necessary, and even this is likely to be
+ * inconsequential.  At worst, unprincipled deadlocks are not entirely
+ * eliminated in extreme corner cases.
+ */
+static uint32 speculativeInsertionToken = 0;
+
+/*
+ *		SpeculativeInsertionLockAcquire
+ *
+ * Insert a lock showing that the given transaction ID is inserting a tuple,
+ * but hasn't yet decided whether it's going to keep it. The lock can then be
+ * used to wait for the decision to go ahead with the insertion, or aborting
+ * it.
+ *
+ * The token is used to distinguish multiple insertions by the same
+ * transaction.  It is returned to caller.
+ */
+uint32
+SpeculativeInsertionLockAcquire(TransactionId xid)
+{
+	LOCKTAG		tag;
+
+	speculativeInsertionToken++;
+
+	/*
+	 * A zero speculative insertion lock indicates no token is held;  Don't
+	 * allow the token to overflow to zero
+	 */
+	if (speculativeInsertionToken == 0)
+		speculativeInsertionToken = 1;
+
+	SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, speculativeInsertionToken);
+
+	(void) LockAcquire(&tag, ExclusiveLock, false, false);
+
+	return speculativeInsertionToken;
+}
+
+/*
+ *		SpeculativeInsertionLockRelease
+ *
+ * Delete the lock showing that the given transaction is speculatively
+ * inserting a tuple.
+ */
+void
+SpeculativeInsertionLockRelease(TransactionId xid)
+{
+	LOCKTAG		tag;
+
+	SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, speculativeInsertionToken);
+
+	LockRelease(&tag, ExclusiveLock, false);
+}
+
+/*
+ *		SpeculativeInsertionWait
+ *
+ * Wait for the specified transaction to finish or abort the insertion of a
+ * tuple.
+ */
+void
+SpeculativeInsertionWait(TransactionId xid, uint32 token)
+{
+	LOCKTAG		tag;
+
+	SET_LOCKTAG_SPECULATIVE_INSERTION(tag, xid, token);
+
+	Assert(TransactionIdIsValid(xid));
+	Assert(token != 0);
+
+	(void) LockAcquire(&tag, ShareLock, false, false);
+	LockRelease(&tag, ShareLock, false);
+}
+
+/*
  * XactLockTableWaitErrorContextCb
  *		Error context callback for transaction lock waits.
  */
@@ -873,6 +955,12 @@ DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
 							 tag->locktag_field1,
 							 tag->locktag_field2);
 			break;
+		case LOCKTAG_PROMISE_TUPLE:
+			appendStringInfo(buf,
+							 _("promise tuple with token %u of transaction %u"),
+							 tag->locktag_field2,
+							 tag->locktag_field1);
+			break;
 		case LOCKTAG_OBJECT:
 			appendStringInfo(buf,
 							 _("object %u of class %u of database %u"),
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index a1967b69..1056e71 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -28,6 +28,7 @@ static const char *const LockTagTypeNames[] = {
 	"tuple",
 	"transactionid",
 	"virtualxid",
+	"promise tuple",
 	"object",
 	"userlock",
 	"advisory"
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 777f55c..1375adc 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -262,6 +262,9 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
 		}
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return false;
+
 	/* by here, the inserting transaction has committed */
 
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
@@ -360,6 +363,7 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
 
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
 
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
@@ -446,6 +450,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
 
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
 
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
@@ -726,6 +731,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
 	Assert(htup->t_tableOid != InvalidOid);
 
 	snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+	snapshot->speculativeToken = 0;
 
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
@@ -807,6 +813,25 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
 		}
 		else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
 		{
+			RelFileNode rnode;
+			ForkNumber	forkno;
+			BlockNumber blockno;
+
+			BufferGetTag(buffer, &rnode, &forkno, &blockno);
+
+			/* tuples can only be in the main fork */
+			Assert(forkno == MAIN_FORKNUM);
+			Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
+
+			/*
+			 * Set speculative token.  Caller can worry about xmax, since it
+			 * requires a conclusively locked row version, and a concurrent
+			 * update to this tuple is a conflict of its purposes.
+			 */
+			if (HeapTupleHeaderIsSpeculative(tuple))
+				snapshot->speculativeToken =
+					BlockIdGetBlockNumber(&tuple->t_ctid.ip_blkid);
+
 			snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
 			/* XXX shouldn't we fall through to look at xmax? */
 			return true;		/* in insertion by other */
@@ -823,6 +848,9 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
 		}
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return false;
+
 	/* by here, the inserting transaction has committed */
 
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
@@ -1022,6 +1050,9 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
 		}
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return false;
+
 	/*
 	 * By here, the inserting transaction has committed - have to check
 	 * when...
@@ -1218,6 +1249,9 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
 		 */
 	}
 
+	if (HeapTupleHeaderSuperDeleted(tuple))
+		return HEAPTUPLE_DEAD;
+
 	/*
 	 * Okay, the inserter committed, so it was good at some point.  Now what
 	 * about the deleting transaction?
@@ -1406,7 +1440,10 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
 	if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
 		return false;
 
-	/* Deleter committed, so tuple is dead if the XID is old enough. */
+	/*
+	 * Deleter committed, so tuple is dead if the XID is old enough.  This
+	 * handles super deleted tuples correctly.
+	 */
 	return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
 }
 
@@ -1539,6 +1576,8 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
 {
 	TransactionId xmax;
 
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
+
 	/* if there's no valid Xmax, then there's obviously no update either */
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)
 		return true;
@@ -1596,6 +1635,9 @@ TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
  * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
  * reading catalog pages which couldn't have been created in an older version.
  *
+ * We don't support speculative insertion into catalogs, and so there are no
+ * checks for super deleted tuples.
+ *
  * We don't set any hint bits in here as it seems unlikely to be beneficial as
  * those should already be set by normal access and it seems to be too
  * dangerous to do so as the semantics of doing so during timetravel are more
@@ -1611,6 +1653,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
 
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
+	Assert(!HeapTupleHeaderSuperDeleted(tuple));
 
 	/* inserting transaction aborted */
 	if (HeapTupleHeaderXminInvalid(tuple))
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 888cce7..64e316c 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -28,6 +28,7 @@
 #define HEAP_INSERT_SKIP_WAL	0x0001
 #define HEAP_INSERT_SKIP_FSM	0x0002
 #define HEAP_INSERT_FROZEN		0x0004
+#define HEAP_INSERT_SPECULATIVE 0x0008
 
 typedef struct BulkInsertStateData *BulkInsertState;
 
@@ -141,7 +142,7 @@ extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
 				  CommandId cid, int options, BulkInsertState bistate);
 extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
 			CommandId cid, Snapshot crosscheck, bool wait,
-			HeapUpdateFailureData *hufd);
+			HeapUpdateFailureData *hufd, bool speculative);
 extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
 			HeapTuple newtup,
 			CommandId cid, Snapshot crosscheck, bool wait,
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index f0f89de..568ec2c 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -73,6 +73,8 @@
 #define XLOG_HEAP_SUFFIX_FROM_OLD			(1<<6)
 /* last xl_heap_multi_insert record for one heap_multi_insert() call */
 #define XLOG_HEAP_LAST_MULTI_INSERT			(1<<7)
+/* reuse xl_heap_multi_insert-only bit for xl_heap_insert and xl_heap_delete */
+#define XLOG_HEAP_SPECULATIVE_TUPLE	XLOG_HEAP_LAST_MULTI_INSERT
 
 /* convenience macro for checking whether any form of old tuple was logged */
 #define XLOG_HEAP_CONTAINS_OLD						\
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 0a673cd..95bc510 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -96,6 +96,11 @@
  * unrelated tuple stored into a slot recently freed by VACUUM.  If either
  * check fails, one may assume that there is no live descendant version.
  *
+ * t_ctid is sometimes used to store a speculative token, for speculative
+ * inserters.  Code paths that follow t_ctid chains must also consider that the
+ * apparently pointed to t_ctid is in fact such a token, that should similarly
+ * not be followed.
+ *
  * Following the fixed header fields, the nulls bitmap is stored (beginning
  * at t_bits).  The bitmap is *not* stored if t_infomask shows that there
  * are no nulls in the tuple.  If an OID field is present (as indicated by
@@ -230,6 +235,7 @@ struct HeapTupleHeaderData
 										 * modified, or tuple deleted */
 #define HEAP_HOT_UPDATED		0x4000	/* tuple was HOT-updated */
 #define HEAP_ONLY_TUPLE			0x8000	/* this is heap-only tuple */
+#define HEAP_SPECULATIVE		0x10000	/* this is speculative tuple */
 
 #define HEAP2_XACT_MASK			0xE000	/* visibility-related bits */
 
@@ -307,6 +313,18 @@ struct HeapTupleHeaderData
 )
 
 /*
+ * Was tuple "super deleted" following unsuccessful speculative insertion (i.e.
+ * conflict was detected at insertion time)?  Is is not sufficient to set
+ * HEAP_XMIN_INVALID to super delete because it is only a hint, and because it
+ * interacts with transaction commit status.  Speculative insertion decouples
+ * visibility from transaction duration for one special purpose.
+ */
+#define HeapTupleHeaderSuperDeleted(tup) \
+( \
+	(!TransactionIdIsValid(HeapTupleHeaderGetRawXmin(tup))) \
+)
+
+/*
  * HeapTupleHeaderGetRawXmax gets you the raw Xmax field.  To find out the Xid
  * that updated a tuple, you might need to resolve the MultiXactId if certain
  * bits are set.  HeapTupleHeaderGetUpdateXid checks those bits and takes care
@@ -455,6 +473,17 @@ do { \
   (tup)->t_infomask2 &= ~HEAP_ONLY_TUPLE \
 )
 
+#define HeapTupleHeaderSetSpeculative(tup) \
+( \
+  AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
+  (tup)->t_infomask2 |= HEAP_SPECULATIVE \
+)
+
+#define HeapTupleHeaderIsSpeculative(tup) \
+( \
+  (tup)->t_infomask2 & HEAP_SPECULATIVE \
+)
+
 #define HeapTupleHeaderHasMatch(tup) \
 ( \
   (tup)->t_infomask2 & HEAP_TUPLE_HAS_MATCH \
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index e7cc7a0..42c10d4 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -80,6 +80,8 @@ extern void index_drop(Oid indexId, bool concurrent);
 
 extern IndexInfo *BuildIndexInfo(Relation index);
 
+extern void AddUniqueSpeculative(Relation index, IndexInfo *ii);
+
 extern void FormIndexDatum(IndexInfo *indexInfo,
 			   TupleTableSlot *slot,
 			   EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index c1e7477..8100bd8 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -351,16 +351,19 @@ extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
 extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags);
 extern void ExecCloseScanRelation(Relation scanrel);
 
-extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
+extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative);
 extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
 extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
-					  EState *estate);
-extern bool check_exclusion_constraint(Relation heap, Relation index,
-						   IndexInfo *indexInfo,
-						   ItemPointer tupleid,
-						   Datum *values, bool *isnull,
-						   EState *estate,
-						   bool newIndex, bool errorOK);
+					  EState *estate, bool noDupErr, Oid arbiterIdx);
+extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate,
+					  ItemPointer conflictTid, Oid arbiterIdx);
+extern bool check_exclusion_or_unique_constraint(Relation heap, Relation index,
+									   IndexInfo *indexInfo,
+									   ItemPointer tupleid,
+									   Datum *values, bool *isnull,
+									   EState *estate,
+									   bool newIndex, bool errorOK,
+									   bool wait, ItemPointer conflictTid);
 
 extern void RegisterExprContextCallback(ExprContext *econtext,
 							ExprContextCallbackFunction function,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index ac75f86..e61a43a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -41,6 +41,9 @@
  *		ExclusionOps		Per-column exclusion operators, or NULL if none
  *		ExclusionProcs		Underlying function OIDs for ExclusionOps
  *		ExclusionStrats		Opclass strategy numbers for ExclusionOps
+ *		UniqueOps			Theses are like Exclusion*, but for unique indexes
+ *		UniqueProcs
+ *		UniqueStrats
  *		Unique				is it a unique index?
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
@@ -62,6 +65,9 @@ typedef struct IndexInfo
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
+	Oid		   *ii_UniqueOps;	/* array with one entry per column */
+	Oid		   *ii_UniqueProcs;		/* array with one entry per column */
+	uint16	   *ii_UniqueStrats;		/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
@@ -1092,6 +1098,8 @@ typedef struct ModifyTableState
 	int			mt_whichplan;	/* which one is being executed (0..n-1) */
 	ResultRelInfo *resultRelInfo;		/* per-subplan target relations */
 	List	  **mt_arowmarks;	/* per-subplan ExecAuxRowMark lists */
+	SpecCmd		spec;			/* reason for speculative insertion */
+	Oid			arbiterIndex;	/* unique index to arbitrate taking alt path */
 	EPQState	mt_epqstate;	/* for evaluating EvalPlanQual rechecks */
 	bool		fireBSTriggers; /* do we need to fire stmt triggers? */
 } ModifyTableState;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 38469ef..5b348fa 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -412,6 +412,8 @@ typedef enum NodeTag
 	T_RowMarkClause,
 	T_XmlSerialize,
 	T_WithClause,
+	T_InferClause,
+	T_ConflictClause,
 	T_CommonTableExpr,
 	T_RoleSpec,
 
@@ -625,4 +627,16 @@ typedef enum JoinType
 	   (1 << JOIN_RIGHT) | \
 	   (1 << JOIN_ANTI))) != 0)
 
+/*
+ * SpecCmd -
+ *	  "Speculative insertion" clause
+ *
+ * This is needed in both parsenodes.h and plannodes.h, so put it here...
+ */
+typedef enum
+{
+	SPEC_NONE,		/* Not involved in speculative insertion */
+	SPEC_IGNORE		/* INSERT of "ON CONFLICT IGNORE" */
+} SpecCmd;
+
 #endif   /* NODES_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ec0d0ea..75e33ea 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -132,6 +132,10 @@ typedef struct Query
 
 	List	   *withCheckOptions;		/* a list of WithCheckOption's */
 
+	SpecCmd		specClause;		/* speculative insertion clause */
+	List	   *arbiterExpr;	/* Unique index arbiter exprs */
+	Node	   *arbiterWhere;	/* Unique index arbiter WHERE clause */
+
 	List	   *returningList;	/* return-values list (of TargetEntry) */
 
 	List	   *groupClause;	/* a list of SortGroupClause's */
@@ -591,7 +595,7 @@ typedef enum TableLikeOption
 } TableLikeOption;
 
 /*
- * IndexElem - index parameters (used in CREATE INDEX)
+ * IndexElem - index parameters (used in CREATE INDEX, and in ON CONFLICT)
  *
  * For a plain index attribute, 'name' is the name of the table column to
  * index, and 'expr' is NULL.  For an index expression, 'name' is NULL and
@@ -1015,6 +1019,34 @@ typedef struct WithClause
 } WithClause;
 
 /*
+ * InferClause -
+ * 		ON CONFLICT unique index inference clause
+ *
+ * Note: InferClause does not propagate into the Query representation.
+ */
+typedef struct InferClause
+{
+	NodeTag		type;
+	List	   *indexElems;		/* IndexElems to infer unique index */
+	Node	   *whereClause;	/* qualification (partial-index predicate) */
+	int			location;		/* token location, or -1 if unknown */
+} InferClause;
+
+/*
+ * ConflictClause -
+ * 		representation of ON CONFLICT clause
+ *
+ * Note: ConflictClause does not propagate into the Query representation.
+ */
+typedef struct ConflictClause
+{
+	NodeTag			type;
+	SpecCmd			specclause;		/* Variant specified */
+	InferClause	   *infer;			/* Optional index inference clause */
+	int				location;		/* token location, or -1 if unknown */
+} ConflictClause;
+
+/*
  * CommonTableExpr -
  *	   representation of WITH list element
  *
@@ -1064,6 +1096,7 @@ typedef struct InsertStmt
 	RangeVar   *relation;		/* relation to insert into */
 	List	   *cols;			/* optional: names of the target columns */
 	Node	   *selectStmt;		/* the source SELECT/VALUES, or NULL */
+	ConflictClause  *confClause;	/* ON CONFLICT clause */
 	List	   *returningList;	/* list of expressions to return */
 	WithClause *withClause;		/* WITH clause */
 } InsertStmt;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 21cbfa8..66027a3 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -178,6 +178,8 @@ typedef struct ModifyTable
 	List	   *resultRelations;	/* integer list of RT indexes */
 	int			resultRelIndex; /* index of first resultRel in plan's list */
 	List	   *plans;			/* plan(s) producing source data */
+	SpecCmd		spec;			/* speculative insertion specification */
+	Oid			arbiterIndex;	/* Oid of ON CONFLICT arbiter index */
 	List	   *withCheckOptionLists;	/* per-target-table WCO lists */
 	List	   *returningLists; /* per-target-table RETURNING tlists */
 	List	   *fdwPrivLists;	/* per-target-table FDW private data lists */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6cad92e..b0fec30 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -64,6 +64,8 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
 							int indexcol,
 							List **indexcolnos,
 							bool *var_on_left_p);
+extern Oid plan_speculative_use_index(PlannerInfo *root, int widthTarget,
+									  List *indexList);
 
 /*
  * tidpath.h
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index 8eb2e57..878adfe 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -28,6 +28,8 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
 extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
 				  bool inhparent, RelOptInfo *rel);
 
+extern Oid infer_unique_index(PlannerInfo *root);
+
 extern void estimate_rel_size(Relation rel, int32 *attr_widths,
 				  BlockNumber *pages, double *tuples, double *allvisfrac);
 
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index fa72918..c3a0634 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -85,7 +85,7 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
 				 Index nominalRelation,
 				 List *resultRelations, List *subplans,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, int epqParam);
+				 List *rowMarks, SpecCmd spec, int epqParam);
 extern bool is_projection_capable_plan(Plan *plan);
 
 /*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7c243ec..cf501e6 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -87,6 +87,7 @@ PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
 PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
 PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
+PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
 PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD)
 PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD)
@@ -180,6 +181,7 @@ PG_KEYWORD("hold", HOLD, UNRESERVED_KEYWORD)
 PG_KEYWORD("hour", HOUR_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("identity", IDENTITY_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("if", IF_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("ignore", IGNORE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("immediate", IMMEDIATE, UNRESERVED_KEYWORD)
 PG_KEYWORD("immutable", IMMUTABLE, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 6a4438f..d1d0d12 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -41,6 +41,8 @@ extern List *transformDistinctClause(ParseState *pstate,
 						List **targetlist, List *sortClause, bool is_agg);
 extern List *transformDistinctOnClause(ParseState *pstate, List *distinctlist,
 						  List **targetlist, List *sortClause);
+extern void transformConflictClause(ParseState *pstate, ConflictClause *confClause,
+									List **arbiterExpr, Node **arbiterWhere);
 
 extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 					List *sortlist, List *targetlist, SortBy *sortby,
diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h
index f1e0f57..d694981 100644
--- a/src/include/replication/reorderbuffer.h
+++ b/src/include/replication/reorderbuffer.h
@@ -43,6 +43,13 @@ typedef struct ReorderBufferTupleBuf
  * and ComboCids in the same list with the user visible INSERT/UPDATE/DELETE
  * changes. Users of the decoding facilities will never see changes with
  * *_INTERNAL_* actions.
+ *
+ * The REORDER_BUFFER_CHANGE_INTERNAL_INSERT and
+ * REORDER_BUFFER_CHANGE_INTERNAL_DELETE changes concern "super deletion",
+ * which is a mechanism that speculative insertion makes use of to handle
+ * conflicts.  At transaction reassembly these will be consolidated, and so
+ * decoding plugins will only ever handle REORDER_BUFFER_CHANGE_INSERT changes
+ * here too (in the common case where speculative insertion works out).
  */
 enum ReorderBufferChangeType
 {
@@ -51,7 +58,9 @@ enum ReorderBufferChangeType
 	REORDER_BUFFER_CHANGE_DELETE,
 	REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT,
 	REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID,
-	REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID
+	REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID,
+	REORDER_BUFFER_CHANGE_INTERNAL_INSERT,
+	REORDER_BUFFER_CHANGE_INTERNAL_DELETE
 };
 
 /*
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index f5d70e5..7cc75fc 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -76,6 +76,11 @@ extern bool ConditionalXactLockTableWait(TransactionId xid);
 extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode);
 extern void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode);
 
+/* Lock an XID for tuple insertion (used to wait for an insertion to finish) */
+extern uint32	SpeculativeInsertionLockAcquire(TransactionId xid);
+extern void		SpeculativeInsertionLockRelease(TransactionId xid);
+extern void		SpeculativeInsertionWait(TransactionId xid, uint32 token);
+
 /* Lock a general object (other than a relation) of the current database */
 extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
 				   LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 1477a6f..29edab4 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -176,6 +176,8 @@ typedef enum LockTagType
 	/* ID info for a transaction is its TransactionId */
 	LOCKTAG_VIRTUALTRANSACTION, /* virtual transaction (ditto) */
 	/* ID info for a virtual transaction is its VirtualTransactionId */
+	LOCKTAG_PROMISE_TUPLE,		/* tuple insertion, keyed by Xid and token */
+	/* ID info for a transaction is its TransactionId */
 	LOCKTAG_OBJECT,				/* non-relation database object */
 	/* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */
 
@@ -261,6 +263,14 @@ typedef struct LOCKTAG
 	 (locktag).locktag_type = LOCKTAG_VIRTUALTRANSACTION, \
 	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
 
+#define SET_LOCKTAG_SPECULATIVE_INSERTION(locktag,xid,token) \
+	((locktag).locktag_field1 = (xid), \
+	 (locktag).locktag_field2 = (token),		\
+	 (locktag).locktag_field3 = 0, \
+	 (locktag).locktag_field4 = 0, \
+	 (locktag).locktag_type = LOCKTAG_PROMISE_TUPLE, \
+	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
+
 #define SET_LOCKTAG_OBJECT(locktag,dboid,classoid,objoid,objsubid) \
 	((locktag).locktag_field1 = (dboid), \
 	 (locktag).locktag_field2 = (classoid), \
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 26fb257..cd5ad76 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -87,6 +87,17 @@ typedef struct SnapshotData
 	bool		copied;			/* false if it's a static snapshot */
 
 	/*
+	 * Snapshot's speculative token is value set by HeapTupleSatisfiesDirty,
+	 * indicating that the tuple is being inserted speculatively, and may yet
+	 * be "super-deleted" before EOX. The caller may use the value with
+	 * PromiseTupleInsertionWait to wait for the inserter to decide. It is only
+	 * set when a valid 'xmin' is set, too.  By convention, when
+	 * speculativeToken is zero, the caller must assume that is should wait on
+	 * a non-speculative tuple (i.e. wait for xmin/xmax to commit).
+	 */
+	uint32		speculativeToken;
+
+	/*
 	 * note: all ids in subxip[] are >= xmin, but we don't bother filtering
 	 * out any that are >= xmax
 	 */
diff --git a/src/test/isolation/expected/insert-conflict-ignore.out b/src/test/isolation/expected/insert-conflict-ignore.out
new file mode 100644
index 0000000..e6cc2a1
--- /dev/null
+++ b/src/test/isolation/expected/insert-conflict-ignore.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: ignore1 ignore2 c1 select2 c2
+step ignore1: INSERT INTO ints(key, val) VALUES(1, 'ignore1') ON CONFLICT IGNORE;
+step ignore2: INSERT INTO ints(key, val) VALUES(1, 'ignore2') ON CONFLICT IGNORE; <waiting ...>
+step c1: COMMIT;
+step ignore2: <... completed>
+step select2: SELECT * FROM ints;
+key            val            
+
+1              ignore1        
+step c2: COMMIT;
+
+starting permutation: ignore1 ignore2 a1 select2 c2
+step ignore1: INSERT INTO ints(key, val) VALUES(1, 'ignore1') ON CONFLICT IGNORE;
+step ignore2: INSERT INTO ints(key, val) VALUES(1, 'ignore2') ON CONFLICT IGNORE; <waiting ...>
+step a1: ABORT;
+step ignore2: <... completed>
+step select2: SELECT * FROM ints;
+key            val            
+
+1              ignore2        
+step c2: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index c055a53..59d14e9 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -16,6 +16,7 @@ test: fk-deadlock2
 test: eval-plan-qual
 test: lock-update-delete
 test: lock-update-traversal
+test: insert-conflict-ignore
 test: delete-abort-savept
 test: delete-abort-savept-2
 test: aborted-keyrevoke
diff --git a/src/test/isolation/specs/insert-conflict-ignore.spec b/src/test/isolation/specs/insert-conflict-ignore.spec
new file mode 100644
index 0000000..fde43b3
--- /dev/null
+++ b/src/test/isolation/specs/insert-conflict-ignore.spec
@@ -0,0 +1,41 @@
+# INSERT...ON CONFLICT IGNORE test
+#
+# This test tries to expose problems with the interaction between concurrent
+# sessions during INSERT...ON CONFLICT IGNORE.
+#
+# The convention here is that session 1 always ends up inserting, and session 2
+# always ends up ignoring.
+
+setup
+{
+  CREATE TABLE ints (key int primary key, val text);
+}
+
+teardown
+{
+  DROP TABLE ints;
+}
+
+session "s1"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "ignore1" { INSERT INTO ints(key, val) VALUES(1, 'ignore1') ON CONFLICT IGNORE; }
+step "c1" { COMMIT; }
+step "a1" { ABORT; }
+
+session "s2"
+setup
+{
+  BEGIN ISOLATION LEVEL READ COMMITTED;
+}
+step "ignore2" { INSERT INTO ints(key, val) VALUES(1, 'ignore2') ON CONFLICT IGNORE; }
+step "select2" { SELECT * FROM ints; }
+step "c2" { COMMIT; }
+step "a2" { ABORT; }
+
+# Regular case where one session block-waits on another to determine if it
+# should proceed with an insert or ignore.
+permutation "ignore1" "ignore2" "c1" "select2" "c2"
+permutation "ignore1" "ignore2" "a1" "select2" "c2"
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
new file mode 100644
index 0000000..a34d857
--- /dev/null
+++ b/src/test/regress/expected/insert_conflict.out
@@ -0,0 +1,55 @@
+--
+-- insert...on conflict update unique index inference
+--
+create table insertconflicttest(key int4, fruit text);
+--
+-- Test partial unique index inference
+--
+create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
+-- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
+-- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
+ERROR:  could not infer which unique index to use from expressions/columns and predicate provided for ON CONFLICT
+insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
+ERROR:  partial arbiter unique index has predicate that does not cover tuple proposed for insertion
+DETAIL:  ON CONFLICT inference clause implies that the tuple proposed for insertion must be covered by predicate for partial index "partial_key_index".
+drop index partial_key_index;
+-- Cleanup
+drop table insertconflicttest;
+-- ******************************************************************
+-- *                                                                *
+-- * Test inheritance (example taken from tutorial)                 *
+-- *                                                                *
+-- ******************************************************************
+create table cities (
+	name		text,
+	population	float8,
+	altitude	int		-- (in ft)
+);
+create table capitals (
+	state		char(2)
+) inherits (cities);
+-- Create unique indexes.  Due to a general limitation of inheritance,
+-- uniqueness is only enforced per-relation
+create unique index cities_names_unique on cities (name);
+create unique index capitals_names_unique on capitals (name);
+-- prepopulate the tables.
+insert into cities values ('San Francisco', 7.24E+5, 63);
+insert into cities values ('Las Vegas', 2.583E+5, 2174);
+insert into cities values ('Mariposa', 1200, 1953);
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
+insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
+-- Tests proper for inheritance:
+-- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
+ERROR:  relation "cities" has inheritance children
+HINT:  Only heap relations without inheritance children are accepted as targets when a unique index is inferred for ON CONFLICT.
+-- Succeeds:
+-- There is at least limited support for relations with children:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
+-- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
+-- clean up
+drop table capitals;
+drop table cities;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 1788270..1d67331 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1123,6 +1123,10 @@ SELECT * FROM shoelace_log ORDER BY sl_name;
 	SELECT * FROM shoelace_obsolete WHERE sl_avail = 0;
 insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
 insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
+-- Unsupported (even though a similar updatable view construct is)
+insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0)
+  on conflict ignore;
+ERROR:  INSERT with ON CONFLICT clause may not target relation with INSERT or UPDATE rules
 SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm;
   sl_name   | sl_avail |  sl_color  | sl_len | sl_unit  | sl_len_cm 
 ------------+----------+------------+--------+----------+-----------
@@ -2348,6 +2352,11 @@ DETAIL:  Key (id3a, id3c)=(1, 13) is not present in table "rule_and_refint_t2".
 insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
 ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
 DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
+-- Ordinary table
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict ignore;
+ERROR:  insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
+DETAIL:  Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
 			where (((rule_and_refint_t3.id3a = new.id3a)
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 9e7ba72..b8823b4 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -215,6 +215,10 @@ INSERT INTO rw_view15 VALUES (3, 'ROW 3'); -- should fail
 ERROR:  cannot insert into column "upper" of view "rw_view15"
 DETAIL:  View columns that are not columns of their base relation are not updatable.
 INSERT INTO rw_view15 (a) VALUES (3); -- should be OK
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT IGNORE; -- succeeds
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) IGNORE; -- fails, unsupported
+ERROR:  relation "rw_view15" is not an ordinary table
+HINT:  Only ordinary tables are accepted as targets when a unique index is inferred for ON CONFLICT.
 ALTER VIEW rw_view15 ALTER COLUMN upper SET DEFAULT 'NOT SET';
 INSERT INTO rw_view15 (a) VALUES (4); -- should fail
 ERROR:  cannot insert into column "upper" of view "rw_view15"
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 6d3b865..b0ebb6b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -36,6 +36,7 @@ test: geometry horology regex oidjoins type_sanity opr_sanity
 # These four each depend on the previous one
 # ----------
 test: insert
+test: insert_conflict
 test: create_function_1
 test: create_type
 test: create_table
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 8326894..8409c0f 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -50,6 +50,7 @@ test: oidjoins
 test: type_sanity
 test: opr_sanity
 test: insert
+test: insert_conflict
 test: create_function_1
 test: create_type
 test: create_table
diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql
new file mode 100644
index 0000000..e330ecd
--- /dev/null
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -0,0 +1,65 @@
+--
+-- insert...on conflict update unique index inference
+--
+create table insertconflicttest(key int4, fruit text);
+
+--
+-- Test partial unique index inference
+--
+create unique index partial_key_index on insertconflicttest(key) where fruit like '%berry';
+
+-- Succeeds
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' and fruit = 'inconsequential') ignore;
+
+-- fails
+insert into insertconflicttest values (23, 'Blackberry') on conflict (key where fruit like '%berry' or fruit = 'consequential') ignore;
+insert into insertconflicttest values (23, 'Uncovered by Index') on conflict (key where fruit like '%berry') ignore;
+
+drop index partial_key_index;
+
+-- Cleanup
+drop table insertconflicttest;
+
+-- ******************************************************************
+-- *                                                                *
+-- * Test inheritance (example taken from tutorial)                 *
+-- *                                                                *
+-- ******************************************************************
+create table cities (
+	name		text,
+	population	float8,
+	altitude	int		-- (in ft)
+);
+
+create table capitals (
+	state		char(2)
+) inherits (cities);
+
+-- Create unique indexes.  Due to a general limitation of inheritance,
+-- uniqueness is only enforced per-relation
+create unique index cities_names_unique on cities (name);
+create unique index capitals_names_unique on capitals (name);
+
+-- prepopulate the tables.
+insert into cities values ('San Francisco', 7.24E+5, 63);
+insert into cities values ('Las Vegas', 2.583E+5, 2174);
+insert into cities values ('Mariposa', 1200, 1953);
+
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA');
+insert into capitals values ('Madison', 1.913E+5, 845, 'WI');
+
+-- Tests proper for inheritance:
+
+-- fails:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict (name) ignore;
+
+-- Succeeds:
+
+-- There is at least limited support for relations with children:
+insert into cities values ('Las Vegas', 2.583E+5, 2174) on conflict ignore;
+-- No children, and so no restrictions:
+insert into capitals values ('Sacramento', 3.694E+5, 30, 'CA') on conflict (name) ignore;
+
+-- clean up
+drop table capitals;
+drop table cities;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index c385e41..5807331 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -680,6 +680,9 @@ SELECT * FROM shoelace_log ORDER BY sl_name;
 
 insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
 insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
+-- Unsupported (even though a similar updatable view construct is)
+insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0)
+  on conflict ignore;
 
 SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm;
 SELECT * FROM shoelace_candelete;
@@ -844,6 +847,9 @@ insert into rule_and_refint_t3 values (1, 12, 11, 'row3');
 insert into rule_and_refint_t3 values (1, 12, 12, 'row4');
 insert into rule_and_refint_t3 values (1, 11, 13, 'row5');
 insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
+-- Ordinary table
+insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
+  on conflict ignore;
 
 create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
 	where (exists (select 1 from rule_and_refint_t3
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 60c7e29..48dd9a9 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -69,6 +69,8 @@ DELETE FROM rw_view14 WHERE a=3; -- should be OK
 -- Partially updatable view
 INSERT INTO rw_view15 VALUES (3, 'ROW 3'); -- should fail
 INSERT INTO rw_view15 (a) VALUES (3); -- should be OK
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT IGNORE; -- succeeds
+INSERT INTO rw_view15 (a) VALUES (3) ON CONFLICT (a) IGNORE; -- fails, unsupported
 ALTER VIEW rw_view15 ALTER COLUMN upper SET DEFAULT 'NOT SET';
 INSERT INTO rw_view15 (a) VALUES (4); -- should fail
 UPDATE rw_view15 SET upper='ROW 3' WHERE a=3; -- should fail
-- 
1.9.1

#18Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#17)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 03/26/2015 08:00 PM, Peter Geoghegan wrote:

On Wed, Mar 25, 2015 at 12:42 PM, Peter Geoghegan <pg@heroku.com> wrote:

My next revision will have a more polished version of this scheme. I'm
not going to immediately act on Robert's feedback elsewhere (although
I'd like to), owing to time constraints - no reason to deny you the
opportunity to review the entirely unrelated low-level speculative
locking mechanism due to that.

Attached revision, V3.1, implements this slightly different way of
figuring out if an insertion is speculative, as discussed. We reuse
t_ctid for speculatively inserted tuples. That's where the counter
goes. I think that this is a significant improvement, since there is
no longer any need to touch the proc array for any reason, without
there being any significant disadvantage that I'm aware of. I also
fixed some bitrot, and a bug with index costing (the details aren't
terribly interesting - tuple width wasn't being calculated correctly).

Cool. Quickly looking at the patch though - does it actually work as it
is? RelationPutHeapTuple will overwrite the ctid field when the tuple is
put on the page, so I don't think the correct token will make it to disk
as the patch stands. Also, there are a few places where we currently
check if t_ctid equals the tuple's location, and try to follow t_ctid if
it doesn't. I think those need to be taught that t_ctid can also be a token.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#18)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Mar 26, 2015 at 2:51 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Attached revision, V3.1, implements this slightly different way of
figuring out if an insertion is speculative, as discussed. We reuse
t_ctid for speculatively inserted tuples. That's where the counter
goes. I think that this is a significant improvement, since there is
no longer any need to touch the proc array for any reason, without
there being any significant disadvantage that I'm aware of. I also
fixed some bitrot, and a bug with index costing (the details aren't
terribly interesting - tuple width wasn't being calculated correctly).

Cool. Quickly looking at the patch though - does it actually work as it is?

The test cases pass, including jjanes_upsert, and stress tests that
test for unprincipled deadlocks. But yes, I am entirely willing to
believe that something that was written in such haste could be broken.
My manual testing was pretty minimal.

Sorry for posting a shoddy patch, but I thought it was more important
to show you that this is perfectly workable ASAP.

RelationPutHeapTuple will overwrite the ctid field when the tuple is put on
the page, so I don't think the correct token will make it to disk as the
patch stands.

Oops - You're right. I find it interesting that this didn't result in
breaking my test cases. I guess that not having proc array locking
might have made the difference with unprincipled deadlocks, which I
could not recreate (and row locking saves us from breaking UPSERT, I
think - although if so the token lock would still certainly be needed
for the IGNORE variant). It is interesting that this wasn't obviously
broken for UPSERT, though. I think it at least suggests that when
testing, we need to be more careful with taking a working UPSERT as a
proxy for a working ON CONFLICT IGNORE.

Also, there are a few places where we currently check if
t_ctid equals the tuple's location, and try to follow t_ctid if it doesn't.
I think those need to be taught that t_ctid can also be a token.

I did fix at least some of those. I thought that the choke point for
doing that was fairly small, entirely confined to one or two routines
with heapam.c. But it would surely be better to follow your suggestion
of using an invalid/magic tuple offset value to be sure that it cannot
possibly occur elsewhere. And I'm still using that infomask2 bit,
which is probably not really necessary. So that needs to change too.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Peter Geoghegan (#19)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 27/03/15 09:14, Peter Geoghegan wrote:

On Thu, Mar 26, 2015 at 2:51 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

[...]

Oops - You're right. I find it interesting that this didn't result in
breaking my test cases.

[...]

Reminds of the situation where I got an A++ for a COBOL programming
assignment that successfully handled the test data provided - then I
found a major bug when 'idly' reviewing my code! The lecturer (also a
highly experienced developer) was amused when I pointed it out to her,
and she said I still deserved the A++!

Cheers,
Gavin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Andres Freund
andres@2ndquadrant.com
In reply to: Peter Geoghegan (#17)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

Hi,

Just had a longer chat with Peter about this patch.

* Not a fan of the heap flags usage, the reusage seems sketch to me
* Explain should show the arbiter index in text as well
* AddUniqueSpeculative is a bad name, it should refer IndexInfo
* Work on the ExecInsert() comments
* Let's remove the planner choosing the "cheapest" arbiter index; it
should do all.
* s/infer_unique_index/infer_arbiter_index/
* Not supporting inheritance properly makes me uncomfortable. I don't
think users will think that's a acceptable/reasonable restriction.
* Let's not use t_ctid directly, but add a wrapper
* The code uses LockTupleExclusive to lock rows. That means the fkey key
locking doesn't work; That's annoying. This means that using upsert
will potentially cause deadlocks in other sessions :(. I think you'll
have to determine what lock to acquire by fetching the tuple, doing
the key comparison, and acquire the appropriate lock. That should be
fine.
* I think we should decouple the insertion and wal logging more. I think
the promise tuple insertion should be different from the final
insertion of the actual tuple. For one it seems cleaner to me, for
another it will avoid the uglyness around logical decoding. I think
also that the separation will make it more realistic to use something
like this for a COPY variant that doesn't raise unique violations and
such.
* We discussed the infererence and that it should just reuse (code,
grammar, docs) the column specification from create index.
* Some more stuff I don't recall.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#21)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Sat, Mar 28, 2015 at 6:36 PM, Andres Freund <andres@2ndquadrant.com> wrote:

Just had a longer chat with Peter about this patch.

It was a very useful chat. Thanks for making yourself available to do it.

* Not a fan of the heap flags usage, the reusage seems sketch to me
* Explain should show the arbiter index in text as well
* AddUniqueSpeculative is a bad name, it should refer IndexInfo
* Work on the ExecInsert() comments
* Let's remove the planner choosing the "cheapest" arbiter index; it
should do all.
* s/infer_unique_index/infer_arbiter_index/

OK.

* Not supporting inheritance properly makes me uncomfortable. I don't
think users will think that's a acceptable/reasonable restriction.

I'll look into making the inference specification deduce a child relation index.

* Let's not use t_ctid directly, but add a wrapper

We talked about a union. This seems quite doable.

* The code uses LockTupleExclusive to lock rows. That means the fkey key
locking doesn't work; That's annoying. This means that using upsert
will potentially cause deadlocks in other sessions :(. I think you'll
have to determine what lock to acquire by fetching the tuple, doing
the key comparison, and acquire the appropriate lock. That should be
fine.

Looking into the implementation of this. As we discussed, the
difficulty about avoiding a lock escalation within ExecUpdate() is
that we must fetch the row, run EvalPlanQual() to check if the new row
version generated by updating will require a LockTupleExclusive or
LockTupleNoKeyExclusive, and then lock the row using the right
lockmode, and only then call ExecUpdate(). Right now, UPSERT benefits
from fetching and locking the row together, so going this way imposes
a little additional complexity. It's probably worth it, though.

* I think we should decouple the insertion and wal logging more. I think
the promise tuple insertion should be different from the final
insertion of the actual tuple. For one it seems cleaner to me, for
another it will avoid the uglyness around logical decoding. I think
also that the separation will make it more realistic to use something
like this for a COPY variant that doesn't raise unique violations and
such.

Your COPY argument swung this for me. I'm looking into the implementation.

* We discussed the infererence and that it should just reuse (code,
grammar, docs) the column specification from create index.

Agreed.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#22)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 03/30/2015 07:20 PM, Peter Geoghegan wrote:

* I think we should decouple the insertion and wal logging more. I think
the promise tuple insertion should be different from the final
insertion of the actual tuple. For one it seems cleaner to me, for
another it will avoid the uglyness around logical decoding. I think
also that the separation will make it more realistic to use something
like this for a COPY variant that doesn't raise unique violations and
such.

Your COPY argument swung this for me. I'm looking into the implementation.

I'm pretty sceptical of that. ISTM you'll need to do modify the page
twice for each insertion, first to insert the promise tuple, and then to
turn the promise tuple into a real tuple. And WAL-log both updates.
That's going to hurt performance.

To recover COPY from unique violations, you can just do the same as
INSERT ON CONFLICT IGNORE does, and super-delete the inserted tuple on
conflict. To recover from any random error, you'll need to abort the
(sub)transaction anyway, and I don't see how it helps to separate the
insertion of the promise tuple and the "finalization" of the insertion.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#23)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Mar 31, 2015 at 1:09 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I'm pretty sceptical of that. ISTM you'll need to do modify the page twice
for each insertion, first to insert the promise tuple, and then to turn the
promise tuple into a real tuple. And WAL-log both updates. That's going to
hurt performance.

Andres' wish to do things that way is at least partially motivated by
having logical decoding just work. The co-ordination I'm currently
doing across changes within transaction reassembly is pretty ugly.
Andres has strongly suggested that it's broken, too, since a snapshot
change could occur between a speculative insertion and its super
deletion within transaction resassembly, thus invalidating the
assumption that the next change not being a super deletion means there
is no such super deletion change (i.e. the insert should be treated as
"real").

Anyway, if we don't do this, we'll need to make sure my changes to
transaction reassembly are sound. Hopefully that's an easy fix.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#24)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Mar 31, 2015 at 2:26 PM, Peter Geoghegan <pg@heroku.com> wrote:

Andres' wish to do things that way is at least partially motivated by
having logical decoding just work.

I should add that there appears to be some need to terminate the loop
of speculative token waiting. By that I mean that since we're not
looking at the proc array to get a speculative token from
HeapTupleSatisfiesDirty() now, there is a livelock hazard. That goes
away when the speculative inserter cleans up after itself, as Andres
proposed. It would also go away if any speculative waiter cleaned up
after the inserter, which you suggested (that would be kind of
invasive to places like _bt_doinsert(), though). Finally, it would
also work if HeapTupleSatisfiesDirty() tested if the token was still
held directly, before reporting a speculative token, by for example
attempting to briefly acquire a ShareLock on the token (but that would
mean that the extra lock acquisition would be required unless and
until someone updated that originally-speculative tuple, in doing so
finally changing its t_ctid).

I think that we definitely have to do something like this, in any
case. Maybe just have SpeculativeTokenWait deal with the clean up is
cleanest, if we're not going to have inserters clean-up after
themselves immediately per Andres' suggestion.
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#22)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Mon, Mar 30, 2015 at 9:20 AM, Peter Geoghegan <pg@heroku.com> wrote:

* The code uses LockTupleExclusive to lock rows. That means the fkey key
locking doesn't work; That's annoying. This means that using upsert
will potentially cause deadlocks in other sessions :(. I think you'll
have to determine what lock to acquire by fetching the tuple, doing
the key comparison, and acquire the appropriate lock. That should be
fine.

Looking into the implementation of this. As we discussed, the
difficulty about avoiding a lock escalation within ExecUpdate() is
that we must fetch the row, run EvalPlanQual() to check if the new row
version generated by updating will require a LockTupleExclusive or
LockTupleNoKeyExclusive, and then lock the row using the right
lockmode, and only then call ExecUpdate(). Right now, UPSERT benefits
from fetching and locking the row together, so going this way imposes
a little additional complexity. It's probably worth it, though.

Why do you think deadlocks will be a particular concern? Sure, it
could make the difference between deadlocking and not deadlocking,
which is bad, but it's not obvious to me that the risk would be any
worse than the risk of deadlocking with FKs in 9.2. Is that really all
that bad?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#22)
4 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

I attach a revision - V3.2

On Mon, Mar 30, 2015 at 9:20 AM, Peter Geoghegan <pg@heroku.com> wrote:

* Not supporting inheritance properly makes me uncomfortable. I don't
think users will think that's a acceptable/reasonable restriction.

Inheritance is now supported to the greatest extent anyone could
reasonably expect. In other words, inference works just fine for child
tables, and now parent tables too (so you can UPSERT both). Of course,
because of the existing restriction on making unique constraints cross
tables participating in inheritance, UPSERT similarly cannot work
across inheritance tables. This should be totally obvious to anyone
that uses inheritance - if you won't get a conflict (duplicate
violation) based on an ordinary insert, then of course UPSERT will not
take the alternative UPDATE path just because someone imagines that a
(would-be) duplicate violation should happen.

* Let's not use t_ctid directly, but add a wrapper

We talked about a union. This seems quite doable.

This now uses a union. And it now actually stores a token value!

* The code uses LockTupleExclusive to lock rows. That means the fkey key
locking doesn't work; That's annoying. This means that using upsert
will potentially cause deadlocks in other sessions :(. I think you'll
have to determine what lock to acquire by fetching the tuple, doing
the key comparison, and acquire the appropriate lock. That should be
fine.

Looking into the implementation of this.

Not quite sold on this, on second thought (although let's focus on the
WAL logging stuff - the immediate blocker to committing the IGNORE
variant). Perhaps you can explain why you think it's important.

I like that I am able to fully lock the row when the predicate isn't
passed. I think that's a useful feature in some cases (it particularly
makes sense for higher isolation levels that expect to repeat the same
command and not get a serialization failure). It also keeps the
already complicated function ExecLockUpdateTuple() significantly more
simple.

* I think we should decouple the insertion and wal logging more. I think
the promise tuple insertion should be different from the final
insertion of the actual tuple. For one it seems cleaner to me, for
another it will avoid the uglyness around logical decoding. I think
also that the separation will make it more realistic to use something
like this for a COPY variant that doesn't raise unique violations and
such.

Your COPY argument swung this for me. I'm looking into the implementation.

I have a prototype implementation of this with V3.2 - it clearly needs
more work, but I thought it was best to post sooner rather than later.
I am reusing in-place update infrastructure for this. This should give
Heikki something to play with, since he wasn't quite sold on this
idea. Certainly, what I have here is not good enough to commit - there
is unnecessary double WAL logging of tuple contents, just for example.
More generally, my recent changes to heapam.c certainly lack polish. I
have something for us to discuss, though, and under the circumstances
I think that's a good thing. Grep for "XXX" and "TODO" comments for
more.

Logical decoding is not handled at all, since I hit a snag with
building a tuple from the in-place update WAL records, and I didn't
want to block on that (especially given the general uncertainty about
if and how to affirm that a speculative insertion succeeded - IWO, if
we should go Andres' way there to avoid making transaction reassembly
for decoding more messy). I have at least reverted the logical
decoding transaction reassembly peek-ahead thing that Andres hated so
much, though. I hope we can reach consensus on what to do on this
point of WAL logging/logical decoding in particular real soon now.

* We discussed the infererence and that it should just reuse (code,
grammar, docs) the column specification from create index.

The inference specification now both accepts collation and opclass
specifications along the lines we discussed, and can infer multiple
unique indexes per Heikki/Robert's complaint (so there is no longer a
costing aspect to it at all).

There are lots of tests for the inference of collations and opclasses
- if you want to know how it works, look at those (e.g. to learn about
handling of edge cases with redundant or overlapping cases, perhaps
due only to differing collations). I've come up with something very
flexible/forgiving, I think. Also, new tests were added for the new
inheritance support.

The documentation has been updated to reflect all of this.

There is still no way to specify a named constraint (which is perhaps
useful for exclusion constraints - although perhaps it's good enough
to have the IGNORE variant only work with exclusion constraints in the
common case where there is no inference specification), or PRIMARY KEY
syntax. Still have not made text explain output display arbiter unique
indexes (although multiple arbiter unique indexes are now visible from
the non-text explain output, since as I mentioned multiple specific
unique indexes may now be inferred). This needs some more tweaking. It
was helpful with the new tests for inference (with complex variations
is collations and opclasses, and multiple unique indexes inferred in a
number of cases).

I've been meaning to revisit Dean Rasheed's recent remarks on RLS. But
that hasn't happened yet.

Thanks
--
Peter Geoghegan

Attachments:

0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch.gzapplication/x-gzip; name=0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch.gzDownload
���$U0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch�<kS����������1�/�
!���$�C�5f��R)�H�Zd���>�����lI&�{��,M������[�]A�����@�����=�5S;���=}n���>:���|p��k���n�_���Z�-�9�p�qw������4�������Yv�pW'�s�#���]��<���A;<���zh�G�v�6��Fp�nN�g�Ak�>�m�^�^W�����&\_��������)\�����+�����q��0�d�6}���G�?\���;�0X��������,Y����F�83�te�]�]�m���-g��}������2X@�'����{�Z��������!���N�w�b��x�r�	���KH�&�Z'��F,v����a��%�����N�	�������#�9�(��P��4��Z��������}I�{���7���Z!2b����}$.�l�����I��F��u�v�#���6��y�w�����&)�J�"��T����|J"������c���B�����U�ihFU�!Wq85[#OH��Q�!�5�k��}��z��qd�P���}k���tPD�ox��]�Ra�X����"�����"Z�}����&Z���H�p(4"dCZ�D	T�O	�"$�:ux\Z8b�3Gy����Y�V��^�G#�-�t;\9���Us�
�Jc���k4L��j�32�Yl/~�����'�u� ��>���-����s�t��`�p�(F�'p3��I��
;��l���Z����2��`���E���+`�F���o��[�i7�&�K��,v�D�g�.A�X�{��]���;�����c,�'�����-���I3/�yx�E����	�_
�`�"����������N�?y��Hg���D��uf�w7[�0�j[�M]���l-\���'Z�LP���rM�0d�p�����0�A)%k/t8��Z*�|	��=+���3�.p1~��lv]�@G<����a���60��v-����%��� ��F�W����)N�sy�����J	/Fnc��dky�\X�c`t��H�?d�����Z�����q	���T�x��PH99�vq�ts���_`i���ai��
�9��/1��`V�o/�������c*\����;	1EH��U�������C������a[u��e���A4}������s�;JI
Lf����5p�Y`�W���k��8*p�m
���b�0}8�~�6D���}r`;�Se��D����O��������F��H	�#��C��L���NOa��P9hH
�MFz\����w�-f{�@��`���,��l�����r��N|�T�;Z�j���B�5A��I�~��A���B�%�����E���l��\����j����b�J��]'��}E��b���&,&3��������b��,#�����5����C��:j�&^�����gEHqW�u\{l���E��a�M�!�D�[������z�]
��V�`��&l���K,b�v�Y��%��1`���m��6�������
���@i��(_����+�����~E��"Xw>������`������n1�c�n6�-�wm���)Y4�����p\������Y����n�Knbu\@t�c�J��z��
��%�����T�����"�|��Z''U-GG��� ��Ja���D�/ju?M�����E%�)@��{�R����-VY
����\f���-��PHne��
����f���G�[4�m,���f:��0��k�u�0�����~d�	+��hr�&�9��x.��*]��9��<��T���C���`�o�;���"������Y�ih����l���v**���A��?B�������z��_�
����{O������[�W�	u�����V��FREQ�����E��*��:n:����F�+�����o�����s��[��c*B������u����zMZv&�x����1U��6_�E0��i����z<=�r��g-<7\���Q-��q��;6�����}}G����9�����������G�����H3G�a�y�l���D�Yr�v�(���3wA��
<��Y��B���{���Z<x��n��{�bx�?�R��K����hB�WR�N�������L����g�n����B��]����VY����
��B����Z���0���-�^K�v���"�����m���tyW�U8�=����/��w��*�/���CM�Up����1��H����9$���<����0��7����U@����y7S����g������@6%��+^+0�B�(��������������gf1�C-!����BWL���Z����^�<�j�V�������W����_-�s��OO&��#�>V�e��V\�Iq��l�-�T����%��7��g�U��`���l��{���~�B6�:���r����/A�^eFQ�E4��?%J�u���}����F�@��G����v/��l����v/Fh��T�5�&�=����]H�6)l������Dr�"�XE�U�6�9�z��0�
D��������c?m���yh�eM����������������F��*
����A����{�X��-[�NG�M�k	�>�^	����e�M;Y��!&�Nh�y�H�J�����3����x��f[)G�����5�z��Gf��bO�w�hI.A���q�G�QPy���\����r��������������S����Q�e0��s�a���Y�$1���"|�au:"���[���1.
�4���R$���MMF�}����,sA���:��	Y�b-u�8�r#����y�zM��J>jhv4�M����������d<����n�nn�'���~��`����U�����P�$�v��NfN����}�uSr	����7p{3>�I?%(��x!/��������P�vj�P
�(�Aj�����f����&����>$�U�z��
����@��I���5E�V�?�h�?���2�����=�p>���H�([�)n�q?��(����[�b&!�Hkr��u�1D����������ufw7������z���|�2�_�mc06�������o�~�g{����MB����M`�VyNM#�U������H3X�+
�g�4��PV+����f����Xw�[����kE^�����{Lg�a�?C�r��h`n{���PD+���Z&�O19B�0h�����d�����70���s�~��#���1\]\��W(����x���.���N�����N'�����/8{?>�7��8ym��F��_�7��5�������d���
�U����z�s������A�9�����1��)�1���	{=�{�*k��$zyj��yzY���������^�R���mo%�XB`D���A�r��K:	��-�
�6"�
���v-���]�V@I�e����El���\��N��j2m�-?RY�*X�e�5j
j_T<���6+ �e{|.\���N��G��N��n8�)]c�IJ��['K~%�L�[����"�����HL���1�w
�jK��-/m��������l`��s�mNX������u�`-5��X,�����y/4�?%^���!$[J�\\h�Q�+�r,������x0>[q���A��
(3��M|8�c������8H�G	`�)��������M��B|���J�1�P�sB(5n-��w�E�@zD)����{������n9!kd'b�X��8_Y"�,2-z:���q#<+��iE�:y(sl�U:�A��9g��?�I�'��<H�����m���K_+gh&c=���q�=���� ��m6y~[�,��uI���@����{��OOW�5�q����+�
�>�,�����X�}7���<�����,�$��}I!l)�� :���\��7r�G��c��.�>���Ng#�!SH�8e�'�d�)�3J�!�l��SA
�p��������#�����i���-��{R�H�-�z�K�Dz����y��Y���%����P��������P%lt-���qK^+r������;H��{��$��c��t���r��
���"� &r	���W8������>���he�]f��d��^�W&�m9��n$j�C8~j)�t�L���6������t����������R_����%�#�cJ�~� 7���cd���DBA^�l�"Ms$�d
�t��b�<���e��\���Fr�iP�+�M�i����d�&w��	�5bk��8$�P	���t����K��P�J���b��he��}w����p8�f�����f�n�b�Q��v"�ub�
/BG�l2�z�\O�:��<=�g��aIxLh����P:���*�2��`���t�J�9���(H���L�H�N2��v|)�F��A�-"�[l�@�+��46����t�8U+np%��t��yUqR�rWu�,��l2jEk9�B_m��.��%�*QaP�>�C�{!��Qlv1�I�Ft���x+�V&�����c��Z�J,�"��;��C�ve���������(��"�FQ��\7�h���ay.����S
����L�.�b�M�����������3�~�?`:�ps=9����=��7wW�����������o��z
�_/�h������X�=��[�'������l|K���*�����X��TMjKk
A��"7�sG"�Q�
;�Y�����3��V�F"<��a�����K��1�����K.L��Z�Gh�u�(���[��`�Pg���Ww��g,�!�b���(S���d:�Q����v�y��`)�����\
h/]Sv����#�&�+����5����m����l�%�\�wY{�#��8�.��Z���1��H&O��J����3�����lPR��Gq&��T}����_��7��:W3e_��.%��j�����$���s�����!y��bZ�i���H����
j���8��!��*wC�t�@���=������D�7J�dB���$����������+&�G�z����Px���q�`M�vA����!��g?�:-�E�|���+���� :���*�yc�Y��.�_=��vI���U�1;��'I,���L��������K
0,�W��o��������K��?�Q�H�9n��������i��qcBQ��������*q�yu�����-��>A7w�,eWVO��F���b~`v�rP��:�^n�GP����z�W��m���#�ZL��w��J��f�{i�=KK�
��&�����9M�tN*�oZ����Y����z�"���6������#���t��!;����?"�!��_�T0��x1}�����nr{�UL;�%������Q2}��i���9������tr����z"��S�}��.X�/����}��~�����g4���v������n������2��s�<.�%����U}*�2%�7���-b��B�4�>
�������[�����b����c?�5��C���L�V���C9�$�����K��0�j�A2wZ�������Fu��������$���,z��@�HB,vW��A��-�e���GOJJA�%%��������=k,�H��^����6�����N�0M��8�J�O|��Ym���a���k�2��
,�*��������1gS
I)a�E����JY�=�p���LQ#9��w�E���+.��DI����eR1����e�\Y�28�\�U�Y@K0�s+��)���g�KI�_!���X1���/rK����V��)�j�
�	J��1�:1k��t��U����bK��XW~M�nC���,����Y#
if�Z�tN�,��fl�s71\v+R1U���P0K�����Aj���
��J����U8#8��G��l���`x�gS� Cz�Ds�5m�s�6���C	�,nS���
H�����FC�qf?��X��S�,��7	*�D��������\"��G��w��8�=��^���X��$�B�[A�S�FfNL���K$�A,� ����aGa��
i�����L��77��ov�r�����9�i|2R�:xOq(E�T����Px�m�����]^\6X�x�&�s��U�N#��j��w��*��W��q����G�3�e���|�ALnS����p��,�x��;x���y7�%+4Y�8es�`��s�\��4�Dr��������.�iw���?H.-�D�A�+��2�K!pb�	GD�\�jC.����p8���u'���k��1������3^�����������d�e���L�J'�t�o��#��P3~Y�6Y�������u���8�*��g�W�0�p'���NAK�>��w&��/�psu��������M��������E������F��w��k��r+j��U��!s����H���.=��&:O6TCKF2�w�rb�g_�8g���G�H��^z=�[M
����W�{�hI���4�
[^�(����t'B��6��|�q<Qdk!^���0��g�a��[�=�������2*^]�
���x������

~�:��x�F|���w]B��~���}��P����*��YF����)�d��L��z/8�+Y���P�������5�B��jO[���{�k�c�����>g�a��EP�IC��H�dh��@Oo��^�oj��c��2Z4b�
�f��j��C���Z�F8G����96�S`�
y��M�����B�Z�Mc]�{�O�x�>S�'��8�vA��(�����5���������%��?��[�����1Jn�2��,��*��Gf�k���Y
�����.e������������C$�����*J��H����O�u���������:����=������
������#m�����'��2>r��v
�{t�x��I��
��\ZH\{,y��^��!�kYXs��#fA��f�!���W��0�����h�3�=�MO)En��5�Zd�,i�-L917�W^��m*�}B��5���%4q|�;��]bIT�A=;���R-�d����G����|$3vE�&�z8K)G�F�}��e�dK�[��wu9��\>$��:��}XF6,%v��;���^4A����Q?`O�P��J��t��Pa[ok��)�	�g�������NF��������Q}-�6+�"9��O�s/�A�V��������k>����dr��Gd��8�1��fx���@���K�q�	r����bL!���'#p�Y�<��{* 
�����JU��&ac�������W�n}�S���sjD��wv����i]�f.�[�Q����������[R�Z���v��O�q�;?�|p����pK�K�ki�<y��~��~��/?~��za5�����]�L9��Z��,����6��d�)Y���`TZUg�8|�a��@��nm��GJl������b�-�G&�1���|�W�=�X�:�\a�L	2���b6�0�:�<�6�J � d>w`�����]�e�qF���c�#�5�*Q0hI�r��

"�>*\XR�1l�"�{�����&��N��)�h�
�����Q�y<�4��)��%#���~�P��p�p>T�?���CYh���|$3���siV�q�>�W�$"���{��=�\j�����{[eN[fJ����_��f	�PQ�,�|�|��\��U����)L9G�;�-T���Z��7�a�������uHKN�?G�T�}�'foVhY&��X���\�^��[���Xqr� ��
7}���� H�
�;W�1�.m�����rY[���fu��&AfMwMH8;h��z��b���lV��V�5�3qv����!$���|%C!�G���qj�[��rt�AD��'����*�����cbDV��yz��%�kK]�{�F�����{/K��%����.�l	)��]4��AV����bo �����^�E��a�	Ag�
�q���U�!.�q�Eio�����V-@c�]��pr���x�
�|���6'����9��c�$�%����,t����V�yH7Y�ie%/����56y�>E^c|��RY�����	%3+W��� o;�0B����x������F:�x�.�	�����>z=���G<1�]Q�����a<t
u��E>�����,K���!n^�yP�]b��4
���xF���?;TSp�/YkDwt��
yX�4�c�2�&��&2e��<S;%s]6�������a2�s������\����i1$�P
l�xBG��O���g��,��+!mVv��X���#��Iu9�e�=�M�Y�k��%���>"�&>(/������3/���d��G��X��=��{<��G������o���������O��M�����y�Z
��NEQ��8�j�A��&�%������u�.�(R��_������"��8	�����)[FH�"#^?F��xq24�������&����P�����������q�.�Vi�&wA�������e�����3�����QLit�������f=���e($0����������1E��z�j`h������`���J�r
�
���kP�4��Y����/����������/���O���8g#������c������7��c�g���%�K��k~�������uDG#;����jN����)]E���L�3��{�9u��� ja���[d���$/{����?���,g�8v�%�kI'����-��WgE�������2����������k5�^���z�0d0���z}�l�����k�O�ze	���n����h�zT��<�&�������v���!��v��G�N^��y�����=���x����{�mU�1�R� ���������=�u��XQ���#�pQxEXTO�!��[������eH������^
�4�8��C�e	��+���D�p�"
����- ��f;l�Wn���=���B�W-fsp�A�;�
���7P?��c7 �kv���ltPHb��M{@k&���58yw=L�8k��x7���k�I
Q��������Y8M�L��a�w�	�,�s��J��sF�N����Y�%�|��������g��@7���|�T�4����?�%7����|��i����:`?��b�3���k;�����P�{p���4i�w1G�6���m�G&�
n{<�J��mB�  �r��7H��$��n�C2g��a�V�V	��/��|���8��>���|w�����T���j����,_��������E�maIXe�{2M )~�����I���F��gT�����sU��?�C<���I��5�_��I^���R�9n��FG{��BW�wk���H7�/R��Z���[�/������}��[����F����F�L�b�q:t���v�!��>��M�����^�6*���9��_i�`7|�Q�n�S8����d�f����,��c�#�F!'�OXZG�P5�� �!��}��=�)��xG�v���I�~�?t���>�9�����@�=5~�������������q�;���F����
1����-�J����j�}-���n�'vV����%�&�/������d�E���e����Y���NU[��_�������$������C�����p��![������9�~C�4��
��~�O�FC�4�-�J��H���{�b��0F4�z�?4��k�D��NoT�S?b�i������%AB��}7��4v�4#���n�)��?�@&�D�����>+c��F7	�����A����>����\v�{W�����y�P�w6�#os�������
t
<��eF*��(=`����:��_w�T1s��($(��Q��_�#���G�!r8�m8��MD�3������E�aT/���VMT���c��[��&J5
�������_�H���D��w��i�2	@R&�'��-����D���m��q�Pw���0���
%���5��[���������D��`.�B��9J<������H%�;��eQ�#
�m���
H�x�fp=�������N���6��~C����#rI!+tA	�hE,����gA�if�#����Q}����������B���f%FT�CX����W��a�%3�b>��Y��.��k�$����'T +7���4H���$���<����K{6h�4G��-�f$�x:�{mb�Q�CF[��2�����bw�]��h; ��9$%6�E�f��j�L��^�<8�����)���0����~��i������	�����V�gO��b�U���� ����M�����d�[�]�o�>'&�/rM�	���l�j��W�������@��k��d������
��*�@j���G����Y���U��Y�wr�9���/�]:�~K�'��M���
�v�WLk�/��m;%{����u�+[OZt'�f�UwK��[A�s��������nH��S��*�8�#	cd��k0w�jq�X�
��0���x��u��>��
�a���+hg��'	���

���Z���k�]u+�����[x�����!R�V�]E��:�	IS�	���i?��
=����f�.��O�C�h:��6E���h8�X��Bt��a&�q,(���S�W��j�/�]cr��z�T�xE�8?����:�/���mGH�	:v���-���,����f��.��G`���k��Ww� ����0"�A�����l�1B3m)p�B�T*��D���c+9�D`=�����x���bx�7C�"C@�W����>������*��!�����������Im��`��y��N}3��	�6E)�L��
3�+ ��T/���j�9�������w�nm��� �+8�3e�C��\E��������;���T�p�O����I���`@~�9��3���0{P~L�Z��.����(�<�D�m����#���D�X1R�>A��Uh�>���.v6��
676x�XJ�P��B��'.��A���s��@S 7�c���g����_G����X���F��M8d��[����F��.�>���.�����.)����<�E�?�+����L\M�����}�&K�%�a,���w���b$�����K$��3IZ�t6gJ��I���[Ti�9=�k����c��y1
?������t!S�������Q]X��t�j�gx�1����+�*�io�x��A=��m�	N�h_��'�WU�P�
bY]/�j8G;��}����e"KL|N���L���~>��\�C�J5Z�)�$s��(sp�W����q�M�kB>����B�"8�R��r��<B��
n�����x��)z������dJ���on�MY�'��k���1;rWV�qd�H#�%�)��M��z�!���1�k 7E��]B
l�C�T#��Q�<"����!fY�N�hwx�x��7IJ���������|��A��g�2���O���7(�c���M�"���.B_�0dzZ�m�|������#��M>��vd�R��Csj��S�Q���S���-����*&We�v>m�v��<j���g�����������l��K���r�
�����la���������'�����+J��:b�/��������������_��
-^i(8��������\����f
Q�\��{d	���+�4��*U����-��	�V�����2�6��["�����)C�1�2`����x�����Z��|=j���\��I*�d<u���w%-<�����:AN$�-N�u1������^��]�z��
`�5�����&�}d��k!�#~�V�����S�	��P����n���,�����h�����lJ�0�8�,0���l��8�������h�(G�����T�[
X���b���_r5��g�Rb�������m��wk�����mF���-��&��k��!��>1����j�n�y������[Lg
+�������I���Z�h1����6
G�S�W�%	��cB���}b(�H���7���i��������%�j��]"yx9�E�d`M�C�y)y*��h�Q����)|���[�9l�)���z��fk%��1�0��g��|�>�z
��4����,c�S[�A���nXG[N�Rt@<�$���qk��K��_r�"5u��04�6���L��vo�Y�%1�q[�s9g�-;}R/�
�������&�����e:d_ln�)�`M1�K���
��
%l�����m0���Pln��#�>7�����]V���c�J����z7�(�y��Xgi*,1��a�)Yvq>��Y���I�<I���������@c-�������6��������Ov�E���D�J��I�oV
8V2�i,���.=�ks1��M9@4�!qf���&#�rS� �D��{�o4�A� y��D&Me(5>���h3������s<(,��Q�����4�i�e�q��uN3$����p[�E~��� a0,�
����s����lL
��N�%�a�����R�`��%�`��o����TJCC
������:�����6�2���w4Qs�P�n�)!��
p��a�}Io��x��['��`�W�������g�jk�k����J���V�P���(�:**W�'�@���t�W�<�b��nH, �f�2������	����]��8���93,��'�rd�X��dT�D�B�4#��;����FS��~��FF��+��h�����i��Z6#���5u�=��V�C����<0��b3�V��
��D���Q�E)�^pxi���tZ���hN�K�)G�
�i��b
j�����z�}}Mj�yZ�v�,�}����)�A��?zhl��B�-���r�<��P�L2c5iW`k���#�����|����a��V�O�R�~PpM�m'(<9�U���r-x�����e�����&�=����0���3������/�1�p�
�B��9��0����[�&`DO��9�e
����������������"���:�N�G�6����0g�4��/[���5V>WK�F������^Xo@�F�r�����=��5����f��� (�� w��)�E���C��b��o�a���
�q*s�\d��;S�#�e/�B,4{�`1�tA*���s>��0)
�60�6����
��Ji���i�,RW�������'���������[�o�=����&

�?�d)2,��U��K�G���;����8���5�?g��S6Ik�g���T�I��9[��'�O,3���j�?a�	��f�*��x'������]2!�euN,��E--��u���l��l��b��L��Nk��~1J	�������N�+�d^����D�S09S85yH<�D�P��:�F��Ha{��v�����GX�w��|��{g�m�5$���c�s�n-�z}e������(�����y�#A2[���K�%iY%{������{�[4i��C���N`a*9tIa��@��|Jl�����Y�0��iVF�	R�%������k�v	��y�L���$C1��$�����Z����e.���e%���1�$�{�<��g����1]])�+����|72#��ft�=v�G|u�pd��L>B_,&��.�^R6�k��Mv�����}��I�����&��i��{�~���e
���xl��)��N�m��i������kP�V���3bK�u�T��d9K�V���n2����)�{����#��'����1YCkn���;yX_�^G��HLl�U�Q/��W�E������F���vkw��y���f�#�(
-f����n����b��[
-R��])A�U���S�<�W%Y��o���F��O��
����b��Q���t{�w��u�K�0���
A�fYE�s�@����5K������
��Y�����Y/��#hd
�������$��I4����!4��X9����d��G�e�oB��rk�55��a9h5���`��!xZ�K>����DI��K������j�*��t�
��Q�x����6}2��|����>�������>j�y,���%-�|�z�p@�<f���e�
U���G��V�4��J�e�u|\-cp�%��f��b��"��������-(�����Z��DD�6PC��Q�P����/��1�H�N�q^=��2�`�]�I<�:�p��8��L��	�������Qnv$Dg���g��y|;������v6����}��\����b��;D���1��~�|�x���Rp#S���s.����o%!S
��r
\?V���	S���y�PRv������h���E���K��5p���l��> B�?N��J��[�O�����VN����6���Gm��57�����"/M8�6�l������p?{X��)�,��Ed*�^���uF(�Sn"�P�H"Z4Vb-�%W�%�o�a����h�B����pww�o�����+���~�k��k������hw��� w���dI�K�+���f�P����r�d�����z���f��.c�,e��
�9�=j�������h��k"2R+����'�.�'�|�*K���{������C��5��%����A�y�4�[U�_$�=
���
GZ����+y��"�E���S�0H{�c)�����#��]���t�h��=��7���A��|jf�s��LQ}A��C���.�]�S�~�J�e�������[���"�U�b�eV�8�~�qB;�:�Z�.��r��������%p�	��
�V�o�r���U��2��k���#�m)�qJ	�i�����U����Q{t��q�YNq��|^����B�+r
����d����x�)����4�<��dB�|.��,���8���*C�\q�0�����?��yL�B��u���6�E���7lx����N�����(� �ZnQ����/�a�tY���w�<�����������_8{��[�~��9$K!�#BK&���C��WL%��E��fE��c'sM�l�c�����+'�3��h��{����l���ZZNA���0���P�}^�(�P��,:�f��c�U*�)�o���m�����'A^�<���1�J�����.>�#�D��m������v�]�pC��{3�J��\Cq�SbC�M������9���X0\#�Tsf����qM^LS=x=��%8��Cv��	9�5R
U%'��g�S��N�4��Z>���T0�O,�j����K�P'�`!���y���������8e���4��F���cV�5����9]��A��x�\�p�>�z�����<�����@����dH��JH��r(�U��}1^'����^�f�-��II��I�=��]�@�s��*�HT��W�b�W:Jg������q�-.�>V���)�xd�����E�����U��n���4��x���`$��;p�D���
�gb '�0�3��W,�����e��0��t�mh$�<��7�pS�#�+�Tv��d�T����J���F^$�W6l������N?��������/�{I�6�	��{;BF��3����{�}���<�{<|��<e����&z���L��y�ef��Jz���s\G�7s���}�10���`�|ULU/�_#�����l�g/������c;�*#"��SE�*��O+U��F��T���Z7����!yA2E�V��-��V�WL��f�n����,3M�=@z��d�5T�W�����qU �B�J�K�F���	��M�:���p��0��{�{��A��8l�Gk���Z��{E)���!���OxD�{�1z�N���������7����O�r�%���U��/9)R)9+8i�i�����,��e��
��h�'��7�W?���W��d��c�l{����?��?$�d�����F��!���{�����Jw`AY�;9kd���$��^��*<�����)�H�������CwaS���8�6�^�-��Id�����t>L��j���O���~����w�W�+����zqB2���ypu��R>��X%����'�k�`�i�tN8;���+��?=2(<z���g��������Db�G��f��s��souF��:g!�Q�������3(��@N��=���y������Q�!Yp�}:�|�hu�@z�]A������EM�.���Z�-R�)h"�}������Ek�6��l;4�	��Br��$�RT���sI*����������C��PR�����'�il�h4�_��1�F\���"��sJ�S^�=��b*���oA��Ip��-�5LD���E�/��������&��
�)���a2�d������9f��v����edK����i]uNl���#�/+��h1oa)���B���h��MyRGv��`'�0���8�*��H����8���2�`�uUwIQ,i������D���4*�2�D��Z��TZ�e���<���: ������"�W�S�������������M%
�����}��(V��g�����F�D{$i;�<V�l�������y���R��Q�j`6j�+/�4�\4%\��,��F'Gr�D��!4���C�BKq�F��!�i���6s��=�������v���V
7������M��B�	x�u9 vI:��f����MO�7�9&aV�M_�c��0L��aD�
��e�o���j��!{$L��XA>�����f�����\ �v	��-���4�|�La����r�U�7z��j���w����&�����w��&�����&��z�U�d��Q@/�w��9I-9)'�Vd�V�1���z����G��M�~^��� >w���H?9���"i�� ��E�j���ef~8��.�{J ����b<�0NyK��e����u2c��Rz9�fJ/*Nr��]�ID�9��eY���@�)�q;vq��A�������dP��
���[���4�ao�9����q�X��x:�#��	:�x:&�/��/��a��Zg�A�,�?2�0���^|�q|w�1m���B�[S���	",��<��1��>b�q��!�AG�B���U���@��q�S��8�+���F����7�CY5�Q':~p�����0%��f�J��p�Mg�O�s�6&Q��e���iv]�6%���c��^�n��>��W�l��	�v��^��*ZV�X���Rs�~+�@�i� �zJ�����$t��bY��R���bYyG�j���_'H�_]_v:��7���i��v�����w����v�[w�3���<���o�
\�b��@'!X;��j)������h�\}~�5�����!"	- �H>88��	�OJ	Q��!��"0s\���x���'v2�:��D7	q��m����(���S�a���8���S�)H�|b��
b�����_1[�fX����p7���Jg��e�
,*��{�t��Q8��=�:sv�������~+�p=�@�����6��qO�0�G���-O�e����i8_L��\4�A�����(N�������Og���1��rI��c��a��1v�������d99��l\O�
g��(9`�:2+i�{RS_�?cUQ���?��������U�g�&����������O���2�	�[��L��/�w�/����K�� r�%����<�/���Dk�q�@��3�T@x�Q�h��a���l5v���e���,b�F�`�DP/�������������C��)�:eiI��,,{=�o�q�H'�d;�1�J� M$�����h }����xm��ZZ�/A�+A������r��v�������[nGa�>l��Fs�d%[)X��r����0���60t�*o���~wN!�������Bt�w5G3�M#Z�y����JS2-�$A�
WZJ>�2�`��E�p��Jv������f8��#)�����U�\B�r�\�HqG��R�����s��l+�[��G��h���������������K%X_u�
+���^��%�
���
u�el�H{1\L& yL�A@-�\�������g�U)I$�CQ��Rf'�*�'0nq����4D��������p�T�-��Kj���q���t
��1;V�k��N�������(s�\V�9+(��}5�����u�2��xp������v�����/+����IMn���1���>��^!7���Z�� B��MV7y�����+�7�X.���<���Ba�VOQ��s�c��_#m�i�[�+V�V9j�������7������(��PJ-�.��,S@���v�n�������\���%d�b��#����@u�5aT��`��j��n��g����`>��� u+o���8���:��q:g����`�*��c�P5��������s	X�;R�$�(�s
jaB%�f��*�"���-Z�)f�d�
]��������bsU����[��0d��M#d7���z*�h�xQ���������g�������7�����������v�l����@�i~�p
_�l��Xl����:k����F��D�[��0����p�^o���F�5�����M����ZA��0����
����R� Q�T��9C���D�g*�b�,�������E!����Jo�h�|@�:P�%M)v��;\� �'���
�����(V���u��K�
�n�jt���h��?>#�T�|c�I,�~z�����y���(lD�z�p�9h5�l�l��f�� ���[k�D��<f+7��x#��%�q��{�W��1A�$�`����B�}�i2�����hz����Y �(�p�
4Pbw`���
���>��,�r�R�	1$i�Y\v4[
f�!�k1��3��@�4
?`(%��Q��j�(�p���d6�et2��H%�xa�D"��xIy��%��Uj��`�����M���=�ff�^�_�'����'�A����KL�q�T��*��b���g��l���<G�5zd�h��	\���:`/�`�oD�C+I��j�_)�g�
���a8?�f4�q�g|+v�����m�6u7�'�'��lGqhoXL�4�X�t�N��	n!��9:���%�6��D��H��.fx';�@��b��
'���&`��V���T����6�b<����AE\&�)���-i4u�9�7%a���Fd�!S�<��$����	JhB ��+�|@<H������9gq�r�E2��xL8����m�\�D��O4�L�v+L?2�O�"�=�tEkH��c����S��������v�P�H��93��Y���~k� ��0op	|���!{���9V�����Y�����f����p�h����7M���3xA�1~C�;�t6�� e��A7)�6�dGR=f'������$���E% ��]{���C8C���Q��f�
�xk�q�����K�|d�n������I�a?5r���-��g�n�	%0�%N��>��]�Y���-�s~�M!�i#�7�ES�������4q!��9[��_<U���x�j�;��S<DPI����4>\���!�I|���b �mS`����N�6 �FD��[0;���t�i�"�S+`�q�������V(,�($E�`DD��d�<��Z-$7b� �A2�c.�[�#�9�A�U0y�*�$��3�
�n����ZE�%��ST0���J���sZ�HP�v��L���lmKq���B[f����O�/�_�C�ue��
yi*�T�.�<sN�bI	V�J:9���
�����"���5��B��)
N�=�L#����::���1�����?x��r.l��CX����t���j`*-F�����?U\|<U�U�A�]�sqr)1an�N:\�8��]�f�S#Y���:���6�&���!�UY.tR�S~�[:c�<�0��`�/r����^��MM���l���
�����%{O�c^L0T<FxYs��4�� *'�����IZ������ ��t,�#5�o�0�P!:�O������v6-����q������|o2
�K9��
��p*�����H�nRoykH�a�V�&�|��Gn�5������L��n;%�
�Y<��F�w���*��qb��O��t����C��W���m��k��y��7�ux�0�E�<�p3,������HD%�����Y{��b�R��E��	VR���+�f�'�C0�\��w�kH�8������t'�tv�	�{�f��w4 &Q�G\�8���e�L
B���	���q:y�Y��H�rwJ"�j���8���!������7����V�������l�B��|�,�	��6!$��	n�������#�s���d$��Ms���n��;t��|*��;��k\7e{a��r
'����@�ZpIYJ��68����[���v�22O�!�`����I��C�?|B���c��b�J..��������:jXP����'60��F(�R��D�A9�L	����E�������3�!�%�t7����7��D�
l/��BK��O
x��M�lnH����I�"���"�P�0�Hw:P�;���rU}��$b,</D��612P���p1�?�nu�|���`� 0>�?ar���<D,����2e\	Y+��m[�=���,.*���!��E�G�9�K�#�{���N�}�����4��1'P���QV���7E&��jG����z��:�.�#��D���b���5�O4��u�V����~�����M��-v���)��������Dr!mi�v0�������/vX����n�4��}������(?�Ds���sN�(��<�.*gA�������������^��7T�xt�^5���S��L���1h�	7�}`�$�]�qpXk4����4��jq�m����x1S8,.���SJJ9��	&�gp�|�vWt�sC�`NAE �����e���#`�`�ft�������Vh�-��O�h���3���g��*c�T����n�X��� CK��6���l�F�d���j�>@%�����hlA�J;<�=!��!��VA����k�����r�����W
8�d��k[�� +F�=s(Z0y/Jyg[����SF�i$�f�w��IvZ(�m;��[�[&��E�	ll�0����b��C���j'wC�
���R�
�k�/N.N;���e��s�=>���c�k{}�={w�q�Jy����90W���"����O��Y5vj�O�2	���K�����`��4��R�U�x����(�f��k
2�I����'�K$O������l%Mz�I��f�
���~�k�����������B?kfF������|����M�Oa�9���9n�8���N�3�9��/�_�cc�]FN:�l�s=�%��m����f$�:���A�_��fZ�I�d��(q��rEI�l?�{��H�L�Eh0�\5���%��N#}�d��c�����.����0����a3�����v��e���\�c�9$�{�:���;5�n
�l���o7^�^a�/�s/Q��T��,�w^�Qq�)0���>�1�q�o	���PnJEt^;'i�>X���sfF~�qd��y.�;YW�K�Q����O�S\b��G��uX���G��G�9���F�1��Cq}���������|�����������g���l&�
�;���5�8�c\T&��
@�g�i]���mQlUp��Iv��3]]/FfRw�hBV�Q��D=D�vJ��'6�j�����>��P�hF�7V���be�INTi�#��D����e-����[;��������	rE�nA��'���.`���/�&\�ct7��P�^���5�`'x���&"T��I4��!Q���*��?]�uf3i�4$��KZq��@��y������b'���2�/��!5�(T���-���k���\�}$�	��*��s�)���8D;�H:3���,%��|�R����{��9��8������;�����\^�\�I��u�6����r������)Q:C�4�kp��p��eC��������V���=��]}��x�9�QP�H1����9����b��!�<~���)^"�`?��/���/<+X���j��?A��9���xL�TDY����n�y��������i��C�:N:o�7'���X�
�O])<B��vv8d#iYX���)(68nf�7�����k5E�s��
�����5�w�e_06+�Sz�_�-%����������Y�t�`g:�!�Q����fFC,���^�������'�f~�y9G{=�����>&�,N
hw��&9���g��
^��:�KC�v����?`�����oF�O��B����bFv����(�������_��Q!�6C��_�F����a�(�L6�4q�h#�
��7��"�����e��LrOJ�nn@`M|*�w"PT&KZ<��f��Z�����3�F��S�s��4�����������j�/:&]�5eO�i�x[��Z��p.�������n��&��������^'�&�Cl�7�m��%=v�lS�����pj��)yV<@�.����Oq���Ru���gp2����Q�E� ��K��L�������`�=��H�j
0��nJ+��
�+vb@�>s*T�[��)�u����97G�lB��[U��������	��:��M3J�~p��1����A��]$��������`�>�p`uf����I��x)���5[�L���Z��E���F#��le���I
���j"�;��G8��	��	�4���LTe�4�at7��Ge�� �2G�xt�u������!�EA�2�K�xl�L8���f
��7Sni�i�,����V���^[������E����?^��~}S�<���o�-����AF%�������G� ����
�~����\���Q����:������j3�a�&����;��J�������O���������a��#5	�&��.X�@Y�&��F�aW����mOQ�U�Wc��U�0Db�a�K�����dHp\r��\��(n'R�--v�����q�&mID���4����I���"DQ#�1k
pAc�K"�s���O��QD�CMW��A&��I�<����^-d(�I]M�����[0lA1��l��O���a�p��*���J��FWW���S�X�B0h�*.��"����T]���g���t_n�E\Z<�-_��(
w�V�� E]�/�	nh�q���v>�0����W�|��S�{g�����0��g����-:�v.VT�W9��,�������*s
j��v0o�h��E��[:�u9���Rj!QdC8���[���]��7��_��u���f
G!��k1
��A�C4�a�#��N�;��D�&H�����
�+9�X��c��a�r�3�(��P� ����]��u~���;�b�\'kz�
)���v�iR������0!r\��	;`�yN��sIS�%�g��		��49D�7�x��3����2��!���������X�@o�|0Q��������8Z2*m��m�oS�����/����i�y�
'�|��
�%!�a<EH���t���f�p�gkk�|+[�9
�AI4�?���]�]��O�����}6�8��G�����W���]��`����b���������F����{���X��WW /���3L,;Q���&�@3W����TA��={P�����:d?�lE��D�X�!:5e���*|��M�hf�P���"ZH�g]���Y�O��;�(s��������_7���"�v0#	*�XZD���E�~.�"��TE��Lf�
�z
<H�����V=������N��k�e��Zq�>i�>��g[���(^�l���s<����C�P.����xG����H�g1F%Q����Dr��������G�$���l�ed7����KA����`�
1)2�),�L��Y����.�u���2%#�I�T���NeM�����6�xb�{�Z�5fC(������`�
-]�MS7���M4��:;#���(����q<|�Q���m�I!P��F�TE�+*�=�ya������(Z�N3pO���x����R���s���3���.�����&����X�vy����c	����q�]�
����_R��0�/��u�3���	q�D���*L�{y���IiAP�vQ�����(}P��X����g���R�;���\MM�f�(hP���H.�6h��x�/x������7�Gh�n�Z���������6c������F��	�!��YC�
E������D�'+���������&��'���j��v��Ne�#�!��+��6�~M�2q���1�����}!�p��R�!Z�6�p�Q!���T �Mtjp���\�"R�e���|!� ���cr|��Q�mzG&�)��,�P�)�'l2G�R4t]V�M����3���i��0��xl��mg����Av���p�Uy+KP�,0;���V��x�� ��c_)!����b�l���-�k�*X���Sk���r�����7����P*4����Nr�����4�[<�;�QP"��<��q��f�
J�2�=2l���e����T��a��&VXDf�����hJ"������"`-
�E 7WD-���5���g��p�d���'D�-9��F)�X�/��C���}~��-8�cV��_�A���[�A'��_s3%�p��?��������H�M��~���ZS��!��Qcw�������LBs'�����gXC���&,���^����9�=���tYi����2�d[6��������CP5���>�cr4-�����j�6�[&�~�9mT�m���LT�'�Q��\x���cR��#�9nJ����>gY�;����3l�����Y2��9�A��|������+������>	`�B�{�iN@�A/���H�&|)�,��S��4��:�6+o.���0���o���;8+;��n��
�hA�M7B��s�X��iG��eus�r�R
��$������G0f�r�B��N�������V�n�.>
���XH���{�6�=� �������a)g�&p�c�T5�m����E�}~�F\|�m��5�����v~����v�R�Z��U%�b7P���aL��P1�������0��}��6�@���,�(��K�������N=���+��oS�#
�+��
=���B�h�\-���	��md5V0U��gJ�)���iU�8����2	{�`�6$D����>��P�����$,i#�]$+�S!����n� ;�
�}M0���V@�!��u*�&�d�%M����sI�d�&i�\�#�����+ 8�H*9S��!O�O��|�^�Fc�!9ci���������~�0jd��K��
���-4d�UB�#���hM�/���A�����@�mWP���|����?����>/�~)Nc0�
n���@`#}���n�T���r���~�Y�ON��+@E�:��b�>�g����w2{��A����&�`3���d����p=(*��]W��V�N$�$�������N���Q��c���#c����3��Y�j�������^�#2�[�0����2�$�j9�b�-�)���8���Qg�-48����,/�ft�!�*�Z�����d���OKb��@�}�-���Y�����f���P��x9��TH���C�/����p���mw^U�S��p5Er�d���P]s����ii�.�K1��;,���1P0�c�eJ��d���n�lQq�gdP7��f�s #��}2��-�n����!`3n.��W������8A?��6�H)�{��U�1vo�Z�j�����	��{�*U�{2�`jT!1��x����K��`A4�e2�+U���{��
�CXV�@����j2�,|�^���#=���be�S�9������.��2�>[��W���|Ri����yF��j+Z�n���q�4_Y5P������:/��M��U�~|qY���8��"I�}C�x3�LP��;|��bn7��nv"=4A���?������w2 �/���3�-��u�1�1��[H-R1�k����+y��1�F6���$�Xo��W��4�
�xLhV���"h&N����U�Q������
���wuD1B�M�O�H�[�i��=�g�X'�<�h��s�O��:$��-`��$���,&Qn����h�:���~-q��(&W�� v����~	�_J A����h��^o���Q{
H�L;KPA2%i�����~R������M�x�5��fx���fZXb-�&�����CX+V3m|!��V_��V��7�
�k{�����3���j�hL� jka��&
0���N3���S�AN3*^�"�P�Vl�����Z~����!�h�o����nc4Z�!vIS+��_X2�����.�$�E���y�ip��}�_�&7�p���+��A���3l�l��rM���� x)`P���([��9��Ef^6v�w4P�st��M'�H���J��	1�@$2P�@��8YXKI/a���Mn|����#�
��&�L��f�@�2U��1���R�[F�c]���:�� �-
��nn/��VU<��H����_���Y; DK�����E�:848~�Nm5=�|'������9��K ���l3�����q��*fl��kr4�Ah�s{\����b�~�O|bzG�)�g�?�7]]�w ��$�rC��=��\!��%��C��n�
�~s�D�\���g��a)����E��d��`%&�|�
����MM�b���8�%o���U�������j�E���e�������Z�]��%��A�"f}�R��8`L���{2m���������W��:�!/
N�K�L���N��>#�	����@.G�4-ct����E�c<L��e�Ga��7��{T�"Jx�3�LG]�����G7a}��5'��N`����{�����)n�����nSv���nZ@Z)P���)W�X�5��d��I����K�T���8�RD�O��PQ�������I����������R}nZ��
l�4(y��yb���!��b:��w�W�N�H��]�kuCO��L���sH�s?#�Zk������G� )hs��������^�tmh(joVK��p@�7y�SR���b�[�&���(��O�ZF+��j#�,�s����Srf�H�1h�e�<�'���45���p���yUu�������S�Q5@H"�a�:`�9uBbk�f��v���:URd�Q?��Ia��E�(��TQ����"��:{kRb%�&��<:��@��x�$S���"d�����E�y������/��FM��d���P���=k�S�wQ��h^�My��K�PY~��R���=���fv�'��-(�:1�JF��Z���Z��I����GJ������S_y #H���O�~���
�*f1K���Se����@O`?eV.�l�,{gz��w��_r���]��6�Yb\���h����Ld����S��v���,�lH�	��l���$����1�t@\G��6����b����D�r&{����d��\�,)�Q]v�3S)����_e���R�4�p-0��f��g��N�P�e�z)g�"�<0+�sF�����
�C�YV�h�wx�]���G,wb�����y�*���-����My{iA�:���s@3����wg Z��	^@�����+(�$�������Z;�&���s���l�R0�|jw�z8<6���$��oV��r9j�����H1X;�����$�<�*NGlX		�
�PV��.e�l��%J�I���{���
D�D�������(�hS������w"�������B1'%,���U�W�j��o��+�i��ofZpE��C���Ad�\w�L��S�
���}D�{.��=�������@�Z�x���_��x$9�R�I�����KB����1"��Q����`B�l�������?��"��&P��d�cE��>Z5lx�sQ��4&h���j�l�#jpg>�*��rV�{W������zv,�]I��4a�n������V3-�E�g����I�I|c��X2����]��w+�E�c��f]3a�=_+��beOUg�L�N�aq��%�p�lyR�����p9{�]���\h6��rS?�x\��K�L�B���@��Cz����m�N�H���)��h�0��hQ��vKQc�m/4��FPr5���&r���!��d�}���RRF������AyN�K�8���OL���(���I3,�����a��F}��i��v%��vnv�#.r�V��,�|�Kf��7[�:�.�����a��,��c
�8��Z�;����>�^�������wwi=e��`�d2�~��66�8j��'8�%
��E���o4�{��U���fD�q@�>�:����d��F�QG�5v�1e����SaU_�z�b{;A��(`�j�T���u_�W�����q*�\���F�i��+���1U����L����J5��=D1���:Ox-O�Q�h�`���d.�h}�4���;u`�^��M��p�����Z0��*H�c ���4�s��d�#�b�l+�79���i^m
S����O|�H�.&��@6s�B���Mt�gR�����<u��M�-��d@?tW@��x��F�V��J��eS���+���}L!��l�R�[t�E���S�����t����s�����up�,J��l�?���e�u�V�����7%������� ���}���p�y�	0@���*[�EU[��)�������0����f�~���������8-p����&����m�7��G�������������8Y�u~�.&
�=H���yo:GA�A�i�B�����h0t���S������8���(����*t��W�8��.���xl�[�="�"����g/�����9��C8�	��K������e$��r�5OV��`����h/��*�B~O���;�TH��x�p�i-������{r��g8�����n��4#U�}@e�N���/����eACz�lTG�b_9�r
�|�x�����<Q�+.8S�sy�����,��h.9���p��l����bD�c<��33����
U�j���8����+���[���-�����F��K`���}��
^	����@������%�/�U%%��Q8����(S��N�mqg@'�@��:�$��hu$�/�S��hr����Zk���{�4�vE��>�G!�v�rWJk���5�����>/[!~_�>DMp����C
1��?�8�*c���&^�{"rzf��c=������
��qG�#�����g�/y`F�jF��|������gJ�f���^��c��#�������7������|�i�#�Z�R.#����'��[O
�'��������,Y��>�����UC�@�������7D)"e���dn��>Xv��us�_�+�:����r����k��VO��!Pd"7��>]M{�]�k�c���S'��<��HY��y��/� f�9,���:�6�D���r��4��]���'�KS�.�����G4�#����}�	������e�r*�������.j��E-e���9���������W����,c���7�v��nX��5����*.�k��
�
���?$?�}�B�GE�`��ZP�����\��������f3��z=^�y{|�)����
V��P�+/V��9�����eR���c��13r�i_����&M/����#�3������������Ds�s�h�D�UZ0���4�V��ZV��Sse���\����+��\��{�n�%w��������\������{��vjgn�/�������$9���j���vj�Jfj��+�J�T����2����c�	\d-.��N�h��,����k�HP0t+|�!�}��$�*�&K��/������G�p)�����AK�����1�?�k�~�#���"���J�������|���`�`x��75�L`�P���b�-w���.Xt��M�
>�p�G����s�|��^�����1j����a���l�b���@���������#g�f]��t@HG`7��d�0�����{�����B�
���b��i�(�����v5��|gXDq��/�[t��Pf?��p�-V�C~%�Qu���[�b��1� ��#J#����mdj�{�u5�h6���R>P��Qh@�Yi��]n���Q�&{�i^�k�����!c&������gr-��,��K�a�b�����D�o��L
�\���f���-� ���3W����9�]�:�]oEpOF �������~E�5��k�����'����R�PS�~m���G��a4
������L�E�b"\F���~�&�SLt�M�NC����������l�J���p�1�I��������-�4Q����ltj&m�jF�k77��l�Q��cw�M�;�.=�������^�����4��8B���7���P�nzA��1����C�mB�w��J�vP�\C��r�i���e��uI�^Vw�H<qr�0���:
�Z,t���1�A�~�,�]����u�;��;Z>���l�X��|2�>��*��/��~������y�N�O�|���eo�]_w������2R�tn��U_��5v�X�Sxj����f�|N+����jA�#��z���?�/L��6��W�pU]9z���e��9q�OKGnOZ���Y2��?_/��&L�T�<5}�>: ��m��xx�[�:���
�~���������V�.�nue�����Q��Z�Z�,�k5�o�j�j��GN^����}�s�r��Q��w9U�Fj;8DC��=�zoF��n��WwTyk����rV\���u�y�|�0	��q=\������������3�B��,���_���=Z��~���^�����J���@���)B����0��`��u��c�xWr��R,���'��e�:�.�{�q���u�?X����:���8����]�*_��5,W��7�`P9��\�"��=�h���6F�j_�Z<���L�k`�l��������=R�;���m���=�����t:g�7�;/7\#�����i�����go,)��]�:
���U���(:1�a__~����__���7r������:��7��X�5��JO�g[���XWm�%�Ci�Z��Z{�JX����Q\$kU���[��6k��
;��V<���O����D��p#���G�lo,�r6��ig{���J��=\��7"�/�b��r���[���A���5����;�m���q��w��8W�b><��{:�@|����}�9�����
-9'��-��d��#[ .�%��"�>w*
��gK��qN��,s��c�G��*H�tcPG�4zvU�,���*�E�MQ���(��\R����y�b.u�q=�A��:b,����Jw�,%OT�Cf';���h4�/a�Yr�lb��8���7��'?T6O�]�U{}������f-h������L�m����������-��{%Bc����4�����sh���e��6UJ�8������92��9�8��#oKJ
������z}�����hI@����$oYi�6<lP���G����;�ec��YT��LL81'��cO���@�/YQ���U���~�">�fr9[	��w����X6����-W����� �+'?�~�����r�z���6x*��cM��w�O,�<\�3��&'a���|Q^2���~	�A~�h���f��b�8���gt����HH|A��0/G�$�X��B����8��������XD~�)?b%�<�5����/�mWH������
g��@%�������u��UlV*��u�����n�|�1�O��_`+�\����j�1H�!���N��1�-el
$�2g8�r��Oa<�]�Xshv���5��^AG����0����pN`)_���$@E�����M�=�3e���b��	���w��Z�C[��hz��'y��)�Om��
sY�t��nXE�M1!���������V#jDkm��J�m��/p�u�A��)k�������I?[7����������,f�dI��R��	!��u�X���U/��w��q8��!���j��?��zb�@|7�)l��rdGnOt����q.e���p������M[`���N�����
�a�~����Z,E��e�3S��LX>BGR���l�a��7��V-�a��d�����S�%�BZ��;�F81�:�$��.���QI�k�������pY���x�����G�P�"�%�����GRc��(���#)�Q��M *	ZJ'%�i����0q�|"v�K	��s��n�*@�o���-#]"< �M
1g�G�	�a4���I2�M����(�3�����9jeul�;�|�S�'�O�b�2�/��-A��D�k3�O&>��1_���-V�����	������5���@��� i�A*���v�A��U`���J0��G�����a~�%JB�b:FDS��y!��d�)�ndB)D��i�����f�W���5�����'F��x�[���������!#�B� �R�
aI���U���hZ�l?�;h33�/��
q&��p�y\��nq���������A3����N'�l�JNO�����Y���OL>�����a �5���y0�h�<����~�A&�����P���p
�[��.�'�x6��r�K�N��oG��y����1���������z	s�ry ;!������ ����T�Pb�A�#c�q��1�������u����j�����Ik(YA$}�d�����d&x����A�~FR�`���A]0-$��L,K>�.fE8�C��B"2
$����EN���G��7��"�y�'��T;��1lv�n����a���dD3~y��u���+M�8#��S��6��dQw��
C�%U3!�!����&�m�I��X��$�?pK\�~�sP,��p���z�"�<bmNQ��dfX�cWF(�p
����I$;��0sI� �����r}3j�J8�ro[S�V���J��9O�.3io���]
��.����H���0�B�B ����nV�"���L
���,?�3�2��r��x:����,����l�_����V�2>����v~8
�����������2���WE�������8�
Olg�^1����JbdX��HC�{'�z	kq���]uT�� m/C��!F��[��u��I���K{�)��n����3(&�����;�>x��'��a�S�z���t��
�����d�NF<]��,.�����*�����
�|��k���������#�$�����������wo�{��k���D����5W��X��,��*F6��WF���6,��9��|��CY�q�
�=������cX���$��P��ju��� ���B5�6�GM����B�p�K
���;j��6"��0������~N��-��T#�4��v���QDfL�
q����'Y���0q���^<D�/z�N���'s?����{�q��o��>�6~FD�?Ei��19��q<���
>�C���T��^��Ci�t(6b1�%�����g D��y�l�z(9(�X(v
����g��#�:�Y��Q�\;�o���K9+�lF	i�Y�QD:������A�&�H&�&��'q���x����L�����l�t3�������B���Hup���&X��
�FJ��Y�����_�xC�G��JI!�����������S����#&�!��_(?����"Pp<�cRi���p����<�,-�.�i�m��,wGE����!�hX�y�
��R+�P���<����PN�X�t�4q
��9��mT�s	�*Ux�8�
+:+����;l��7��NZ�)�T�c�P���������z�x���4��i�s�l���q��fPrUpt�,�z���/f ���Fn�U"
�2��(E��2�P�s	����u�m����� y�_%��)�zq����t#�m����<�c��e��y�>u	
?�k�=�m��m��B����]�._�vX�-l���
v����<�r��~�I#n�8�=X�szU��,�lN���2����O�Q}C����e��o��V����s^q��� �j��$��nPU�[�FQ��X���p����������6���?j��xu�����J�W%[����������=�c���V��2S������m��Bb1H����U��N�E�dME����%��75�Y�!�#oo�w�e���%BFQ�Q`A7�{��5�HC������d=�A��QuC,�����^���l
�E��u�X��Y3�%`�a�����!w�1Z�BT�[�@��\R�10�~�;'�����Z@v|i��V��D06�w`�ES�'3I�OU���������G����Y�r����r8]5l)7\>���(`�r���s?�>d�&n������i�nk���1 ��E���9�j������wx��
O����@+'��R���)SO+��d��C�t�x�*�^O�<)���x���1��g��bG8m������8Iu��T��Md�	����\wf���U�*�~��{�����Ryq������`���&��������������[mr\qi���8�F)��h��!�=\��,�����?�;I���@(fW2~��q����p��f,����$���s���;��/���6���='����������a/l���n�~8:lE{�rW�\y�\r]8��<��1GY�������H�-��w��o_s�:�1�0����9d����Qrv���z��o�;�������Z�Tb����[K�%~4d�B5]����g${*p�W��Y<%�:�����S�=,LrO5���+����x������:�kEs�������5�Ag\�LzA�CW&�j���ws�T�l�)NW6
J6�4�C�O��/���*F��`
�8I����s�Z��W']�� �����l���W��$�J�csS�0��\��<��/w#�&4Yo8��Y9�1�d�oB~�w� �L�b��LV5N1�Y;�	i7d{A:��d������d�b3����S �:n�q��s�F���w�+bo��j����qfg��!�0�:tL3�����'lPd���Cw'��*��GM��m�7����sJ{�����~�����5��&>��1x]����U����� �gB-���v���
���ug�px�[�'���b8vhZ�L9mD'�>�oHH��9y�7����)�#4��#.o�����5K~���QXx�"6v�!�!e��$��)�'�$�3d|��_�L/y�]v.
�!����)Q��~B~#���p�(j�������23Q�_`r����I�GWue5��2�y��� O��������"�)6���}������������@sy����n�����\x`�����2���U�����C1�����0��z�5���J&Yj�r���c$8F��cX$�*p�/#L3���8���nQ�Rb
�	�q�FS�O�g�D�G�'
�aw:OxQ�1,-e������� �?��L��
l��'��aO)��e�r�>������,F���-����6h��5�V����F�=����������9�I�e�g��
��j
����)�n#���p
v���'�.��4�����4�S��
�������4�o�_zdC�z$ ��o%��*�_�39ox����iN�m���n���f�����Npr|v�9�W'���I��k��������������9�{�{�
�/�'����������\��������sh���
�]\u����+�vq|��:��\��_��.]?���o�t�>���_���>�/����s	�<�������.���s�������c�����:�J��=��_�\^!�0������@7�l��u�B�A���_�
�����������.g�����g��+X�7�N�������>~��<yw������~�|r��={�O�\w%H���Y�����g���BQ���V���g���;�����S�h��gx�}���u�]�F�]�������3��-�R�~���_`���f!��/k
��9l�S�5�v����=?�|�AB���g�Z���uN����Y��#����������w�����U���??�a~��9��|�����]�pB��x��\�??]�s��_!�?.�@rV�n�}��V�~�L��J�����x��U�����l��1�����i�y��g������>��\�:�����*������	��_���:��;�sZ��W�������_R��c�IL�S$��'��3�<�M�w�o;l�����1b@���&~�9}��~���������q4�;�-���o�eN��s��|.�������Qk�� ���}��=�(�h����i�B�wH�E]����9h+&rvZ������Z�le���I�fj�q���al=k����LU������c���M.����o{/%s2�pexx��6V|�(���r^:��`k��a^��������#
�D%y�,[k�C
i�,x�@��������\0��{�y��!��U���$M��T����[�E^v�}j�m�
����-����V�����5��+�C)���������L<�(�1eC�n�g�g�F��	�E�p<M�����7kns���@�����t5`�Q�`@_�B��{
$���x�B5w��"{<�t����d6|!�	�������a�d��ma�����'�mI�Z�]�����)�AXA����W{��r�o����9�����,V�-�/����h�����wwG�{{�+e�L+�2s��*�A�i>����������[�o7��/�Q�����}'&�������b��Ww7=��o9"}��,��2^%�����3�y��JXV��Vf�~�|�%<���|N���1�#}~�>�� �F:��`>���:������?���
uYy�S$����j���Gt�������P�TG�#{J�*�&Mfsh�E#�D�(+�;���K��dpZ������Sv�%�_��v�?Y�j��qt�0(vl���C����{�8}��������Cc;��s�,��3e�QL���#�+�^c_5\�Zc�H������������5E��+��&�����8U����Q�O"-���v��`�����?d�P������D6�M
M����l9�@�d�3��^z[������+7,�m+���(�����&?I���?�������s�#�=�I���)����=�P���\�|��Bd�+�q�y����6�=��M	�
�	nOC8K�2�43�x&��� ����'`+�+���7�Qxs���*J�1eW��~����:�����b�].��c�G�c=��a&(qcI�
�}�Mx����+���d���u��-���ug������tQ���8dw>�k1��H���HNFX�['����W-d�H��7h��&h��&wsIq|u�&���R�6>@��*x����~N�KCg����:���V"
����K+��&co+����)F:�%.A2h���p6� (�qD���IH4��/&�r�T�����dz�s��2��'$�����|uqy����i�����k
1����x��
��,j��M��+�
�*%�K��3nz-�)gs�(�1"�p�0���,��0,4�p��b�������A������Ck��I)L��y�5��/���T����f�6D�;�{�2����cM���{�L���m�sDgw�.>��:ap��CU���!�e�����	�Cc��\K�����b,����w#�@��9��`]��;dg
@~��������>�;����,P:�����~�9�bDL"D�����#q�8�����x�D��U�)����C��@&����tFiv�H4$<���8�i�f����.�m&"F�]�+�6��:Fv��&�;��`�
���<��d���
��R�m(�`/�^&���%��q��w��i�<��\~~B(����>��� ��q���o-����r�D���+t�7�3�g����,�UIPc�?]G��@u���\������<�u����1���E�#�J�+�[)�l3'��*yX����(�6���9�2}�{�	�M(���8��H�x�h�3�Y�V��k�'�������_|�f$����b9�v�4fgx�`=�e2NP���*q.=��s���"2lAux�t�M�Av^�;ws��>K���;�eN�����8�w��C.�@4���;N�T���r^�T�l�a�+G��dL����_]_v:��7l�,2{1f�S���~������q+�s���
0R�D	H��X]�������;��.	�5����n���*���B���~�O�kog�l�����$����<�0������,b��~d���fR�	�'Z�L0�)�x:��P���H���1q|�tK�RJ}���R[�k��yq����V��'U�aTI�SLx	�TA:
����(��C���v�faT8���5��j7��]��m>r�pGf�S
�v�]]����Fb��B�6*X�al=�Oqto9"�<xU���������#���@����1a��n����U�Fq���#��$��i�:��0B�:��b������q4T��bge�����\�J��c4�-�!���FH_��J�?#1�(���/��-D�������D�M2N���K�e���
1�]�p	�v���Q�E�NH$1
%�)�<���7����\!���7]���pP�G���PWA�/��Y%��Dd�f
���[]5�O��&*���]����E���������M�R��G���V	��������C�0�G���!�#A�����>9���K��y7����rT�W@4�J���
�)aHN��&��!dX���fl�{����ae9�X�4��U0���T�3�=Tu���F:��UV:-$f���~�h���?�;��5��fV��LA��������I�Ae���Q�����d��{��p=�|�*�y�*������#��W�bnx����w�KG���t3A`�?G���������P�1�E�!Q!��|�l"s����y���$�p�Jw0���0a�	�2?F�����eEe7G���QkT���h�w�*��K�����ig���?���	_��Q��k�38�'f��?��g�w
���Z�v5��/F��������N]�7 �����\�|:�M"M8U���>��^�������0b�;d���C��#�K�&9�X����5�	s��J�Q�-7�h�`���8�I��0���{?t������g~�����=�8	I��rpK:�M��'&��^Th3~G��
;�I�,Z�..�K���������l���Xl��o�gak�](2�[(��Q��C3:��:��u�;KB52~�t�(+&��h�4w������a��)���"�PR��9*��{~�����$�:A
�u���3L2	����������R"����*����x�6R���_�6����m4<e���5@�2l%��L��,�u���r1&��`=;q�s��j�X�)�!���Arwm�*#(�[�a�����������{I>��|}�x S���,0�����QS� 1b��Z@`�z�t��bl�,cz3�i��������ugzP:��G���y
2s&��]��������rh�1��n��2�6��2���n�B3�
���K>
8�`+���
� �F�j�g�G���,I9`0���-�'oN{��b0�H~f4$�1pN@F@��H�(����D�9��F�
��0NQ�f|&-��%�Y�5�W�~��]0���o�Uw�@�7�)U�?��-��B>z4z�h������		LT��7�w�y[�+}a5�K^]
ns������d��Y������y4���s�4$��n��W������q���=���z��|�3���OJ����L$�g��\:|����x];���W���;[�
MRK�����G�n"�_�������G�����sqt��#��c!���d_l@2�m���dh��b�y������U@�Od��n6L�y��6{T6���nnu��ceS��'�f��&S�� �P���x}6[G������/����{�)H��V���q<�IE�&�W�J+�.�}m	��b��\����2-�����q��'��(��o��F���b�=e�\�%QL�"����������y�]�{�!V�S��jD$��TU��Y�P
������6��/*>���K��3e����X#��3�����xr3�r�sA	a���V��?����������<�\TJ��P���t&N�#���d+XIdr+Fo�9���m4��/"�6�x�O�p�S��B���[�G��;�f�������U6�-�&�`�i/,�\�;hQ����� ������)��qDj����F�����zJ���w��5��l
��i�"���	��@�l@h�a �$�S��I)��<�U0l;N$d��z����w��N2�)vK�P���}�����p�F �V���stK�]���ne�8tn��~8?�T��4�?������3*,�om�x+�w�:n>C �|5����E��%B�9
E��>�� ���kP��fi�K����2��N>��0��e76���x@��lF~�D���6�7�����	`c�?(��9uX�S�T,���	F��a4 �4E����q�H&��&*��)�}u��B�o+�.�}��C�h*���$�+����! �������&�����4q��.�1������4�*� �T������wqj}�1�"l�h�+�K�d{��Q#�p�8�������yx�
���@�����8�s4KJ�h��s@_�����F��� 8E�����=#Q!4�|�f#z��g<{��
�����t��q�����=��x���
�I
��V~DDk���w��Rm���l{�s�W��/���gPH����1d���P�C�����f�����R�����z�=(_��:s������.�pl��$��5����
�����'}��,�&jJ���|�I�;(2��Y����Hb��C+����puW�I(X�l�l������a�?��u��j�C������jtD�F@9T�HOr�n��pL����D���������'�2��tc3�{}C�����6j��6}���V�����9����v��3���W��L%�t�� c�3���t2Cun������53�uha\q��	%��N����f�m�l�q~�p�;���iN�(,#�E�8�?�������~t�(0������H9GQ��CO����#d��D�����G��P��ITe�F������S<�/��g�{{cD�	&Mpj�|�����=�����8Mf�������Q-W2�n5����A���n�-��*h�l��B4������)69S�`�S���76�Jp3�����/�@ll}�Q}b�+^&����NVh�����B�2 ��(/����n��bvF���^������4�&�����i@��o�?hTI��'oUsWq[���8���$\���u��5���&wE���	��:y����)�<g~�������\��
�f��~i�([S���\��ASaz�1�W{�l"X����V��}=X�@f��>M���V�l"N��<��#�vU��|GY��
��g�t��+8+��s�9���M��.���Z���<b&XCjVdX����,A����N��=�x�b� ��^�c
i	f���A��>JJ3��>RF?xLn�<���&���1�#����<����Sn~�Z������a��m���
m��*�����f��P�{s�=���������%��|9GH]x���
����A��H-�����s�Q�3�e5�I�F4���4NE�k2SJ$RlJj2~P1��i6���fp��K�����B�$�M�d3�}�M)��j�ml����*�����Kf����:��]�%S���������U{�Yd�m>|����2��4�a��|B^�?��Dn_2�P3��K%5��Y���@����v)���c���.�+�����f��O''��y� ���������E������^��kE�8�F��*�O�`���F����a�R�����~��q��1�w�9>����c��Na,�x	�]r2�$�
&����a<b����)���7E�-*����K�E���=DWsgH����0�����w��=k`���?�5��P0$�`v�EE��4����d<�)�:�^U�����n�x��m�x��jInQ��LRM�#2�usA�?������-S�j��� t���hz� ��]q��pg��'���O�+��U!pj�}F�d&A�N����a|��
�7�d�[��9�M�<7G��sDG������Y�P���m}���a���bR��+b�4�������i��=�����U�(}o���G�aIj����o���=�&M�k0�u�����9��b���Nj��u�����M
��M����������cmy6�xs�@�(��A���s`#�H<�����������(�'p�R{N"��!%�y���������inq�$��=G������� �4~�x@1�&ZX}��I�XE����"	k00���e�o�$Vn����H�|�Ur�:�DPt�!����+/��T���4�
��5������^oG{GQ{?�z*�o�Ne%r:�'��(gX�G�����o{���6v?���6�z}��5��������
.��d��!�;��!���B�@p�_-��h�)�`+���^{
���3�>c��#&�|�x������{y��?�wM�V�J�*��/B�t
I&���;?\_��]r�2��)NQ��v��|����,IS"g�-��vG�q�z�u����i�]�4�����Ce�N~n����4������X��NH	/O��i��u��%f�����=��/���N|�3!".;�RD��hwtx4����F��`����� Z��g-��[e��������<�ozg����������2��d�q/w�4��Q�a��*�����?�@@�7����B��U��A��`}w��:b��'�5��'b������X�5���q�T%�v��������!�@A�p#��,9J8�&7r��{�x���=Y���V��8)�s�J�Z���Q��o������F����fe=��J�jB�3����z��.����p���.�Y�b�ls4�O�������B�O^'����<��US�!��+QU�W�[���X���0z��>-]r����n������� ����o�|�������� R�z�)��r�����c27�>��#����������wo�i$r������.�t;�t1�y{�3�e�4�mLU�*�=���{�cLyk�w�z����j�2���z
�>A�7f[�h�Q�NF���-���� ;8�7�#}����&�����1����e��P�FM�x���x���]�����@�D�yMO4�c#��-���>U����q�4w����*V��ML��1��xT��YN�-���Y|�����A��~��k�]����
m
DB��r)�1�����w���p,&.l�q��S��g���q�@r��5�o�{�-G��p�an�x :�� ����u������:��"��`��b���)�������$��B��=2�:�R5�F����G����9I�����W�7��7�����o����e�_u<���j����M���mS8H�F�q�Z������
���t1z��i:P�l�T�KS � �������n��������Q�x��A`��}-$<D:v�t�TV��K�)L��a�5�,��������q.�����E�C2J�d�X-=�f���v5��'��g��M���R[J�J
T��Rb@���� ��S"Hr�n���C<�,1�C��K�0@������A���D�0�������G"�P�{�R�"��jb����a�>�O��e�M�_{ro�J�/0'W��*
���o��G�����gf]�,�������9�p,jHq��]q3�d
o���h#k�,YP����
��ZP)�%��p|�{s|}�C���U8y�T�2C���K��=x��n��
���3@�d�k�e�u�.���B�YrW��?�*b[�,+��"a�{��+��i��)�C�<����*���R�m>�Y�����2�B�
+ni�Ub������#��r[�����V1��g�,0��������@�wpP�5�GaXm�������k����p��OW�D���:�]��g@�2�����p�	��-�R���]4��B���BGD�N�����	Ky
��I��v������r�a��!f��Y%�c����G�fW�2Gbm�h�h�����������D���i�a�n"���}�9ap;!b{P�� s&������/���Cl}
��fr�{�"+���M��jO�ebAF��|�������Vr����f|���H��sfNp�������u�vY��N��x�x��Dv���"���v�P���E�V�+�t�����z)�J���l:�JT770�������^q�A��
��WT�L�tw�}�^`)h�-w[��+08h�����`/-Q��[���2���3��ABbtw]���)`�f����!��"�#�9�<���Y2�F��x@�GH�Eb6k���T��\�P�B``S�M}��`�������u����F��-���������}�=�n�� 1V}h�?p�K�����<��l����S)~bX,h;}�i�����m����C"8�6�2�Ms�aG�."(�q��9�C4��L��c���4�l{,l�&��������~�������:c�}��U�@�3q7�#fCp?��K�Qfy��n�\�G�^v$H6���&*��wR`�3���������vq"���b�&m�^��4���e�
�w']���l�|�m����#�%�h��et��|��|���������*��p2rD�K�u�Ea*z�bU�4�"��K�� �^����5$KJ8�s�zI������Fwb�6���������_��?�����5���5��/����}d�h����:J�a���*r����{���Z�{�G��^5������;i�}��E�}��?�
4P{���o�e
�}4D��i�>�?!�Q�W��{������������a�3H�\����(��u|����{�h07��f�����r=��������d|�bL� Ol�v����Z�L&����������^e�~�]��k�y��`��8&�Bo� �4���>�?���e�����_Q�yu|~�E%y%�8/�O9�=��z��l��k���=�~U�<�ftSR
L�������P�����t���t�F��p�>��,���!NQ��#(�Kl2�N�(~a��<���i����Zq��������f� ���{�]��#�5Qv.�B����������Y������R�Y�klY���-���?c�������n��	@K!9�Xyb.o�+��G��Un���r���[���^�I���/(|��b���C����nZ����/����?3���G�u�>j�U" g�����y�Rl;�X���vd���6"�
�#�X�x�UA�xl=6;�^�C �SB�l�Z�s�<C�B�@��f�R�k����d��=CH\��
'�A�]6O��	�}0
�_�6�_�#���9%���L��^:�$7g���O�\����@��^[&��&=����3�PU9�m��a�4�DV����9��9g�&���k�Aj�������}��.���"`�;��f[R��2[k2q�Y
M�K�]w(�.8���qg��K69�.M�O�Q�*�������f��,���6o6��O!p%���@���,*f�`�)0����]�/[
L/�[����������=�����$"_cX�>��{"/;�d�(� S���������IP���V��m0&}���%2q/�>��@�p��E��9i����Bt�N�	Kz��?���\Y�Mf��IM�T@����_\E��"I�T��6���F��|�g�W�*'X�����5��(<��w������K��R��)C��E���b�?�.�$�)���Q���N�����t�!��p�����30%���>�_\|������|��O�kH��erv1SV��m��}&�-H�����g8L|n�|�v��ra�e����\]'�}Q����Y�i��_����H�i.M3�6�,�,����l��Qc�l������U���N���)C���p���f�`�{R�����,Mf=b���3��f�h��P�������p�A^M(<c��pJ���@&)�����d`��hIL���p��f��}0��cv��:�a������>��,t�3	B�����@�M?�+�T��^M'�9���
ILh��T������g�4t���+�:�pR�1q�`o/�}k���\]M���n>�����^f���9�Z�����o��G��d�p�����5�����wt��D�H:��$�Q�����������z�����6�G��������������!U7�9��n��#��+y�1��
U$��������i����M����-����.UK�=������E���6����I��)S)����|y�����j�Mbi�>�omt�#�E�5}7�G���)NG�p@�_s�Vt��"�4��!
Z��~�Thi#�6�#�#�Al�&B&�����c����<6�L$��LD����KEZ�,���=!�1��e��@���Wb���?�E����B�)�[(W����S�k�=N���G��4�f���:(�����=������5���{�h;b��mDKb�����![�1��M"8�~����������&��n�P���/;W�K��B�'5��V+]wN������U=?ywy�9�>��\����{����w~��S�e���M��~���!�{������TE�w�~OyOp�s�J���)>"\�����{�\�^�U�*�&������8l���6�Z�pq��2��B��]���Q�3��qI���������7����z�L�Y��I��^��~�U��?v��`�d|%\3X����i������&�QO������YI�UdC��qb��������G���1�6�+IH��RB�)�y���	^�V�����UI�j��{#��
�P���&������tI�Y�3���=��F_���_9����9o]�+�f��e,���[o��6S��;vw6��W\����K��	+��3o3�?�zX������%�`�~~�fK0V>C�$b���#M~q�|���;��-����>�K9����]T+��*h��ID�pA��)���r��18?��^\M�wW���q��Qo��������
)h��Q���L�2"��OM���z��jd���D*�*��[�w��=�{��d'�A�m4���hI|����F�S�A���r�au��~����#���N�
�@A)��8�`� �&P����z�gq$�-������P1���/_�,U�N�+�A��"YC�+��]a�w�b��~��W{x�����z|.	��T/'2R���R�u\���92�:���w�,�(�3:�q����\�v�o"	\�y#�����LZ���SN2�5���^�����B������P�����9N�{�������:u�LPp�����0d�����JU
Q�gj���&������K��P���bW����
.����jk
u��Zv{��w������~��D�����X�����
�]��i�FEH3T`�"8�M#�2�Wd`��������@�:�:Q�9MF��X��<���g�j���W�^��<�zm���	I?m�t�$0zMT��d�cl�f���.�T���N_��x;89;���������W���7����5��:���,W�U�+�;)@�
�����c�o�!q������8Q�����^��J��V0�`%�e���,�H���)� c�c1n��( �%[�&�����E2S���e��I':��I�f��} ~5�r��)�A������I
TK����~������hTJ�����A�������`��D(�k��D~����/��w���v��"����	t�����Vx�����N�7�g�p�t�����W�7x�?���m[����T�u ���Wo��#T��<���D�8D���������g�~E��m��k�����h3����fA����!W���6�RA�������j�S@8��Di�B���_Q�HE���� P|	�����Z	2qMQ9�D@���!Ee�-y	��6�p/�����;���E�X����Y��!	g��fP5�]�a������=C0�gj"�R�:��z�[Dh��a&����B��B����X��	.�?���Th�����Tl�s���&��b8�B���9�Vl0dLB;�r��X��;��p���v�
`^��N*��c����BxJ41sKS�^g�QUZ��b#;V�-� ����8d���d�^����)���o���zp�x�}f�G�xNy��%��@l/�$�*���m��:����8M��z}�#���c���V}����V|2������9����3;�W�G��A3lMz>�>=�hR��|�g�0��v�i�5[��M�H8M�-8�i5MRh\ =|���V?�"�f������M������
��__1�1z�|��j�.{�F-x&��U�|��K�����l7���^�'@w��������;�bZAF�B|�9���
�	Z�^��?�O��F����jJp��N��]��_dIB��������4���*Zb~�!`�����|9
:�@�������>(��W�TJ9
�r0��	7u^��;�\��D�aH��w�=]�b���k��
��R���/���V��vx'��w�%�vL9x�~}�Kg��!-L�H)���\Ym-�?F{�~k]������#�����a���?�4kP��S ]s�Q
�4��t7K����?eP,���Me�;s�(���N�M"�_
2����K��}��b��4X�Y�P`��\��y��u���J�&h0Y��vH��_���C��-@`�vp��ar?�vN//�:�h)��f���?���}�<�^]��Ep���s@oN�w�B�Hl����/_����5)4�>�1�� 3��WJ��C)�LL�r#`�w�3�f����!��4������08���v(S��d��8%��I��c�����e����)����s�;o����F�0_��@�"J1�e6{Ju�\�:��"���A��:XN���C[�������_����	����(v����Ab�����R��WF�
�B,����k$�%���� x#���s�&�:jCi���8���5����g;�LK����w�9�g�^rG�<E��/?�q��'�e�����M��w�����C�:����+�KC0�h����� ��d4"��	\f�����
��� ieH��,��G��?w�b��B1�������'�� ;vhgxo
���h!#���\��(]��Z}���Lp���g�2���a���i4��-��pg�A���x���9x{v|�`��|�?�)�o�����V�Mt��|�����&�`���m:^����
DG�s*�N�/��`��1}.V��4�T8�aZZ����E��X������
dw����OO�N\	����v6�Z����V�C'��
o)��������{���l�_��?�S�VX�u[a��\��O�+
�I�P7zZ,����3\�7:�����|
:���Bnr�~�:\�_���Y��&�����	 ������L}�J#1�4��0%��3*47	&�v
���$��[6�<1x����X+a�����e2h%�*$h�w��������R�G��������R�����������J��|K�K�K�K�K�K�K�K�
�g�6���S
i
�0���u��C�����Tk�����(����3-r���qQT�_���M��g��2��'���� ��G}"��4	5��[��E�i8��Q���4�������Zn�7������1A�n�H](�O��Y����\"$:���L����oZ������c���	��}<�0@}�H��-�5���U$A�
���P��x~k`�)4�B�%*f��fB��8j���q�y��>�&%(�:<����B�EB�'
W_����aP8�G�Q$��0��������1rD������#G�g��X�bD��(B���b�Cf1�a�m71��3:��q�Y�1��p�1�cF��Z�'�N+�z���=1��1b��)n���N0m������2w�������������F����B_Suo��+�a�4K0�1)'�-�=iwb_YPBk�]iGRc�+��a3����*��i%w�E���!g��a/*��������Wd���|v1����������^Q���xc�������e�_��J���xi9>$3��d{����{2�2��pf��P�g��������#�Z41�4[@_!�[f�)�$q�����Y<Et��5��<s����{�*��N�v��g������q�������`��;5�7D��H�0&@����c^�DL0������q���F/����LD�w>Cq���g"lG�j�F�C�����w�s�yc� �?zQ���.�����f��t��i3Dv�)��x�DW�������h��1�|��fe�|��|���"� ��yx����@��y{�.�[0���p~�CD��a�*�?`G��ue����k�M�����6�U�XZ�^s$��$O�I�>]D�L�����IY��+#��)E��)l�lq�Q4,h�]�E�Q���?d��Y|s����F������^�D��Q�"I>N$F���H��+�����Y8��f�����}�(�U*��*��g0oq:H����zs�����-lvY�3�f?E7a
����a��5{�j�	g1��*6�� 04�������0���������E����
�	�1H2���Q��{��<{�}&���=%b�!D*��z���c�L�C����r��
���?����t2�I��+�mGm�-������'�PR�����0P[F���W�D�.U}�r4/�x��{��I����M�i��5�f��|���������y'{Q�A���W]'����H���Oi���&r���R�U�����6���zc��j5J�&���**��d����R���X��No ���7Nn�����e��� 
�d�%��~� &�$%�
�'`�_j��T��]���`{"����8���������[]���.�]���&�
*����n�)���&��S�A�-��_p5b�9#�V�G�g%X�c�?x��;x�
�����O
���y�E*l?�9��4�����S�W����l��"�?���� �m@����_�9��~5��q���<	VP��������A�vS��
��#��x�
�T[���_�hUU�/)�pC+����7C���7oc���\��Ynj��	@m��2t�,��D�'�mm�^C����xE�P���E�z8���?O����%s������lOCJ0�)��xZVn���dm��
�
��3]��D�9	
'�,�� (Q���\�
��klh�J%���CZ<�����<�����=�������r�E���Y����k��*����;������?��saOf�T������*����A�s�������f��{�B2>o���L����4_p��O|�������J�2Fv<u�x-f��x<��j��
����'��T+�|����
�rZ"�[
D8s3[�`F�L��M[�����A����2l��������3z����3�2����k�%�v�;���'g�����W1�������^��Z{_}�-?��r<�s�i��4���6��������8��fJ�u� ����<���(�D���6�?r���oPS�;A�����4�bj��n&�>	�
���bD��6Fw�t(��'���vX���R�������7ZL9�Z#�bN��#���W
��*�ek�)��[�����z�po�h�;Z�b�FJ�+[�2�Pz.��Y-]��8��]��y��8r�
����Z�K�&���C_*������8�0�������@/�c���HH�,�D��%����ET�����-!��+�~�Hqf�7����s�������q��s��'s��'v���t���x���D{�/�o�h�z���o������~�H�i�*>i�x��_�������o�#�:jvCZ�-7D>h���F�qw
�(��%�r[U���d��G�w�������/t?*�4>����L�{{�
r����*M���z�]�5�6���c����m?�n���������\V�����SPTM�����yT���G������%d��8�#�GG�����u�w�����2���w��o�q�?�ek����y����7�
�T���C����.��?��So<y���[���"
0004-RLS-support-for-ON-CONFLICT-UPDATE.patch.gzapplication/x-gzip; name=0004-RLS-support-for-ON-CONFLICT-UPDATE.patch.gzDownload
0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch.gzapplication/x-gzip; name=0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch.gzDownload
0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patch.gzapplication/x-gzip; name=0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patch.gzDownload
#28Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#27)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Apr 7, 2015 at 10:59 PM, Peter Geoghegan <pg@heroku.com> wrote:

The documentation has been updated to reflect all of this.

By the way, for the convenience of reviewers I continue to maintain a
mirror of pre-built documentation as outlined here:

https://wiki.postgresql.org/wiki/UPSERT#Documentation

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Peter Geoghegan
pg@heroku.com
In reply to: Dean Rasheed (#8)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Mar 18, 2015 at 2:59 AM, Dean Rasheed <dean.a.rasheed@gmail.com> wrote:

Yes, I read that, and I agree with the intention to not leak data
according to both the INSERT and UPDATE policies, however...

You're seeing a failure that applies to the target tuple of the UPDATE
(the tuple that we can't leak the contents of). I felt it was best to
check all policies against the target/existing tuple, including both
WITH CHECK OPTIONS and USING quals (which are both enforced).

I think that's an incorrect implementation of the RLS UPDATE policy.
The WITH CHECK quals of a RLS policy are intended to be applied to the
NEW data, not the existing data. This patch is applying the WITH CHECK
quals to both the existing and NEW tuples, which runs counter to the
way RLS polices are normally enforced, and I think that will just lead
to confusion.

The big idea (the fine details of which Stephen appeared to be in
tentative agreement with [1]/messages/by-id/20150109214041.GK3062@tamriel.snowman.net -- Peter Geoghegan) is that an UPSERT is a hybrid between an
INSERT and an UPDATE, and not simply an INSERT and separate UPDATE
tied together. So at the very least the exact behavior of such a
hybrid cannot be assumed to be any particular way just from
generalizing from known behaviors for INSERT and UPDATE (which is a
usability issue, since the fine details of RLS are already complex
enough without UPSERT).

The INSERT policies are only enforced when a tuple is inserted
because, when the alternative path isn't taken then it's really just
an INSERT.

For the UPDATE path, where the stickiness/hybridness begins, we
enforce the target tuple passes both INSERT policies, and UPDATE
policies (including USING quals as WCO). The theory here is that if
you're entitled to INSERT it, you ought to be entitled to INSERT the
existing tuple in order to take the UPDATE path. And we bunch the
UPDATE USING quals + WCO for the sake of (conceptual, not
implementation) simplicity - they're already all WCO at that point.

Finally, the final tuple (generated using the EXCLUDED and TARGET
tuples, from the UPDATE) must pass the UPDATE WCO (including any that
originated as USING quals, a distinction that "no longer exists") as
well as INSERT policies. If you couldn't INSERT the tuple in the first
place (when there wasn't a conflict), then why should you be able to
UPSERT it? This is substantively the "same" row, no? You (the user)
are tacitly asserting that you don't care about whether the INSERT or
UPDATE path is taken anyway, so why should you care? Surely you'd want
this to fail as early as possible, rather than leaving it to chance. I
really do expect that people are only going to do simple
transformations in their UPDATE handler (e.g. "ON CONFLICT UPDATE set
count = TARGET.count + EXCLUDED.count"), so in practice it usually
doesn't matter.

Note that other systems that I looked at don't even support RLS with
SQL MERGE at all. So we have no precedent to consider that I'm aware
of, other than simply not supporting RLS, which would not be
outrageous IMV. I felt, given the ambiguity about how this should
differ from ordinary INSERTs + UPDATEs, that something quite
restrictive but not entirely restrictive (not supporting RLS, just
throwing an error all the time) was safest. In any case I doubt that
this will actually come up all that often.

The problem with that is that the user will see errors saying that the
data violates the RLS WITH CHECK policy, when they might quite
reasonably argue that it doesn't. That's not really being
conservative. I'd argue it's a bug.

Again, I accept that that's a valid interpretation of it. I have my
own opinion, but I will take the path of least resistance on this
point. What do other people think?

I'd appreciate it if you explicitly outlined what policies you feel
should be enforce at each of the 3 junctures within an UPSERT (post
INSERT, pre-UPDATE, post-UPDATE). I would also like you to be very
explicit about whether or not RLS WITH CHECK policies and USING quals
(presumably enforced as RLS WITH CHECK policies) from both INSERT and
UPDATE policies should be enforced at each point. In particular,
should I ever not treat RLS WCO and USING quals equivalently? (recall
that we don't want to elide an UPDATE silently, which makes much less
sense for UPSERT...I had assumed that whatever else, we'd always treat
WCO and USING quals from UPDATE (and ALL) policies equivalently, but
perhaps not).

Alternatively, perhaps you'd prefer to state your objection in terms
of the exact modifications you'd make to the above outline of the
existing behavior. I don't think I totally follow what you're saying
yet (which is the problem with being cleverer generally!). It is easy
to explain: The insert path is the same as always. Otherwise, both the
before and after tuple have all RLS policies (including USING quals)
enforced as WCOs. I think that it might be substantially easier to
explain that than to explain what you have in mind...let's see.

Thanks

[1]: /messages/by-id/20150109214041.GK3062@tamriel.snowman.net -- Peter Geoghegan
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#25)
4 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Mar 31, 2015 at 7:02 AM, Peter Geoghegan <pg@heroku.com> wrote:

Andres' wish to do things that way is at least partially motivated by
having logical decoding just work.

Attached revision, V3.3, adds actual support for ON CONFLICT with
logical decoding. I've implemented something along the lines Andres
wanted. There is now coordination across WAL records - the insert WAL
record, and an in-place update record that affirms that the
speculative insertion was successful. This does not involve
redundantly logging either the heap tuple header (which only appears
in the first of two records), or the tuple contents (which only
appears in the second of the two records). I stash the tuple header
metadata within the inserting xact's ReorderBufferTXN. This approach
appears to be robust.

Other changes:

* Significant refactoring of infer_arbiter_indexes() within the
optimizer. More tests, simpler code.

* Reworking of ExecInsert() comments, per feedback in NYC from Andres.
Also made the control flow there a little simpler.

* ON CONFLICT IGNORE is now fully supported on updatable views - an
inference specification can be included. ON CONFLICT UPDATE remains
unsupported (this can be revisited in a later release IMV).

* Dedicated inference element primnode - it wasn't cool that the
CREATE INDEX IndexElem parse node made its way into the optimizer
(where unique index inference occurs). This simplified some code, too.

* Per discussion in NYC with Andres, arbiter indexes now always appear
in explain output (not just for non-text explain output). I've also
made the changes to explain.c a lot simpler (I've further isolated the
kludge used to display quals from the implementation level sequential
scan that can appear in auxiliary UPDATE plans).

* Misc polishing. Obsolete comments were found in a few places. Fixed
build of contrib/pageinspect, that didn't get the memo about the
t_ctid union change.

I talked privately with Stephen about RLS. It seems likely that that
needs some behavioral changes, but nothing too invasive. I haven't got
around to implementing those yet, but I think they're about to make it
to the top of my todo list. In any case, that is still split up into a
separate commit (I anticipated that it would be a good idea to do
that, since the RLS discussion has yet to fully settle down). Nothing
has changed about that commit, though.

With a concerted effort, I think we can get this over the line for 9.5.

Andres: Please take a look at the logical decoding/WAL stuff. Your
input on those aspects would be particularly useful now.

I'm now pushing code to github regularly. The feature branch in
question is: https://github.com/petergeoghegan/postgres/commits/insert_conflict_ignore

Thanks
--
Peter Geoghegan

Attachments:

0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch.gzapplication/x-gzip; name=0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch.gzDownload
���-U0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch�<ms�6���_��'�����w�����$�sdW���t:��$���������.@��HZM�f.���X�;@@�|oV�.j�Z}��Ns"'F�0Z��U7�Z�9����!�>z.��%��N�?�k5���������7���p�tI
�Y��m7[������p)By��,A>
)-0z'��I��Z�V+��&�J3<��o�G��6���r��!\
���T*�����������n��Ba4�p=�����oE�#V��8�S��!B��nb�=?���`)M{�\�"��p.A>�7)�9]��Lz���`"mw����Ci,l�MR#�D~��gh&+�E���Q8+	���N�{P0=7}a����3��\����-��DT��T\��Q���R�sX�g�B� �Bq�$�����^�4�����F���{��O6
c�(���=��0Bb����c:�����v@"*��(�g�+?(����tV��<Z��QVx�NQXq�`�-x���_E+��G,���b\���N}�R[����`����t�rXP���p8E�X�LH�������3��JK�
���(46��%{����Q�l`������<�����3$��U4��8�{�-`I� �����PhD(����9R(��)����<�m	����H���"2+4~��96�Y-\p��*z~A~���L@��mD������ P�����7�!1��'t�P.��W�/M��
�D����m��5$��FE����h
��1���&��C��,�!<��WNh/�:�ne���t�@���
��YHUW �2�`�kd��gCV���������:��$L�"��a�2�,+.��8�/)zq�a��2C	GN�\�3Y)�����EU�X�9*#���+�*&���7���8�<S���\����!���������z�Zr)�@���w�����X���
����qe�wb���qy��ur��I���X�Y
|��-��e9�I�R����]��-3g	-�-������w�����c��'���/�U��hIc����yd!�b���1�,�G[f1N�a�#+��u�^����4B\�������q"L��VUp��R�\vmN}�F���d���HuMB����K&5���r�f;����r%���*ts�}���a*��=��B?z�v������I�KIm�=72��/f>e~f�n����E���Y���%kFn�@]&:f�����8Y2��b��7���|�e��� ������F�T� ��c����3��T�X�-������kf������\X��NG���\��_��/?�s���+������B#��j+�|��}��zo;���A������!���A���!��k�<����mF��a&�H���m7C��2���K�lvU���������M@6}�{��nf�Xm���^�����k�4����L3���������v|�M��q9���fw���qWh���Q3�Q��X�5�|���9��oY%���n���33�s�Bn�����2V�����v���&��W�B
���-I���U,Q5���O�2`D�_��K,V�?YM��J��v��� �~��q��w3|�������Y�?2`�i�#�	��
�4��t�T�)\4CY
��s\����M�#��W6h7;3�x��4��<��BO��,����
�����62cc�lJ)T��!'���1�>�M�+�I��1n�c������ln.^�q+��2�U��]��_�zG\��/��m�d\���%��~���I�,s��t{xrh%���GZM7�]�xK��n��`r�&Ar�6������K�22ps��j�t\o:}��7q/�Uq/p�2�{�jZ;D�R����(��W��d��������R�%%� �QX��s.��#3�NvL�kAV��P�6�M}�=���N�m����2��Nv��t*��lp��� w��O8xe$�s3�.:-�;B�Vv�k��\������l��"��V/�{;��mam��%������<j��*aktc�����K:R���j^X`�EG����O1�=�~_R/r>�}�y?
�-p�6�ryf� �/�rO^�(�w#]�h��f�b4z�I�������{)�i���w�A�h7JXq��&���0�X�.�����������f0>��Cx���z��s�x�Wn������W��$0��?�����C����v�XE�-����8���$��t�}I\���+kC�G���_��Ga�Q�P����[����k��(
�3������c���P��V�WA����c�����nT�I'�_K&���OJ��"�
��N���I��^HI�!uSp���>���|�3����:m�'k�Ri�z����p�=in9��X���z�Y���y�?V�?���\\��@}�#D8��	����g���*�!�w^^*F�Q>�9O�rh���_
�#�_��|�7|(�����e{=�������E������x���`H����,�$�'zK�/��W�k�3��^H3�[-��HE��s�����3���
���n4K8-Yk��3���i������|F[<L��B{��9���q������^���/������*�$���-�[�[��@ =����F�e=-��#U�so9�y��0mw�
�����R�K���_����z��������|���]	/�G��CM�EM�G�o�'/�h�kN���V*��>����&��VpI"3�6;%����r)]�=C�m�I�3T�8,����h2W\��}v���V�`r���yaI��xx�3����*���N��+ C�����������&���o�Gi����Fk�y����J��$dDk�'�WK
Q~�jj�A��������#����s9JOig�����x��w���#�����w@����C��%�Obw��	�+��0.!��Ln��jdM���~�X��3z]�|�lPwc]7�)���=E��`K!������wcd8H&BTt��iT*������=3�d��f#q~h�;���js���j0��ihM�f�P�C���h���h����\��px3<�d����h{jK����y�
�1^"�A�ot~u�$�����0�b�� z������Y����a�r�a_w?\�~wx�������S���_������6�u��
��q���5��F����^#��.��~�J��oIvO��|�5L���
�>Oi�#=�QP<@��<C�P����
L=\��?`���y�z������6w�T�v�����.:��s�{q�4�rd[f-���zk������L{38R�����4�����w�vh[�#�A��L��]9N�'r��9T�ae����!+�W�P:P4;]i�����)Z]��G����� y�nt�d�k�"BDI�f��}r15�P�����X�����?M����(A�����N�5JR���InrR��������U�s�k�d��b\�i~���D�MQe�C�u���SQK�&�`����Z��|��rkRB�W�]�E�]|���#5^�\�������a<����ooo����aT� �"���'U���$v������\Sq�����pw���8�����+���7�7�(,�x3=X8X�QN��e�
X�s8��[�����������~�\p�X�!�8����*V�����X�F������L����X�B5M����p��VN���X}KZL�[e�q*�.X��!�f�:�M��C���<�O�l��������U3��N���������o�w�����5R2�W��6��\e�9��Vu���c_�����g�)M:*�����zO/�n�/�[�&�H��2��7�-�=1�NC�a{��r,0�����lE�Q+g���w�GB����|����4���^�-t�|pI�O�v���>&����=����G�!������Kc8���`7�c�����C���Xv���-�8K��i��l��
��z��z�����[0v�����'~���M�0[����T���l���c:�2��g<'l6�����������3��������7��!�����S��3*Ve*�<�A`��S-4�����[���|��v1���:�e�T����h����)Z��sa1�L������
���	������<G�a�����u���1�._N94��:�LY�i�g�L��9u�\�Y��N�gL%��bl�O6�$lg�T��&b�>@'��3m>w�=��5dt�N��)�SN&��&�N�=>A�k%���E��8�W�g���E�g���Q�I�cm��a	��T*Mi����c��R�x�����9�j4����Plw�������E�6�����x�|��:X6�����'����M[�T�F\����z�m1�T��g��D/��]�Y+��eN�b�D��Qh"��e�y��N[&��?*�����+���\��5�����a=r-�ceD����=e.�8�<CJ����<�F4�YS�5�T$�����cDI�J��k���.l>Z�����N��ZP���'}�w�qH~z�����&��s�t���J��F����AHep�O�/s�Q����==5y *����i�����f��wI�h{�'=R8~u��x ���)mn�@q/��u������7�dsc�.W6!�K��>1������n<�8*�*$��t�ty:��@�'}�T���v��)DXy���xP���L�G>QV@;�T�ViX�������fYT*"5>!�X^������I_����C������u�c����&/�h�lw:�v[T*u!&�v=�6S���g*'�F�#��6
�>�?��������>��{��Oi/�0%��oxrJG��HH������<�1
P�>�O��J���`���5���k
��b�H���G�_(o�_bq �l� l|��]�gVozX��@�3F4"��@�l�3'z�*����2:�0X�k
���x��PDDm�^���1:���z��=*C)���;����VI��6��d����Z'm���������t�~�5�6@Q ;rb�*�Z����"����^���ct�����Gk���_�|~
������������fx>���=��7���������f�?��;���������������1s���������e�blfs����{�E;�h�}�1b�M����^�4'�&K��Z��X-�h��c�7<�E(��g3vQI�/�A�8��i.���u*����o�O@�3ofPX���������\����es�;�v�0�UZ���+(�+�V����l��{��W�m�B�^�O~�����r�L*q@(�����t>y�c���s��f]�\t�?d��M��L,�c��*;�Q�������)������>
����(���"P�����u��T>O�&�j�����p���Z�x�BB��I0���pH�R?�B��~W����D��_�
��2�(���8������'�+��h�F"3���%��H����V"������w�J�t�[���m�Zj��,���9���������]y��k�)��{l�8���������sgr�*I%���RT�1I����u��:H�v'������{�>����!��<SD�a�������;�C[�����F�t�3��	��}�I���t��6J7`������'�P@��$��a�L@�e����O�d��s��
%5\��"�s����gI��d��uW��Gu����O��H���V����y-2l����a��������B�
�G�kXY��F&�9B�#�\��'��?��	~\u��]]���<�
v��K�����5���S�'_�]������[�sW�?p�G�������.�j���/U�
�����pH�s�w+�c!�2���i�W�����V�[4������G��X�Kga��`k����4aZ�B�	�:;C���;|�2��	�XQ5���|E��?��c����0���r����%����!�d��n.����[�;�]���DI;��O������a�L�8�J�O|��bi�������8�eJ{1_��U:g��x3�<O�
���4������e#<���Fr,8�j�`�0��|�����KK!����U�wN�2v��1��U��A]=�D��\,��l)����;u �����v����h�,�)J����O#�pQb��a���`^
�S�N���]�cp�X�KE���h<�q��
����Hw@��z�s�H4����nb.��V(�b���b&��5�Q�������|�FN����h~�:�!�.����M#�-!���
�xN\1bn�
��mj��!C5I@�>4����S�D�9����yCCMM����Z����i�
��<��T������z��\c-���
��
�<_�D��g��Hp��"Z8L�(LY'��f���H��d�xs������;��n��	f9J��C	��}�J9t|���HN����4�������Q���1!��)v)Gh�Vj�?����+��8���N�h���@Y�A#A�����y�[<
�+���w|��g[��^�B$K���zK#��\�4Gr�86+z���Ko�m�4���}��6�w�DYt)N� a'l���R)1�N���xOwb�����3�f�=hyg�~�!!�&�n�Sr���Q��� D�t2LG���6��3s��uh�US�;;���^]��W������;���#�)����'���Z������3��?"���`���W������H\������
��,h�z3�~�1s�\�q�#R���K�/����������L�t1CN,�,@�L��f��=�5��s���DG�ht�'
�4
�A���X�u��9)�Lwb!t��-���0�@�&����*J
��p��;�M0��{�@d+��Qe|f�����-���mk5�99�@����� �.[��Dq���!������3�Q�Y��@�O'�3���.����d�&^���c�64��kM�\��X���i;��9=w�����$"�G��a�n�*n�,�^�!�.��4��;r�����:��<����j���[��L�b�ai#�#�����zr�_8dSt�0����M�XA�^��"��W��	aS�]P�/�A�5�F;31@M,n�gr�`�p������G5�V)���s�����&4K��h��$���a_>�`���63����DYgmt%�������r�DBP����;XE	�i�7�����p�P�XX9�j�W���@��6�����G������'��2>rC�v
Pt�x��I��
��\ZH\�!��^��!�k�x{�BMbA��f��Z�[�-U��A�����e����E'��"wp�N-�~�4�6�17�N�8K������	�f;|���!����.��(����	^+��r���-���`���C��H�Y:EN�0���,��[���f2
D.<p�[�������&���I+��|v��
�B�=�a*vB��f!��!�!4�	�9�V�T�� *l�m��:�?��L}1���8���[������(����X�r�eLSA�s/�A�V����j����3�C����<"�3G�p�tx4��e(�������w]���������b~$IO��%���0	.�.�p7�����V����u�?��G��������^P#����[���M���0s����Rp��]�3�n��m	Hk����m��k��v~���Xa���R��z�y�l��?���_~�3��j��/!&|O���rDY��7YfG��m��j5������Y�v��&hX��,P�G��C�������c6�{��eK-���`L>e.�Mv�*���D6WX3S��5���M5����1��������X�:�'�^��e�e��e�L����|�k��A��:��lfCCV��O�
��f��H�^d�II54&�uj�OG+���eoV�:�������a7M��.�\�����x�����������Bk���#�����K��������N����c����R��_p.��*s�2S���u��&��?K�x���gi���k�������x��La��K���l�R���*��y�SwS2�c}_��CZr���8���c�;1{�B�2 01�����XG���� /�Z������T��u���@jT������jth�M��t8p����\W7���5	2k�kB��~��[�-���Md������������G�e
!����+A��B�mV��S+�J������x��8�=�V�g4����������-�k�X�R��5���mW�{Y���n����R��������:3(d%���d o��Fm���i��&��*\���{T}�T�4F�C��Qr�^��k��Pu�����i����-6���6�h�`����0'7����x��AY��#�OSb�]�f�;��!�d�������f����8�yD�y��5KJeQ�j�Z�&���\I��G�����~x�&�a<��!�|I������K������^�~dSM��<�>��PG�}	3_0��B;�,4R�y-�~-xv����(�q���Y�� =���y|�Z#���T����1�W�5�0e7�z�h�����)���y��]4������/O�~�v�r�?EN+�r�j`�:"�g����e��,��6+!m�hv��X���#��Iu9�e�=�M�Y�k�h�����L�V���@�sj����R�M^���������u�&��h6
N(�q���l���
���A�$*�$*��������T7��S�v4�kq�~u�Y���"U9�uH��,,�s
��q45�9aNMR���EF�^�;��dh��7�A���%L:Y����/��1��'%��%�b]u��M���O�����
:�#�b�}�9�3�)���U3�P���:=��F�P[zS��� >������[���F�����^�"���������������N�:�kptv�:��4�����e8�S��d2����������_�����-F�������%Q�5?������:���!�+�DkS5'<����.����E������������[y��0���-�yY]��=�l
NG���GG@3d����5��c`�c��.X�YQ����6c���c���."�����Z�����%��^9�����AT���A�?�O�z-��[�,�������lR����.��iW����`��g�������=Lt��������i���h���������G�o�yZ�O�/b����,dZ��M��hv��z�Q6���?�<��-C����}�J�k��	��`�%�:��B~���u��(N��~��S������~o���)�3~1���`��-���.��
g��.��������5���*)F��T4�F��p���N���9�Z*w��J8�%(��O�����k�*�X����Zv�T���'��/l#���`���-,Y}E}O���b�/}1�r����!����!������[Lc3�;�����O����3<�gd�u�;+�����mt�>�5Ri���t\�S�*�MC}x����>BQ#��n������"C�	g��LhP@#HBt�
]\ci��D4y��/��l�	��ur�����n��fNR;����4�%���8�a,�u�����}8r��~��:d���@*�Q���gp��	B`xG�v���<
~�]v?�=��==6���t�����W4���#���=�������S*�W�S���+9-�c�e��P���	�D�Y��L�*L�r������������)x��:�eVz����E�V��W�~�\��Q�LE���`z��=�}3����G�D�^V2��zs���I����{��VP�Yd�HQ'-jG}��I�\`Iw0ESJr3�RH�E�4��&��d!����1���eG�gx����L}��$�$O�������RjPcA�dp���<"��p��8�S��A~<��H��-��D:]�M:\���nf��8S�!�h�a]4�	� %Z������4q:!2q���I�-�*��6��h@����2I�;��h1��Y�
���W�s�����J������LI��`���������= `���n���!��Z`2}�;������Zn3�/n�Y�����a<	G�]I0���3���"uzo+t�mS��K&��O^���Y_:	��_ �bb���2�5�1A	������V�.����������M��Ci#��0������t�b;%�] C#n�qU�~P�e�k�E�.f.xwvts�K���v8��0��@N���������(�"�F	)��y���y���5�`����GpP�ksj�	cN��G�-&��S���c���n��������4;Ggg�_N�O_�u��g�#������X~K�'��M>n��}��+������m�d����Y�����tw�0-�Yu��.�� y n�&j���&;@X�g��Ut������;v�_u~|��M������I�7z����@��	pX�������������]�O�\������~���������=�&{�|N�?ni�����wT�l1p(gM-��I�a��L=����$��ck:Fp�n�W��2����hcq��/6�.�f��������\�jfX3�<3���x*������D�SF��1��*4�����EC����.�b+�\U�p���t>��'��r����?=����j6��d�!I5��@:)i@���st+b�#�L(�KDf
'N0D�ZQ9~`Dc`?#��?*��\Hn���<N��[�T	���7��D���)�9�����sq
h�@���x�L+�<����[oQ,&�~ |.�������- �����eqU��GU'�;R�U%�����x/����Y�%�e���_�tF��I��|�C`LY�i9���w@�@{���G1�f�����pG����!�}��C��h�.fn���U��D�f.�u'��S�Jb#d�n/,qk!����	�?�+�6�8AM���Xi�+���C��2A��SxP1 ����t_��[[�x�c�����-�I�z��v��m��0���X���i��*��*���������+>%�B7SHM���	�F��l%g���������M�� 7_����e�=Ds2������x��u�����F���>�� n[��y���@�a%:'��l���iZh��EC�3���%���^c�����n5����F��e�n@�0��p;�w�@&��M�N��
���������f���v�>^o�{UF����>*���%��_���e8�d�b�z�&"����%sv���	%�B"�A����@
�[����hx���e�9j���8b�l��i4���?�6QsL;�>�����6M���������[
ats$\���-a�b��
eh�|/t����)N#������M�5���)v��b�1V��~�>�c��'�l�Q����W�&D���4U����g�u~��~���Tz����,:�YC��e�'�4�C��X���l1�a,)��w����P��%�1g��:���|�x%��}&u��������v�����c�xI4��������������Q3G��7l����=���2�zj��$��^G?h����(����I��B�:T�z�XV��Y�a����fs��,1�9>=��{1?����k��7���fI�:�O'�=q�I��^�<����\����5!�R�Jq��J)Yu�"�=���j���?,�o�)�rp�5��3G��:W
3?��������4����V�`��8��$�����,H�'��	U[�8DV�#Vr���f�H2�&3��@cNOJ2|�4)xD���CL}d�L���u4�
�t�6"m�����"r�+����c*�Y�
�?���@
�X��q�����kFiB�R����O��5��~������kGft2��Ss�����\��j]��n���MU�2�*S�K��=Vw6���g�����������l��g��9F%������la������������tY�)��4��4�b
�����,�6z�6{?��K�P��+M�5��������K�X��L!��yu�(%����C���%�@T�%�5����%
���2u7�?[b;�=�zFyc�d��b������K�X��b�z�R�_����T��x�6!�T�J�������4I`&8H���}5HH`�
"���VoVUy-�f��x������A�}`�����9�=��0!��y��G����x:9Kn1��$�'� 7b+�Ql���5.L�#�[(7�;�Ufg>�6�����@��"��^c�����E���\D������f�('t(y�wv�=K�U�2�(D��v����D��'��`V���x++�|���V�jEq�T�%��uQ��>�>�<����j&��aj�������$���g��^�� G��
E%�H�������cd�G.+�:L��d���&�DA�-���O��S�S�!��K����;#S��7�vd#
P>���"`�f��N�i�Y�Cn����'�,#�����e_�3i�V�d��J��gLu��_���&�4�5����2�apt�$�Q�4 j	1��~��0��	�sa�B�����
)<#��b����I��B������B3&Nk��������Dq��`��+���D�D}��W6��_�O�������a
*���S.>A<�~t&���Y��
�G �H��B1:��8�S�p$
����
��,�[���`^
�������0�3�G���Gp�<2nBCmf�,?��EZ���Lg���`|���}�o�1�&���;�E�y;7����S�9%b��t�9Y������Q����������k��l�K������TDK�����8����Ouy|�B{��Zy��������y��MY��"b���"��"V��B%���J
�����(�����
�\L�p���7 �]���������:�^���C�F\��I�*5���q�p�r��1�D�Hjy�9�2K;������i7��
�>���]{E�b1�'�8�a7�$��l/8[]@�P���i?+���yB��"]6����E�����S��r
�����:���������R�6��oX�L8�*Qt
8�8�-Z����d�J���.�05W����;;��u�e1�J��
�y��[5"��\,rP���d2�������4�pm`��|M(��+x������s��[�p���n5�{����c
�7�F#[<}�^�L�[Zv�L|���b���_���B�'Y,��o�t�e�{X����X�"U�K�n�4��4����n8
`��B?�k�XG�+�Uzb��o�+�6O��"�}ko�[��$m�����T:)��e��XNaA�Z1�+ZH[s�$�V��mq�(\w5�;����W��� t���.Ji� ���Tzzq	����7o:W�������y��g��h���?�lz3K���0��B�-���u�C/����O8�p�k�����0��?U���''"L��t���x�)��?%����~�j��r�D8��W�l�<��X������+����@	X3B���r���h����`���������_c��1�l�H�@���4T�f�m2�PW�3N������b����h�e/�B,4��da���e�D^�/N���O�
���������<����d��Z^�����5=�����
�6W��O~+���$�hi���'f���`	���4���D�����~]-��2��X�5�g�QR6�k�i���T����9�9��'�O,����j��l���C3P�Zy���7sc�*�u����v\0]���)[w�8@�l���.V���}�t������QJ�M]u�N�v�8_�dh�%���0�V81�2Oa����:�Fs������Z�u������.�{n�����Xb�D�qWpn��K�^_�6���=���{�"o q�Hf�n���&��&��=R������!~���QS4HK���bB��2H3�5�x�V�+�i�����t�.�U~e������Xis���\k��-�"�3�E��1:���������u�K�`sY�&�%G�:N�����L2��!��+ex�~����Ff������w������#�,g�A�b1�^wY���]���h�iVL�#�i
v��t��4O����s����]����>�����T^����t/���Pq]�S�&�B ��:����u���J�g����F�j�k2��K
CJqO����#�^����0|'�����$L^����l�A!~t����X#��mc�({�=���O��~~����4��z'�����^��	bn5h�H��R�����-�~y��J���4P�������Q��6�U��5���d���(��O8P���&s�+�xC5��S�q�����gG�>O||��|U�Y����if���J�@8Z��'���s��w�����[H[�bW�p������H�XqX�,"93^�3$���i�[�|�Y��k���trG���������A��������~'bPC��3�����Hn���������^�k�t.�'G7G�������a7�����qH�
���[���.9�+�a�^�p��rk�I4���.y���;�������5_G��D�,�������u��O������0f^��6��h9��R�W����:���
�D�u4?�>�pW����j��~�p�]���N%�
+��	r�9+�������*J7����$�z��n��:)9�G���4:~��GM�WK���H[*�>{�-��X>]�RU�Q�D������Z�g_����eLs~T��>Z'
2���hL�!J������	:�~���aw��[w;���M��\)�����y(z�D<����e���S��P��	�t�����v��za� ���??N�l~�,|L4]��������r��+��r ��.rB����� �:��G����
�+����BP�Q���=er���A'�he�
+�$i;�hb�A�����Q��{�s����{Q8b&(\\A�v)�5�F�)��#8[���*�@�j����}�\���^9�
�2�g	�s$�>������T9�4�|
)��oAIT�Fzr4�\!��`�+����5�}�K�D-���e�����>j|���v�2�;^l�R�\����"�z�%x����u/�Y��F'Ia�D�W�'Z������������5`z�!��;z�
#;%p��+���-�2b��?8��^����l�c"��1d������p>_��0�L�\-f	�2��k�/a�,�p	C�n��-�A���;���f!��5W�	f��l����[�u��!�k�����C��e$��E��\
����P��+�S���8�Kq��r��)`��N�����f��L�,$+�!~�U���m9!�%�3������|Ae4��k�`��rl|�������p�^�z�F��&�5������W;�G�$��������V6x��y��V�s�Bt�Tw�R�gwcO�e)��'�I�K�\�H������m�:=
M�4+s"?���GZ���gN�v�1q��m�������C7L(�������[PS��t�t d>|JW������~Y�$7FLT%�����j����N��|�qB;�:�Zw����q��p�9;m��,"�G�lbe=\��*���W������E_������T�)%�fw���[��p�?���Gk�v�S�$��}D��J��c���}:x�`�$sG02p�G�O����&���\�cp���@�t�J��_�V�����e���U���K�c�B��k�4���bp��g�a���P��ub/���^v���9J�Q*�`��'���\2��)a��C�H���f�WX�\�/����-�:��9$�G�#5����`*��(R��,e�o;���G;g!����^9��!����A��n��w�)��ki9��rz6��n�!�]���(�P������|����^YL���\��B����,O*O�w�X�5�������dTyj[�5,rq��!�]�pC��{O��s
y����y��@������9I$}�$p���P�[��O{����}�l4��0�s8�!���x��B#JEwM<g�GZ2�(��O�c*��f�	��,�u>V�7���=O���N��	���	j����5Z��H?�*l?��������(�R�(�,��O��w����H~y��
���Nr*��JH��r�?�&(�b4�I����2�2�r[���,��1z �����`��:����`�����qS<\V��.�����(�*���v�I�1����,��%����-�X��w`�S����?|�D��`�\���T������
'mC����,�����>|��KegJN6L�L
��t��k�E�|e�f4T�&r�3L��1U
y-�n���~�I�i��qsoG���}�6	��Fx��X������l,X:��D�;��	V�Yw��4QIo��������fa��Il"��)���������k���\b�� �b
��<�$Y��Z�;��j&��s@oi�j���#�J2YB�����������j\�W��B��j}�4�o�fH/
�2�d��w9]AV]C��qu�-�
W�)T�4q��lx��X���k���%8}�&���������V#���1�^C�yp�(e|�9�D�����p��.��\���~��|p�7��s1�O�r�%��U���8e)9+8i��r�����*��e���?f���o�o��^]�o�C�|9����h�vw�V#?��!$9��{��Zp��X�Wf
0~9�B������b�QP������y,>����Am���l}��#�8�;����h����2T�z��k�����M�'���E5M}�"���cD����]N,�>�����|$�R�r�!8������E�C�OJ�����h�wb���~���^-��TJ
����
v��=�US�����@LfSI��b�3q��6|�Vl��.|d�;����������0�p\9��f�)�k2������j���5b��#�o��^��G��bn~���@&WK���m)x�xw���NZ�hv0~�W�kzdP:x����g���1����L/9���N�)�b�������$�r�Ki���Fe�����:����}�
��[#7����{0td�i7��N���
�.���Y��"�m�E�"E��&�[���-���^��j�/L2Jp�s��)$��|B����]<`�t�Sp�0R��3q��b����=4�_��I{[	�6n�w�e�
.c�>���s�uZRU�=��\4�������o� DB)��u���b����|��p����B@S�[Y��p�f�������?��u��q��`E���f����U�|��P�,0JH����B���6AI<19	����h��|#F��4�;\��p�yl��s��\6���[����{��"�6Y7hW���s�te\>�Kr��F�:=���#L��>	��"��c���>a��� �i��m��dp�85��5���#�%KI$�{�\��o2�R�����F��e��)���`����@��?/@�wa)�����,�E��Z��#T
��5I&���|�f���a���f��8�M��/=�U�8``a������ewytusztF����>|Hez���
������d`L��Ti�I�4F�m�3����mz�X�)�1	���o���o�0IW�77��A�w3v�+�td���$>����f�.��F�\���;���+��o4�|�L`������Q��{|����]n"8{Xj���Uw������&A�r��0�*$C87~���U��l|;�5 ��R&���=���{�z�p':wVJ<EM��<E��$���nF1|[��#
�)��AY����v&[�B56����/)s��/������)p��j	LsFm��p�"dp�dA p�����!���&4���#U2��s���ro�e�����.��dL��(���#��y<���4��h�sc\��e���7��3������:�� ��^�&����[�����pw�]�w��� *�������-A����K�?9���"
�Z�5d��It5�X �a��]�!��5���^�)��W�,��7���_�����^T�$���.��V����C������v�b��A�����Y�/}y�(�����A~��@��"Z��N��h�\d�	:�@���X*����6/��Am���2���l��p�a*^�h?�f��hP<�,��5�����	�*�t�M����A]�+��g[U��nH\<nsB|��3����7h�9z��d`�b1�dF.��p��	��t["m�P�uD���1�z`�Of����	h�������P���ptN�!������H2���\���|�Pl��C�E�j��V7�Cj��?�cErg����R=�E����`V>F��rf�R���?A�(U�Wl���Y��7W�N��m�����������)����z�[w�3���<�����)���_��tR���	���/�kh�&o����^������`����H�Z���`�����o�	�����aO����DL���IR��p\�8�u6�n�&��qc�.&�V)%�,���z*S6���H������W������s���O�r2���Y�x���J��nF?�d:�����^��n�7P�I�����J<L��(B5�W�F<:�i?D���d`��4a7�@�������m�E#X{lY���h�����f(�2��0����9���!g���S�
��fE��dE)am(��3^"�p4 ����������iH!L}�����<���D��zJ-�dB����������R�P���2���W��L��/�w�/����K����s���%�+1���_���r�@��3�T@x�a�h��A���l5v�k����
v�1�94I��1�������f����N=rdO��	������7��V���t��v���J�"M$p������/}����xm��ZZ�����+�
��5�8������e,*c�& ����`7�5�K\�[)X��r�����0���1z���_�����w���=����*DyWs4�4����s��I}5�bN�0���c�@V
8�Z�
�^����Y�,�Q�_{$����X���K�T���)���TjY0��}���2��M|��Z�z}���\h���t�R	V�J�6���^��%�
�����:��26j��,���<&� ��H�g1�`������$��(�r)�X��8�|EUbmjc�V�lN������8���~.�\�7���3��0����($�3��������Tm��Y�9���q��D��O�D�������K��~yv��N'.��{��+��/� 77������e���gZ���"������u��s���s\]�N��;W7��{b!����W_V�;�}����I���g�?Y��k�_r��tD!�_���y��S��k�N4w�
x&����<��n��2���n~q����o.U�"���,��&��6��������r��0�i�%���P.��k[�z��n��E�m���-E�L���?����p����P�BS@��kX��9/0|0oTb�p�x�d���c���?$��tA���o���l�d(M��A�x�x��s[10��&�!��RJ������������:�#������4d9[��p�	�F�Ft��h�X�����V��r�������.e||���&����W���x�d�4���8������-�Y��6���Q��Fs�Z+�����,|>��
�W�h����t����&�7�~�ID��!�=���5���%j0`�M
�N�v��eE3=����)����	f,d�� ��2"�n�����6���Y��������yR���+��`tx����&o��b��@<�����������
7v����U�c~,�- =/?J/���ZM�d��$U����<�<�(J�@6c*���~`
����ME�Ac��3�)�D�<����<��4��-����i`��vFi��4��Az�;.��N���z���|���\����hT�t�����dG]�����l������l��Z`�����qmJi�x�����d1�l����666�{ w 5�1���G����P��^c��k��}!���5��mb	1��H��H��}>��#F^�_���M#�.���%�%�L��7@����������:m,YT���2��O`b�AF?G������@er��e�E
�Z�`Ze*���.K�������f����z8S���u�Y�4���L��d�	�^���a�]��$U������K��Y��?�
�G� }tF�C�56&����`	'������[���o�v�klM��%{�-�a���@�����F����T(�5�!%�G�"4Q�$vqzr��������C)h0�W�	���i=Yxa�JTL���������/�P`_���q����iv�`�������P��=����'2�,I��Fw����Esp��8�d��=[�����ZxI��O	��'���#��.�q$$����M�Vp�#��R^7�}@b�4��_��$yE$X�,iLP��1�� �'#�Xu��uZ��l��`ia��:&x��J)g�����k�R��Di�K`�'&_��y�����6F;�0�+C��d6��$h[�"T��d+k�{}Iy
$'k���	�X������h
1/����Q�����
[�k�n���|Y���h1��a39k}�l���<GOS�J���E��p�Z�v�C$��sd�KG�z�|�R�|�+��e�{��9�F�_L�.��S
��'[789���L��l[�i�&��8i�lBGGw�b�*����9:�9qq��6S@~��gX3t���������)�)T8�54�4��
yh�������S��F[��=�8���34e���M�1?M�%A�!�{�g���c"c���&�+��|��ARP;44��I*�y�C�S�([%��
2���$�d��x:e'"�Jx�4!��v��c����%�
R����E�3���,������[J#��w���M���'L2��=T���+�&�mB�H��f������p�9��9Oo�,f}�>���}@W����uf�����3�UA*�6�T��jo�bi.w��HO�)1�����=��a����9�*�I�j�'�V�Yc/,��F�����
�6p<��E��KNws�)�FI�	5
\�A��(9����N&xO���=B���������:��h`W�9�H�7�~�R(Y�9�%u��}���8a�)�N|{G��;��@�Y�}"�������+�GSMANa��.�����j�d	�M�����"I�������J�A$(�\�0�������(�������QI��g4�1:�1X�|���O��I>N�`W<@�C��C_��lh�p����j���3-5[��H��^����(���)A"�_��B�n����w
xi*��t
��:��*�C�_�b:����SI������l@������A�:/�h2��
�a4��-�8I<F�S4�
j�zL��oWPr�-�n��K�����������A�O�Mi3=zbHDO%�o��7��~�&�=�an��tl��Q���3c���2��ID��y�`2�����)W��<8������1P5���,�E�9�!9�����	�)N�|���0@���E��y|.{O�0_L�
D�O��T.�Y�1��8>Z����� ��t,�!���?�a�8P!:�O�$#�
���|4��!o������d�2��[ )Jzp*�w��v���=�����)6i�n��h0���7x*K�l1�u�
�>f}s����
�������������6�n����t�	:�e������J�3���0��	�EMI�,��+F'��7�3*�+�CusU�������\�����y�J�t�~����t0a|����]�k@��������M�6������y3]L����l:�8P/]+��f&u9��Nhh�_$ �1Sh��&,�I�P���������?
����pH~9:~����f ".�~%�9�OF�x����>��62a��1J�n�ha�duR�/YG<�(g	$��Ms���n��;t��|*d��t�`�m���uS�v����d�(@$��������i�y��8<O���y:��n�Lz����6n"j� ����p`W',��Z��5���aA?L��a��
[n�����}���1�16(��)���0`���P��)�Ep��)f�t�wJ��0�yA�s|3���OT�%~�g�GWW �=�d���Dks��f�N��E|��a��,.�:P�;���rU}�g����	X*"V|��(��b���O13��^�M�&\���[�g����>^�8��0����H"�}�T�2�����"�����������b"��1]t0�Q4�3��=�4M(��oQ���Fv<���0CJ���54}���"3yA	�K���{���p�,�n,jc���)���C���������(��f�
�zT�3����3��5��Dg�\3���&%{D�?��|�j����h}g�xU��t#������s��@@x���(?�Fs���sNp�%���R�,�"3��5�#�q����_U8	�Zxt�^5@�d���@fR����hz[��f#0�����5����������u�w%��+��@�������#��2�DM��8��H�S���� v�s��'@4>��>.k41Ga5����i�'}��Zx�Q��M	�2J[��v�u���m�����@�zqs*_���YR���~g�p5�"6�V~���T��h�����*��k$�`�U9!����VA��i$��;N[q���i��pP�.�����A�'�{fl�4`�n-�����m'�|l���Dk�x�-�d��������U�os��5�������PQ>���~p0�7;�����q��T@^��"xq|q���t����������wD~�o�N��]u\��R�8��l��U7��������.���j�j<{��'t����X!a����H�`�=�$�$+���d*B��b8��y����d�R��x�H��9��;�<J��f���&��������O1�C�FjD�R���1��ZT�'����I���������3�p���	s��-�\wB�����~a�"0S~�2p����d+���-9��h{�5��r�~5i�V����o�g�L�&l� H�(WB����'[����Q�'���U�>��[7��w��%����d���DQu��o�
�a��������L�6��k�b������������V�M�F����m�d��_n�^������6���>���6S=���������[B29�=pp [�������>H8r�
���6�BRnmgCS�\ot��pcY�l=���9����l�2���Mm]yq5`�Y�������
�oy����&C_����������Fe�u����[�86b���`P!�}E����G@}C�����B����39��^Dz��JKnG[��&������/�G��@,A!����X��f��I��)��
��#���1�����<d���q!7d��d��
R��
.d?d�My2(p��f��xEn���0vqc_
U�u�v�7;�lh"BUQ<G��b��^��B�����3������� ��#�r��cO�M�\�
;!�G���|����E�B�@la?]��}U�z�K�	��*�[t��)���(D;���b]��wd)	����<��8v������9����^+2\&�$
�)��D�t9�c�_�9�(�AG^S9��l��G��&`H���"���=b,���K��X��F8�"����\��D���;p�^X=��K��\-��.=0$�<�A5w1���)0%����c.���/�9��+ p	���@�8}�\����7ghE��f��������s5����e�Rv�x���&��_�����T��i`����?S�Z�t��%��<�~���)^��R����p�z�%���*#(�������\uN�GN��p������f#'S��a
9A/��]i�H�d
C`(Y����!(�of><��������f�U�#�P�G�{��/$u��m�_��,����������Y�tc@��8���9��=^�������=��5�7�����_=�L��a����Z��4N B�����.=��������u7V��\2�x���X��"������L3��V����?\���e0��E�i����q���[�x@�����B����03!��a�g��%Y%�Q���r���Y �W� %����>3C�5��o��,DeI��J��M\�q5l���:��Y������nm��)��v�w���e�q2-r��)���,�q�&g"�Fsx�c#�>�����^�Q�o'�X�� >��@=����sqj�_����;9!o��{���)���7~�^�WjW0�<�
����Z2B�
95_�%d�-F1��u��Y�BW�j���,wSZ)�l\�c2f��N��y�:I��#B���9�d]���fp�d���R�����]�Y{�[���U���"W4���`�p�A�����,�T%��$�3�lQ��Fcw�����C��c8
Se��_�I�i�k��q�3���M��`�R]����
�
`�?����`bY
���{�R���.�S�����F�P�y���A�X��L���f�-��e�`
�t�
n//�F	�8./o�6��t;�k��/�<�W�/Z:�h�)H�6�v��
��� (h���A=Fy�PG��K�ja�	A�{����AE��A�K�y)K���N�7�1�+�mZU<�"�\���;��dn\�Ig��c�MyK�y��"*Q�_�:���Xo�������4w�Q��IX�6�6H��}e�8���
zZ��B���'>�`.�b|�/�V w#id�p��*��[J\��Wc���S�X�B����*.����L�WT����|xjL���K�(<���Z5�2���(� Ef��	w
�C���t>t1p�������������LA[!z�a�0=��l�"VF[���\`���uf}Y�^�s�T���}h�ra�'s1�,���t��r\,���q����p�-�����O�K���WW���n�5�Y��@,'��l�)�l+:��(�1*��%�^6����`F)��$�9��$�D�8����a��������k�}iN���5Z�r�r��5=7 ����}��&����9%��K�U$(�d�LH@���� �I2������Pr����r
)�(<}�A���R5��#��[j�)��n�
*�\�jZ�('��=��e�?�����ZG�a��#�HI�SQ������t�/n���.//�n:'F�ft���Wr�1���B��~6d�a�^�W��'���=B����x�w��������>)���1n<�t� W|��A3����w
�|�)�3_�\���Y�����:�~���Q������!7�!C��u���f���&������kaq��fo��om/	����^�K��^���;O�
�z8D�������*����9Dhf���J�E����{���[�O��;�]����#L!���W�EV��`�
	�JLD��
�(Ep���J,�_��4�����I%������f��N��k�����[U�} ��}`���x�uF���`�}@\�7��|������,g�;�`��D�W�[��r������Pg�`_
�Q*Y�|A:"�������9��a�����u���z��fu�5}��^(a�s(��|��l�0�q�tn�A�ta7ME�/Y�01����V��n����:W������"L?)0��Hjc���2Rs�V��^�w,F��Z(t���������3�l�s!J&��{1�A~��T�����\@�7[e� ����)/U<��"����M2����<n��
��PkR�31h5��v�3���1���F���JH�{y���IiAP��vQ5��2�(Ki���X�;�������u������\j���
�+����Q�a�FEI�8�#������i��Fc����TF��A��x;g�hV���2��s�HC���G��Q�b���b�~"���|^a'��s�C���������C���F[���2�G��L��vKs��_��L�6���f6B��K�I�Y @�{����S�.��qx�5�J�$��\�4�<X��U�!5�Yd0���LBT5`�(7�#����P[,�X��1��TCxy
����s���(�%��rTM�.P?V�?n/1�x�&�q��]�N����=Wb�Y1=�I�Wn���J�S�)O��������4��#���tdN��CsK���BO(����b�/��mo\����<��d�*�PP�?��Nn�T�@�T��A��~_�5�$S����Y0~�;����h�\E3�z�E_!���*��8�=������a����v�y�[����\��e�\�&i��;v�[n�� ��Yu[~������f_�A'��_s�Q�p^&����dD4���w��&\s=��H�)!�Q���)<���Gu��8_���/&hH����fi��d���������^>q�G��v����M�.l�3��c����K��>�����T:M!����
����m��%l����
�r��rw�lb
=��������;fp
�Co�&|���f~��g�z����>e��er@��H�@r:��4�IBSJ�$�,�:�>���	Hcx+J#�/��6����&�3R	��\����������J6r�����8+���n�!C0lk�r�M7B��sN�&�,B`�,��yIR�y�R$���OA��f��
�98�����tZ	�F�D�1l�@�)����>���j������0=^8�J
�y��AV
��6l�M�2�>?g�P������n������r��]�����Z�R��?���b���7H
��M��cTr#u`�>��Nh!��S�j�m�%b|s�G��D������J�*�3HA���b�Guy�%��2A�����c�S�Ox���b�M�V��X�
(��I�lC�L����X����}$aI!D���{!�!���v<�qT�k��F�����0�1Sq@���@Qij3���KM��H���Bq|�`
\�����F�D|J�)�a�c����{���h��q�@���fC�g��O���
c��+�s�;������3�������	&��WBS1�|2��h��
j3��X]		;Tu���/E�F�����)���/���>�m�
�Y����/�!k^��vx�(WGxZD4���1}w��ef��
H�x�dlf�tq�,����7�:�����
��d�&���]Xc=���Q�)���b�~d��"T"s@bF��#KZMvZL}!!�5;"��#��z8g�L&��z<g���V<7=q�
!��"�\�����Y^8�@/C2�U�"�*,t���OKb����}�-���Y�����f���P6W�?k�i��B"6/:~a4���s_�n����:��w��)�C�!�P]s�����mi�.�K1��;,F3�1>R��cD�eJ�������� .��5�`��c�N��F���d
[2���5�WC x���8��XHC�q�~O}m`�zk�jkK��KL&�R!����5�����T����r�	��������)/���|_����TjD3�
[*|DaY�"`Qzh�[����L�zu�����Y�!VV<5��^�A����}���+#��}�jy%�+vf������|��G��3���0X�:wCT����|�8��	+�u^����<4�x������F_�*N,�HRt�X��6�1�_���[+Q��N��&(7H�T��&mS�����|k�(�` ��i�h��Zn��H����gX:�%���^����F6��0�MLb��NNxe�M���������-���O �j������H�,�����PGl'��D��`����*��3z� ��&�N��S��3����0
jR~��]E��(���E�M�N{��U��f��!R�O�Ur�%�/%@)���`����w���pw
��L;K�R2%i�6)�<����f�y�����sq�4�(�9p���&�\c�	>a���;W7g��B6��u��o`�����g)����6
���B�3M ��
(gY�/�'gT�tE��X/y2'�����0<����z���o�����7��
��	7i�p��7���cX�00�����������p|;��)����r�-��:����6�/���~)|	���(5vtCek?�K^d�E`cW��E}��G�K��pb��>����� 1	�"�	|1��������Is���lr�`n?�aAW����H�0	sR�d0�J���_�� 4jPe?��)��U�)��"��Y�������]U�������B��	q��2@����[���C�n(a"��V�3�w�A��K��q���J ��l3���0�Q�Ffl��krt�����\����k�~�O|bzG�:�g�?�w����@JEI@��
�{�=�B|��^�Qc�I+�V�	�Q�w����=7o�lov:,p��%��/��
T��u6���lnN%��7�E�,��S��o���f��W{-f�[
���H6x�~�b -��r�"�\�#oPB��P�=�:h{���O�����h~����������j������aZWA"���)��]l��@��C�m5ra��s�g(�<�`�q����.L��0b������M���`N`�����t�W��C��4���++��D���56��I�](���.�wp�QE5���9��O�����kWH��|J�7+�����x�I�%��UR?�i��l�����f_8��DX~O-z'
NM�D�[��K(���Hg��3�����e��Tc��Ie�|���="*��2����1����(����q�=d��p)�S��S�++��N�����.�;K�������O�����wH�����Z��
\_�FQ���\���	��m)����FQ���9\z������K�07)���OD��]P��	����h�z���v'�8@���M��j����@\�W�x���F�oH+f.��:�M/)�G9R3TPJ�E��e� ����(�w�	������/}
~vE[&��j�n����r�����y:ku�I�c�����P�#d�5�����:����c#�=��A���cBC�����i�P)��=�s�!����<~��C��-�Ig�1���{�Q2�k�����gG�*��S*���Yq7#L(�n2h��/��&�:T��`��J�B ���*H4���I��n���Qt'���;��K)�AY2�-"�>�#2=�`����9w��Lr�)|n6�[�>+R���g�Y����I\v��`r+�P,�!��P��5yk6)7`KS	��Z�}��&y��A������"�M�Ni2���S3w.�/�o���g�Rn��Y��60E8
���S��������m��������Im�D�<�1�s�_�G���dD�Y|��
��
	�����s����X�9��b��Kt�l--h^;7r�������Y�.�wg n�����G��e�����V	�g`���$T��H@��o�%A.��0�|j��\zCN`�3S�g�����
��Q_'���\a��2C�N�Z�T�8��q�����������	r��j'��1*��
��c������������z�s����X�vkM��}8q�hhNF_$7���������<�n��j��8����3��Kj|%X���<�������o����8��F�j�z
T�{�V
Bmd�x}R���:#($���XRg����qK���aj-!�J�����w�
�4A�*e�"x�z�����,$NC�Lb�{:`�)������W!u��@��RP����dR�"���$����f����eJr���(�L"���������d]P#�������!	&���}D��W�;~�Ma;�i(��N��+��Wo�(.�����;;�Z�Ud�O���]s�t�@<�KV��B[�5,���#����L0A�uW��?��A*FUi�5��s7b���>8~�d��C���w������pdrP���V:����i��%�zs6l�H��l:�1<�G\���l�_)�z!��?��UU�)������.��������.�C4#	,:�)H������?L�z�p���xp7������b�	D��<�"�G�Z-��n�rq�����8��E���O�1n2Twc����5v�1e������^�z�b{;A��hy�j�T���u_�W�(�E%�x���8�x#ywyrt���#|�l�N2�M���GSF�F���9����d�y�k��Ev�����4���������`K�����z�67�w�Yr�wfj�Q�WA����SI��D�PS0���X5�
?�MN��z��E����1&���O�
-���@6s���1����gR�&c���<u��M�-����P��K��������������P�ej|f�Y��`�p��t�:7}�<����S�M>�g�����L-�W�U���oJd���
��CU-;W����.���U�y/f���T:��}&���J��;�;�z����o����'����Fp���&������;���\���%S�����7 ��������b�8��d�� P��2�����&0��{)��j��`C�$nq�1L8y�:�������I2�B��|��?nQ�"����f�����+b��N1}����O�Cx?`#[�|+�g
���<S��)�^��5��Q�����aX���-��T�Q��C�?��b���> �H�c��_\�N8b��v�N2R$�T������*[�a�^4���VAu$/��S)�R���W	��/��/���35o��/g�'-@s��q���Z�i�S���!A���z���+�:T�T3utF��w(�^�J!_A��)��"'���It��6�8�����WB(yv*��d�(�bUI�d:���ay�>���"����n���::7!fx�`� p���}!�F������j��z�q�P��N���lO���])������W*�F��l��}��5��K����������P��^6�2�c��3#��1��u��}U0t��;���g��M���|��3��8���c�D����=SZ6��w���w�����~�����^N��m��(*$_s��H������qv��Ir����	�w%!.,q;KS������t(h
��=>�{
��+e���xn��>Xv��ts�_�+�:�����r����k��VO��!PdZ!o��]M���$��1_��w��K��f�,�t�{���s��\nT��h�"MyW	�����K���Dsi���E}r^���c�yH��� �okN���E_v.��S���L���VQ3>�+j)Ct��i��u��������d�.�q�����z����Wq�^el�W7��A��`�.Y�ukA����sr9p�nv
.�4�y��������:�a�n+VV(���+c���-w�2����1z�����'���q���i���������K�|D��U���\����{4W��*-���m�k��B-+����2EwH�P�f��]�DVw�j����nu6�}X��7���ss�r�nA����eWp�6�M�����n��g�f�ta��}����K>Mq,�(9�!?����E�������f-����������C�R�7��'�I��2o�d������G���h�#et����o��R<��|���������V��x��+Q�z,���^�}"[p�
�-�-��{���3��C�rR�y��i�O��l`�QZ6�+���m��
F������{a{��p�W��;��*6�m��t���i��
?-�084���@�"G�������������������8�2~hP.f��O��*7}�p���d��"��L��_�{�O��2�%���o�"�+i���?L����xp�f>A�j������62��=��
�Fb"�`J�t8

V�:�!-�����p9��d�5��}��s�h04C&]�<��k�lg	\z�
x�
��%z~�gR����o6�,oy1�6���������#���z+�{��qp�-���,
�+���w^{_�����SJtCMe���f�u��0��R'�#($��t�D�(P�;9�8I�'�������F�1��8l�9������G����L��L-?��!J�J~��F�fr��f��vs�(�f��=B������������h:3z%������Rx��+������B���M��@��I�	��!+Q�AUs
m-k���F��5��%1{Y��#���e�X���(�Xh������9
��x1G������[��X�6���QMfK��'H���	wT��}U��5���7gG7���tr�~��Su/{��������d��Y�*�s�����G���s��][P�����9b�9���������U�^����0mle���^��Uu��>4��{��?-�=i�of�X��|�4�_��]S9�����p�hhk�!^������u�#.�H��9~]�a����UX���2�o��/�|��`�lY��j6�������5������-�����������t�
�r�.��vp��&�������[�$��U��=:���fypc�7_5L�y\W�7�5-`���2A�9)���P|$��2��}-j���n�ypX��{�px�R��4P��p��ii�.~��w����p*��U)��y������&�.�{�q����r�\;�!�_u���u4�5�r�;T�z�kX�L�oX��r�a�2E�am�h��V#L��C-���eQ��50����Q���rS�)�������S�<=����w:g����/7\#�������c�{��XR�75��u:M���-�gQtb<r��9���s�9��������F���Mu��o�i��k������{��,���K��������������G��H���+��)�;���o6���X����>��s������f�����L���<���
�*5cld�pI_V�0����q�����j�����v��m�W�1ne��[��+��\)K���|"��`�FJ�G�e�td_*��l�����QR��l�����������h4X���-u6�9������W���W�� ���AE���u��pv ����7E�k��(.�sI�b'����8�A���F���xCX�i8K�������N2�2��Km��0�X�5�$Sf�����O���wWp��\��|�����V��5�iB���W�4�U������s�Dh�Q�7��6uutM�>�����J�Zx�%��@&���_s�mII!t���a�U������h�+����$oYi�6<hP���G����;�ec��YT��LL81���c�1�6�4�dEE��V���~������i��l% W�%�o1"Pb�4N�o�\�/S�8�����u;/�����1��������4�`�`�X�y��gb!M����W���,d:@�	V4b	�����,�]L�q����^	�/( R����C+ZP(S�����4b��SP����2���d����F{�9����
I�>�����pv�����~�P��!��9�M~e5��z#�C���,�(��D�����9�f�87mu�7�5���87	��p���O�L���98��I�A	�'��~
�mg��c��	l�F|�'�����u�qWz@M��g�Ql$���u�����bBW�����!0���F�X�]��J�j���n�di�A�N+���n�GI/�o�gM2�o?�A�q��RG����'.XA K�p�~���n������X�p��"���d��Q-8�|�5;��^O���)�Av���U�O���Q�q�W�o���`���t.��~1��a/4�z=����KTh�
-�����P�����
���Z\%��3����#T�u>���F�����St5�	N��a��%��.Y���Y�Z'��o�r�T�]`�G�<�q�����M��0��,��x>�H���w4��e�-%��|�0<��������L�2����w����A;hU�,lC���:�q:���)0��5��ae��~��C�Zn��T���;��p1�|A������`��a��T�����b�Q	��AkG�nF�E��QL`]z����cN	����9%���!�&�po]��/�����a�>A�%&��@Dv��p4�#L�&��tNB�A������J8�|g�w�������6D�-j��?f�h�����,2��{��3�V�m�Y�f���CR�	�F��4GH����e���5h	�6%b\�Y~�0}�}�Y��/���bU�i<�I1O���k�P����x��GwSdct|�}�@]&/�~�6A��Y~��7���������!�����0Xp<��}��Y�-@A�8	���!�R~�L�j^M:v���Lv��pM���&}����Id������.U)7�1��K��"mfN` �!��!�+��?pYx��]�i�fc��c��tKG^8�@�r�JfV�ug[ �Yrt��=u#F�4-�h&k�$�������PP�r��q!�X�Kx*#��z��t�Y'��&J`��d���-`kH�D<�����b�jV�������2���)� C�gI9������Kg$���c��/F�{��lL����7����\,�{�]���{�:�wNg�"3$t�Q��9S�� F�"��$���rY8]m�����$|<����J�9�`<'aL��b1<3�@�'��\�~�P�/��0)�������5�X���8q�%����b��p{�2�Y+���K��zBF���5�kg�R�`x��iM�W��L*���<Ax���Q�5�2�/^��F$@��!�@$%����qj��[��A�p"@��#bF�����C"FB���Rk�C��E���Ig���D�b R������J&��d���!���
r{�)����#o *M"�,]�6�,������H�$N����})�
|z�`�S�����0��@��!��=&<B`�RI��@<���OR"S�0<R����ix|�6�����4DmSw�����G9	J{Hk������cRL��=�e!��;M�*8U���Pu0h=b3
�?�b�[W_8=�D\2�V=�M*C[^C���wK`����0�K>����/Gg�'����wo��W��k���E�	�?��m�2�SFy$x�F�-���tVW�l��%����8�J]��j������8����#�"WD��P�x9�m����1`#>vc.]J�.e��3��&�#��	��(��������ds�b�c�"L��������8=O���E�`����j�� 
��mW�~���.&������XD_�)����
���~L��6|N�z��Lq�0�Q��u>����K�2�H~O 
�Gc��(�/�X�c���KBi2pi�~goz,@~����[3F���R�^d/���O6q
�D����!�xbn$2��M7z��- )E1/���� �'��f�S�w1����JT���b�A4d�;)E^uq*�o���M<,%��m9;��P�C;��+s�~�(�r���o'�
 ��1)���j�8'��@^p���O7��6EG����"�V����4���K~Yh��T�N��V��7�a�[:U��D�=�H����BP�cC	b�.
w����o���{m�a����������	q�������w�O��%P���m��.����9�o]�n����\)<_0��E?����W�R�z��~P�e��c�Je����G�������Of?�1���(D��$]��H�/T1R��,�i�����c��A����HNO��y�t��=�M�4,����Q�*�X��KS���\ux��
���^BI�}�_2�L�R����Vw�W��(�]Z$sH'g�r�3+�E�����9%�j�wv��t�t� '/I��@�HYB9��G�C��g@K3g$_�J���&��x�9�"SR:|p�����;G�EN�14QR+x<���+��=�:3����n��C|h1�0j�A$Yf,o���mb�zU=q��`��p�'�����{�>&r�&#=
.y���;�}���������q����h`��7�r�ITR��P(���	7%+��F7]�����eEfk��������<�M�t����\_N��R�8N�S'5��/�;$@��;��A{hN��2������"��T��B���Z��pK@����Xc�'	�6G��#�$E�fYZ34%r)g���Z�4)+X���CF{�&�,������X�7i��z���-K��f�O����!
.�K-�]��t!�5]�li��+
n��Rud����QW���=TL�j�����=����2���qk�gR�aG8��0F��(Iu���M���"c�C|K�:�;3�>OM'�L?�nY+��*=CA�Y�/a����`����v��UM��:������[m�Y�
��s�!�V�`[��Wix�0���X!~$s�r�,����#����*���c�1rC�y�g�\�P�-j��D�E4�V&����w�p���3�A�PSI�p�B/��?��
�H=��C��?�S���
�S�]���=��`f�{�i��&}�y�' v��?�6���c���p6`�+\��\�����j�MG:G0,���>��2I�����`�"��GR���&g"c����=O��Nmp�!#��������+o0 

|H��	1[��;:nD��\`�hE��
���h�
�	��h3��F��F�n%��������a3��5�r�(H�E�x�����G�6lM��3a�����P�i7Y�>_z���]"oS�^�:@�P����,))����T���l�*|�����#��N�}�8�/��#x����1 $���~q�����D��#[�3H ���pn��@[��JI�8�����`�VP��j,�9r1�:.�qm�����h4��r�6�u��~�����U��!���jbq5x�Ah�
W� �Jb�z�Dl���H�x^�+6p��i�����$���^:q2�:�*O\�����'S<}	^��	Vs}VC�Y�z�2��ZV
n������������WN
A��%�fQ5����3������j�Lt�e%��"��s����:�!����_����"-�H�=�N�����E��u�QRa1���f�L����P���"mc���[����2�+u������$�?){��6I@:���L������������eb|Po��	�:��E����������3��I&[����E�"��A����8E�i�������m��O��D�[��-Y��J���Y���\]�����������kF�h���5�@��5W��"R~���5�hH?�4z��J�wI|4��G�5��32�81,=T�kEr������?���3�t�����d��v��+-a*����[K��#dA�j�N/6u'!���^/zg��b!l���WN���0�=�f�;�$�b��>�;���ups����������q�j�9�_��i�>��o�A)�+��.�G�O���^z�k����fh�o���q�c4�f�����I=�(IR��w[��� 8~�\J>A�$��Z�G �P����������E���(M6T`��14k�����R�h��s��8������9��)�,�i7d{A�X����(���'���_�d��@���y����u�o���rt��sM��a����
�3�9(6�O1����b������|����sC!:��a��s\6����gx��� �?�qq��b���6w�p���%7�c�Tm@��n:������:<�]�����Nw���{X;��������9]�I���A!Q���������,<��<c���;r�Q-rh����s9e�q�$�z���XSh"��AJ��E��Q�xz�i�g%���s���H;8G��9j"��
2���2e��������~��T_u��feP�u�����9f
��K��&^1��h$�
��?�
i�����M�����f+sO�K�p�{�-����,�*��v���L'�S\������A��w{��%�~�R�V�3�>�Q�s��(7'�*�/f�8�K��
V�w(	���dU� ���T���l��+$������)><��^|��hA1b��;����?��L��
l��+�9�a�H��R����L��q
�Y�����1�Vc���n�V���$��CF��/���tN��y���a�i��"��pJ~�H�s���y�O����9�~J�95�"�����J#�����z{Sm���[�����W�L���+�c�
����]�J��w|t�S'8>:;�������������
�sstv�c�28�������^b����M�*0��^��_����������sh����]\w����k�vqt��>��\�����S�~�4?���`}�yM���/���`O/���tm����J[9?�J~�J��kj��*px�������K���,��p*�:��'[k����nP+�#�t&������@���"����w'4������������S����}�\���4O�]�������8>�����C��������{z�����|���_P�����c����	����&,��	t������
�sv�s'8�mtr�����9z}��]�*�����W�L8��l#��e�Q?�������:��O�O:�_`�O��3t�?����)��?�og��i���|z~��^���\w����/����
�Opz�n:?v��8}�(i�o k�/��/?�������K�| 9�~�n����oF�'�x���a�p~q�?)>��-y>��)�`�/�cN��@�6��w��g?������v~����:������	��_���;7�;�sZ��W���>}{zvtE����&15N�����`��6��]vX����a�1�x�C�?��>�B?�������%vw
���������%����qn�������4�-F��ky��m#���akO���D��=dM����[��tHtE�����h+��$ �������le��d�E[	��s�[<��u����LU���O�`3o��]��R���i�bPy�
��vO��������U��|�����y�P?����gW�34�a{��!���Z���4p<� �_j
�A�mf.����<��I�_u@�gyJV)����B���0������>��5����-x������b$�{e�3�8��1���U�I�S�'�n�g�g�F�p�U(����i�����t����;�*�D���#4PC������}h}�K�")H�V����V����b���������}2�����s�N����X2`�����[��a�����{�v[�w��{�d��C�od���y�����y��r+ec�!�Uf���(���p���S���Asow���i�Tf���Ye=�������is*�>��M��'V$px�e�����Iu�o�j���-��d�r�e�d�YDelp;���."d����������`��^K���6���ac�i��v�/|>J�~���t��t<�}5�n�!v��=�����a�.+5��o�����Z���C�"����!ux�*����4��	s
��"�9��{H$H3��b0�������S��!�_��v`�Y�j��u\�M���>$M8�	�I~��Q�d�w�y���*�
V9)�fYDOg��#��;�n�p��=�p5j��}E�Yc�W��dS:�e�j���|>C?*Tb��@�u
'����b�+��Q-�]�D���"�|�'*�H5	��������wb6q1����K]z3�T
{�;v��e�)c6��h�Q�a���B�.?���#}�V�8�����
���S~�|�a!>���W�}���1����G�����3du'z0��r��D4���vD2��
6�;��=��j!j)��Es�$A��h<�[���c4��t�� ������^]������3��i���j�5���5h� k%�R�h0�����5���.D��3�HC�Y��9N�f�=D%�b>z ��>��e�0��9����Y2����x�A.D�����|������?tO:o��������,F��[P�fQ+�6h��d��u��,.-������1�l�i�C������9��e	'�����b�������B�����v.k-�I)L�F��j�$��q��<���"%��1���hGx��V�Q�=y[����1k��D�l��178)����s?l�c��%�2
�s-0 �����^��`O��^#�%��7M(���w�����m����"��m_��Y1s���d@�4�p%�W����`N`�k�kMqM?K�>{L$�.��+OJ���O��XL6G���#Po��$r��S�u�] ��}�����w�qF���vj��L&Vp�O4����~"��H�����L��P,=� �K��F���p�t%/��e����`4��I������ wx���^��'��1m$T7���W��##��m��1o��fy�S�.������������:/��?����`X������w��4b~�7J�������H;'8��m�-�����z.�n6Yw���>�~���:���xf�i<��N[!�����^ww�L���<'�L��C���4�������������E8~3fu��F���	�/���~X����[�����7 ��p�
f�`sR�NL�����L�\�W2���q������ii?F7����wf]9Rq�ENn�������V�;���� �]$+��',x,��Q�t9AQ��O���M��������9��{�?,��E������:X<��$7	�naT`?��	��}����Z1+�
pS��Z+��S���K��F����\H��J�9O1���M}��+L�s��~w���I.0��&$[,��t4`
�4��[�u=f���t\��	D���\-�-��(0�N$��*����1�K�mC�=�ZX�Rd���i�CE�b��
Y�
`V �f%$&}��/2o��^����������)�
jh��0���;���\X�'q9$��A�!���+�[�W`�W���%���o����yP{D�h�0l&�#�Q���(,��"�;Q��2����t����J��=�u�-��3r�x��,�^=��br������
�qT��p������
��b�d�$�f��DfS�e�_�~����VZ�C�����S8���+�{�r4������U�-$����^�h���h�����5��fV�KLANO���$�8*��n��\��$�$]��y��j���7��������2kE����f�a��FeY�=+����e�U8E[5x
b���Y����G�W� L�p�b���[�eEe���~��5��[Q{�=\�hic�-��8��PX��p�14'T��&*g\���Q86+?�\>�!k��s�zA���{��c�p�{7E��o�Gs}���"����x�h/Ga?:J�h��{�c���a�/��5�}u1��K�b��S������>������/�9��L7BloE/�l�x�����K�z�Q�x�������&�Aq2CH�Zp����h���tI��� �<�6�7�i�t2��Q����|b�%)��b���$����(��
�~C�$��T�bs���b�B-d��-��n�_�����Q"P���KN���#�c�:�C���}=�@��D�j�!P5D]}|;��$O���4W�t2��st�e��������KU����U9;��X
K��t��s ��v=	� y��`��
��wX`��)���;�-s��a�]��&��D%���O�f���#X��*b�=�������M����|�.��I����
����962��S< 0H?F����xZ�y��O���WQ�vD���ZQ���O�E�u���XA�3����������}��S�to�]�u��,�q�/}�
X��Qv��!��rn��j(28$w�d�@A��������1����CEH�.�e�~��L�}��~1�ci(����>�$
�0����6�g���/(}�OeK�����mW�[��|F��jWf��{���|��Y�f�q�+��n���X�O�������>=�/*I�:(���[��M�!	�?&{M���=���_�A�2��&�5��u	�����3�Iq��=,���v2�el'3�+V�0^�<B���R�s�.�h;����O�".On��	Q���YzS����+�x����q�&
��t�:7��f�������]���*}�n���^re�P�I���f��Y�������dA��W��(^q�� �OG���L�
9�����|��(�B(��K��������=r��"����e�n��#vGd��t&��
�f:Z�b����x]���wA�C�u��g�������E3�;�N�O
o������8������Gd�
�J 	�w���U0��+��s�^���,f��t>�_BW����8�|���S���)�����}�%�vB����/���������l9���	��������v��Zv�B�_nr�(;��N,�����{�c�%;��J$�G]���Z�(���(
g�y��n8�pOnG�;Y�T�~-���W�OW�3�/g��#u�e�������@t�jj�C�:���a���t��F4�(a���t+���J�B��:D��A��
��Z��#|��<"�����������+d3�H�z:c_T�5+��"��e����Y��'�����@0�'��9E��A^�G���rRK,4�h3�E��d�������D<����r����v[OO��nb�bB��8�����^��~�}�6�l����R����5g�������~��h|���v����zPy�i3�FCK��#�_�.���������'�p��=anuW�"�BY�(p����+�i������RX�.�k��2i4���/U�l9������H�����V�n�C����VWm��m.�,�!7�����M���_K���!6��N��������a�n<��ms=S@���4)�r��j=�W8�^��6�lq�H#�-&��n���Z����eY��/��`�a[A�|���H�g�:�K���������e��{��j�E��y��{��^L�_2����Z�~���&O��9��S<m�4�H��bT~NQ,��2�z�P+���u`E�%�`1��#����`�����^,�7^��o��bBY�~�`�^���D;K)KICE���(i��ao�g ��O���I���#:�2 6�:K�����b96_�g��3�/5���>���	P7XL�������"��'���J�O)��V���t�#gn`j#J�~#=y
��1�����mTzO&*�E�*qy����.L���J�']�^Q }%__��(���<Zn6�zY4j*�)��&�+���9H�t��aK�oFD
�h���O�h,qCA��b��U4�Af�d�s�+���y��W�4qFn��G�q6�t[z*�l��5�j���u��(:���/x�Lx��f� ��j�1���,P�!���������t�]"��8Q��8�f@n]�e]/��
��i��x��_�nR�,:��0�f2�����X��Af�_0��3�W�����_�a'8��p�\J����t0�_�����c������LH`���2���{���0��]}a]���-yENns��U�G��, &�F�n��|N���"/j������i�R�������q�C����d<���fz&�8��=�8��{F$�g��\:��G�q���p��OML�9-[�;��h";�m�?�n"0H2!�c�O�b�s]�h���85��;�X}n>	*��	��bfl� C�W�O=�O�����* �'��P7���m^�v����_7�:[��b�l�'��^��)�p�H�8�]B����Z��:��6�����~���Yi��p���}�`Y�8.m���F)�^�.����>?���
��;����gW�?!l��&��7����(V��n��c�8����q��D�Y�[*oQ8������}`m��F���d8bH�R�W�/F����c}J�;h�����;��~�9(�z���3�E���#��qC��D��|�������V�L�T|�����i8�m=���L�D	%�pbxY�s=7]�$�����*���=�=�9O#g�)�\�i�3����2�<���9=	$��0E��S>IEcf+z���dh��h$]�u5�'q���J���%�>%,�$��3F%Y����r�%,�@����fq\����R�m;n����#p�S}��a����;�-i"'�dv��M"���3���8�&��(�H�8O����p�8a�1��Ky����Y�0����6
��3JL�>�1������@��(�0���,f��`�[MwG���$���ye���R4-G}r3��_�'>2��]roV�q�vdp�X���)45P�2���'N0�)D}�6��|�q�|s�1�����=���(�S~�n
d�}��~l4w��$Qqb<��!�qrU������O�x)8fXx;�&4
�����6���S���-���-�t%y	Y��de���2\�|s����<����������'�fI���f#��d��$vb����U���$`�XuN�`���h6��	}����d�j�e{������te��S	�x���Q@�6'J(qVwg�)��r�����y����*���,?�B�l�02�q����0N���5.�C�������o�o�n������en&��K3L�>o1)����
�����'}��,�&jJ���|�I�;(B��YM:���Z�������4\���U|
�,[:k$��\��,���q�c��Y��+H<��5tV���i�5�������pw��:��Y�������N��]��'�2��t[5����U�~�zB���m��iV��������s�N�Kl;V!�{U�����KK��
�r�����cGQ���m�~�A����o�������8�Mz�����0p��8"qoKeF'��IHs�Ea�/����~o��^o���=�KK��f�"Fq9��Q>����P�}�vZ�c�Q?v�y6��'��P7�ITe�F�����S<�l�������f�b<��;f#�^�_��S�&���U��9�_�Q�V2�n��v��?����p�pgo�D{M���W��x�������� ����58�-rs
�hN�._~�bc�jU4��@F%cw�F_W����Tu�����	��"W�YTso���S��M��4�h�@]v�n��O� �����bU�+��q�A0��E������I�`~�n�Ej�����}��a����a�������g��g�^C��1���eK��X��,��)��C#��;e!�n��8f=6�����O����������f�D��yXkG�=2������3tM�����u��X�[�!v�|�eq����Z���<b&X���'��5�v�N.g	���Y��c4�
����A�B��#<S����7���$1.��$��������N/���3���]-x:'Xn~�Z&���s��3T�Am��u��1.��5����w�\\�|����RF>DyL�R�ZC�$e���-%w�!n	����d6{�8*�e5�M�F$<.�4�1`���	$��T��w"&6mC��V��Z,M2R��}(����Up����$�^5
���Pgw�y���?u~�%3�����m�a�k��Tj�0q8���j�����>������yw3��	��N����?���&��f(��Jkp
�Qe�&�:��������9����4U 1����_������s��;C��~����T���u����n�l�����b��/�_�Q:�-]�f�/:�FuO:G'�\|�	��p2^���sr5������H_�0�1����SE��b��?������'�v{�B�3$j������S��z��5�U'�Z��	��f0;����/c��O'#M\g�����k�l7��9W�nT��0����{����Dq�t]r�R^!�,���N�+�N������o�HN���r��]q������hFk�f�G1�B�x�f�3�&3	�@w���0�.�}���5���?��9���<7���@�g!�W��f�C�����s<���b\����)I���}{{�K��[9=����7U����d������:� :5��T�,N�}�&
�c�'�R�:�
s&���l�����c��1gl3�"$yTF�^������
�	!,$"�=d��	�)a�#�����ke��M�a�G��0���X�)��JJ���;�C�inq�$��=B��HL	���@���}k3���h�#���O���Y$�
^�������
�q����H�|����u8����C�z��P���E&��h�J�j��x�_���w��A0OY}�w*+���=	�mr�����������Gg;�wvvK
��~������\]�����i?�*l���*w�+�h�P8������L	hF+�3_��������~�A��gL��m$@A��hHj���Ye�e��]%�p�:Qq���z����U��ff�|�h]Pu����|uk���'7�����pb���"��w�c�R��0�?Xcd��9��O7���W�K����!%@�����
1���4%Z/��hi�	�q���M��#�=v�d������+�����q]�������g�
�����U���	���0�/����`�3e�YF���P�����p�������zkRDmdY�b�fn��h;C-���!=k������CrD���FN�.�|C?]�hc;��;(m���u�yy�#5��o���ww���V�
��^�k��w�����=(���wo��~@:��{qv������n�J�C�@
U����Fv|���J���������nN����W	pk!Z��m����\KH����GLn�L�����k�hg�A��q��C=�#DP�4y`�P`�P2c�
w�DO&�:�������0�I�!�Wr�z; ;7��^�w���W>�\~��5�t��I����g�����].��~�<�:���Y2k�s��I�j-3A������'o�YiG,��b�i3��{-m��tK���W�;z�VZ��^Y��po�������~8W���J�&��Q�F�k����TGO8~\����HBs �l+D2������w�����P�m��C��!~�F$=T�4]�#���~� ��P�D)�=�|29�s�a��^P/�2�u��^f�i�pR��w���RT���d��$rf��?�GP7�Z����xj���i�)hX_���=Fk�:�� ��1K�i<�G�L���!��P���������~EB�9%��(5
��q8��U�����rK1/��Rt��P�S�9���Y�a�hK��,u���"�^�+F�p�M�0��'�E�=`���BXE��;ID	�;k�x_*y��L�66N0�=�II(|1&�s8��
wI�����'�k��
~BOgBFU��3�l��o1�)YS�E�9kK ��W���P���;T�G��#k�t�"��t�hsr��������;6	p$�>��������:N��C�_mz��M��1��ZH��Q%�������e��S���������M�t= �q_A�����T�wz�>Q��^]��,��&V`��BB`�CD�g?aG�!����0����X$�Qs��4���x���(n{����4����0�AP����\S
���V���|W�����V�RU��T��	1��!Y�t:����r�jJ�D=K����8�S3cB*W
��S]�^C��S�W�2��#=!	Eh������D�I#��A������-j��.�����D��_aN����UW���~���.�����+��sS�b9��]2���)N=�v1������m��a�b��%J>�S��E��_*����F�����o�n�
�0��/��Le���[u�iCO����D����3@�d�m��8\��1f��r�������=�%$O���k��)�� ���L��"�U����1E<]/T����[Z��q8s���3{���s����� �E����3t��KnUJ���A�i�#J�y����-j#��E���m�x���Cg�i!;PX��4��&�Ifx���"s��*�Ja{�hr
��m����P.�jAl�������
�����~�"WE�-��b���U�=�n���xkvE+s4(�f��&��mZ)���6K�>���xk��v��n������!z�LkA�L:�����y_�=|M�6x�)-P���q���[,{6��<�)�3B�����|��/�����3��	��r�����a��1N�?6SU�.�.kW�	Q���:���X2�D:����XS�|~���J,]D�d���^����.�N����-L0�!�@		��8��o1a]���Q~�_r_��`J�~�������w�{�z����K�!��7E�c�3�=�%lDw��4���X]��i������4�*��`�3M $<4���=�@&y��l�v���~
;z�/$��yt���=�����X�wt$�����9`�����9F8��8������G�M~�$&���k�?��/"��T���4�R���X�,v�p�9��_�x� ��C`�5�32�Ms�aGSNA�w��#a;D��P���CS:Y��D]�M��k5�\��\3<�k���u�,��c)����le�n�G��`�h�W�������".:;��C�s(�E���6*��wR`�
4�w�9������L�H��w ���I���'��*w~g�s��N=l;����H���s�G8K�A�����W����Ur��S2+A�D��x<���W�����7(V�PM��_��m/�#�+�c�y-�dI	GsR��H:�������f�6�����d����)��G�Tf���
������!��Wh�Q�>2
B/J�cD�B���kn�R<����{���Z���hX�����_�v�}��E��v:�
4P{�L�	o���}4�G7�I���� �Q����}
CL&�E�x\)M%�w&��b	��g�\�o�Mot
��&����`Z�)��T��&��G���hc�jy���M	(t�99e`�����������^e�^s���k�y��`��8&��C�� �4���.���������C-H�����)*��y�y��(yW�`�P��)�+e����2*3��nJJ�����o�/�.������*@wpl�ijD�)�'T���1d���	>����0���lr�?�RQ�[��&�{q����T���o�E��-�6������������8�^e��+�v	�Y�{3gn6"����x���t��E�M�����gL����7=�-^07�h���/ ����R��xD�YZ���(��Q�����<����&�R��_P�d��&��U5T��rk���g3�b�g��{hQ�=�- �D@��1�5��#�H�im�	g(����>	���T�	��M#ZP�����^erC "�Q�'�s������Z~�T����.�/7K�9{�����.RE�h�F�`�1�� �*���e���
gN�%�8���W�!�M�L��P��x[/���PV�u�K�-�����[�`���3��d��Y5��i����wsB,t�`��.G�l��f��F�:'W.�u�c���+��w[6�L����e�v$I���l5�g$�n7Pr]L�V��3B(�[�&����-^�*�������f��,���D\�q�%�d2w�=9���,*��f�)0����]�/[
L��[�������f�{���'&��4�$yh�E)�R�S�z��1���B�%��#g4nx���te�/�2q�L�K�O�%P$1�i��4B0H��L!z�'�����Y����/Gg�:�V'C���lnF�W�t�v��^\EI��"I#T��6���F��|�Wg�W�*'X�����9�����A����{��_br��P�9eh#�����R��g�Ee65������)z)	�������C,��<2�^���g�b���otS��`}���6h{Q2�R9�$g��	���d��nT����3��p���j
�0m��������67K���W��wW���?��&����Y�i������������\�fVmvI�^���{���ac�l���f����w�6��B�fw��fG�$�����,����m6m1K�Y�X?a��������%�#��k��#tzL;����	�g��N����$E76��$�����OHLP�&�|�X7���wS�����3+a�',XG�������B�3�1�>����1-qe�SEY���j:A��nl�D:�Rr<��*��[.
�r2�
�N7�5�W����2��������M��p������bu@���918�6�m��V~���������\�F.��F�w6+v�,�gNV����/	�Bn`���	�m�;�%4x+�5���z�����%�����/*�q�.#�?�����E�]�v2���0��K��]���w�y�P���'H���U�N]R���D�Y0���;*Y>�bP����?ak+���\4]�h���{�D)L�w�q��y�����j�Mbi�>��l���6���Rk ,zn|��
��S�ga�:��B���-�E4�%�o6�C����=\��C���M$���u[���	u�#L^�1���	]��@&��I&	��t����j{����������	���	���X�+Qi����H��� WH5�x��x^:q������9#��|t�K�Ia���;/������x�d(�-2oeG����V��1��iDhde���![���M�(\�������W'�M����cP��_u�;W.��O
j���V����UO�g��z~����s~s��Zp���N���������Ny�Q�v7����wWG�.Y���l}��*2���{�{�����W��lM����7WG�������^���or�����_��d�k����:�t2�~w��\�|<@d�9.��	������7�o��z�N�Y��I��^��~�U�N�������J�f����������:��D=������g%�V�
��ei��x�e�����v�`X���A����dZ)%$�r����0�%_rp;!0j��T.��F��7���p����!�2�F2S�����m�}/���I�W��@���l�}�:2������og�C�n����N�I�����3�x>W��60�|��}������G6+��;vw6���\�����M
�����%%�ajD;�]N��7����em�w����H��2���
���8 �}
�ta1����*ACN�*Xo���e������ldX���|�I+�E`c��l���[��b��3~��� �L��]U�F�H^8��aF�j��������#�3*|����eD#��1�c�)��E.���bi�]n�I��������1cb���cpS�z(5��MaZ�9/���zh-�v�[��6�{�$��Pv]�����1����U0�vx�}��	f[����2M��Ac�.T
(�5	�1f.�n�����)4��!�\�x������������@�2u6��`p�H�lF�0R-�@�Ii�tH��A.%��pi ��,��|S�rP���a����q/�3FN���*�����:���L�����{�~��I����d2��*YR�K����k*�Z�a�A�k�-�e�E;���3�>w��������tK�5�H��Js7l��&3�
��q���AvY��M�-�I�8�Qq�?<p\�����\p �y�\� �c��~��1�rm��V6���5����Y���P�1BsHY�&�������^zQ���]s���53���L��jL�,����v+�'�X%�hJ���SS��P�~��t��2q��a�jR��'�u��blv��4	
`���y(�����F�Q
V�J�q~O��zq�.F�����U*�+A)��(��� ��Wc��z��(q���l��P�P��8�_���\��tW4W�E�ns�>f���������K��w�;�n�������j*#���� ���Mp���{d���vKI:�h�V4���H��%�x{q�!�1�([��[I�Ta�J�IK�!�������$i��t���a*t�M�v���q6�U�
��|W����JB#��L���h�I�Qae��'�R�U#��i�z Mp�
[=��l���\
�����)�\�W����6R�����������h��?��{����-�!d�/9V��n���y�k'!c? �,����I��q�@R2�K��_N�n���\�_�b�BP���
�y"Q��������r�0Y��njK\-0v����+'����� <I&�����Q������[���k��n�gG���;@��~�~��^�s�����I�*B��u��C����e����5�XD��d@��I����3� ��s����Z�:��Re�L#�E����7)�s2��CX���!�Vy��xX�X{i��T��dY�5f��bpg�����A�_���{J~i#vg�|R��%�3����+�b2�Ez��8a���������n-Q����I"��S���f���\�+;�������	t��RX�Vx��d��7��o��~�\��������_�"@�y2$X^f�1��m)��CS���p��v_�
��Pa����
�xK��6)�J���*)�pnd_������Deq����%��\���+�YS��z���R?���&|��1�'j�-��!��<���@�I$8i?|�j%�"j���eh4�z���]"�.\
3��~i��/��!��)���s����F�Z��ACv���nV^���3����;s�^�u��[5�XC
��Iv��%C��P��P��|/������il&Z|--1$ky��\"h��2x�Na�Y�:n+6�m&�0q�6��!+;n��JV4O�[h���2�����s�avc}@�J�G�.ni����U�F�b#;V_�<!o|�_�n�{�s<���(\�������w� �=f���hN	��%qB$M���r�Y��9�2����8M��z}���
U��m64����V|2�����>0��$t������~�6�&�D�^ v�C~���H�vj;����-�"��&h����������H�28�h��������l�F����k+a�c.�RC�N�o.���=`��Q5`�J�<����A>����d�qM6��~��GV��������������)Q1� �~)�
uu���-@��?���'No#���y5%�WM�W�.I�?dIB��������4���*Zb~�"^�`����i�wX;������~�$���R�QP�C�9�[�Y��2~�����'�����m���)�f�<����P��Y
���e1�����6eg�N��t�������s����E���Y�bH�2RJ��p*WV[��Q��k�K�W���m���79�p~���f
�`12��@&[d���A4��,�Q�����!(]?z7����kE�,�D�t r��@��U+>�
`���4�&�4,��3+��kb���8OP�Nt5V��"
}%?-��3��X�p�(}�����G�l��O�����K�-�}�L���_w~<=N�/��i18���9�7'���[!B$�o���������>5)4�>�1�� 3��7J��C)�LL�r#`�w�3�f����!�bW��1�mK�B�� C�6��&�|"���`cgc��I��c�<��>���2Jz�����d�������F�f��Z�R��TL g���2�]�:Y�x���1��A8��I�Z_shac�lmo��e��d���+q�P%!�\[�a�\�k�p�@�S��|��jI<�	����b����h��
�I��y�'S��b��II���v��M�dJ�wE��/?�q����e��T��M��w�b��C�}>����+�KCH�h�bX�J?��d8�z!�����#���;�%��h6{"��L�RU(|�suuq�2�Lj��ab��>�
2&;��/4���]4v���x ~�����%����y�E��t�d�:c
V���w��?�gG���8�o���Tn��cBP��H��%���G�jE���9����A��fO�*-Lb����o��j�]��_k���F.!���=Z�b������f��"Z:�@��Ek��D��f��6]��������8}n��z@�	=�Z	�F�xp���J�)T\���AG����C���~����G����S+`D�L��EPn|W����2]NF�Q�����)H����b�,�Z�w=��
�������T��h�� ��L���<��%�5�eE�R� A4@=E�[u)���h/Y��-�7#����
*��"u�!>1ZT��r��k)W���K�H1���J��}��x�"�����#e�d�e4o�=n�����j�����S%;f��fc
��nj�l���/�OfS�&-���}�~�"�$�4o9q��0\�h�E[�$2�$+����7LU��v�	������g�wX�,���!��8&����F1L��&�csFg?��V3��$�D���\����iE�t���4���G������8�HN0m������)���N{,�\�4
6
��j�U	��!�
����wa�`�3���-p�r��W,(�R(�����
��C��$���-����Q��YT�����D���cm��!��w&����?��c�E�+�Ms�������(�-�8�u��^��-veeZ���>h������a���Q�����J���5��2����t�����g�a/@~���g�~����G����Ti��z����L&��0�N����`���R�������8���I���"�&�M0hO�N:7G�g�9����9������bq��Z��gFl��	a3w�6��I���z�hsh��ev#.�����f�.�u��f�����<���D��1q!z,r�HLwF��e�|��|����w}�9�`<��@s��'���&����(	��0�h�D����`:]��;�4Na�F�4�Anl���Y���u>�5�@�F�yN\��"b��e"%��uV�Ycn`��d��[�q5��~Wz�d�b?`:���(����j�����	ix]B���XDkd��y�}Q�0_���V�����5�,�8���Q>����u8	��`����<���f���[�Z����g@�~�n��5��-��l��W�|r��*6�;��5w���1:}�#%������!��E�����
�
q�L�������
�<{�Lf{J��j�T���]<Fg��^?!\������?����t2_��+�mGm�-�������GPR�����0P[F��?���I������5�.P�9y?K@ ��c����A�k��|������:�dol:���S��Q[3�3<�)�]��f�cd�@j�����Rh��0l��z��j�4�vK�3�������������ON���5�wI�3�Qr\\�t�����PLWUR<��	����P!����A-w�j�K��!lO$C�x���n���_�w�k���A������8�E<h������n"�E���B0���9m3m�Oqt0n��� ��������*<���;�c
��1��*~*..��nf����I����l��"�?�&�>��t��3+�7z�|��C
�n��}�O�T�������v9j���#������J<h�@*�~�{t�k���}���F����������"q��7�1��\�D�,75�G�B�6{xV�Q_<v]&^�G[����;7"��6jG��XT��#������9�}�46�l� �(�@F���/������;Y[�z�aC�e���D��j��P��	*��3J�-_�����qa�
�S����qh@�'�=�Z�Y|{�E��~+(/7�a������~cgo?,��~�����:���p�<�z
���pcW���
�:�.�-8�J3�k�*{���:b�
II�	�K4�D����M�����W+kL�6�V"��o��3c��W<�JX�c���J����u�zI{�n�n_�������i���?<�8{��<���`s�G���<{��4����.?O�`4Bp��(��B��
�`o�^��D��^o���7Sr\�=TJ'x�6J��y�%3��o1��-����%H�0H���Lj�Lg�����0P=�,fA���A4�&1c�1*NH#,�\������l-�T��������9�����&[�j)ev\�f�"
��j�������a� ���FJ�+[�P���uVKWEG�,�>r���`��<�9����9(.�V$��n�����(��D���0��a�@���W�?�I��������/��G�������������_n��r;������JO����?���?���?���?��l���<���K��7�`n~3_������?�W�S�O�����T}�W��.�����5��D�D'm
����:����:���~��Xe��X����P��y����]y����n9�:��T��G�s���Mi������p�hZG������Q��u���+NXw��u�>3_�������d=�����j0���G��z������]�jQ��2TT�e0����s������8��������w`���^f�_�[�_�_�����6O�^f�xM�6��8�z����?���T�L
0004-RLS-support-for-ON-CONFLICT-UPDATE.patch.gzapplication/x-gzip; name=0004-RLS-support-for-ON-CONFLICT-UPDATE.patch.gzDownload
0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch.gzapplication/x-gzip; name=0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch.gzDownload
���-U0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch�\{s������8��)���e�u<um��9~Uv��;w2	Jl(R%H;:�w)+m����H�bw���E���������
����������f�#����9�����	��B�(V�w���S�������������� ��B�y��Vx���'���K��-��+��S����[����N���~���'�����~Nr�������t�����*�v}�8�>1�������{s}��>>\]<M,�i�KFa[&<ty�'���A�"���
�'�a�x�,�X�$br%�[[��'�Y�L<�C��w���5~��$]B����9</E��r����/�M�_���4��R+���T�(fR� �]ZN�$�~(\6[�si���"�+���+�K�<�>[�d��|
����
�%����0�/~�y��j:��,��"s)�|�id�Q7^�d�D�`Hm��������U`�Ja�(ZIP.�&��	tS4h����'����]dm�U����a�Q�,8���8�T��M�fSoZ\D����s�z�f�������\_:��>=`�N�?L� ���Y�a�������E�6���x�B������p��s�C�$i,l��N.���������
&(
�9�|G��~�����ks!�a�E,�0b�"&5�����g1"!�e��0h{���%�!_�E������x��k��a��Ka\���	-bf )��UC�R��PEk&A���c��5 ��,�H:A*�B����A���<�5Q���?/���\F_�o=��|3wFK��2C�,|�|�3��	b�W��Vf^�FEl��4��aL�������m������l��2/���B��}�?KO@#
�62�FQ6
���)��i$�3P ��~�J�>����������cPlj�6L�%�����D?PA��3���
��B�	��,����1��M�M!
(S�-T�
S����}�>��<��>�F.d�'���
x����V�Z(%��RFN���<�'��<��t�(�g�S�)GEZ�0 �,����_a��!L�������q�����>��>z>������9��I~!	$ 
�����uG�1G����%�(�/0�"���l��D&�6�F,���C-&1x2�!s>"sC��4�]jh�q�K�L$��L�Hg��O,~O}���t^3brF�V(��������%_
+��r�T�4H�BA�E�_�RE�{�!�)n�(�	$@�&[%X��:���i�����i~h�"o��N�85J�����&��� ��%)x!�X�C>"�K�-R\�~z�BB6��J��Ja�:�<}��]������.��N��I�h+�]�TO�?]��>��[��U0��t,�=+�I�k�&DI��n�hI[
��~��.
(l�Fe�w1qP5b�1�-��/�^����Au�._�Y��E�Kl�b�HCJ�8��w0�o�L!��oC�����	
��*��a��DN�@"u�;���Qn�dr*�u�`��9��Z�T"��+��RFQ��0�O�}<c��.
b��X���j<�9x�����bFt������?�8�a�;�Ea���N�����s������EH�EW���@|��K~��p��(`�*:���4�a���#<�hZ�
�IUj"�����0�~���K$tb~"�)�4�������(X'���j%xl%]�����L�h+{�#)��������:���������u&97&_V�W@4������(��>���YC2v�&AZ��]K���#VlJSw)+�I*�5JP�e�)��^B��~s���8 [��'*�j� ,-h���!�F
�E��Hq�4���[���j�����������=�l�����ha��>y�K��t��B��jiK$4V�i������L��=�u#�#c��:�����H���haB
Xr-s{�x��8���} �y����+�@�����M&���b�T�Q9�+C���mb�VL���PjT���G;�)�WSkd�"����z���h'��/�l�:���2�n��������f��e�c`�H9��7t;Yx��A>y��i'_�Z�����O�[Pso{���w�2��L���U��d����������A;�.b����GC���|a	���6v�W��2?J`���KCG���4f��(b(x��Z{_-�~�����2����S�]#�F��*�6i�U�/�����eb'�]��b��b�������$�F�K����z�� ��o[w l��&�Y2m��e-V���j�^ok`*��Um���x�xDh�&��|i�khy�x���>>��_�g���8S��j{��f�E���'�F�.l��]l��=1Q��!���Y�*���,s���q�e��I�*}�6�����f������G��~H��	I%�/{Q/w�@�L���<���k��so�O�"5��:+�w��sc��k/1�U1(�2C�����3�m���qU������[h�����������Gq����$U�uWR����I:��+��90.�e��x[�TO��WPr���i���^S���9NH�X`}.7�������,F������}�1����,x�vct��3��{W��Il����v������1���z���p�^-���
��������qK��dT�Y!���p�&��M+,�e���(�c<����^!��:��lL�p+������Z��Ghe���u(�U�e�'��#m�*z�&���pV9t��uro�l�p�tRM+���-7���\��u*�y>�o:���ax<��ve�����F��+���T����;��vG�aU��+��]�#��a�GN�[X
�6V��kW���s?a����~�������Qw<�C���8�~�U�������	I�������q2%}�0���t	�J[���\J���^/�����N�[���
��xx������7�J�-{Ik���p[����������M�)����'�X�:���?�~0<z���13p^�In��o��d#k���4{
�$��-��/�&��Q��m���?o����������+	48ON�a��v�;���Q
8�c�������A����c��~�~}�t����pz-����������F����v���`@�����g�d����~?(\�M����)S/���p>0o&�s v���R����(��{���q�+z�j�Kl�7��p�+�������u�WT��N'O��w�����li~����#��>��{�����FN����|*��	�
#�\�'���*�����$;�9X�j�tq}��s�^�!?txV�L�� ��
��RE]i�{D��7E5�W~J��;�FG}�>CJ����
>;�}�I��^�?F��^]�<��i�G�����]n<��������8���P�a����7��oJ�[�"�k���M��"�w��Rp'B�2��{|�\~�]W82H�o���$Nf��[����u+�[��Y3l��-{�J�.��G���U�� W������7��p�y$�f�aY�]�S�3N�:5��Kkvw��A4o����-{S\lsT-��.������`&M�sF[�����5�zVC����x l�y��WWO��m�+�G���b�8F_�<M������	;x��a��qq���w�O�L���.?L.�������.Zn����W���>���Jp�H����_��z�4Hi�o������m�2�������k�:��p�=����u�n���
����r:�P���T9)�Y��E0���M��}��1�����A?2��*<8W�<�������,���m��N�.n'9A�%�@�r\��4�>�o�Q5�������l���p�w7�$9�>*����/����N���7�RCR����U�J(��Z�N�������Q
CS�R�H6�4��J0q�6x"B�����D���n��O'�wW�_���~������0+�!�j�A�g������k�[-��)L��������������������E�������M������	��C�$��A$��x����_���hP9��@1{���EQ����F�O�g�)0�� C�nx��R��f��/��V]���n��&G�$k����7[�uK�Rp����^+UD[m��]=(����M}X�{�k�*��sR���2��:Y����(��K��#�<�_��s��EB��;����hVsCC�;9��FPQ8�X�co$s�
0�7�V���V������L}9�����iSg�S�x����avC6��P��f,����oO")^��O~�Gw���+\�Tw�����{�#O1L�
>y�X�&�(`j��- �=����]���.����gb����lQK.��"F�4���"��e
.o`�|��9������{}�"��(����M�;���0����3��O��Z�q�DI������?c�9�O�UJS���::�;������FK��b�Z�B�<�;����P����6���;�=r�,f�b�L�=��a�}u�5��Yk������Neh�'��3yX��� C�Y�&urCL	�t���k��	8:�)��EWTx4��
g��T�6���W3�����E;@	�QV���P(�!�3������B�8T��z�P�{!t^Cj����u��#��2a~��8�:}I>��;�_�����.�!f�aPaC�C���An�)�|����Q!>��
���B���SG)
Rm����
,�C<v����L�S�4NO�)m�r�1�_1�-V�
i�LN�	���A �M4��K
���{f�h0���e��4���Y��H������X��g�����J�^g����>���e��@�aY�:�a%b<��s��x���Z�l�v�C�W��b<&yZ��$���diwu�K<!�f-������]]����]�\%$<K��X�f�0b�x�Q�
�J,��7��-R��L��Y�>+�|�p�Y��9����t(�� �3����<��<�`�\�N��^N'�$WKLw9��az}{1���g�+ �=M~y�������>����9���x�
��Ae+6)� ��x���<�=g�����,Z�h|�Z��UW���W�a%��H��)��H��V�����g����n�3AF�!:��^�3�%Xc_��(&��:bS��HW����S�����@FyM��K���+��:���d�����a%�(����W��������������
h�������Eq���B�F��ry��jrEV��S�y;ub���U�����W���G�5�W�PWeCn��v��#?l������W�=w8�l����h���)�XT����0��������*���<�a�����K��|H��9��p"��j�L�>Fy�|�Y�S��B}~F�^].@��P�Yf������T�l.1��)����
� �|/��
�h/� ���*g|�K�v6!V��a<��G��m������%�*���g<���C���0��A��L3����w|��6g)��G�[�f��e,�7�?9�/�9���.�=*G������������>�We��U�B����cZ�z����PY�iU�V�Yx���J~�7�J�f��=��b����b[^�����s��q����3xeF���gF����������d��NH�]���ik�K��I���73�s��zuP��+�RS��[��f�le������,���2��.E+�k�W
��3�<�����s�����������m{p����7�k���@�����n$#d���q�,'�`p���_?>%���TD%�����w���"	g���'@����{��g����fWXK�+,G����[�9K���AB}srx�
������i����^��]t(R�����VG�:���R�R�%�rT�2���n��uN�E���?".��G���6k�C@/bd�d����l}t/�Sk����e�DE��92;�Z	�L��m��`1<��DB����u�9�d"3����YW�����N��f��t=!�K�A��2iz�H�).����p���h���f��Y�8�,fNA�{�T����=������������~�#�1���~sv�J�����_������������B�{p�*8=��=V��nJm�;���w������8�Z�I��I�"I5Y\$+�/���X�,��rS������K���H���d�SW�0���U~tvvV$T��%��%b�%Z�6C������1P�y�������5��E���������������������p:!P�
�$hc)�d�k�����y�j�����Ohjbr0E�����.�3%�$���(Q��(�?�!�Wg�D����o��K����S��;������;W���?�zJ����� �.��W�6s�9�b�?���t�"�U}=�(e�^ba����Q�~4��J�����f��bkY�Wl9���wp�l�OW������>�D����`}?6��F�$x�LG�M|y�?�V�v.4�"���x>K�@���b��gl,�~4V��(����}�Z�S�Zu���pm��]9����^s�&���+`��VT���`8M�R"\��xl5�l�PAL���[�P�J�y������I�ERz�&8/l��x�5{|�4�����>n���(s��^K���s�"�/��d�V�(��x��B������A�M���$[/�A����8�5����,�]�������sJ�m�9%�$�zLU����|.�����i@�LGd�$�8u��u+��,&NX[���]
�QT�d4��+�WD�Y��u����GY�����V���7��z}�>���D���g�q�6�Z��Fi�
k�@>���u�j�_~?��Go�/���[��9R��6e������?����KD$�$-���=�,������I����*������:|{r���?7Zx�����k8��O��9M�o?-��h��[���i���^zvO3WDi��O�S�ha
U&���y������T��b���-=�(�wq�^2�4�+�8���CX�?�q�>�?���-�G�}��3��������mY��_<�������=
Kz��<���ya��ze����/�U��@�>)�w.�f�U��P!���#H�)�����O��Ir�s���A����]���j�f6(�1�u&����:t�{jEH�W�����\$���hWi�C���*�{���i����W�}��d]`n����"�n��A@�,�OCx����-������:������m��k�0�C8�j�B}�r9�x!��w�����3G�gT��Nn[��q�PM�h��D4�5�dj�������L�sp_�X��}��)����;��~�s�~Q�[eDm��R�^Z�:b�	L��g����t����Q?B��0M&0�{��VoXUGk(,D��o36R��Jh��O�2P�4�>FwG�k4w��y�o����K�a}���W�C����t������[�q��Ioz��g��\3���:������e]t��~g���P9r�^�<w �I��d0��<q{���N���g����iP�[X��_�q���.�0���&h�X�:\�_�/��l�H��������u�,p�<�a<M�u.L����P��A�����d?5��w7�(r�	��Z�<��Ff.�WJ��W�������V�����K���p�t�[4a�mn��a����54mi�������X@[�����i=
]�x+^�Y���k�w���(t��Z4�L���[�qqG]�0���M������*�~���'o?�Ncn���	���%C'4��m	[ZB�VI�^A,�IY
�+�jQ�SRcb��Mr,A�w��
l���_�1.�p�)���Q.�F��]�����
T=�w/L!�r1>?EPI
�x/�{C��g��C5�bJ�,��S����<���H=zV��|��48z{~�=��^��xf\
b�8A�������T9��O�nD&u��#�l��,�c�J2�a�3e2��jy��3�"�������y<�3}��v��^b�/��CK5�E1
�g��
�!����$(!�}������in�9��B�9��0C	L� ����ovw�Oy��\8��k$�sJ�Q�h�d��d����$N�I!7Cx	��f���v1Pee��9vj�t����$��A"���J����2���8��$Q�z+�*�Oit1^��#�`V�&I�:�s�;��|�P?�/v`g����g n����<JP���tn9s	�g��W����WBL��l|<X��8xT��]�3�_p~��;��o�K@)���qJE��G�V��0|_,�w��*�����V��O>N'���#�1q�7R+����0���,��F��q������EvVa�	�'sJ��@��������5� �a��Y���
�!����D�7�K�Y_�L5�F�X��)������CgQ��'/�=*"k����v�����6)���6���6���:5��8�*�s46zM
��F�"�t�:L�����3�e����>�6&v��q~�j�����%��#@h���oQ��}k����3�x��`K�g���p"r���g�r��r�G�Wx`G��������V���K�-q:�u��g�-�����&div������
9
���a�Yx�	(�� �������tsx` %�Qh���U���� ���z�O����X�y	6X���q��	M�1��;W�����rVXG
���H�l�hH5J��.����w�r=<�-UQE6��fj%�4���.I���{���T������I��Ix(���Z*Gk���J��-�������zw�
r��������D&/��y����l����pO�9_���}��Y5�5:1
�|���"V�Y<��e/����;�0��u$�K�p�%,�U/��V���hO���y`qh�Qx<�6=ds�������2�4R���9�X:�}�
�7�2�o;���c��*��)��~1T���C����ZH��b��,Ez���1�o-��B�*	8���;T�?Z <��c>$Y�V�w��cm�/���<$T�af�64z�q �R*�#".���)�_�l��7F���+Jx�h�����(\qB��P ���������c������%�������'2���H�P�3��gP{�)����1���Z$�?D=}�5�;��'�������I�?u�WXO�.�����T��h����L>t�f�g�����S5:�E�oOn�E�S4Rf������<���Z�^��|D����;�>�#6�o���������jQ� �[�M>��u��?�2H������^kA���4����+E�rlT���8u'��.��i$%��L��'E��H�C�j.���n�Y�[>�c7������-��k��(�H �'��D�O�w5�Ue�6-��R�2\t[	������b��U"����x�
D��`�e�$Qsk
��6��a������
"�6�?�;�{�?����,q�C�iG�������S�X*�c�9ta���p��z>�X����<����������'�Z��L������(VL�Z�L��p������b�<5��Q@�k�������.>���F\���F��%����*X.!�Ul��.\�Y��]8��o��;�Vg�Uk��<�~<3�&_o�%�K=;��Y6�����N��5�2EF'������X=p�z�,��*(EL��z�^I3B��J`%u�\�g�5�rk6�e:^�2������a���t����Z|�(�������_����
X����^�Lt��e	���_��i2�	u~�T�,�uY��g�����1�Q �
{���`bGM+��,�f|�L��Dho��X���V��+�|�
�8���^�L���X��j�]���H��g@�76W���X�l,KX��u&2��� �sA��-���T�
{�{�,���,����X_pUZ�V���
����B��2-�6��`���>���Qr���'5�r�"��������k��s��9�<?>���Y�^�o2�S���kA����S�h��`�h�p~�|j����"w] �;��x]<�Be_����c.�m�1Iz��k�3�����
�����=��E�
�������	c�^��^8��3��o`>�~L�sD�������J���������]g�N�x)��#^���) t��%rs��U�d7�vm��&���j�����h���H�&��TK�������b��I���a�����UQ��iGuv(��n�D�_���
�f�;�����1;�h-���R���uQ��r@�H_��;�W������r��A���%�m{;x�L6�b�&��hd�������w��8�V��f�����|�G�K-d�x?���-��S�IRc�K����\�m��&���g�������x6���Nc�cvw�QFQ|����oS�a��(��J�B��3�<6v�G����Fa�^�4����E@��=r������{
�7n7���beT�23��(z��^�8j�������rOx�����X���/QC��A������ym�d�U9�:���([�NwV�C��+�%#��A��6�K��9�����X�E�cJ��K�����U���yz�����G%�����(��%�^#���L-�/���������9<�1������^�YC������ijxt�sG0g$y��<h�y��6]
����W��z��x r��`<dm9N��N�r�E�O�[�.�G�n������DUF�H��y�V(�\��m��U���m��e�^X�d>V�K����fX�\���/C.N
�UXk�.��r<\��lkp-Yu�j[�U��3dT��[)���v��X�g��{15��Z���^��$��7�ta�1�8��,~���k/���\����3N}_z�� ������e�X���
)�x��0g����������0&�Lt��O��S�g����gZ�;-���06Un���g��Y�Unu���H�P7}������:���A�
Jy����@���#�5�x$�h6Dr"�34p��I�������A��U���o��ha���V���	Y����\����c�e������Y���,�,%��{���\�K$��dg�&3�(����]F��_�����������.��^��y���RZ2�ZO���EZW!0Ld\���r��3�G���\���������jX�D� �1^v_J,��c��PN�eyw.3/�b�����XA{|�6�UrW#n�_���^2��]"��Q_�r� '�I���'j/5��\�b����h2x
,cl0���0
)�I}�=�i_��b�����v�����}���RE|^�(��:���Z���E�x5�r9o�Y��������[Lr�Y���%���u/�W�������Gk�|�v�z��U�k��������u��>?<�t����es�u��kG�\[[;=[[�*g���qP�Zk��
&��`�v�V�� �M��[��X{��Mi��4���UxM#*�]I6��#\)���
^����^��t��pw���(��|!�����	���%q2@�@����E��?��_F {���Y<`����t����=sOk�E���d#����x:���$�>�R�Z�5�5���d'��+8�F� ]?���Y\X��[0��+N%�n
�d���/�3"��
�>���5}�D���n�k�/Xy2#4��&�(�pu8 ������&���\���3�q����MC���+�b���3��UL���
�����+���)*g�^���Fa/I��<�#�&�M����J�sSU���3_q&�Lu-�*���[Tq<���W�B#��p�����+_�������'�R@Y\���o����gk(hWx��>	^ZC�Os��
��Hq@ni�=�J{�(|��L��W������-%�s��/���n�}�P���/���q��?���+�^�@���@I��IG�� J���?�z�����)����0�?Dy������+"���&�T&�����B#E����Sk6`�{�Z���C\[��!��s���6�/�C6�����7�1��!�����3]����:z����yn����mC�l���Lo�jX,XX�[�]/��������0�Pa5��|��������z6���=Gq�|��E���)�2�,\�[z��0��B^�R�L~-�P<���+>��.�t��\U��h����'������V��0([����{4)�C{!t�>���A}A
|���������_�����IO���w���'t������G�Rb��YS,2Tk�����l��C��8_M2a������������&��7r�~w����S�{�C����S�n�E�h�aQMf'fj��X����;�k��E�t���7�F{���O`�:oA��d���y�\H��|i`��?/EM����64-G�a�0��C���@e���z0��T��
"����dc�",A��h�����d~[�������J���<

"1;S��Z�����	�zh�AX���7�+��
���v���a�q4WDJ��bf�01s�~�\,�W��5Za��Q�3��T�,�!��.Z�[����i��+P�"��.�S���.�-^�����>"�.�)���G���cJ���3��l�t0g���l�EP�����
��P���/�^:��^����Fm��c��V�,��qX���8�C�������L�,=P���y_�o��%�-��DR-�$�J���EF=�7S[��X�r���F��V��"8(����7O�1������p�3������Rt��C�(_R���;��$�*�*�I�^2�9^XE-?A`
���n�b�Nt����E�Aq*�z�U2�z������@Cx�)r��?]k���X��%�z�#��p%i=�����M�|���F~.{�����f�R�a*Iu-�g��[9��x��'������u�e2WlIq�O��*0T��f����;��#�\��"�\���bL)3��rk%P��nt�Q�����Ab}��)��D��N�:��IS�-W����m��S
�����������984xmr�����n��.�{��NK�\aI�����g-�x#��F-�M�����wrY��3��B�*�x��l*4�4�)=}�E
0j����H��~��+
8
ZH�{��|�Qo������K�����B��� nE�����z"Qe�w!V*$� '�Oi���r
<
qE|Db���R��t��nP(�&�L�h�9�����h0��G��P�j���?`w5�QdV�c��R��RT�������Y%Lm�i���5ER�0��?����OCt��W��'�Wu�2/�w�������������W���^m�o���}T]9����`*�IyqGa�����dLxT���P����%7j��'�M�*�����M�*cD��c�=*lB`G����+�u4����{���D��������>��b�P+���$f���*F����x�=�xM:���4�5�
���k(�V�	��<�|p�����KX$T�/���xr3���>����#i�i!��V����)*�, V
'p�������y����n�*�y+������`���V��a��;�}���-A������-�I�u�x>b�Q'nk�����<#���:.����^G��M�aL94��u�����!F����*R��7����V�����f^W�9zr���
y����[0�@lT9�7E�Q��Ml(z+`��{3\��
��@$
;������M���G~�m��NBF�+�"�I�h�~h��v���G�������NXSZa���L�Ah�
g��
�*�w�'�S���sZJ�������J�
����)�����nA&en8������O��m��s%�����a/qn�8� �fZ���L���3&�v:�7�}��9����v}k5"���~�\����f]Hu����Q�������Y�����Yf�R�H�^ZL<���A+�__^v_�o�Q��p����i����h2nu>��'t%��?�T�=5�*�>]FxA)��o=��<U
�o}k%���4��%.!�oP"�Yx8���$�?�><jN�1�%e�R�&�_�Hr?1�$Y]D?�s;bY�����s��
�	E�I�����+�����7@[�*��P�$�s�����{��.b?�8����nP?VAR��xv�,X������t�$��b�S�������|J�dZ�\�5��l�.J�O�Q�g�d�7�3���zpd��~�:���!��`n��:p�u
�S���o�N�m������y`�ZU�te�x��
�rSh�OA�P�j�	5)p�#�L������!&��>M
��F�o�������H�&��xF;#4b�0�!0 �-���b37�NF4���"����T����S�C|1�5U��"�+H
�N����z(�$R����r��r�m	�sm� �����H= <��S�����R�
M��rg�r8�uFg��E�0n%����>%�^�0/�`�$��.M��t�j_i��1G
��IF�F�p9��
,70$5	&w�����#g�����!m�F;	��X0�r��%�1b�D��m�_6i{�c�3�l}@���E�"���*�I�M1�=���?��@�1_oC8�Y2g���x�8�a)��{�����H����$�8�sF��2����O3@Hi�V��Z����5�����M��}t��	��i�p�g��c�g������@B�=�Y)���l�k�U�:���nP�6e����J1����p�Z��3�BJ�X&`���	zxy��4H��5��� �g���������q7��)�d N�T��U��E����"D��	��)��n����y���}4��ec��P�7��US�������	�a;[�
��)"��!�����$W�d8����E�z�%�$Njux3p�1���f���������^��Q6����5x����{�;8�1�RB���L���7����}�{�g�YGB~����Xc��!q�������p,��x��s��PeL�������8IY��k�P�}����5��H�0�
;�D��
<�'Rh��,�|_0�����8�P���/�W�a��?Zw�"��$��	��s6MsRJ�c��HtEC�������������l�4��Q�����������U����t�$CMz�U`��{�4@P�[?���,Cjx�LigM��gM`�e>D�@���-�O�����/4�O����"�E��X�� `�gB�cU�{mNC4��1vIp��
aD��US9�[��'��fid�D��'`}��DTD���7��MX���=�V�]�����#��+[���%�G��K�!��	�aH��������>���t�3��;2�X�7E�	n�HD���XbhF����Y591��e�B]h���R1_40���	����/A:Jf��ip=nH���=�J#p!�J��������������:N�x!A:SJ���d�|��%*f3{B��6S�����Nl�QX	�l�Q����'���^��`�=� \����2��k	��{���YK.�94�D���\I����86��B�,�k�W��9U�MC������sUo���]%�
�`$����j�#v�`Rd%��}��=:����g��Ho4�O�%��]�xU�e��N'�mz���!�������I,R�9Ag�s�v�(�������o��a��G��{p}j�$�����Y����JEc��<��?$��;���&'�^�Iv@���!8�8��z���.�,(!V��5��
;���1���
V�����%�f�,���]��*A��5�56A��7����H���d[�iI��2�W����{@����9H�{	LB�����&B2i~?���Qp�H� �^���L(�����Cv�gUO���l��m��d��o�,�U�&�ye,��Y��%�*kLh|�fW�����\��^��c#DL�*��8,4���9@�x�%G��7 ��)����:���\�!��0��������L�:��VW$j���D�%���a����w��A� *���%D-���z�����',�e8������}�D�~�A���[h>d��i����I���aN���K�������N����#gCj�i�0k��!�~�^����� X�������WY�_��G��V��N��Y`�r��������@��[kI��#����<����������p���c�S6�o=�h�
2�(��1j~��>��Z�����^����	���h��n��NS��	�9�A��'fC�����S�)�^��Bo���Cz||�Qx���]2Qb����h��^�����_	d���a�.��@���+��}~"�8J7I�/�d�����
Lj���a�,s'qt��!�a\�89�D�����7��0���\��}YG�PU�����J�c�BU��2"���5����z;����=�Q�By%dW3k��$�����(Z{�����):f��^��Ldy*�=��7U�rU���ZPA�O1���������wh����87�|�i��#�t.-���z	��R?���2�X*�P��/�B������m�as(+�����fCq��Y�x���;;�ZkGy�1�+gj���)�e ����������<��qB�y@��-:D��-#���3?�ar��"!q���U���J��:$g�B��C�pOVV��6�V�^����X����A�������������o�Q��Fx��H����w�����6�H1M	�� �ki����q��
d8�[�������-R]j*p0��S�������L���4NEk1�����7�K&�T�����:%9ln�Md��'@L�m�VK��8���Er~�5s(����Y��h������/y=_�uJ��C;B�f�U�,��e�i�y��*�"����qw�������Q�[EI���7�_��`�y�d6��y�t�-�������E��\gz��v����a���+\g�U-����$A�K�T�������������7$��8H�[������c������������0����2�k��}���#�X���=��n���s>4����4.�I�f��uY�O�p����m�x�����eFVk���d�@iD�+pJi2�����jC@��&w���/��`�����D�#�������Q��Y�=2;�u����/S���N��+��{�z[��R\��g���U�Yv�Gj�]��Z��:X���'h��c��8? ;M�B��.$u���J-��������+T�P�Q�������	QmOV�T5XN�
�����3��\dtf9�p�5��!����]�G�fUj2��rw�"��^�k����n��O��{�����b;<����4�:��� b<%�v��V���m�Z;�6��|�A>��*�	�����f� PU��=~��+�4YU��"`����KCG%l.��7'��`T�F,g}62��++&�=��������������!4L%�$���a���s��C��Px8��\p8������?.��7�|n����|����+�uEDH��| ��J�n�����&���f������C-R���x��AKI��4~��$�F�*2f�T_<�x��J�x�5'H������G��������&��m����5$�_85�I�� ��qj(�d��~f�����;���I�f��$v;�Z��x��{A��y��K��:��Q29Mye]�v��l@���=C4T��NO��B�I��JF/��F�v��v9�_�9�������
�
�����b\��B��z����/L�yb6�sE��������tw?�m���<�+���V�*Hk��<��}�FT���
?L��P�I�n_TM��CW�Ef��8�J�a�-Q��
���g''&q���S
�r�m�T�2�*����s�Y)�/g�����B��L���S<Q= �������w��3����i����tM/	 ��*-�2J��
�~��$�=����=�f�z����H�����.��\^���5�2���"3)��~��"��X�R��I��tU�'�����\���� ���i����.�"um�W
H���i��^��E#��� �����*t%@G�q�p����$-v���\Wk#��0���4
a��8��sKv���x���j&�����,��H�-��$��;�^��kZ�����={����NP�P%/�����R�HS����������	l@��K��{|���.Km�\�5��?����x"���<���T�����GnTp�O"X:rR��h����#J�.%��}��.%������Uk����;�qu)�E�E<(�/5�,�<pp
�j'gJt/��#q����{K��:'����������\�t�0�����n.�����rA�����������
3-�c���%Z��c��	��x��
gu'�:5N�t9J�5���}}G�������w�Zr�����Q3%;K�	�w��F76�^���	���>����vsg�ejQ�2��B<��U(���56u�!E�q���T�ZDF7c��2�x�
�NY���_��Sj�roIE����#{�]���8H;+�����`�{$e�\W5,��U�h5����
��S�a� ~�=O;S��D�r��>�����8�PZ��;?��hz"�&T�`�0%X���W�o9���E�`���e	��\,d����q*�$��J�8z[�����=??;7��M�}2�����^��<>=<9������g'���o�C(?N�+����>����������
�^��Ye�;�@+|[)K�Z�;�gvq[��z�9�N�����h�b�A-�l�������
b���
�7/r�W0Qr�,��x�E���8���Rw��"�����Z	�d��n3|k�
�X6F� �5�r��0����`a�LZ	�	�W���h���?UG�z}r���B��xh���n�d��b�j���d�������������	L��7=���bN~q/��U�RFl�H��Aa�2f�&Z���|1���(�P&5����9�^<B��t��	���8��0N(���wh�fr�D�;FTe���5�����Rjr���A!���A�)�:���K��)�b������tlR�5�� @`H�������I�/>�2G�+Wr�CZG8����X\�)��V��j��������_����ie�����p>\�8���9%j�5��C<T���J�O����%f��W��yP��F�w�������8��y�"0���Y!�IMjsR�S����3���7� j�{�(I�6������V	2���s����q��p���jpb��o+.~cb���p�b���+�	���FfGz�k�K0"6h
��s������#Y�P
c��v�d'��Fd�5�[��1'�g��4���o���7�j8����L��jV��sV�����=d�k��]e[wA+���]�(RE_�"`W���7,CA�*iU�9�cU-@�������J�����VI��\�C<����)���$��4j5Y�/i���\������������rb�����b"�@Oh��n�����1��\jV�,�i]j���@Gi�p��1����z�s\�
Ug('�L�^ &_����b����0���7��B����Cp!��a�NO�������E�������gV3U����^����?�C�,.'o� jM9TC!y����3�{�!�������LIoN�[����}U�X�x3$�beDu�}�LQ����]9
��,�c�d�5���C�����fU 'G{��_�(��w�(��,�H��h���Vh�1l��b

)�t�\��nN,��������f�^	Z�H��7�|Yt�DJP�$_Q��m�]l;("Wb=����Yw�u�q/�Z� �y]:mZu�������E#+��{#s�����k�[�P
�o���;�L�Z^���y7D� Y�����r#�Hh�A�s=����&}����������)��n���h?H@���k5�f�A������K���\@��ck���O���_e�v�@I[
�3H��N�-Yb*���`E�����~vEc�hD�����<$�~	����@�I�y]0���1�et�2����(���Jn^{B�mu����0�9���bv|+3/���9G����#�	pi�K�%7��#g�������9�����<<��{�[�p+��T'���������	X�k��21�
��w��B�Rf�B���1����*#=�)�����(H������B���S�i5k�5��'
����6q�/�)���>NV����9�m���! �tiJ��H�T��6���vA�[�s��M4{�)��������$��o5�����F�,�1�����k��RM�t����p#�k���\y�F����
�������#m�����������U}��:R�S��t}�X������F#���4�6��� �������[tAZ�<Ew����(�n���^��U�zI1�����'�*o����}?�/���z�i?�xO��M�)�sQ���/�Qd�<��Qp��o��'�7�p����u�&�c�A'��8l���u��F���N���L2o�|9�Jg��[��=�K}�	�1(�{������B	�PCx�	��/�8=F��������& �\�o�.�v��v)��uq�{��?C�d��,��F�d
�$t�q4���X�����ZC�f�c�C���u��h��7�PVX��J�!j��M'\#���T�D]��q��t��X�A����4]�E�`�e�Pm���[D�E�J��h"HDr�D+>��
������M�!�bu����H�3h����`D���lv!������+M��F�3z>�\��	i�,�ZDt�]3(H=�}W�)�Q72������lB h_r);��2&|������ 2^OH��q��`�y�,��|�F�b��_��3m�C��F@���=<�&��)�u���X�Wdk�����t@[�W�R���"d)/lP���1E��.�9�T����{n���T����EnB���&\ '��4[�a�M,nq��h�;�<������U��M���a[������C���V~���wx��60����I��b�H
?�4B���|4\��F �;>3S�d��3�?�D f����$�j�:9i>�m��?���+(�yO]��.�35��]�������~�d�n,N���:4��H�031Z��m��pq���Z;������\GP�<y�[r-fn��J����h �����SE ��v-�cl��i�y���h8
��}�yZB�(��F�����@gI���
[��D���7yD�o�Q#�~!�i�|�j���\_h�'����j���m4���L�R�g)s��(�eK/op��F	d�c	��c����.�&�G��\\|���-I8��wN�?��X&)�������IU����2�^~,;O��p�A�+'��X�L�;r�������_��-�{�����:1��'����`�
CW����U�*�UN������MAg�M���}���y�I�)������U-cy���Z�Mq���+���`B>"�=�H'e�-�Ap#.%�����x�)
������kV���~��G[�WR��G?:��8
�L�(h���U�
��6��������\�t=���a�����o�E��v�&1�3�\8��I�p�Hol�0�	34O�Q�����P�'�3�1���H�yT`�-��4R��{L�ZK�'�z�%Fdv�e>��)_��f���wN�x>��D�X�����&|��/��$��?8�$��W1�N�E�4B��_�:�	�
>!�o�V���� ���g����d���(���HtZY��V|����wA��"7�����~��z��Iz5vw>BR���2����K�G�I`}���Q��i��^]\c�<
e3pU��_7��B��i�\(��w���4�Pm�T���=�[|�\��k�C��DB�+Vy��J�me5��I�0�\������y�j�����u���XOO�������h�E�r����,<��uK?������:�I�D�W�
�/��+���;�x�oE��d����1(�^�Y�b��G��xv���b�N��&�h
Q������ _�2P����"���~��
�n���br���l�G�V�����4��r|�|
yX�|��l�|�_��M�xog=A����������{���{��bo
�������\�!5�W�G<~�E2�yQ-��$!���:�4F��"���`Z�fio����&
����#y��gf��Ce�S�^�����_�[����_���`Q��$��4���`B��eQ{��6��nn�n��v��l��h�'�^O2x���M����5>"G�L����Rc/��e������<�]���'gGb�2���5�b��W������:���+���3(7���1�f��,�])��r�.�K��Ga�k8pnt�a���({���9`�U��(u������iA�igE!����L����H�`���+C��5y�K*���@/�<z.��W��OK	2�-:%�rL�t:�Ak�^�����^e��W�q��8 �`a��������l��>�)[�b������T�?�	<:{��������{�3�22Nz����xO{��+h?bf�s�������?j��Q�F��^��{1�(�w��q�G�L���J1�\�<����f�C�!�<pgn����e�Ev�*�^��J�e�h�"+�.L����E����+��>������������PW�cxi~�JMg��%��%���j3u�+��~���l���^��^�K��X�[��E�Q�� $r�����D����,!B�T}uvvrv�����E�����J�4�g���x��\
&Z]�Y���YxL8}���N���G2e{�%���2��U7Y��D��L^&�0�|KQ6�Bu�K0���a��9y��5�����k����������G����
�0}��8{csf���������F���q� �	���������
}��k��,<G��X'P�yt�����f�Xa�H�~�r�/Z���V��5U�
�-;��)b����F��q�P�m�Nr-��CO��5.���(�������W��U�~���@T����������Q�Q:v*Fk��]1�����5�u����l��f�Pp^��W���=��6j�]�9�K�L EEQFIa�I��l��I���nL}�?WY�Oql������(g�.�:SK6���YQy����J��5�F�f��R���5��f��**�����S��f���
j^�E��U���5.��*sS'uf�i+=$�������e�	�O.I������^��zBs@*n)Je�m�k��+���[�:�+��[�Z5T%Ux��E�/�����
��V@B�n��j�����nA��p�M�i$#��V+0&��9���Zi����%��*���e(�Y�&~��-zY�B��_2d�2?%�f�B�F����������_�������]��6�1�d��i-(}�8��3�:\������[�r����u�\�#�]���>+~Z�c��r^M���m��4�������S�g��������wW��"L^�Hg����D`����)���Rd�[��Gp��d<N&d"b���r��g�����9���n�^����:�����SA���)B+�J���%x���.fxI.n�l�����N�����n�m�_uO��vTL5�����Sf��,W��L�+Sj'��,0���Y��<�����b�F_�T<���L��!&3���������'T���a\��M���q��.���jFSL]���:���dU�:����W����Q�{�}���OD�'�Q���&+��[�s�����tE��z��Vk��4W ��"Y}q�@N[�j4I��j���_�I#h����4���Yg��w-O�T
~��n�-���r���R��X(n�����7K82b,b��{�P�����N��b�p���'�5�1e���Y
j�t���q�p��hq������vIK�������u�D����sT�hp&K��VW��ef<�,���lq������h�8�(r&�O������i�b�z`{�$h�<���P���8�pC!"�\1z�n�U�Vh�M��p����q��x�����_��o��F�ZZ��D-���AS����_�7���������CauA�<�V�X��7�a�M�����Y<��9�QV�x6u�P�k�������o�V�g�E�9��-[���Hfg�@?�\QD,*Z0TV�t7=�F ,i`�'Z=cM,�����S���K� � ����`�9�F�V�����g��������%���q�HaM$�.����DU����\S��xm�����\6��{h�)�9S&�(��c�@��{����&���38)������Ft���+�z7�t<�����+�i�Y�,����5.��.�is���!�4@�F��ZC7Sr.���f<�K�-1l����������/�2��}U=0�����t4%������&�A	��-��"L/��x@��p�<�4�@|��R��yZ�*�*2���V����z��T��%1Jv�;x��g8����kA�
fh��}��S��B��1���@������*�v�Sn���p�lIa8����0:�T{Q��o��pru-�9��d��ot@l���tB�����Z�rx����$c� ��
}�
���$f41�~�����~5S�{���?}����,�5?_���~�y�(r_P ��l�������k�F��M��at^��f��n9���6�M,���{l"��gIr��p"��Z�������j�ha�`�DP����i�,Z2(^��~��%q��SP�	����R���Z�M1����F����N'��k,�i.�h�.�E�q��6�-���K��T)����(��r
?kz�&>�s������'��n'�3��W2��3�/.���J�}_��@��b��	 59�WiO��\���/0�����A�{���{T����k���:�2��P���!��Y+�:P�1��}>���t��3�L������;6S��:6���h����/f�������8�]���B���"����R(��f��F�3��21!nC�s�
���������Cp����J�*�h�F�0��	vh�oS��qr������d��[��TS�I�|��"��2
���p�@�d�����'�����I��  bx�3@�\��H�AP����7%��3�j���U"�{+Q���������,��DE�����S���(�7G�S
9UL��Q*�<�M�R&�b����>_%68������)���7�a@����>��oDC��C���^��?Mawg�uD7&�nd�7���{n���+pWf�������+Ls�MPO�l����
��9w��� A2Qb�1�5��s �X����M��p���S����Y��mA�4����4)��i4����Oq��%����.�\�=���+����
��B��F��T�k`W�Yp�{������v�E+6���ZZ7y�$�	��#�
$7;������<jn��T0 ,!��n�����5���������"gg�h��d�1(�L�AQ*��E���X)��vxxX��*�L.���`@��@����T�H�����[�`�\�� ��AS5�WTX�����#�YPc�'3$h�*��M$��*0�x�wv��ik��K�\��;���H�2�#���_yo��<B|�X�6x�v��x�:���'��f�/%�N��I?�
��^Ru�����a�P�	�����B���i4CU��K�)����a������i����
[��K�-����������E�����
��&|K�_��Ng�R�+YJD���k�L�U��a�m���=���`%NA��"p���"�A��*�2�-���/n�+�nJ��"�q�R*���f�#��@�U��'��u�&
�d�"�[H@2������y�Z{����������o�z�����8������������c���<���$������N�Q4����a�8�w��;M�w:�s������(����#.D��)#624z��BrKRH$v1���;��?e�������E����%"���Y��\�/�U0�
�
)!��tf+�h	����#a]-�e��������S�!W�L�TP�����0$��w�}0��d�!fXA�����^����^�^o�������j9�v
�E��(�';�Sf���h��*��A��\_^�G\V�
�������z�����9M���(�������d��'��;�T�fD�9����>%����#��r?o@$�Y�$��`���U��r#Z)��h�/f(h�����v���DA���O��������$�&�[~�"�H
-��������A�c���|jP���Wa(p���r|=I����&� �Og��y��kI������F��d��IV#�������3�C�tE��Q��|Lf�9g'���	�Y�P����P����6#�No���
o�ib-�::�������*��}D��&;�P��?��bN�����\U��sp��#=�L�e����a�[*��mt/�6:��"
�l`���w�� ���6C}����d�j��=��^�%�J��.����\i����d�Y�'[M h������}zv��l��*����B���{'
*�a�Y�D?��C���Og��w_���3���l�r�.�Z����;@����V��[p��U�gl�"��O����.�3�5���� }1�*o�C����,�:x�������{5u/�[��t���k	���:SW�Xb$>���$_�KiQ��8����M�@?���N�	�lS���N�w0����f�;�d�4����N!c��N�}m�P{ ��.9��b�amg���Y�����)�.��X*���(����7���������X�2���I���k���50'{��	�B��W������`��W���4�< px�,o�����2�@-���������Xs`6�q��^�(g
��d���w�����.�'���Z ��+H��i����M(�>���('��Z�k�0��+s�����#������4H�j�m�,�����������,���4GJBj��%3�z����c�����I��K��";6G�$'N�����{}��F�`��R�|t3:{k������I��h�M��^��:x$~�1F�?X��-��v��T����/��%K����p Cq�4���A~D���.x�*�EYU�N2�[''�=�����{�a.����%!��)j����#�5T�K�(��
�R��e�Q�b��X��n-��04)�u-ip�VC�cd �2����N����%��i�?�6Zc�����1��llQ�	�h������s�5c��;��r����M�����
�H�V��y�\unA��l�s[�����I��2�����o)M���c���������<����h/�|��Q��2F�(p<�zx��E�%|���"Ts�TR�f��JE�t��J��%�e���:��lnS�p�ir����NF�� �M)[����%��A���	Lk��	3��t_:����F�W��8��r�;B b�O�k>OH�����p2H�q<�a{��E.o���+�A�f��Z�O�ifg��P�*a�J.�CH��|7��*�=���')�^�E� @zn6?u��V��W�������d�)���m��r(��v33+Y�9�7�=��"4��
�Gfy��z��{vz�7a�n�4��V�����dA'M���������%K���.��$v���� &d�������G��K�.pQ����!�N�-Q��k��1��Z�����T�"�qa:M
����H4�8g�<�!�6���T��o��/I��MW��D�L�?�8�_��g������`��+
7�b���%%����5&{�0�k�{ �f+�#I��� ��4�\���v���e��o����~K)�25?;�?K@R�F�T�K��l��Gr�r��H.It�}�N	r8�+�`|K��Anj<��������j��B���R��-�"����-��#�����������gO���F��X!z�����V,N�h8T�[f����/����O��'��o.�&�F:-R�t��rvZv��F�t��c.�b��I��B����H��.�3k�X[��^����[���}��/�J\>;qg��0��	W`R��\�]{c�����y33����M��8�|E�0/-�M2�_'���JoB�JI��L%�5��r��P5l<�2��M��~��n��Q�c��|�S�pq�!5����2
"��
�(��w���ao������s%S�C`SS�|��+����>�)���"�1�v�r��.��&�N�y�I��F_���U�I�� Gi
D�)�zK�:?{�p���!'��F�.�Z:
K��N����jpjQA��,���O��w����.V��>K�0*v8+%E�X�U�����������K{^'�A�fH��]�r�3>��h�#NG*�1���M���L���&��'��Ls��>x�]�R�~l�VY��m�Wg�
.c||�l����^$�������b�:������<���E�z��C��X��WG�~F$k����4��<f1���]�
��c�nE-�(�q��sU�������q�[�"7T�"�3���rG0TVTg^�~2���m2�v�d��Q��Z�^b��h=6(�)}&D��@P	�i�@���h�6���y�����Y.�q*�2�������Q>�.V���F���
�b���i��y�z'��-q�������y�*`7'��4���� 	��a�@i��eR�?o������SL����rA��;�����8f
b�G�����{J����{_�����X�����)��uv1B7[�L��6sS� ���3��,��p�"����L'����&W6����(��s����q\�f`m�.��F<�Y*Y�����T�X������H����	j<B���"�r���84����',P��H	5^���
Y.�f*���J��B�#h�����lQ��J�������F[�+��-Nv��s��"�c��<]sc����,5��@[ y�)3�c��I}�2ae�b$���)��=,M��%��S�|������z��\Y}YQS�a�F/r�"���li��5���	\�S�T����������^�DQ_�t�8u�e���$W{�s������`SG�)L���%!��p
kF����p��<sgj��ud#��v�k��
��C`7��(�z��T��WM�[��7��B�q��m	-��������qW^X����xJ�9^��)~����<w�z���v%���U
���|j]xr
����$lt�*Ju��	��G��^�g��`x����ug���;K�Jw�s�%�R8����~�
L�OAbwdi���e+���k���;����/��J��;rB����`�����m�I�q$���LP@j.�����%��&L�����1�g$�kb��]��j�"��'\<�����>\
�Y��&
@-�1�Z���G��^�-�/�m�D2|��?Q ��$yl{uq�!����6z ���)���+�F��7`�����P�,H :�L��X�����	�P���h�T+;�EcPh�w�c�y.����J�9m�wD��TL��V����g��!�}�*z}��?�9����3��vc�3����)����"�]������WTF��
�����{���&:)�$w�z:�j�J�Pj�0-���
�0��#�����jL��������^g�����Z���.8�����@N��A���R���h���|��M@�U��R-H`��qr�C��:3�uvwv��vc?�G����:��B����N�4��s�w%a�a�	�#������QP-�D�R)�p��#�|#,(x�����w���Q��yJ�R�,Byf�O�N�fx�q�q%�U���0Tc�M�TT?����q�9M�]}�{�L��GZ�2�N$����K�+a�9�����B�`��f���i6Yu�C���5�"s��H�����)�=l	�+U2�+�V�F��]U6$�
�����<�7�
��r6]�g�<�U���-%���x���e/����a�&�x�r	�Jk�v��a�~����XEu,��R���!�����`e����Hi=,D����|�K@�����q�n������x�<���V��|�C ::�\)�������)���l%�� O^�Jq)��$����3y����h������P�J�\�F��"�v.�A
�(J��������D�.��o(�1��3��Za����[����������<���()Nf*Q����:"��3���|�g�V�L}���[��W�DY���sn���D�
��p����&H��tJ��W�70�!,_�`k��9��Y�A!�&F�.�LzT~�~ ���X�_ke������,O>K	
N��Nb����&�L��|���8�lF��;��>%$���M~%C\���)T���W7R�@1	��A�Z����A�^&w��T�C�����\���G]_G��qF� �����LFN%N�rHHWT������S��G*������[DX���&BsPJc�+er-��
�Q�9�<��9!�rQ��a������ _3���7vj�������������N��r#~�S��ft1�C*����o+N+��i1\V��?Ug����!�%�~�t����%rv�Q.K��hl�M>Y�����L����8������'�b��0�b#�M
jh�5���]���wE��<;9|��%���������s�og'/�V�4M�q&:X��,�F�|OE�{ ��0`�lL�p�a�UND�'�s;�(���1�I���P���^��DnMZ��Md������,kT����$F�xC�x��
��*��8�yp��#�(�o}+8����L9w@��GF�O�SH~�����wu	I��=����~��}���j�4�F���9�������
gUL���"ACC��
aL(P�0�,� �}M�x���Y
��PY.�#���k(n �1�	8"�W�-�$�x��B<B��0xku-����hfWY����q�
�a<"�v[W���>��z����d^��i /����-P1���,�8���������3Q|�Z���S�A�uD*U��(�MS����29���Yv���;���V��l8pw�-������7)=�'����x��� �-i�W����,a��F�L=�!�s1	�1��#a��t? �d
+h7���FUq�h$9\&v�[�IS�F���d@8���#�ZgQd>�����Y�}��z��z�N��L).Wx(�b����Z�g/�����HB�c�H\��V��Sk������u��Mg�A9���&!����`�8[&9�.���h~� �������f��LlJw��A��S#�8`�R��h�I�0��@&��}��������������Ug��H�uJ��u��8���Tk@��x0@\��
��$��)�Lx��[q�m�Y� e��$�`�}�X�vKV8�QU�s��8���Z8��#����s^��\���e�����R^�j'Sp�	�i����@p|�rb�;�k��%A�S��V��/9��
��&�wd�U�Z��a��� �����V�hLPbx���7$~@=��T1CA��j�M,�a�0������p6�1}�V���~{xr������������$��Y}D}j��j���+����t�al0�n�~�~��$�h{��-�c.�(��R�����>�eBV#��5�A����C4������-����/�=)����~�YEn�|uf4��5���!)!�(B�2��������x��N��'���������*�Y�h�	��� ����6���q�������V���Y��X��4�����J$�P�^��+�i�dl�(.���T*��5�/�CB�Q52D6�qzh�,�uT�z����%����P4Y�}�[�W�>����
2�}x���3S*���:�
}R�����I��������������8���&	�
p�4���&VY�-��O��\��8/�lf�3it�f�x@�����������Vg��`k�!��7�����|�/��3��
1�^&�'��yO;d���~���0Z���)0	X�5�=�fLi1g����p�	���@:������rK���W������5�B������

f���8�l��
8�.��&����UNU}E��[����6h���N!���:��|2���"KT�D���D�:0�
S���
#Cr����>J�eIB��pE|o�U�%���j��.&�c�Z�����=���?p�l��;�N�zk���7Z{����$����{A���,o,s�Ig���6�������T7���/)�Uo-�Z��j������z�6����]�j��9`*.3�? }fOQ�K����2"v4z�^�W�7:=�<�����Z��Gq9
���#)��4 �z��WB�Vq��zV� at��I"����z����"���i�o�&�-y���������b\}%t�v�j�}��"��?�[<r�9����a5]����C���Q������W9��d�D1;IaV���n-����AJ���e��C<���ix����f�c�F��x*fJG�������m��U�Z j+���@^eV���7��>���D�B���K�k�y��z����Y�'t���$a��Q�n����R���1��5�U����1'f��F�5�Lb�h��.��	���������nT�y&��VI"c�Z@����.��g�/Jo1�����,s{���Zs� ���'� o�e��^�A\�]�F����>(2�s�m���"[�^#2|bv6�'�S
�j����������YjE���LM��P�����q
]����
=h�^����@F�����-��d�p���&�Vz�a-��l� �w��F�����Gt'v��%��l%D~�Q�:�9�����Ws��R�q�/�57�K&��i4�[����=rb��=�;�4�����%�<�M�=E�-qYf����p��z}��3�+8`s_:�j��]�1u�`�7(���H�����%��$�T�w:����O�d��sr��h9��������B_��'�L<��E�"/���{�������;.�����*-Y��\U�Q�a����_���+3f�&$��V4��,��1�VTX�M�pP^����
�Hh
���U�
*G���X��iG�#�V�r+}/������A�b���z�yb.��uyB�c@���-���3����~aq���h���tE������}�=�_ '#���nY�5�IUlg�QT�wq�8��}�4����@u����"�-o�x.��#k��zN6����J<�ir�z�?�����D���i����}u���W��Y}����d<�
�B=�L����$I��M��?��G�6J[\���}4��6JO=����t��{��� ����d�4����H����q���2R��qe	8�I=N��=��r�K��_x�a���x��~���Jj''����e"�91�F_]\�-�$�4�))�����&�.�R.�����c��uv���A���;(�*���s�\���n��XI.BI%�V����5z�
����Q��K�\��r���Ec~��J	�4V�<�:����PoKz{:�.��YQ�`��B����~���~}�:;����88={��LR��
;����P���R�~������j\�s����12�6�;��n��5�j�AO��i�%6x:���
��,�Fl�~B}����o`	�"���
BG������,�d��j��s)� j������n4���l=�����-7+;�|���3��!��.���*����
x�"�rJ����t3�rm&U��C%nym����w��l�
NO#q��/
.��`���y�O������t��&�m��m�\��w'�'��[��K>w�a��q��e��1��r"ip��-�^�#��X'<?_�mc�h��Z-8Kt0�j�<��O�{7K�G�@���~����C7U��g��l6��?�'�&�X���8o������n��H���?add|=�v@3��/*�h���a������!��["�Uv#8�q�,�`p�LK��[J����fn`XsR����74�.bo��&��t�{{�����9(�w*����9e���B��R���"Rn��$e�z������f�.c�����\�=��T._R����V��p5|DQ�2\����^�85>�6�m�;:se�b��Rm�[�`����0�|A��^�C=��o�0��ed+��dk����}���A��o�������SC)�:e������`��p�\�������7ZO���H�O����|�x�{�yKi'F���S^r�j�q�O2~T����'��c�i�&��*,p�W�b7z�}�\���*��H�[\��)�?t���!9Y�]� ��
>����������0�:��x���(��Ie�,s^��	����O|��L��4~&3�����������WJv���of�h<�&#��7�n�S�����:r.���:4��g\D#3��_5�a;:t*u�[8������Y��\����/�a�.��9�X�K"�S]Bk<i�[�b:��]�{N�����CO����~��$����5��.U4l�]_�������n�pD�1p ���G;$!��$P������c���g�$��X��B/w�! �M�}��+}�
�!O�Grc���RM&���/���'+��%[,9�cS7GQ��<�DX(��*�+!%�8�"�x$�(��]��y����9������C.,�i�W�iJ���n��)Z�nd��fx!����`�8
����%�T�ag�.~2����`��D[Sc~E�
����E����`j�N�# �
Q����8x��Bh����������Fqv�����@Of.7���:��O@�A� �]��6s��3��jK�t�A����dV��+��#zV6*�KeB��Z>y�"N����B%Xe�����@��m��/Zx���5�
��� ��I�C?�/Io�N���
��<�b����h(��]�F/	�d|��iT�Y�~���9��^�V8���Zt�S�g2p���qU�����<��`f�[�C]G��?w!9����
gt��a��b��sC��S��H��~D293	=/X���D#;�=��cm�D�-W3�fc�@;}����\�[��r�=,�FrN�d��w���OK ���
�
�K��������r�0AU����2O��?�5jqsw��"j�k�;��v���v:.\+��$
����Cx��K���Q:�+���1[.��@��|��$w����K
�v��`w��~�����w/���,���e��MD��8��wo��x$g?����t�~E�e�:�<qmC/d����-���i�Z����J�(���%��BC��N���������N;*���:�$TT�������K���E�1&��,G��`�d�5BX�C��s�s�+<	�����Jmk��A���6�D ����i�5=U��)b[��ls���u���}����R7DQs�������_��v�aDK�t}x:�c��Y��j�x����jMT6�o�����q"[n�A*R�D�M�B�9\�DTi��f�o�G� �6���V�<�;����\&��}��mx?JB��=<y���l�J��f-��o7��-��PZS��Z�+�m�����p����������u}��O���<��k77�{�n>��gg�?�������������PoQg����~t����enX_�jA\���~ ��L3�>��<n�����^�?$�SB)$�e�b����t�Ng�*:a��:;����iK�h�S~��~ke��<�{;���[9�����+n�
�o�Z6���up��7d���3�h8J�i�I���g'�B�-Sh�yvG���Q2�YpxT��sw
7���~������G
�@_��8���ZPi������>�y���Q�?��n2z`��0e�e������������������f1�ol������MdV�l�F��;���C���V��5\v'���_������a�-�B�����Mtb����v��O�e��V\�v��#��_���!U]^R"C8��Na������'��I��_J��y� �)E��8-|f�oUR����v��o���[!���S��~���k����0��)P�-���A�e$g�qWO�����Q���A��5)*�{���l~����,����I�F���\�����%�[�6ws;�%P
��ng^A����F�m �i�!y-]{<<�r�	�-�����+��h7:h�|\���s\�t\������L����b�K�S�q��n����M��6/����1��s�7h�TK:VH�|�z�vmv�f�B���~"�8A%C���&B��X�QC�V3r���Zf��C��D��-�/�������<���+���a@FJ�Z���k����/����m����R��:7n�r�E�tR����������|��F���;�U��������y��������R��e�%�B�nC������
$0}�S��Y��!=��%�LW�Q������eU/��r�b��Pgb����N�g�He����4�A��<���!'dd�EU�����1lh�
e7|���!6w���x��
�D��}/�.;����F$XU���
�'��
��yJ�@h�t�?g�Xj R����Ga��O:uZ{��V?u�a���i�N�����:gDT�
���g�������=:��&�r6�0�c�-;�B4
&�~��-����d;�O��Ixd</M�g7'��3::��9Gus��G$f������@����9:�"�(�`^tJ��)Vf0z2<��X#q�`�����`����g�U�_�C$�A���7:~oBt�A�	�z����it��Ae����Q[�����J5G��p����yn�b&�z�<�*h,�LS��lo�`u�tHpq�<3�t$��f"�R�F�B�������N��.e�l�
�����A��2�uMhL����=f�$Z����'f6(��.�����hLBB�S�w0~j�/	�B�Z@~51��pJ���6d�mv|�|���x��A��y���s����*�M%S���T���j���*���a�49�N��/����dJ�KH������=�����
B�*��;{�P�;
��/��=�++���cp��5~��!&���X66C�I������&����T�+���5��A�u�`��j�c�����a�f���tW��4�.�����8{��
E2�6���eN���{������4�@�����=�H�1I��d����><�[������ `���pt�	�-A���U��G�M'���T���(�|jE-�|����g���+��w��m<l���������-D�7���;�i[x�qo��7��hz�%�q#��=�9s�(�A��*�};PDz�;�����]&y
iSV�_!n<��2���s�2��M�����u��8[�7ix��%������$�Y�)�eSs��
��r�5�sU�e�5fQ�.Q���>�~������6���A����M^�����4J��K}/�����:�L�D���������8���s����n�A��1�����h�~����nS��k�>�-��V�t��k3�52!4V{�r.���
��&����U9;D��]�G9�g8�t<���q��4�����%�Jya�e`������ ��z}�����oy�3|�o8�v��hc��n�����ir+����F_���[���;��JK
�w�x�laHg)>\��c"����p-A����/i�?������ u���HU�IJ�����
����q�����Q/�N���p�#1w��O�.��(��	�����V�����mnVYS�{��V[��l����h<��Ap:��?��M������i>���mU�x�dd����q
�rO
6�.�������|�d���z�/�fKp#?���`�(�������57Cz��YP�9��K3G��I��J��R�F���fb�
 8�F?�2���A4����`9)|45��h���^�����2y�h[8���\���i�C5�ol5�T:�N��B2�iM�L��������h30a`���ZLSCO�������������&6�D��l����l$/����N@�F���3M����^��~c�����wyj�:�o)
��U-n#�M���j��av��?j�78sT`�y[}��<z���yB~\�o�pP�	���oi�"��Q���Jg�����~2�x��a����A����h.��J4����2~���x�od����I������������!_�7��������.��B\�����k&$�~��K
�#�xz�sF��c��A�?������1�.E���c�X�"�?l6���v��3tl�������J��?Z"g-�# ��>��_�Vl�)gZ[�R-����&^�x���c�����zB���hb�I53$�8�-���-V�y�S�G��U�YC�3
&/P^���]|Y�\�!�vf@��rf��@�u���3�t����9���Y��:p[��@@h$E���O���G�O�U����t���q���9hc7�t&���D��Z\�7��7����&?�3A��<h	��-U|���S��G�x#
��X+��
#|
�(���3��!v`�'�vt�������	�
�lv����C�������+���%��$Ew���r��VV���O>���M$Lo�.��+��_�w��0��)�����V~����#�6���0!�W<T<��nJ���� �����P�"6��^U���:�0�`���'�YC����!�0��DZ�ToC��=����y�A�yq�d{{`�>Af[���h���m����B`h���B�_���!+J8�4���x�(���Fy����lB�����p�ov:�X�&�Da���7yD���^���s��FVs��V���� �����M�2G���_�Vm��7�8�>�T�yx7>� m6Iq4��I�F:������M����9��[`�����^P!
�4��#>�[]�#?R�+�g@Z���>��Z��#����T<�9�j�(��A��G:0����������4��&�3����C������[�D�����(����'�W�+&���`��Nr�������*�������v���l��Y����w�L���9��������e�d���Oi~�*���R���=o(qw�$8���68�����y���K��CN�2����OE�B�r��]�9���:����8@5��i�Al#����-�v���a)��i
*0 3���[/����'P����B/*��j����<��^H���5�)O�[���S'�P<1a�8��<���)�3%�%�>�?�T�$�����)�S�������L��h���tIE}i�O
EejQ�n�S����Q�>
6�������H���V��JS����	!:�l�*��+�xp���G��_�a���{2��^:��bIe�{�'���}���f�M96��
d�e��a���V�^�����8	�#��+���S������Ep�Xc]\);�����E,z�Y�6r�{�.����F'�j���v)�#�a�+J����p�R��i?�������\�'0e�F�a�Y���~��^����WLW������"�OC<{�����S}-�9�R��y/��8o����m�M<�m[5�� ;���@���]�.�v�;g(�t����������`�����PB+c�m������*��B���{����?�O���z�:�i2�^���A�eK��;���c���u�"Q�{��JS��Af��P��$��������t��3D��E!�$<{6��a�� V�BQ_Q���d��d���^�$������z/�I�
3{�i_p��v'{������FB��k4�'��N�3��'��m+���n��\b)���j��"��C���b�i�z���G`����R:>��F��P�N�3CO�M���M^F��|�3l�-�(�(��������<�����rwt��B����
S����r��������d�>�n'i
��S]�~������~��c��g�#�(` 2I�����pAz���eW�D�������]�Z�
�D����k@%���~��(�>��]�s����:J��Z�Eeh�k�-���?��-�z_V�20�Lq�W4w0�#��>X��f��U�T�J��R?�^6	�4$�Q8�tt��(�G�%l�p,��h�=n�i5_�;�G���:�\t/.����7g����9N����=�"��T�	a"��h�_n0�*�������#��4��[�\o47�|}~xz�s�e�5�����(�+���H�.����Me�''\Si��sZ��.a������u�g;�"�7������T�3�"�����G���g�3��@��v3�F���5��4v���O�2;cp�
�����Z��'�iA��~�0�nq�����08�~{���B�$SH�����pj�g���y�_�OO��X�)%���j�4w;�z�u0�5�rg[�2�lKOn�vpk���eWH��W�dp5����j�rc��Q�����������H�����!���	�c��
mX���Bed.r
C����A+�����������o���;_e�,���|�h�)�NC���}3���b�� �?{[��9���U�������+Q��^��9�[�m��D�'���-�I����&�������xT�����k�S�!��
�����;]���N3<?�<�L�>s�[x'�n])��qxO�g���m\*a��G�����h�Q��Zk[PHr����+lE�:�o4�nQ���~S���#��0���~����y��^����{�v��3��za+l�We�~]���_Y�n�9$�hR^r
�*;Fe��i,L�)k�MM����L����7=o�Y4�[��V�����)�J�k>�mk��!7�ZS�k~�:�l"����P��v5���b�@t��`���U�p�R���;Lc���M���)�|��n_�g`J���D��k�����f�o<,��h7u
7�yfNu�'��O��u�'�
k��K�!p�-�c���l�*j�a���B�ML�����md�g�p�'@b��M����80i�_���9��z��>�M��HP6hqj��I�TW.g���:�.�/�����v���^�����U��W�26�&As�M����&m�l6����W��
o�aMF�h�m�l�^�^�<J��x�+3�g<G�� �����$�Ax_A^�����D�+��t��=W�\�{��~��o~�Z����t6�'�6����]kd�W���_���������>NO�~{�u7��^���p~s�A|y�$���F'���y�=<�&����^w����k��-4��������y�ou3"���Jg��f��?���_�=�lF*u�U��.��v0;��[��6��������P��D��kn�6`r�Jk�5V}���@��Q�.`���V����\���T,�(�8�����z���Ax's�f>����\t~�O�.����8�����������@��_��AY�����@����K
e-�kQ��z�I������_N�pN.�I�9���=�@�n�%K������������v�����	��=��9�]F"���M��4Uj�R�5�W�NsF���blMI�������s����mt����|T�[�'��9�LH���..�ay�WW�A:f�,�B�'�d�]��Yj{�� ��?���\��F���T���A���5�����7A����Rw���|<-/;�F����M�!����C�`z� ��-?/�PEU���+SPrg��rt3��J��{���E���� �]������a����?�o���x�_���r���"\,����*���
���B�7e���$�A�Y���Z������/�v._�=��2�^�xfW�������C��?�����60���0�n���k^)�Pb�c�~����]��������\<�����Zcq��?�����$��
��������X=
���N���Q�^��h0�F�XN��hF:�-�'���.��1��k^N�K<�VYc�eZb��OZaw���Zz���L��I�65o�0��^B;��O�O����pwCk�V�-Y4�N��������}��7k�Ruq9�'TZ�
��������}V>
����N>�L�j�X�d�;w�)(<^�;��JS�o��>_�����(�kJG�s,D�Fm=�(;�
���dY�a�t`���|�����G����@��LC1iv,q�8���?���{P�"nww�->�������^m�>z0��X>{j��9fc��M�@��LA1q�Y�Xq�Md+��|50�>�P�3��~�{y�6�����/n�<n��g�)�g=$Q�_-������D��2{N����2P�h�����50����%��B������Bj�qt��RP^L!{����0������Vce_���YD��!e}�|��&��^+��; �s����{b�)Qu<
zh��~SR�[�5��;j��A��QL����%Z�^���j�y����O���\���>�|UT��G������������{�9;����`�4�a��/�U���p<2J�=,\�xG��#��<zg�2*E�H��0Fb\�p�C�R/�����Q_X���q��3%�������S� !��j�7O����r��*���H���,�!q���f7�Bss��]���#gO7Q3������������M����rd����j��~��c3*�k��vz5�&���J���)��+����!4��S=���R�:n�����KW1>������y��E�`5=�M@hJlb�!r|g��Sy�&4�v����n1�]5v��%�@��o�.����F_������A���{���_\^h7z�g��4��G�,��kXS<6ON>�XWh�����~��.k%?Z?���BW<R����@�Br���F�o���h�3l7Vv�6�,;LLA:J�wvQ���;�(�{
�n���t�����m��)����7]�/t:��Z~@�i7�����J�V����|�]G��]���ay����l�@��NpS�5�>���
/�K�?�>����\���lZ�"~.��?8�iP5�'EI�s�������*���9�t��������z�����~�7<������X�O@�t4�-��a�P?;Lh<�=��4E;���k��#\�d�m�������������`{-]�����k��'�v���k#��N)>nz�Z�_m������������^��}����W�	��~�hx��~6�����7m����v������s����p4��A��a����@bf�W R%�C8�����/�M���������Yml�6Y�.��o �6�^�?A~�gl�����%+�8��7%�/tX~#89~}��!�z�lo/��C�w���?w�{<M�{�#����Y��J'���wR|TR�7q�6{��� wr��yg[�w��h3���B���g��#����w�����U����������2��� �)� �M�������GV^��h��u����z�xr;�=u F�i2���")���\����ng�^o�����e����J����	��N�M��vg������N\����n3�����^p3��_�,��Si#�tc���B��������T��s?:}��5���@����m+�&]�6���d����-,*���u��7�q�$�T��mR����7�7\LW�
��e;ZQ��B����N8�m.����l��pG���H�[���ZaK����_D�?���}�<��)��,�� �'���%��O�e{4���IA�)�;I���N��x��6J\(��R��Fo����l�*`|}�m�M��~J������p���r�of�oE�4�"@
�D
��^�gw�}��g�V�;y��~��d�y��`}�"����)�����|B�;����-a�~���?c�Z!��.�~7z��^��D�pXXIQ���N�����A+<htKhYe%���82��.a���v��Idv2�aQ	�(~��U�;!� ��VJi�"������p8�~BjJ��f�zhN�A�BW�����C>���f��7g�
��,��	�)���e���"���&�G�z�E��p�w�a��8��(��0�=�O
�����%�G��uC��9:A�2������������o��og3��m*N��}��kZ�?�d�+XNJ��A�4��mb��`��t��7�E:��;�iNf�@P��}����i����ACge.�Mi�,�����hoc���	E�
�����C2�`u��|���\qL����!����;��7�EP1C������6��]������:F�L��_�v���$Gq�9�:_W�=0���o|�L������-���>+��9�Wl����&���2��G�l������L;����>z>�U~����s}����������}����%�^������jk���z�6?C��U��	�-���A����~V?n�U}����3�g��=�pN#���e�W�Sw�v7����>N��GL��K��{��L���d�O��+K��{J������\R��eV���+JW��9y�����;������[;�D�Z��{R�_KR��~������}���
���oN���lf����l`���R
W~��u�:LQ��e���,�v��$>}@@����d)[�v���c��ON<�lsv#�q�u�����,�_��V�P�&�Z)���2��FS��L���5�l9��Y��)����;{�V�^o�{�hg�e�X��l!��}����������RP�xv6��I|����,�o#��o#�T��e�Z�al.���+$����Q{�^o���~oo���W�`��	��M�����c<a
5���q�(E	�{�������>�y�J�~c��>O�������`G�e4ip6�x����~�W��^-��W�d��}�D?�d
z�@�gL�R��ZcG�r2$ �h4/����'�={��/��FnA*��4��8�K��v�"h�<�%��|��/[?K����Z��s�����,��fm��+�������{���3��F2������>G��������L�R��#g,)o�qr�����.�z���<jf��f��E��C���h�}�=���\��;��Y���{��
�����������G�_��{Ys���6p������f���������������z!+��a/Z��W���-9��5��~�~����+S~��G�����m�;7Z��"��+����
T��!����������=+��o���*���/*��>���!������/�q�����"����{� �p}�v����F���m���WQ6�pS���9����<�"i�
0��������?D����
���+F����o������E���4��"�Y�o�Q?�7���+V�B�
0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patch.gzapplication/x-gzip; name=0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patch.gzDownload
#31Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#30)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/15/2015 07:51 AM, Peter Geoghegan wrote:

+heap_finish_speculative(Relation relation, HeapTuple tuple, bool conflict)
+{
+	if (!conflict)
+	{
+		/*
+		 * Update the tuple in-place, in the common case where no conflict was
+		 * detected during speculative insertion.
+		 *
+		 * When heap_insert is called in respect of a speculative tuple, the
+		 * page will actually have a tuple inserted.  However, during recovery
+		 * replay will add an all-zero tuple to the page instead, which is the
+		 * same length as the original (but the tuple header is still WAL
+		 * logged and will still be restored at that point).  If and when the
+		 * in-place update record corresponding to releasing a value lock is
+		 * replayed, crash recovery takes the final tuple value from there.
+		 * Thus, speculative heap records require two WAL records.
+		 *
+		 * Logical decoding interprets an in-place update associated with a
+		 * speculative insertion as a regular insert change.  In other words,
+		 * the in-place record generated affirms that a speculative insertion
+		 * completed successfully.
+		 */
+		heap_inplace_update(relation, tuple);
+	}
+	else
+	{

That's a bizarre solution. May I suggest a much simpler one:

Make the heap-insert record the same for normal and speculative
insertions, except for a flag that's set if it's a speculative one.
Replay as usual.

When the speculative insertion is finished, write a new kind of a WAL
record for that. The record only needs to contain the ctid of the tuple.
Replaying that record will clear the flag on the heap tuple that said
that it was a speculative insertion.

In logical decoding, decode speculative insertions like any other
insertion. To decode a super-deletion record, scan the reorder buffer
for the transaction to find the corresponding speculative insertion
record for the tuple, and remove it.

BTW, that'd work just as well without the new WAL record to finish a
speculative insertion. Am I missing something?

--- a/src/include/storage/off.h
+++ b/src/include/storage/off.h
@@ -26,6 +26,7 @@ typedef uint16 OffsetNumber;
#define InvalidOffsetNumber		((OffsetNumber) 0)
#define FirstOffsetNumber		((OffsetNumber) 1)
#define MaxOffsetNumber			((OffsetNumber) (BLCKSZ / sizeof(ItemIdData)))
+#define MagicOffsetNumber		(MaxOffsetNumber + 1)
#define OffsetNumberMask		(0xffff)		/* valid uint16 bits */

IMHO it would be nicer if the magic value was more constant, e.g. 0xffff
or 0xfffe, instead of basing it on MaxOffsetNumber which depends on
block size. I would rather not include MaxOffsetNumber of anything
derived from it in the on-disk dormat.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#31)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-15 17:58:54 +0300, Heikki Linnakangas wrote:

On 04/15/2015 07:51 AM, Peter Geoghegan wrote:

+heap_finish_speculative(Relation relation, HeapTuple tuple, bool conflict)
+{
+	if (!conflict)
+	{
+		/*
+		 * Update the tuple in-place, in the common case where no conflict was
+		 * detected during speculative insertion.
+		 *
+		 * When heap_insert is called in respect of a speculative tuple, the
+		 * page will actually have a tuple inserted.  However, during recovery
+		 * replay will add an all-zero tuple to the page instead, which is the
+		 * same length as the original (but the tuple header is still WAL
+		 * logged and will still be restored at that point).  If and when the
+		 * in-place update record corresponding to releasing a value lock is
+		 * replayed, crash recovery takes the final tuple value from there.
+		 * Thus, speculative heap records require two WAL records.
+		 *
+		 * Logical decoding interprets an in-place update associated with a
+		 * speculative insertion as a regular insert change.  In other words,
+		 * the in-place record generated affirms that a speculative insertion
+		 * completed successfully.
+		 */
+		heap_inplace_update(relation, tuple);
+	}
+	else
+	{

That's a bizarre solution.

I tend to agree, but for different reasons.

In logical decoding, decode speculative insertions like any other insertion.
To decode a super-deletion record, scan the reorder buffer for the
transaction to find the corresponding speculative insertion record for the
tuple, and remove it.

Not that easy. That buffer is spilled to disk and such. As discussed.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Andres Freund (#32)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/15/2015 06:01 PM, Andres Freund wrote:

On 2015-04-15 17:58:54 +0300, Heikki Linnakangas wrote:

On 04/15/2015 07:51 AM, Peter Geoghegan wrote:

+heap_finish_speculative(Relation relation, HeapTuple tuple, bool conflict)
+{
+	if (!conflict)
+	{
+		/*
+		 * Update the tuple in-place, in the common case where no conflict was
+		 * detected during speculative insertion.
+		 *
+		 * When heap_insert is called in respect of a speculative tuple, the
+		 * page will actually have a tuple inserted.  However, during recovery
+		 * replay will add an all-zero tuple to the page instead, which is the
+		 * same length as the original (but the tuple header is still WAL
+		 * logged and will still be restored at that point).  If and when the
+		 * in-place update record corresponding to releasing a value lock is
+		 * replayed, crash recovery takes the final tuple value from there.
+		 * Thus, speculative heap records require two WAL records.
+		 *
+		 * Logical decoding interprets an in-place update associated with a
+		 * speculative insertion as a regular insert change.  In other words,
+		 * the in-place record generated affirms that a speculative insertion
+		 * completed successfully.
+		 */
+		heap_inplace_update(relation, tuple);
+	}
+	else
+	{

That's a bizarre solution.

I tend to agree, but for different reasons.

In logical decoding, decode speculative insertions like any other insertion.
To decode a super-deletion record, scan the reorder buffer for the
transaction to find the corresponding speculative insertion record for the
tuple, and remove it.

Not that easy. That buffer is spilled to disk and such. As discussed.

Hmm, ok, I've read the "INSERT ... ON CONFLICT UPDATE and logical
decoding" thread now, and I have to say that IMHO it's a lot more sane
to handle this in ReorderBufferCommit() like Peter first did, than to
make the main insertion path more complex like this.

Another idea is to never spill the latest record to disk, at least if it
was a speculative insertion. Then you would be sure that when you see
the super-deletion record, the speculative insertion it refers to is
still in memory. That seems simple.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#33)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-15 18:53:15 +0300, Heikki Linnakangas wrote:

Hmm, ok, I've read the "INSERT ... ON CONFLICT UPDATE and logical decoding"
thread now, and I have to say that IMHO it's a lot more sane to handle this
in ReorderBufferCommit() like Peter first did, than to make the main
insertion path more complex like this.

I don't like Peter's way much. For one it's just broken. For another
it's quite annoying to trigger disk reads to figure out what actual type
of record something is.

If we go that way that's the way I think it should be done: Whenever we
encounter a speculative record we 'unlink' it from the changes that will
be reused for spooling from disk and do nothing further. Then we just
continue reading through the records. If the next thing we encounter is
a super-deletion we throw away that record. If it's another type of
change (or even bettter a 'speculative insertion succeeded' record)
insert it. That'll still require some uglyness, but it should not be too
bad.

I earlier thought that'd not be ok because there could be a new catalog
snapshot inbetween, but I was mistaken: The locking on the source
transaction prevents problems.

Another idea is to never spill the latest record to disk, at least if it was
a speculative insertion. Then you would be sure that when you see the
super-deletion record, the speculative insertion it refers to is still in
memory. That seems simple.

It's not guaranteed to be the last record, there can be records
inbetween (snapshots from other backends at the very least).

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#31)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-15 17:58:54 +0300, Heikki Linnakangas wrote:

When the speculative insertion is finished, write a new kind of a WAL record
for that. The record only needs to contain the ctid of the tuple. Replaying
that record will clear the flag on the heap tuple that said that it was a
speculative insertion.

In logical decoding, decode speculative insertions like any other insertion.
To decode a super-deletion record, scan the reorder buffer for the
transaction to find the corresponding speculative insertion record for the
tuple, and remove it.

BTW, that'd work just as well without the new WAL record to finish a
speculative insertion. Am I missing something?

I'm, completely independent of logical decoding, of the *VERY* strong
opinion that 'speculative insertions' should never be visible when
looking with normal snapshots. For one it allows to simplify
considerations around wraparound (which has proven to be a very good
idea, c.f. multixacts + vacuum causing data corruption years after it
was thought to be harmless). For another it allows to reclaim/redefine
the bit after a database restart/upgrade. Given how complex this is and
how scarce flags are that seems like a really good property.

And avoiding those flags to be visible to the outside requires a WAL
record, otherwise it won't be correct on the standby.

Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Andres Freund (#34)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/16/2015 12:18 PM, Andres Freund wrote:

On 2015-04-15 18:53:15 +0300, Heikki Linnakangas wrote:

Hmm, ok, I've read the "INSERT ... ON CONFLICT UPDATE and logical decoding"
thread now, and I have to say that IMHO it's a lot more sane to handle this
in ReorderBufferCommit() like Peter first did, than to make the main
insertion path more complex like this.

I don't like Peter's way much. For one it's just broken. For another
it's quite annoying to trigger disk reads to figure out what actual type
of record something is.

If we go that way that's the way I think it should be done: Whenever we
encounter a speculative record we 'unlink' it from the changes that will
be reused for spooling from disk and do nothing further. Then we just
continue reading through the records. If the next thing we encounter is
a super-deletion we throw away that record. If it's another type of
change (or even bettter a 'speculative insertion succeeded' record)
insert it. That'll still require some uglyness, but it should not be too
bad.

Sounds good to me.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#35)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 16, 2015 at 2:23 AM, Andres Freund <andres@anarazel.de> wrote:

I'm, completely independent of logical decoding, of the *VERY* strong
opinion that 'speculative insertions' should never be visible when
looking with normal snapshots. For one it allows to simplify
considerations around wraparound (which has proven to be a very good
idea, c.f. multixacts + vacuum causing data corruption years after it
was thought to be harmless). For another it allows to reclaim/redefine
the bit after a database restart/upgrade. Given how complex this is and
how scarce flags are that seems like a really good property.

And avoiding those flags to be visible to the outside requires a WAL
record, otherwise it won't be correct on the standby.

I'm a bit distracted here, and not sure exactly what you mean. What's
a normal snapshot?

Do you just mean that you think that speculative insertions should be
explicitly affirmed by a second record (making it not a speculative
tuple, but rather, a fully fledged tuple)? IOW, an MVCC snapshot has
no chance of seeing a tuple until it was affirmed by a second in-place
modification, regardless of tuple xmin xact commit status?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#34)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 16, 2015 at 2:18 AM, Andres Freund <andres@anarazel.de> wrote:

On 2015-04-15 18:53:15 +0300, Heikki Linnakangas wrote:

Hmm, ok, I've read the "INSERT ... ON CONFLICT UPDATE and logical decoding"
thread now, and I have to say that IMHO it's a lot more sane to handle this
in ReorderBufferCommit() like Peter first did, than to make the main
insertion path more complex like this.

I don't like Peter's way much. For one it's just broken. For another
it's quite annoying to trigger disk reads to figure out what actual type
of record something is.

If we go that way that's the way I think it should be done: Whenever we
encounter a speculative record we 'unlink' it from the changes that will
be reused for spooling from disk and do nothing further. Then we just
continue reading through the records.

You mean we continue iterating through *changes* from ReorderBufferCommit()?

If the next thing we encounter is
a super-deletion we throw away that record. If it's another type of
change (or even bettter a 'speculative insertion succeeded' record)
insert it.

By "insert it", I gather you mean report to the the logical decoding
plugin as an insert change.

That'll still require some uglyness, but it should not be too
bad.

So, to be clear, what you have in mind is sort of a hybrid between my
first and second approaches (mostly my first approach).

We'd have coordination between records originally decoded into
changes, maybe "intercepting" them during xact reassembly, like my
first approach. We'd mostly do the same thing as the first approach,
actually. The major difference would be that there'd be explicit
"speculative affirmation" WAL records. But we wouldn't rely on those
affirmation records within ReorderBufferCommit() - we'd rely on the
*absence* of a super deletion WAL record (to report an insert change
to the decoding plugin). To emphasize, like my first approach, it
would be based on an *absence* of a super deletion WAL record.

However, like my second approach, there would be a "speculative
affirmation" WAL record. The new speculative affirmation WAL record
would however be quite different to what my second approach to logical
decoding (the in-place update record thing that was in V3.3) actually
did. In particular, it would be far more simple, and the tuple would
be built from the original insertion record within logical decoding.

Right now, I'm tired, so bear with me. What I think I'm not quite
getting here is how the new type of "affirmation" record affects
visibility (or anything else, actually). Clearly dirty snapshots need
to see the record to set a speculative insertion token for their
caller to wait on (and HeapTupleSatisfiesVacuum() cannot see the
speculatively inserted tuple as reclaimable, of course). They need
this *before* the speculative insertion is affirmed, of course.

Maybe you mean: If the speculative insertion xact is in progress, then
the tuple is visible to several types of snapshots (dirty, vacuum,
self, any). If it is not, then tuples are not visible because they are
only speculative (and not *confirmed*). If they were confirmed, and
the xact was committed, then those tuples are logically and physically
indistinguishable from tuples that were inserted in the ordinary
manner.

Is that it? Basically, the affirmation records have nothing much to do
with logical decoding in particular. But you still need to super
delete, so that several types of snapshots ((dirty, vacuum, self, any)
*stop* seeing the tuple as visible independent of the inserting xact
being in progress.

I earlier thought that'd not be ok because there could be a new catalog
snapshot inbetween, but I was mistaken: The locking on the source
transaction prevents problems.

I thought this was the case.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Peter Geoghegan (#29)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 9 April 2015 at 22:18, Peter Geoghegan <pg@heroku.com> wrote:

The big idea (the fine details of which Stephen appeared to be in
tentative agreement with [1]) is that an UPSERT is a hybrid between an
INSERT and an UPDATE, and not simply an INSERT and separate UPDATE
tied together. So at the very least the exact behavior of such a
hybrid cannot be assumed to be any particular way just from
generalizing from known behaviors for INSERT and UPDATE (which is a
usability issue, since the fine details of RLS are already complex
enough without UPSERT).

I think that you're over-complicating this. From a usability point of
view, I think that the best approach is to keep this as simple as
possible and make the behaviour consistent with an INSERT and an
UPDATE tied together, as is suggested by the new syntax. The key point
is that, if you are using the RLS INSERT and UPDATE policies for this
new command, the rule should be that the user has permission to
insert/update a new/existing row if and only if they would have had
permission to do so using a stand-alone INSERT/UPDATE command.

On the other hand, if you believe that the behaviour should be
something other than that, then I think that it would need a new
dedicated kind of RLS policy for this command because, as I will
attempt to explain below, merging together the quals from INSERT and
UPDATE policies leads to logical problems.

The INSERT policies are only enforced when a tuple is inserted
because, when the alternative path isn't taken then it's really just
an INSERT.

Agreed.

For the UPDATE path, where the stickiness/hybridness begins...
<snip>
I'd appreciate it if you explicitly outlined what policies you feel
should be enforce at each of the 3 junctures within an UPSERT (post
INSERT, pre-UPDATE, post-UPDATE). I would also like you to be very
explicit about whether or not RLS WITH CHECK policies and USING quals
(presumably enforced as RLS WITH CHECK policies) from both INSERT and
UPDATE policies should be enforced at each point. In particular,
should I ever not treat RLS WCO and USING quals equivalently? (recall
that we don't want to elide an UPDATE silently, which makes much less
sense for UPSERT...I had assumed that whatever else, we'd always treat
WCO and USING quals from UPDATE (and ALL) policies equivalently, but
perhaps not).

OK, I'll try to explicitly outline how I think this ought to work:

For INSERTs and UPDATEs, there are 3 kinds of RLS quals that come into play:

1). insertCheckQuals - the logical OR of the quals from all INSERT
WITH CHECK policy clauses. These give the user permission to insert
into the table, provided that the new rows satisfy at least one of
these quals.

2). updateUsingQuals - the logical OR of the quals from all UPDATE
USING policy clauses. These give the user permission to update
existing rows in the table, where the existing rows satisfy at least
one of these quals.

3). updateCheckQuals - the logical OR of the quals from all UPDATE
WITH CHECK policy clauses. These give the user permission to update
the table, provided that the new rows satisfy at least one of these
quals.

In general these may all differ from one another.

If we go forward with the idea that RLS quals should be checked before
attempting to insert/update any data, as we do for regular permission
checks, then stand-alone INSERT and UPDATE commands would work
conceptually as follows:

INSERT:
1. Check NEW against insertCheckQuals (error if the result is not true)
2. Do the actual insert of NEW

UPDATE:
1. Check OLD against updateUsingQuals (skip rows that don't match)
2. Check NEW against updateCheckQuals (error if the result is not true)
3. Do the actual update (OLD -> NEW)

Of course that's an over-simplification. The updateUsingQuals are
actually merged with the user-supplied WHERE clause in a way dependent
on the presence of leaky functions, but conceptually it matches the
above description.

I think that there is universal agreement that an INSERT .. ON
CONFLICT UPDATE that follows the insert path ought to match the pure
INSERT case, and only check the insertCheckQuals. That just leaves the
question of what to do on the update path, where things get more
complicated because there are 3 tuples involved in the process:

1). t1 - the tuple originally proposed for insertion, but which could
not be inserted due to a conflict with an existing row in the table
(aka EXCLUDED).

2). t2 - the existing row in the table that prevented t1 from being
inserted (aka TARGET).

3). t3 - the final new row resulting from the update path. In general,
we allow this to be quite different from t1 and t2.

If we think of INSERT .. ON CONFLICT UPDATE as an INSERT and an UPDATE
tied together, then logically the following would happen if the update
path were taken:

INSERT .. ON CONFLICT UPDATE (update path):
1. Check t1 against insertCheckQuals (error if not true)
2. Detect that t1 conflicts with t2
3. Test user-supplied auxiliary WHERE clause (skip if not true)
4. Check t2 against updateUsingQuals (error if not true)
5. Check t3 against updateCheckQuals (error if not true)
6. Do the actual update (t2 -> t3)

Step (4) is the only point where this differs from an INSERT and an
UPDATE tied together, and only in the fact that it throws an error
rather than skipping the row to be updated if the user doesn't have
permission to do so. The important point is that this is consistent
with the rule that it gives the user permission to insert/update a
new/existing row if and only if they would have been allowed to do so
using a stand-alone INSERT/UPDATE command.

Note that there are 3 participating tuples, and each is checked
against precisely one of the 3 relevant policies. That is important,
as I will attempt to explain.

As it stands, your patch classifies WCOs by command type and combines
them in a way that results in 4 additional RLS checks being performed
(not really in this order):

5.1. Check t2 against insertCheckQuals (error if not true)
5.2. Check t2 against updateCheckQuals (error if not true)
5.3. Check t3 against insertCheckQuals (error if not true)
5.4. Check t3 against updateUsingQuals (error if not true)

Some of those additional checks really don't make any sense, e.g.,
(5.2) prevents you from updating an existing tuple if you wouldn't
have had permission to make an update that resulted in that tuple,
which just seems completely backwards.

But the real problem is that by applying multiple checks to the same
tuple, you're implicitly ANDing together the quals from different RLS
policy types and the problem with that is that it's possible for the
quals from different policy types to be completely incompatible (their
logical AND may be identically equivalent to false). So by doing these
additional checks it may be impossible for any tuple to ever satisfy
all the checks at the same time, making the command unusable. So the
general rule, as I alluded to above, should be that no 2 different
kinds of RLS quals should ever be applied to the same tuple.

The nub of the problem is that you're classifying WCOs by command
type, which is wrong because updateUsingQuals have different semantics
from updateCheckQuals, even though they both originate from the UPDATE
policy. If you factor in updatable SB views, which we might hope to
one day have support for INSERT .. ON CONFLICT UPDATE, there will be
additional WCOs arising from the view, which will have different
semantics again (the SQL spec says that they should apply after the
insert/update to ensure that the final result is visible in the view).
These are then another separate kind of WCO (that doesn't depend on
the command type). I think this will actually be quite
straightforward, as long as all the different kinds of WCO aren't
lumped together.

If you take a look at my patch to apply RLS checks before
insert/update, you'll see that I've added a WCOKind enum that allows
each of these kinds of WCOs to be treated differently and checked at
different stages in the executor. I anticipated that INSERT .. ON
CONFLICT UPDATE would extend that by adding a new kind of WCO for
updateUsingQuals checked as if they were WCOs, allowing them to be
checked at a specific point in the new code (step (4) above).

In all of this, I think we should try to keep things as simple as
possible, to give the end user a chance to understand it --- although
I'm not sure I've achieved that with my explanation :-)

Regards,
Dean

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#38)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-16 10:25:29 -0700, Peter Geoghegan wrote:

On Thu, Apr 16, 2015 at 2:18 AM, Andres Freund <andres@anarazel.de> wrote:

If we go that way that's the way I think it should be done: Whenever we
encounter a speculative record we 'unlink' it from the changes that will
be reused for spooling from disk and do nothing further. Then we just
continue reading through the records.

You mean we continue iterating through *changes* from ReorderBufferCommit()?

changes, records, not much of a difference here.

If the next thing we encounter is
a super-deletion we throw away that record. If it's another type of
change (or even bettter a 'speculative insertion succeeded' record)
insert it.

By "insert it", I gather you mean report to the the logical decoding
plugin as an insert change.

Yes.

We'd have coordination between records originally decoded into
changes, maybe "intercepting" them during xact reassembly, like my
first approach. We'd mostly do the same thing as the first approach,
actually. The major difference would be that there'd be explicit
"speculative affirmation" WAL records. But we wouldn't rely on those
affirmation records within ReorderBufferCommit() - we'd rely on the
*absence* of a super deletion WAL record (to report an insert change
to the decoding plugin). To emphasize, like my first approach, it
would be based on an *absence* of a super deletion WAL record.

However, like my second approach, there would be a "speculative
affirmation" WAL record.

I think there should be one, but it's not required for the approach. The
'pending speculative insertion' can just be completed whenever there's a
insert/update/delete that's not a super deletion.

I.e. in the REORDER_BUFFER_CHANGE_INSERT/... case ReorderBufferCommit()
just add something like:

if (pending_insertion != NULL)
{
if (new_record_is_super_deletion)
ReorderBufferReturnTupleBuf(pending_insertion);
else
rb->apply_change(rb, txn, relation, pending_insertion);
}
...

You'll have to be careful to store the pending_insertion *after*
ReorderBufferToastReplace(), but that should not be a problem.

Right now, I'm tired, so bear with me. What I think I'm not quite
getting here is how the new type of "affirmation" record affects
visibility (or anything else, actually). Clearly dirty snapshots need
to see the record to set a speculative insertion token for their
caller to wait on (and HeapTupleSatisfiesVacuum() cannot see the
speculatively inserted tuple as reclaimable, of course). They need
this *before* the speculative insertion is affirmed, of course.

Maybe you mean: If the speculative insertion xact is in progress, then
the tuple is visible to several types of snapshots (dirty, vacuum,
self, any). If it is not, then tuples are not visible because they are
only speculative (and not *confirmed*). If they were confirmed, and
the xact was committed, then those tuples are logically and physically
indistinguishable from tuples that were inserted in the ordinary
manner.

Is that it? Basically, the affirmation records have nothing much to do
with logical decoding in particular. But you still need to super
delete, so that several types of snapshots ((dirty, vacuum, self, any)
*stop* seeing the tuple as visible independent of the inserting xact
being in progress.

I have no idea what this has to do with the email you responded to?
Maybe it's more meant as a response to my separate email that I want the
HEAP_SPECULATIVE to be cleared?

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#37)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-16 09:43:54 -0700, Peter Geoghegan wrote:

On Thu, Apr 16, 2015 at 2:23 AM, Andres Freund <andres@anarazel.de> wrote:

I'm, completely independent of logical decoding, of the *VERY* strong
opinion that 'speculative insertions' should never be visible when
looking with normal snapshots. For one it allows to simplify
considerations around wraparound (which has proven to be a very good
idea, c.f. multixacts + vacuum causing data corruption years after it
was thought to be harmless). For another it allows to reclaim/redefine
the bit after a database restart/upgrade. Given how complex this is and
how scarce flags are that seems like a really good property.

And avoiding those flags to be visible to the outside requires a WAL
record, otherwise it won't be correct on the standby.

I'm a bit distracted here, and not sure exactly what you mean. What's
a normal snapshot?

Normal visibility semantics, i.e. SnapshotMVCC, not SnapshotDirty.

Do you just mean that you think that speculative insertions should be
explicitly affirmed by a second record (making it not a speculative
tuple, but rather, a fully fledged tuple)? IOW, an MVCC snapshot has
no chance of seeing a tuple until it was affirmed by a second in-place
modification, regardless of tuple xmin xact commit status?

Yes. I think

a) HEAP_SPECULATIVE should never be visible outside in a
committed transaction. That allows us to redefine what exactly the bit
means and such after a simple restart. On IM Heiki said he wants to
replace this by a bit in the item pointer, but I don't think that
changes things substantially.

b) t_ctid should not contain a speculative token in committed
(i.e. visible to other sessions using mvcc semantics) tuple. Right now
(i.e. master) t_ctid will point to itself for non-updated tuples. I
don't think it's good to have something in there that's not an actual
ctid in there. The number of places that look into t_ctid for
in-progress insertions of other sessions is smaller than the ones that
look at tuples in general.

c) Cleaning the flag/ctid after a completed speculative insertion makes
it far less likely that we end up waiting on a other backend when we
wouldn't have to. If a session inserts a large number of tuples
speculatively (surely *not* a unlikely thing in the long run) it gets
rather likely that tokens are reused. Which means if another backend
touches these in-progress records it's quite possible that the currently
acquired token is the same as the one on a tuple that has actually
finished inserting.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Stephen Frost
sfrost@snowman.net
In reply to: Dean Rasheed (#39)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

Dean,

* Dean Rasheed (dean.a.rasheed@gmail.com) wrote:

In all of this, I think we should try to keep things as simple as
possible, to give the end user a chance to understand it --- although
I'm not sure I've achieved that with my explanation :-)

Thanks a lot for this. It goes along with my thinking also and matches,
I believe, what I had explained to Peter on our call.

Peter, please let me know if you agree.

Dean, I've been working through your patches over the past couple of
days (apologies for the lack of updates, just been busy) and hope to
push them very shortly (ie: by the end of the weekend).

One thing that I was hoping to discuss a bit is that I've gone ahead and
added another set of hooks, so we can have both "permissive" and
"restrictive" policies be provided from the hook. It's a bit late to
make the grammar and other changes which would be required to add a
"restrictive" policy option to the built-in RLS, but adding the hooks is
relatively low-impact.

I'm also going to be including a test_rls_hooks module into
src/test/modules which will test those hooks and provide an example of
how to use them.

As for the discussion- there was some concern raised about extensions
being able to "override" built-in policies by using the hooks a certain
way. I don't entirely follow the logic behind that concern as an
extension has the ability to read the files on disk directly from C
code, should it be written to do so, and so not providing a hook to add
"permissive" policies is denying useful functionality for very question
gain, in my view at least.

Thoughts?

Thanks!

Stephen

#43Peter Geoghegan
pg@heroku.com
In reply to: Stephen Frost (#42)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 17, 2015 at 4:54 AM, Stephen Frost <sfrost@snowman.net> wrote:

Dean, I've been working through your patches over the past couple of
days (apologies for the lack of updates, just been busy) and hope to
push them very shortly (ie: by the end of the weekend).

One thing that I was hoping to discuss a bit is that I've gone ahead and
added another set of hooks, so we can have both "permissive" and
"restrictive" policies be provided from the hook. It's a bit late to
make the grammar and other changes which would be required to add a
"restrictive" policy option to the built-in RLS, but adding the hooks is
relatively low-impact.

I came up with something that is along the lines of what we discussed.
I'll wait for you to push Dean's code, and rebase on top of that
before submitting what I came up with. Maybe this can be rolled into
my next revision if I work through Andres' most recent feedback
without much delay.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#41)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 17, 2015 at 1:38 AM, Andres Freund <andres@anarazel.de> wrote:

Do you just mean that you think that speculative insertions should be
explicitly affirmed by a second record (making it not a speculative
tuple, but rather, a fully fledged tuple)? IOW, an MVCC snapshot has
no chance of seeing a tuple until it was affirmed by a second in-place
modification, regardless of tuple xmin xact commit status?

Yes. I think

Good.

a) HEAP_SPECULATIVE should never be visible outside in a
committed transaction. That allows us to redefine what exactly the bit
means and such after a simple restart. On IM Heiki said he wants to
replace this by a bit in the item pointer, but I don't think that
changes things substantially.

I guess you envisage that HEAP_SPECULATIVE is an infomask2 bit? I
haven't been using one of those for a couple of revisions now,
preferring to use the offset MagicOffsetNumber to indicate a
speculative t_ctid value. I'm slightly surprised that Heikki now wants
to use an infomask2 bit (if that is what you mean), because he wanted
to preserve those by doing the MagicOffsetNumber thing. But I guess
we'd still be preserving the bit under this scheme (since it's always
okay to do something different with the bit after a restart).

Why is it useful to consume an infomask2 bit after all? Why did Heikki
change his mind - due to wanting representational redundancy? Or do I
have it all wrong?

b) t_ctid should not contain a speculative token in committed
(i.e. visible to other sessions using mvcc semantics) tuple. Right now
(i.e. master) t_ctid will point to itself for non-updated tuples. I
don't think it's good to have something in there that's not an actual
ctid in there. The number of places that look into t_ctid for
in-progress insertions of other sessions is smaller than the ones that
look at tuples in general.

Right. So if a tuple is committed, it should not have set
HEAP_SPECULATIVE/have a t_ctid offset of MagicOffsetNumber. But a
non-ctid t_ctid (a speculative token) remains possible for
non-committed tuples visible to some types of snapshots (in
particular, dirty snapshots).

c) Cleaning the flag/ctid after a completed speculative insertion makes
it far less likely that we end up waiting on a other backend when we
wouldn't have to. If a session inserts a large number of tuples
speculatively (surely *not* a unlikely thing in the long run) it gets
rather likely that tokens are reused. Which means if another backend
touches these in-progress records it's quite possible that the currently
acquired token is the same as the one on a tuple that has actually
finished inserting.

It's more important than that, actually. If the inserter fails to
clear its tuple's speculative token, and then releases their token
lmgr lock, it will cause livelock with many upserting sessions.
Coordinating which other session gets to lazily clear the speculative
token (cleaning up after the original inserter) seemed quite hazardous
when I looked into it. Maybe you could fix it by interleaving buffer
locks and lmgr locks, but we can't do that.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#44)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/17/2015 09:02 PM, Peter Geoghegan wrote:

I'm slightly surprised that Heikki now wants
to use an infomask2 bit (if that is what you mean),

No, I don't.

because he wanted to preserve those by doing the MagicOffsetNumber
thing.

Yes, that's the way to go.

Glad we cleared that up :-).

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#46Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#45)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 17, 2015 at 11:19 AM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

because he wanted to preserve those by doing the MagicOffsetNumber
thing.

Yes, that's the way to go.

Glad we cleared that up :-).

Got it, thanks!

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#40)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 17, 2015 at 1:30 AM, Andres Freund <andres@anarazel.de> wrote:

However, like my second approach, there would be a "speculative
affirmation" WAL record.

I think there should be one, but it's not required for the approach. The
'pending speculative insertion' can just be completed whenever there's a
insert/update/delete that's not a super deletion.

I.e. in the REORDER_BUFFER_CHANGE_INSERT/... case ReorderBufferCommit()
just add something like:

if (pending_insertion != NULL)
{
if (new_record_is_super_deletion)
ReorderBufferReturnTupleBuf(pending_insertion);
else
rb->apply_change(rb, txn, relation, pending_insertion);
}
...

You'll have to be careful to store the pending_insertion *after*
ReorderBufferToastReplace(), but that should not be a problem.

Okay. It seems like what you're saying is that I should be prepared to
have to deal with a REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT change (or
multiple such changes) from within ReorderBufferCommit() between a
speculative insertion and a super deletion, but that I can safely
assume that once some other insert/update/delete is encountered (or
once all changes have been drained from the reorder buffer), I can
then apply the speculative insertion as a regular insertion.

Is that what you're saying, in a nutshell? IOW, when you said this:

"""
I earlier thought that'd not be ok because there could be a new catalog
snapshot inbetween, but I was mistaken: The locking on the source
transaction prevents problems.
"""

What you meant was that you'd decided that this pattern is not broken,
*provided* that the implementation simply account for the fact that a
REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT change could come before some
other (non-REORDER_BUFFER_CHANGE_INTERNAL_DELETE, A.K.A.
non-superdelete) change came? And that we might need to be more
"patient" about deciding that a speculative insertion succeeded (more
"patient" than considering only one single non-superdelete change,
that can be anything else)?

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Stephen Frost (#42)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 17 April 2015 at 12:54, Stephen Frost <sfrost@snowman.net> wrote:

Dean, I've been working through your patches over the past couple of
days (apologies for the lack of updates, just been busy) and hope to
push them very shortly (ie: by the end of the weekend).

Cool. Thanks.

One thing that I was hoping to discuss a bit is that I've gone ahead and
added another set of hooks, so we can have both "permissive" and
"restrictive" policies be provided from the hook. It's a bit late to
make the grammar and other changes which would be required to add a
"restrictive" policy option to the built-in RLS, but adding the hooks is
relatively low-impact.

Sounds interesting. Perhaps that discussion should be moved to a new thread.

I'm also going to be including a test_rls_hooks module into
src/test/modules which will test those hooks and provide an example of
how to use them.

Good idea. I had been thinking that it would be good to test RLS hooks.

As for the discussion- there was some concern raised about extensions
being able to "override" built-in policies by using the hooks a certain
way. I don't entirely follow the logic behind that concern as an
extension has the ability to read the files on disk directly from C
code, should it be written to do so, and so not providing a hook to add
"permissive" policies is denying useful functionality for very question
gain, in my view at least.

Thoughts?

Yeah, perhaps that concern is somewhat overblown and shouldn't stand
in the way of allowing a hook to add permissive policies.

Regards,
Dean

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#47)
4 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 17, 2015 at 1:04 PM, Peter Geoghegan <pg@heroku.com> wrote:

What you meant was that you'd decided that this pattern is not broken,
*provided* that the implementation simply account for the fact that a
REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT change could come before some
other (non-REORDER_BUFFER_CHANGE_INTERNAL_DELETE, A.K.A.
non-superdelete) change came? And that we might need to be more
"patient" about deciding that a speculative insertion succeeded (more
"patient" than considering only one single non-superdelete change,
that can be anything else)?

Attached patch, V3.4, implements what I believe you and Heikki have in
mind here. There is a minimal, dedicated "affirmation" WAL record now.
Importantly, it is not possible for committed tuples to be speculative
(i.e. to have a magic value in their t_ctid field that indicates being
a speculative tuple - Andres felt very strongly that we ought to be
able to assume that committed tuples are not speculative). There are a
bunch of new assertions to make sure that this actually holds now, all
within tqual.c.

I found Dean's recent argument [1]/messages/by-id/CAEZATCVDdYRFbF4NMXTF-NKYibbR2VSfNXRWPyyByaCpV1jwEw@mail.gmail.com -- Peter Geoghegan for mostly following the existing
RLS behaviors convincing. I'm now I'm tracking what Dean called
insertCheckQuals, updateUsingQuals and updateCheckQuals separately
within the executor, while enforcing each (including updateUsingQuals)
in the manner of WCOs (i.e. there are no silent failures when
updateUsingQuals does not pass on a TARGET.* tuple, just as before -
there is a WCO-style error thrown instead). And, as Dean and Stephen
recently suggested, there is one and only one tuple (per ExecInsert()
call) involved in enforcement for each of these three quals (or these
three OR'd list of quals). These three tuples are the post-insert, the
pre-update, and the post-update tuples, for the insertCheckQuals,
updateUsingQuals and updateCheckQuals respectively.

The new implementation has extensive revised tests - the only sane way
to write something like ON CONFLICT UPDATE's RLS support is using
test-driven development, IMV.

There is one intentional difference between what I've done here, and
what I believe Dean wants: I don't bother checking the user-supplied
quals under any circumstances (so I don't "Test user-supplied
auxiliary WHERE clause (skip if not true)", as described in [1]/messages/by-id/CAEZATCVDdYRFbF4NMXTF-NKYibbR2VSfNXRWPyyByaCpV1jwEw@mail.gmail.com -- Peter Geoghegan). I
think we should *always* throw an error, and not silently skip the
UPDATE just because the user supplied quals also limits the UPDATE. I
don't want to introduce a complicated further distinction like that,
because it seems like a distinction without a difference. Adding this
behavior will not make something work that would not otherwise work -
it will make a failure to UPDATE silent, and nothing more, AFAICT.
That's much more subtle and much less necessary in the context of an
auxiliary UPDATE (compared to a regular UPDATE).

Other changes:

* Changed magic offset, per Heikki's request.

* RLS documentation updated, in line with new ON CONFLICT UPDATE
behavior (this made it simpler).

* Moved a bit of code from the IGNORE commit to the ON CONFLICT UPDATE
commit (this concerns new possibility of heap_lock_tuple()
HeapTupleInvisible return code introduced by ON CONFLICT UPDATE). It
clearly belongs there.

* RLS error messages advertise what type of command the violation
originated from, and if the WCO originated as a USING security barrier
qual (i.e. whether or not the target tuple that necessitates taking
the UPDATE path was where the violation occurred, or whether it
occurred on the post-update tuple intended to be appended to the
relation). ON CONFLICT UPDATE makes this distinction important, since
it may not be obvious which policy was violated (maybe this should go
as far as naming the policy directly - I'm waiting for Stephen to push
Dean's work, actually, because it will probably bitrot what I have
here). These additional diagnostics were very helpful when writing
those new RLS ON CONFLICT UPDATE tests, and will probably be helpful
generally.

Thoughts?

[1]: /messages/by-id/CAEZATCVDdYRFbF4NMXTF-NKYibbR2VSfNXRWPyyByaCpV1jwEw@mail.gmail.com -- Peter Geoghegan
--
Peter Geoghegan

Attachments:

0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch.gzapplication/x-gzip; name=0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch.gzDownload
���4U0001-Support-INSERT-.-ON-CONFLICT-IGNORE.patch�<is�F���_����)��-�VE�h[�2�P�&[�kID @���M��_w��"�q��Z�E������g0������4���jt����������tF�ov����K�#�>���54��h��h5��{$s
�2�|��b)���kj�-��A���JXv�pWg�K�S���m�$<���B����9����4��p��4�S���|r����/p������n8�@�V��\���__]L����f<,�&K��u�~ Sxf�`�"�%�v�|��t
��ta�t=��4��s)X�,���H@��0�t�3]�Y�m��LZ��}��,��j"�F���b�|��LVz+�I���C	���N��_2\�<a9���3����9$dS~�Ua#;y�z���V���@<H�T���vX{�����{T�Z�`��`�����Ba�`����$=��k$f���M0���k�,$��1 o�2��z~�F'hD�����h
++@Y��3Ga����IF����RX~5�\����C�$8�	{�{�A�I0P�q@:
mT)��L�k�	);:�+�v��z���>vC=��$
���dJ�Z8�?-T1��5��\e0�i*p���	�����*Z�C�=���&^�����p(4"CZ��)���_b��N�������Q_�ars��W���l��n�+�B���W�_h�>2�ww�����b}��E���u�v$F������
p�H���zf��HC�c�QX�%l��P��h2�8`���T��B@M�'}�`�s���]�?�j�*!��	����0p,����U�5����|��4If�����\�#[��U5��Z�>9�)��G�VAM/��m$j�&Q1]IQ���QVQR�dH:���h�(YF<B=6H�m"�N�����b�-X���]j�;3���P�e�8Z�}�����lK��{�T�VK4�-�NF�]"A��?�����w���qy1��:�������!r�
�)\�+���T7�Zx����;�g;N#�A��3�5
lr����V��t����2����g;K��Kh_F���o����i��&WJ��.v��<ic��AB�z�A>?����;����X�1�no�����I�����=1�"�@����.B~�d�d�|d���E�j5�8�/��i��T��g��j����u������)w��+�A&,��\��!�t�C�ATw��'ga�vr��BG�]���`'�'�<+�E�g]�b������N:����.��{n"{��G�@7���	�LKQ�I��Y�H���=er����vD�m���0���E�����k��y'���1��o1�-d{Sd�/�
�dZ��������W�/?
w��o)O)���O��k]uN7�X��g�����r�<O��9�)?a�3�p��4�f'�WD�_[�j,�&��,`G�����T��Ea��Gt�6}���Aa�Xv�d�����
*dL8�Ko5��ij���#�:����f�$��d�p��\n����@rp��F��e�q�����)*��#�F^x���0r�t�����Z>�\Mmv;��Dp��J��B�ln
��zS��b�y���@�~y���zMz�p>����7#-lQ��]?�:1�Z���}tv�D�mLW�Q����_fP�yt���
N���z@n_����Fn��)�I-g��O���X���/(�\
Y��D����[�����X�Lq~+(��P��J��-v�9��7�q�&_���uxFL�+�Z�ry�;���������P1��-��_�zK\��V/�6{����Lq�H���fqWTVXf�q�@��d�Z�����H�o����8��_�
\ZI�`:�B3�!;�n��������t(���/w�(�u���:�|��|�#���-VS��Z�n�������yI5Z4�Z���Z=�����H?HQ������&N���vL+M~Q��P�v�I��={�V�M���x��][uF>ZW��,���*7\���x��Os�2�.:-U�B�Tv�k��;q?�[�,8qy7���x��{��o
k�w-�A��g1��Y��gk�Q9Y�.VP�m�6�r������Yt�F������%��!�&��9�G�@��N��P�.�D�����Kj]f����Z�}��u��-ZT~����=}�T��vK]�`����+��_��0}?��\�����wh�^�R��~������	��2�A�*DZ�L-�>O?�,��U����M���OB|�'�����Z��2:P?)<���<
���jB�O�o]^����(T���D����C�o]_��W?q��o����2�I��_K&p���Jq�#��r��J��,��^HY�f#B��,1I#|�S��d�g1���{��l�Z��8�u�N���Is�	��"�l�z�
�x���W�#\aH�!��sy��}�����W&P~G��Bn����yy�2D���<��Q��/k/*�(F���ayro�@x\cm��z~{;�������1��.\���7���
mKE8Y������z2=�r�������a��h��xD�?��Y��v���kw*8-�����JV�4GE�D��������R���^��0�=p�0�/a2�����
K�<�8���&�'pdI���F�!HO��<X��.���8R�<w�s����]���� x^�*G�O��5���G�h'l�C4��S��oJx�v8
lj�-h~<��:{@G���\���Z���f��AA��'�\�����~�tu��^K�d�Pv[�C���+%xt-����<w��M8�UK��p��nPQ2�+����5c�J9z�^���
��P�,�� �-�)��Yhz�Y�Q�SA(:������
�F�d���	����I����BE�_��[�(7�������������������2#��������u~}?�; ��Q�����Mb{���	�+��8-!��L&�d5��_�l�a����9�o��6���������s�"�W���MM��x��
�!*mc�i�j���i'{8f1��Z������s~�����������A�l4+`��=������� �0'�A>�`8��O!���U=Z��T��G%�� hN�yPB���_]#�"b�������C6�PyV���Z�,l2�i��c�r����A��;������&��H��������_���7�
��8����^#�k�{�p����`?I�x��$�'�{���H��:��i)��q�/
�H�,��I8�z��L=\���?b����=e��]c�D��mW�}�+��1�����\��^*
����B��x�\lX�[=�i'�#�&�e,���1��R�����M_D�<a}�����v�'r��9T�am����5��W�P:Pt��luj�YGtfw�@�AgGt����z��%#_�" J*�1`P����i��,�t���gtu�m�i&(�?OG��XgG���u.0�Q��W�S1�I�o��6�/V��9��J����H+�U��%��h�*���O�`��Z?���)�:�k���'��I	�_��������(���KFj���N��'���tt3�������'����TA���(�- ���I&�d==*��*������wpw;���8���j�P��M��nF
K��BB'�i���j#�~�p
71���CH(�*�~�p$B#�����>[Q�������"V�}�Un�[<S��t9U�PM��92��"�~hQy[I����LA�Q����u"�o�����.=���������F��^�h�_�
�7��j�~�-��?��{G�%��� %�'~�_h.�U����jU��<=f����WLx��������R�X��e�����������`��+Z�NmO�D���{�^.��Ov��s������a������#��q�F~�8�h�����;������������FW�p�*:���09��z�.��������d|�%Z�������b����w��,��	��u�_(Pc�4�T�3��;hnceM9w�,�]�h�����^�f��N���1�9�?�9a����W5'��Do���p��T`t}}���
�;�����Q���R��y@���+���6��w�No�!���k�L�P�o�&��p����h�Of��3��*[*@7hZ�&Xa��*g�5�u�3*��^k�c�ST��sh|���3e1o�m�g���[��5a����m����WQ��e��"��mM���>��DL������kX"��,��1dt�M�)�sF��f���=>A�k%���E��x���������/6��C	����MS���V�H�����I ��7A�pk4�=_�m2�2��������H�d��M��=8�#V:�,��vM�m��z$4�}�h+�jN�u�����M���BG/��%:��f��L�4v�9e��
M�F��Do����:��!d���VRP�����3��5������=r-�ceD>�jSYz�R�q��){Be���6�Y����y�&q��j6wQ=������e����V������uu-�q{������4$�=K4�����t����q��_��<��\G��
G5n1\���sD����-�Y�VM�f�����m9���I�	)?�-��t L��-mn�@q�.��u��������W�;c�.W��P���\z�@a�UGw�I=����
l!b]���A�I����>��~
�D)D 9��"8S����9���J�jw5P��4��R��]:����zPf�{"���_}��P�+�u��*sO��^�����������ZB��������c��p���=^���(\��T��7c,�o��/������K{1�!YE��w��S:�}pFBz[O������s�i�*���z2&�O��{7���}�DX1D�1D�H���G�W�n�_bi �lvA��:�d��VozX���s��F��H��u�DO}}�\-�3K�e���O���j�BED�&>��,*�s��[����Q9HaF����$6J��(=d+���:iCW�
�GM/��������E�����������*2��n�K����}r>���Q�|?��������w�����/�L��no���c+��xs?����ax�j&�3\�����?]m�������KJ3�����q�9��Q�(�f6������c�
��;E������?�eNCi�j�y�I��K�����?��p��W�g3�Q��/��^������T�:�����L��7�G��[�4��?2�[���+��?gIN3�n�]�k
S���2rn���JBe��$p��0��������_W�1*�\1��J�/��(m��]�6���%}W��t.������_Pf���w���g�
5m���dJ�i�,5���v��9�0)���������2��I��$h���M�\�X(%����
�CZ��O�9�r>�*!�#'���),��N�����,(����hj�c[XK��
q����i$2s�XvL��_h%�o{o��6r��-
�2c�Eq�b��W�h�������f��IP��$�����~��l�a!)��df�ObI@U��Sg�<B�_�!����h��R���K�o�����C�,������!�
���]�9����>�J��:�JIH�����Y�b���M���gq���� �|�
��{��OX��Z)Ip+F�`��(�:@��/���+g�%�Tp%'�d�5�J
�U��/��rU�U}��C�"��������J�!�pY��B���`�?�V���h��VPH���ha
+�W��$@Ch~�����x�������stsy��y��<���ASL��*��g����;������S�s���9�#GF��WZU�E�����2���[��8$�s�w+�c@�R��U����I���������x���!y*���0�l��?���	��0�m����^�� u[�>k�F�M����Y��L�����bzi=��lC��`s��M�'��U�|v7��b�+���i_y����m�g�P���<L�Y&�B��a��o�6+�j 7��}���A,R���B����9m�{���yDm7�'i;�/4���.�G0t�BG�dXp�;����0]�pA�:z|zi)<j��{���&����h���_e�qPW74"�r5�B.C+vb����/��E�c���x�?��}�����}�NL�j8�bw�Uu�z��Q�R_"�������m��7�qGH�Hw@��z%s�H{���71\v+�R1����"]���?�Aj���CD�Q!�?&�#����l�`x�g�@kK��&����W����d�fq�;9F�PM#�0�
������c�Ny��c��PS��/����{@jZ���?��3����8�=��g\c-�����	t���R8��%���ZK�E�����a~�B8�-4eG�@n&C������\��y�u�hN@��Q�:��������3@��2
����1HS����l�,�����'�ra��r��j����c�0P9������������q(�7h$��B�4OArg�|Y:r��s��a���KV�d	�Qo	b�e��q���K�I �]�[���������O@)Y���l�lW��eA�B��#v���\.�����?����N�|����c�A�=hyg�~�a�F�n�>��HV�q�u"T8�#n[/�V���p�Z���)oNN�����+�\�U��>9�b�|�;����S�Vu�X�;h�/��pu��1���-u������p���px`��q��zwh�#����9���U��!s����H�B^z~�M�&��|���dg�9�����1�z��8�����^��VS&:�hF���=Q�&�xG��E�"�[��I���
�c�mi��8�!�5
\uUA��Tx��c�	����H�l�}0����W!$C}�%^@��i��7�&�B�-�=3��Af���]�(���4du�qwr�7r<K�(��%�F��I��v���D4������'_k�5�B���lN[���{f���[���5>�%�a��EP�Dz-��H�dh��@O���W�oj��hs��0Z4b�
�nET�2=���b��
�to}l����
yh�M�c�!��m������,�Xy���:�0���E1��f�hk&������L��.�0�������*9�5v��P�����z�����P��>23���]�RX�f�����(�Y5���y�[y]9D"!(����;X�"+�"Bo���n)��!��0r�����-�%��������K�0����'��2>rC�vrP�,��u�J��[si!����.{��$�����
5�EN�]Z*^��Tup�HS~���6(-�{�X�;���;Vsj����7y�����yl�M��Q�����/�b�I����Bs�y :�Kcd:\�sj���?��d�Pr>"x����5��y8K)�w�����`C�&jK�[������v��C�
�3���ed��Pb�{������^�/D�
{��j��Ut�K&%l��'u�����b�f�q�����������Z bmV�Er�1Q=��h/x�ZU���t��vr����/�����u���%��4���@�����U��#�vG��)� D}���(9���N1w)��I\x�����T��6���>��G����
Ny{��jD46bv�p��i]�f.�[�Q���?&��<�
)b��r��m�v�����;-�/�2�R����;'[9s��O�v����L����d�k�	��6g�8��Z��,���Rm�Ik5�����Y�v��&�Y��,P�G��C����#6��l�hk�����#��|�l>����X�>�laMO	2���b6U3���y(��*=�hz�����q�op���MX�]�]����o������D����X%7��P!+��'B�sK�f4��H�^d�If+�	��-�8Z�t-;�����=�
�n
5v���C�.T�C��>������?�P�Z��7�����\���D����U4	� d�sn�>��G������R�-5%�����������W�({��>K��x�H������)L#`�B�R6�J�����3w~bbF�~��������>�l����~#foVh&��Xi���l���e�[����Tr� ��n�Z��pA �j1v$���mZo��3���2�$�������x�5��5!�l��~��v���������m�g�("���aYCHd1+�J��B]!�6#���n%����b�y<�f����g��S��G���eX�S������5�
u����^��+��,��f7W�sv)P������:=(d))��d o�:�Fm�Z�&i��&��J\���{��]�T�4��C$�qt����W<4������9�'3����)6���6�(�0a���������q�9�cP�%������t����N���n2_��J^BF3�c+l�<�}�����%��(GU�X�&�F��M�'�v�aD?�E�(���F��F:_|.a�,&���������.G72�����2�z
��m��������x{��Y��xC���o��=�D��i�����?+c�TSx_��(g����I���gL*L�u�^:n�y�v
��h��o��5Gm�t���'U?Lzp����D9|e`�:"�_����e�,��6+!M�fv�rX���#��Iu9�e�=�u�Y�����%���>"�:�%+������Jm6y�#cO�>~���=����x�S��������9Aw����p��p�)�A�Ja�)/n�D�q���2y���C��n�x�����Cb��h��)���I�����95���>�Pd�����o N���x#i�,[�����8����:=:A�� ��@\�
�N���p��)=� ^�u �AK~�B���3�!vF!����j��/���NO6C!��++Bor�������S0��f}�^���F���W���T��[V�����{���b]y�ONV���X~��G|���L�9�}�������..��I�S���&�K��+n$��'���vD�G#�W�?����V[�d����|��4�>?�����}��
S�L�"���%Y�����tT��|t4CV��/9^�H:�=�������k~Hj3��J9���Dw@'�%��jwv�|����)H�������Q�{C����������kn�+�M�C����r&�
{=T����2�����G���x����,'@Z{I0��8�s[�H�-8O���EL���*%��=Gd��:*;J^=�Q6���?�<��.C�������`���c�K�.��u^��������Q8=���p�V�v���`��q)�3n1��t/���)@)���T�����s���c� �M�C�U����.�]��������wIX��#1E\O�|��L���K��._����Lu��'�j�r`�(D���H}
��w�!��T_��n�]0'�������=��M8OJ�3���s�g��$�����a}���>��aB?��������a��-��f���.�3�����,�SL3�3%o���5hx�=N��������g���zZ&�����aB�n}��w�u(=���D���zEX�_�F:c6'�^q[X���F��h�[�|��|u�9��7��8�����;�Y1cQ<�������$��l�-��2����mt���`�]����h��t!a�S�r���9�}���:�p�[�}�+��G��~�9�n<��O�g�oCXK+��'R�?���MV���Ue������n��9R��g��ac�3B�����}��8����?ci5�,���@����g��
�)��8G�t�N��=��w/zO?��g qt�u�1>/�8:�#��#���v�o�:I�)b.���[����1���Z���f�'vV�������o������H���,��L}���+������j+z��`��<W�8�f
0�Q�5��k j���k^RO���|#�g��=�p�����,���+$�Y4��\2�no���V��>���]v�{W�3X"��
����\w�d:%��'\*���U�@�^���Y�!�����u��N��k�R/\�����x�sHj��,�Q�A{�o2��]����J��&��"��2��Aumz�)v���?������{�yL��0�����xr��G�Bv���U��I�wt�9��{��h��-�3���6���~}��+������m�d�����C�j����<���W�.���C��gw�6��X"r[k����L^A�o*���*bO��o��k��;�&�����Vr��B�����X�%�s�j
�%�	��8XH|�}4p����q�����?3����+��0�D�Oi�j�|S1o�3�T���<������
��������%�_s��z���}�Z�Xh,�6���?J��2�nn�������:F��-/�GW���b�K�+��c+Y�D`���bT��� 3_2W ��e�=sR�JJ��.��-��X�Yw���d;.�4��JyR&��W�nH
�����2 R�������������1�(��#����*
:*g�0�d��5 �=����%5
v�����x#�+��xI�8�������=>���g����=��e����,fA�Mi��M��i{���1��47�e����<�T��)�q�=�XOz9������G��J��[�9q[��=cD�nadSjM9��E
���F�����{�H�������=�p�|Y�C	��`�'_�y`v�8K?��75�e��O��t0�[���\��d�d	��J������n1�[wIi��%��V�p���	�G�L�;w��t��=�w��>~^4��{�$�q���;g3�W*����B�gS3����g�[��+��Mr(�fH��wQ8@iG?h����(���������T�z�PV�I���a�f�ef}��,1�9�o2Q���x#)c�<>�	��V��T�=���; t��~c��D\.O���4!�R�d~�7J)Y���!�2����R���H�l���P�����)�����7E�Q�t�i�l�"B�+��@�]&Z�^t?M�	6R]q ;F��l��f�H�������c�u���KD�}��Gl���t�W ���A2?
��^~\�WD6�X��$���H+�����*��vE�%{Z��a�����*�Tu�����<M�rQ���#S��?4����Y%5�4=Uua���S6����H� ���hY�j�Z�~���OJ~u����`#]��ESr�B�����v�0�kw�*�����G��tY�	��4��*E��O�x�`M�?lk�
'�7��h�JC��`?�D

��K�<4��L!����p�,�t�s%����Tf��[ *�����j@�"��2V.� [����,�L��:��p����c_�i=��R;�����:����N���O�S��.��s%��Xx�K�:AC�h]����]G
5		��&2��h�������e��[���b����#N�8��]Q�	���8�nrHFM����It�qZ�� �H-���xx-b����T[�/��r������v��ui\	^��R�W�����h&�����Q�>���So�	H"�Z���n��@�����������M����O4iA�i�UJ�|��v-�e#��+f-0����`���y��!��Aaj���������CLF���n��/G����!"1S�
��{�p�	�(�<��E���E�T�Q[�}Xg��(>J�4$��{r ���52+�5��Z;4.z
�F>����c�f���z�!hGp�)}���B���F�'[����^�g�r�d�o��[���j��!9	x�7��X*���C���7���3�:����lrl�IFW���1�+��&���j�7s\�(���k%�d@��5���W��mJ������q�D�#����4�@���O����������bScH����MF���b]���@��T���"bP���mi���)�`5�	2gJk��B�P�^�bM�I���1\C��,�f�C��6�^���b>�P?B�o0��ha�+T�9�D2��<!S�0J��dVI��;�.b��s;�s�
�}O,%�Y��_Z@t�n��Sv7�D"
����|G#�I��i���ip�r����.g��(�,������+�[4�hM����R*?��C}V��<�N2b[�J��qz����r�>X�����������u��
C��B�'�^��L�~���C�J+���on9bY�F=}%������u�=���aGE1��|���7O�X8����)��Tt5e'�x�����7e�Zj����S
���|��
�4����&�����W�����i7I?��m���)�A��=��I���eNc��l�*l��eJ�ak������F1��.��J�����R�J��k��C�4 qN�A���xR����}��������,ZG?i���+�~/���,���B���l^���f���S�rm��iM$0"��kI�����w�����G�[����=t�GA��"�?@4�V�FX��A���|���\p��X�L-�	����-+z,�����`p6Zu:\[�FK\ �M�_��X����
Z:?��}H���e%D����(�=����b����k��Rj���^�<=_�@��m:P��]9P#����1�9����������K��R4�k�]�|�W�6�K����6�T��~�
>{�fz�����`<��]���5�W�=:�����_J��N�n��j��VN�,u����z�yJ��n�8��������
�:���H���W��U��_��U�<���,�'U��oh�U������g���p���X�UvO�Y��4���i1r���"&D��uM����j�h��-f�1S)=[V�bW��&��o�av�q$xt��	� !��
���'�b=aN�\|wf�h�IB�v��K�	��>��g���"�`x*�l��1������=��THU��R�T<Km�6���#��9q!���|��.��9��P�C�h�����%�9/��U�I����d��(�	���j�� �/�4f�=��&�=��Qk00����$��%'�`�ag�u�Jia�wl�ENa6���(�{{a)�����r���(���E2�>E�g?\*�J"�3j����}�^@
�t��i��+��{�A�B�0$��>:����|z76��@��|!P$��������1��pcc����S�����2�[����FF��!���R�P��*���t1��Rb��^.��}\�N����u��S��S��>_���#c
M���i,������l�G)����N79#���x�����x��E@��e���M�0.�Wj1�v�����������?�*|����{���f�-���o�g��j�m����U�Lb�u�)�I`)D�;��ED���Qb��4��l9�}'�_�����z1��D
�m���"��N�N��R1	|�,R�������'��e0���p�f�XL�1�9�F�c�o��p��2�:$_�E0������e�)}��]�
�G�1�:��'��f��/;��H�`��^�.�x<��F����S6�v�+�A����������*��\��a����}k�sv����Fm)�����"�(�����m�nC*�9��	�
�O���lRD�^��+���4������Tag����]Y��N��6F��������i��_�&g��r�S��-/�����>w��R����r��2y�����1�:kp�?�bu$WKY\}r��?�h�2�1i� �:��^.f{��������v��O���*��dw���M4�S���1�Tk���U��9��d�3]��"a�{-9-����s�lvT��*B�����q��,Xik�����\U������;D7�c&����y8Zw?b�=�>:�L!��/}�9���>���S`_B���{�����j1���y'4g���[���<��%x����U:Z�Z�����Qn�T��S�p��}+��T�����l�������Fv�gm��:�J5�l���~o��>�|Om�A.�w��R}4$	��j�MD�~ID���C�
}�a��<�u/>��M�x�����g!��5W�r��������{�u����>���k�v�����!&5����Xx(���u�
��a�����t��q2S��sZF�M�/���$�A>������uPC����0*�YQ��0�|�S���9��]v���&��%j
�u��V����D����>�VF\�u��2BTV1LS�&��T�=m��d��������w�W��[���,`K�������+���2�b�U���/��>�[�N��c��J$��N|�>�@J�+>���^%h~�.\l��kooc��`��I����8g�~�2���b���'�d��_��)���2�]"������Lc��xA��!�q��WRM�*6����������h9��W|��A}�[���z��]H�ZX��Fe�%�<G�$�<�����R�"���m9�������
M�C9��������{����EqUmq�����t��
��).:wrF�V�A7�G���I������e!��'�f������B������!��y�!�n�������,�v�:%�1�J�������N��X���A�;��Y������[������V&.�q@���[��9�]K��E}/����E��/(��R*c�Z��m�A�Z���ho=Zc�����%���!��P�b�<��Kg<��Q4�T6��p���r��4�2�;uB��6���<�
�����U�����em�z�Kv�^:L�:�r��u��\�o�����*����F�J��&b���Y��T(�G�@�`�P8%�P�<��xk���H���6	��+g��u�1
=�8���'���[��N����p�"�Y�<�������z�3����z:��~�@7���j���k��X�������e���~r��FO�n*
4�~2�M�Y�����K��4�h�����|�E�"x����;9��	��t���.DgSzjZ�5����n�];wC��}O,�D��D��4��V�����.nH9s�gN��������?U���oa�0E'D��}�����8�p���>z��#7�p�2�NF���]4��",���T��s��e.���t2�U�P�z{{m������PG���3UyM�J��^���1�������X�9X�'�4%q�'3��ax���`�����-�����x��I
-+	��\[��#������,�?���i�����T0�����%�_�hT�Gb�����2|&5/l��t�G�pY���l����#�|��R��2"cT}�u�P���p���6/��;����Z�GR�31Z�d�� �>��e��NO�s�U��	� o���"_9~�����
S!��5��5�"Q��f�4d�G��S�;����K&[]�n���n�j+��#��������5l���k��"�W����z����Kg���ug:5�
��'�3MT��fN���pTx�0�� 6���n���sU1eu�}�<^�K����aH���f�AcJc;�*#"n�S�"~*��K+�6a�
��H�2�����-Ni�H4E��G6�C�in6�a��7L��e���k�t�
���*hlH�P�����2�e��qhyz��&V%�=�C��a�R��Zl845�|t�[�����	�o�����j��_����1�NC�yp�(g����%����h�N����%�~�d|p�7�q�z�O�r��%��3U��/9�I!9�9i��d�����,��a�^y��Y��������O���J:���3,�io�����@R�� ?��l9���hw���U�=o��Z
�i�p���������m$62Q���s��P	��O�c�D�?b	�M��MG�cx�T{]���:|���.�����0}7��7������<Pc=���@W2��zqD2�����y�H?Ux��j�U�{�
����|M�T��:������<�����~��lx"�=���g���UBbf/�`=k$(�s��%����k�=����49��
��=L���0���CG��v��H �K�+h{�x����&h�mC-���4���>�l�ee���5V�)�94hvH\��%|G�>��K�/��L�e6���L|�Y��
�����K>iOCsG�?����l��e��@ B:�I/������s�"�n�U�<��<��vd�SP���2�\��M��
R�&$��	����dBIc�]+�g�Q��;�|��(;qH��y���s����Ytx9�T�E�y�(	K��[
�|4���(�qb��plG�mf���R@��1��:5$y1����CFl����l�?F%&�����kWW$�`�)c�t����������@P�V�X�G�z���b#R|���Z��H!�+�)�`;!a��*,t3��
���G���x6��l}�hdJT�B6T��M���a�����@:8����<���q����qd}�%=����a�6:
�!Tv��&�b�����>�G�N�.���U���8����x��d���Vr7@?P��{%|��Pp��g]y���I��`���3]|`���'�����0	�����1�.�th�g�9D���W_QT%{$L��XA>�|�����a�cz.�:�-�E���&�/�)�9�U�}x!�:�'}�OU��w����F*i��.�+�7��W�����9lW�!�?D}��Onc\n��"�'��J�8�a��j����h����T���W��,p��_�0I��KWs������tR�FY�~���S���S	Q����z�
�+�E\���Q�X	�=L�f %�&F���b3��|D�u�P]��K}]
r:���;$}��__}�i���,���d����{�y�Z��g�k������������|�o�4`o����U�o���G���������[e4�{��j���7����#���=��D��t�'�F�Y��!Q9�A��Q�2+�X(����d��:{�f��W�c�a����)�'��m���4�������B����O����������br�����Yb��;�.�hP��
��[Q^��1���5��n�q�Xo���#z�	:2��:?%/�g���#��:� �]��9�����hqH�f��h��{����
����-`���?�I����)��yW������:�Yq���)����+���F���)0�����hL?Oe��������Tp��9Vi�������8��Y6�������=�=%>�#�����{B�������1����}dI
E=��
�������b�����8ouS;�b���0TP��
�q�Ni[�
�.b�(@@�P�3��j{��}qps������?AV���e��;<��w�� :g�6g�������Q�9T���q��f�2(�E���`�p���D�k�j��{e��s�	k�?����y
��StE�3�'F��oJ��@�rm�����%��2m��X�fwAWJQ�W�^Q��j�h1`�s��I}?1.���.$8x/��v8>�3���C���S�g�&��+i�����*��T�.otp�f#�z��i�6��[�>��X��Y���C(g�J��G�=D���3�"��Y��h�@�'t����b��6���4l�Z�������L�1��(�2����`����
�v�)Gvwk���H��(��RZ2.������p���
��$��jY�@��
���/��1:\���_��H�RO�D��c�3U�Irkb���C�����2�El,�o��f��L��;��N����[T������g,���O��	�)����_��R�eGA�����j�����[k����
v�1t!O�0����������������C��)�:f5��}�[�:o�q�H'�Jh�@^ ���"M$H�����` }
��(9m��ZZ���	�)Ap�k��������e�+������_��~��-Y��Vr3��5�p�&@r�>�8��+]����9#���������]���L�Z�
�adg}T�
s� ��!-�1�1��8���U��b^���"��F����H
%��$jU7���L?�#R������(k���me|+���1�6����A�/#G��K�/�`E�$�i�t�5�6�&%��uJ�Y-�X���o��L@���@�2��b��5q����$�� �q)�X���8�|yU�b�j18��_5<Fqk�Z��d�k���B���8f��v����d���1J&��[p�r
�uO4�������wy���D���}`!���%�W�,����K�iV�m
8���
�M�A6>3��#�L���u��2�S���C��9�(�L"��y>��c����nvqTsO���]��E��AY��M�V�ox�q[/��-e�}�u�n��$J*����vc�Z��A=8X��5M,!T��o��~�/�]t��DJD��5�tJ��-�h2W���{�&�/���X�����������������e���4=�_��	�jXL���"�v�����lS���_�oY�][5�t���Jc���Q��<�s�t
������j�(q��G�/�����A �=��4&{{/
���v��fc����z$�5��@���Y�g�5[w���=EUL����h���*�g��������57�}wv~������-����BXg� �_��F[:������\4J�w�=��	%��e�����%U����$A�J�d��Y�����
I�7��Z4��'�~��3�H�
�2P��l����������Y������u������r	6K��qy�Rs���u!��[��y�����&�}6+��R��	~�>H�)���q���h1+m^�k�666�y $�^KzH���bjfJhX�f}w�[�6������jc	=�J14�c�g�6���{.���I,�`r\Js��
��R����f#�^fb�
sB���<R6�k��M��:z��~?��C0�}i��K�g���xx����j\�[����	gd��e��sx|�Im��[�����z��V�A8�2]?��%����*M�u����e_!�x�S���]��	����[���f,���PI`E0���~�DR�aJ6��%I!PH��?h�_O��R���Hd}>2�'XZH�T����p��m��
/8��
��Dlb��~�<�9�^Q��:b��h�����c"�����2�'L	��jO���W�d�1n�6�?�Gf�N�������l1�^�V�O.���]����2CNA�������'[�uI��[�d�h�������
��I0��0��������|�"L+x�T�u���V��Z���jx>�B0{�Z'��{%�V9�o#XL�4�b���������z6�A3d��-�pm�����$�6��av;��*-(��y��	�A�	�F
0��z�SY��r����y�����J�h�zI�4u���7/�;�,�dW��S�y%}�cz=������4�O�n���6g	g��!�)
��C	W�8�R�\���>R�6b��Sq@�O"��O�U��;������g�����h�������O�[JU�4TN��0op	|���~xZ����;n�s�:�~ ����q�����_�h�p���D�x@��y/�jE4��LW`�?�Q�v�p�BkN����`'���vp��{���F���,t�>sw-n�*�'c�Yc+7�Zc\c��t�J8������(e�����Z�0x�B�w�2���9�d��
1Yf��i��cL�\uV�[*N_�`4�+�����}6>�CD/���x�����*���9�q�,`x{G��;e�@�Y5 N�D��B���7W2f*1&}�	
��8D�6[��%D�6=R����s�:����D� (r�&WL^qg���9$
!I�Tvr�-E��3�
���,�o!f�_F���7
)�E��/M]�����"i�s���=-S��H��~�P-K�~@)Y��p^N&o��Z�$�gC^�R�)ig���>��k�,+&�����-0Z2�M�0P�S��&�U*��r�HP:��*�	l�d�M����)�cD8ES����ZgX}pv.#L�N\�%1�WZL�!H��`g!v��&e\|<��T�A�������4�p3CW�a:�E���Z����1�@I
��l�)P|NUs�yI�+�AqE(��)��-�����k��"���[awO+�t��$n��\��s:��e�)��S����S�8������R������0GW�1�/�z�)^����9���E����-��B��v6

${�X���B1��t�i�k��No�P$�B�S)���L���|�0�MZ��2]Z��ay�����	d"�X�Y���X[�LII�3��<����r�:6�	'f2o[D��L����C��g��E3�;��B�,��J���������	'���R�U�7��(Xna_��<`%�t�~����xt(o4��w�!!Y��9������M&�d�N��M��({�X��`��IlNF��6!;�{��h�L�Tq����<�*�0S$2�f�>��X���G77�j5=qa�K��I|rlRc��e��'��TV�q���������K��R����)	�tS��8��0��{���� �ES������n�3��z�L���g��[$�K(5�,�
Ug��	
����ed���?����7��!��"����N~VV���X��9\�������:jXP���A��>�h�rC�m��NPO
�ieJ@9�d0Q�(����lQ�	��g���an�,���@������%��?6I�Pr������hm�1M���)����4�3���L
��#�\e��Yy�s�+�M�X�ws1�{:a!���?���p1�?�n�"���[��dA��p��"�#yX�vR��q%d�T-R�koK��y
%&�~���E�#��G=�4�<_��(b
MO3}�)�kh����<�ZN	e��
�{�v�������%h�ym,��Y�X�w@�4�u��������~�����)6�2vf�F":5��67��@oqK��3�hv�S���8���tu�6b�������
x��R9�s�������#�b ]T�Ud\%=C��TKfd��a��aY���������F0�����\���XS��^%����������������V����df���|��	/���;���e��s�=<���C�ez{�=����}GNq���6���)�8Q ����A���,k;"�&��c#������h����!g*��RxZgt\[}G��a����>-59��T��'���K�/Z��J;�N�d~j4�.�h�6���D�����0���r���51~������:XJu�.�gD%���rE����\{B���@#}e�<�$l`#���^�Z�p��vx?/�.��^�{.�o:;��]s8��W-�����9�t+���7����"�T����nf�G�����F�d���w����,�w��\�������W����Q_��f7���U���y�!:q���,5�*84���z����e�	����D.5U�
�s�9}��m��$;����t��[B���=���/[��5C���66lLL��9�C���<�������*�1��������@p���Ho5,�k��L� f�qS�!\
��0!9�H��V]`B76>�77��)pt��=[i�QC�I�X{��%7��2�8qdT���^�/�I���L�����O�w�la��0��+-�U�o(��L$d�zbr��@T������!y1izT@~Cz[+&q0��tRM����������A���j���������X���g�'��y2(��u�z�l�rn���j���Q�Jp����E;��x6T3K��`X��`��+Gi����b��c�/��Xw�I�5	A;�N�u�l������d>_�=C�,��c(�����r_U���@l����%*V���G]���bU`�j���e�K�G{��XJ?��o�xNn����bu��FX�B����YH���9�3��1�/�
H�NS+��'G$��C���Z�@`��MD���{��Q!���{c~`�r2rq���?�VFlAa9�O�|'���~�Elz�I$�y�����`pe�dJ��zE�\B���]��;RF�
H?��16�\]�X����1Z"|2��Q���vq"�P"����O�o�=98�$|��+���S8���_A��@GN�#��*K>t�r�0r�%]��$�|�+�p>?���Z2��\�e�Y9����]��s�;��������f��h9��R�+W���m[h�7#%�5���d9��"^�j3��!�v�6��;�(���0�\��}A1�3|m��RK��?7�=������d�B���u�m���rj4�]}�@�����
����He��Z�Uo�*��W�8	��B&����d��A��wx���-c��4���x��x��iv8�Z�A� 0��V����?Z���q���
�!��`Ia������D< l�|��r�F!
�Qz�(�L*���Y�nyZgHViT�B�l��4��� �����3�g���r���Wu���,i^`�h�Y@7K:��c+{�K�J7���^����[[�J��u���A��:a9�����|	�\I�b�C������s�>����^)��gG|:�v*~DSOy�H�m�D���M1�wrJ��{��7x�V`%�?�/�K�K7�6(t���j�M�rj��K���9���#�w,���vUG�Y�����Y��b�22z���K�KKo��*���y		2�.��K3�r�G��R C�E��)��*	�<����E"����g���V��ncR�o�H�P�I�<���^����C��0������be�a�[�Zig���}9}SdB����LT�T
`*��V�A�����+�R���.N�S"Qq�q^��L�
�{<FB*�rIwS��*�4�����D�hd����r��%qyyc��M��a�U��~���D��d�<`�G+�HY@b�h4[-QIJYMrc�<(�3�7�Q������N��t����"C� ���_��(�V+�������*!��:E��\��fpg�\�a��$ �G����bS&y��"�Q�_�:�(�P���������`n��$�Nex�;X�(�n��$��C��Co2������3-<Go^nA_d�T_��@�F������U���qI�vl����V�|}`-W���W
+K�����v{��~�a�2
�2H\F�B�����G,)� Ef���w
�t��;{��S���?c�s�'��5S�������s�VF\��L`���e����`��RxV !�+�U�A3�yc,�E�Og�����?G�(x@�%��Yjgys�w�KI�w�WWT;��4k:R�`���XN�x�)�l+:��(��p�B�0rd3�J��ft\>��@�L|�%,�v1���"���[�db�V'kz��U1�(9��$����#���&����9��%���~�'0;Rp�
^g0�����2Hff�8pf���.� M�V�v��Z�-e�)��n�
*�\��[L)'��=��f�s?�����ZG�a��������(�v�o.;�����������u�X���n2?G�<:��r^H��O�=X��N.{��|rm=�#�xjQ����Y�w���	2�4��	t:��W��@'��S�������N}=uYrJ8�/��d�����p/�
��N_d
��i�\���F	�P��F����H7�yI�n&���^0_���$�?�z�X$��<]7,���fPJ�o7WbFM���C�6a���(��_��,���g],�e����3��!@�j
Q��)�U��v�Q�Z���`@���Q�@:�~�#J^�JBPJ,����� �A4^L�����'�������������Q�"���cE<�uZ����]�2�7M�|d��2���i��wD�y�<�n��i�M�����~��"����d@�tD�v����.��Y��Q�����u���\�����k��Y=_B��P@�a��e������}�U�s��+�vx�2���TL!�he��&:�l+'�p���-]��'���BM|�7TFj��0a������(�T��f�6`�0ws��1��\�"�h2[�Eg��y>/paEF�'�oS2g���P��K���H0�<~�I����n�����|����F��� �[�1��O0<�6`g�������;��U�v�F�Q��e���^k� _vR�\�"���|�������i�
W&���� ����q�#O��2�S�����A��p���~��x;g����- 8C�;�Ni_��#�h 4�P<�YL��dP�+�+D���;����.�<�X9��9TZ�Pk�rTd�!]�/3)��-������e�0<�U�}��/L\�?a�aI)�C�X���n��j.0{��\�$�<X��U�Cj��5�
���L��8�xS+7�#�T���J�X��#�=c`���*(XV�M����EhfG�3eR�������q{���A�|����wY;���B�1��\[���1�xY�&{�	��?�5���Z���������{�Y����*�|hvIz��\�	e�s����%W����pHS����7��
��#x���5��_P����a��&�(j
H�5K76���w`
�~c����j��}��p�X����M�Q+&�c��ei���y�{���e]*����lL�aTw�����k �����k��7��A����9��1v>K�hp�"�dZ�;�p���xp���4g��I���|V�*�_�Sgn��D�y�P �YmBU���m���$~x� �wY��A��u^'�6���������%MPUnv��F*���R�,�����C��e�4�7����������u0��Q��{H
����M������D�-�e��a��.0����f��0���8��	M	�v�,t����J+I`��[Q�~A�!����.��Jp\��z��%���1tkJ�F.�ckge��7�0�m���a����9k�F*�w	�W,�������T��l�J<q���!�+D�{�T�~�����P1T�y�Mh<�}�����@�`�r�p7�J���`�����U� +{�
�}�����/�8�����������m�����=jW(+�Q�Uk^,�����X,ZP�>����j�2*��:�}��Nh!��Q�����KD�f��RP:�qW�;+*��U9� �zD@�1�������[&�7�X�U�2`����j&
a��U�q	�X��O���MXA�:I�v��AIP��$,�F��ATA�!��*Sv����U#�xZy�L�������R�����9�TG���&����"��2��8��I$�."�0�;�����w��X���-,�71�?�O~FM��k��X�����P
��!�����hu0/�"4%��%��f���jFe�S.����*���|��B�������"C$/<��>R�.H(�$#��_�C������
PQ������g����wl/}��A����&;g3����bpX��p=(����O�%F��$�"�U���j����k�3EU����O8��l�H��=���V��a3�D�{��Hon�dq����$�W��������uO�sC��q@e���{��,/�f4(�&�*��j���OK�������YF��D�#�vV��=�;C�����E`����p������}9����o�p
nrWS$��C�����x3L��!��.z���"���!�C��@�=�(d5���n�lQo��3
�h��5;�3�)�����<��k6��+|abg�|�y�3Z8#�{���o�7��%�R1���pg!����Ec�Jl���Ig��t�v��f���F��3\ ;k�K�P���0������7���MF\�R	t�{\aI���
:�8�b��2���a�T��6��QPu�;����9u<�����-�ry�	�.�U2bY�<����mF�SfQ�#��N%�W>@�j�X��T�8s{�����K�9����e�B��B6];���U�G����I�[�:wC41���"�|�8���p��_�I�*!��-���]�hV��*N���t
[l����K\�^<���=[6;��Z�l������1g�H!A���3�-ht�u��
C�D`0�H��^������d�us�"3,��L��q��xe�-���N����s[Y&2��@nWq�s��*�uD'X�O����]1*�(�g�t���A��Z�d���-��,�p�����MF���0�b��M�7�K1�b�����J���`K<+S����1tbe�)���TY$vG�F�Zm�1%�:(2���`�dKV�]�U��o��0�!����.�[���N1���)��r,��z��A39l�t��rL����p�*��=:$�F��-���V	�W0P~���F�VT�����L1>#!+d��!��g�0���3
k`�H�9�Cm5�@!T��4!�,b�,VHx<�(G~;Q�) ��M
*���
������0����N������b�� l$6��x6�+�M2@��j��=/���R���M����9|��0����J0��wr��H�i��o�T�8���R�P�n8�B#���
:,�k�`4*���'>���(!5�S���Z�`r�P�C�9>��\!��\x/j���R��e�
��>��};��lov��q~���:�?�^GU%;.�X4�v�
��7��,Sa�Y�Nh\Rv�D����k��l��]���Y7�e*�}<�R�^~h�z���dH�A��>G����pk^�k����4	N���w���S�i'M�>G�$u�'p�BD%���2��\T����iEa��t�b�����&������h�U�l�AC!;v!>o������<�����O����Y����L����yV6s3�'���d�����NLB��J�L���kL�rR���5����N�.*�a�����!���\v��b�~�y�!���$*��N��&ht�����yB����x�+q����&��p�����F��������
����x�{�a���vF���{�����B���zU4��$R$�����Z(��q��W�O)qv� N���*+`�����I;j
��������y[�Z�R�5����Z!�)X<���}�������8�j�S���l@Z�Q���#W���U�,I������M�f�B.B,�pRGz�l�?H6���3���:���sS��F�����3*#_�W�O[���a���M�:	���r��_��8FK�Q��
z��e��B�i5����s�1�6��8���C����e��K�:�2���c���|�8M�Ra���n#��+|��������t�t�d�]7����&��+'������x�-�g���i������_�����s��z(�r����K�0�����x���0���d[ps!d��d7U���VQ��R����'�ne�3�8�'�#QI���p_��=d�F"�j�o#���o���C��M�����<��)��y��RzEL|Y�����O�S�:�5����Xa�^�/��F9��p��`�9�.��*|*���(D��u�~����>�����J�k�8�0���@�{������~m-�����/8U)�j=<�7' ���1��N���e��I�s��l�U���� �{��/Lo�����;����\+��i�x�k�wSC�A%�N����J+�2^"��	�hm)1�V�
��Rd9�g>�L�"��/��J�N�)�_4��_gv$������Y.,���)���-\�\K���F6����2%V-�(�� ��h�-�p{gL����O�6����3��0C�����1�ke�W�,�K=Y�A�*=kK�����]�3L����z��{�[�9i�i;/f��Fs�jM�����\�����oD}�g�

QO��MV�N�����%��a� � ;�(W��#Ui�ue=�,����=cp]?������^/�q�6��$���]���M�3�����GN���5~i�*�l�i���*V~/m��"F_2�J��z�vC���lZ}�@llx�I��AG�}���$k��s;P�2�=��,�&���(6?�`.9,�:���G������g)P�#>��4���}Bl?h3b�6�h�#.-rD���&��9p\|1��1w��sv�p������[F��*%�wQ����lO��j�g���X8�<�3x�����G�_���PW7���Mp��-��X��:��T�/����\�Y=�*�z��R)�M��N)��=O{%�����h����p2���>�N�m���:0k/��&��G�xY$	�yd(���Z)	�D#����8���n�C���~�r��>��4�h�B��������8���L0�:~�C�Al&%�So�+�)=�G��4��#:���}E.0�_���6���n�������8-�+3/���
}j���}�9�\w��u�X
��tz����2���������M��������������J#7�cOfa��^�������F��_����\�������_t�xpu��Mh�11`d���bq/Q���~�.&
�v���v`�G��B���������|C�t(
�&=��e+xh��)��d�����VIyY���s��8��]��i�"Z�lS�^�C8��s��l�)o%v6�9��Az7e���<����zP����V0��7T������!b�OIW�]K=,`�)�9=�Z!�����/pXP��99NI*|P�������t)��x����K�r�#�0��J�=�.�
�^<'7&�����fY\d4���������>m���*���N�G����<�SJ|��_P���������#��B�5[A��*��"���4��]�}P��$���%=FW�Z������RP"���I8~X^f@��(���(�m1���������tfx�pse�6/����7�9�`k���������r�t��(�{��4�J��:9�:��k����/X�&�~I������?9?�*���&^�{$rij��cuRe�q�_��-b�>C2��9�#n_=rO�|2��D%�X?�>�cO���$��v�M���/n��� �4@����B\��kN���:�r1N��,I.�zR�T�$��%n�h1�}�������|��]>���&�Rr Q���O��<��n�����@����;���;)��Y2��Sr�	q�� �5�E�Y����c>��; �}��HY��9���� 1�9����:K��EY�r����M�w�4������E���d�����^���coo�4�?!���}��,VN�j�`"�f���q�_^K)��lNS�(w�St,;�<p����`��p���Z����j�1��Vq�NEl�S7��>y�����Q�5X7���>�+~N.���f���t���}u=��^�i��V�bE�R�Xq�"�����G/�jO�����;O����x����i���������KSD�����N]����{4S��*����m�i+�B-*c���2ywH�P�f��\���DVj�j����n�6�yX��7���s3�2�nN����u�s�6Z
���f���5S�T��Sc?\IW��
��X
Q����kFx�"kq�EtBF���`�o�EzX�E�����;I�'�I��Ro�d����{�N��y0�'�btw���k6�R�F����������V��x��+Q�z,���^�}"(����4����qSS����I���2�9;!���yGi����s�qv�+Ay��Z��
����v�Z����Fch�P��e��4Y����9�k`odc��a���X~N�!��������{�����\����Y�2�S{?��u�K���7�~�YD���[�4��G���g�N���t���zT�~=��/�N��A���)P�	��{�l#S�3Q/�l$�!���,yH�_���������s���L�Z����=���>��`���A�����v������0�W��P���D�m��Lr�\�������-� ���SW����9��N����"��	wW�-��O}E�k��������'�����:PS�~m���G��a0����P����k>���p��;wr�i�O1�3Y�uY����-1��<;=�{���SK��jo����-�4Q*e�<m�"Tt������nf��t�����8�8w��z6�7��fbF/���W�4�^?@���7e�p�P�nzA��0�������	�~����������5d��v#[�q��������8���a,mE�(�Xh���������~�,��{������w���
���&�%u�m��'C����}�2�c~��oO�Q^�/���K]�������uGUT�$����T)��%l�w>b����v-�'�N�Z�����`e��U��y$�]/3������nc+��{�r��+G/��^�t�S'.�i���I�<xG�����`~�CAu��S�gZ��=���Z}W0}�����Q�xll��#�O��u�K�t����FW��~�}}�[��eK��f�N���F{��h=r�2G7G�w�[���v0�%7����4R��C�4�m\���m��%��n����������0���1fy�U�$h���p�~�Y���,,T�����
�G��/�����h������j��m����j��"��U�NK�ua��=����v5�x����TJ���*�������<����g���`�v�C�����Z��
ER]�*[=�5,S��7,gP��L�<��O4�l)#L��C����eQ��60`���Q��KsS�)���\�r�)mv��v.;gG��I�T���W�g���{\`t�}��J��F[�NC�����w,�2�s���}}x��s�9���eS���z���
���d�b����*<�m1�6eY\�a�E��:$��ST�X���"i�b�@��������o����X����>��s������f�����L���<���
�*4cl��pA_V�0���������m6j�ju������;�n������weK�+e	����=�m �E��H�L������A��������[v2

���.�=��c�~�9�:������qN��,s��c�G�g*H�tcPG�4zr��,���*�E�M����(�d\R����Y��/� ��z��p�y�(X���J3?N�������N2,&��Kj3�X�5$-\�����}i��������������,��.3MH�9�������s�7�w������f����.���7'�����B����
�dw8a.��!oKJ
��w�f�������%�d�����e���p�Na���
�!.����l_��h9y�p��M�T~��������������n��l%U���O1P��T��n�2�Z�q\�����^�����d����{��9�K�9�w�E����Op*R��J=y�-�Lo���H�����Q��~���`�w1YL��3j����0H|!��y1���I�����=��a�4��~`�%�"��L�+Y��A��Z��m�B��f�7��bMp��c�	�hz)Y��k�5��z��P�Y}���?D�j�z~��i��MS]Ah��o�+�O�x���-�0&`�N5�1�)�������~8�������s��W�I��`��G=y��PSt��`��.u����"�������]o�A�����6�������['Y=���
p)����q�����[�YQ��V�{\RE���}�H�;D����8M���U�O��vJ��?�Q�p^��B6��s�kj{�^N��	�:=rs���'W�s�(s��|������D|d�p:��N����~?6�j���ZKTh�
-�����Pm���bC������p��������a��G'7W�`xqxy�EWC� �����]}��e;���J��<+�0M�r���=`���<�����)����%�1\/��p>�gH���w8���������nL�O����������T�2������mp���-D�B
�!F��:�����h��������`���������/|Af�6=\LW����6%��q T���Qn�t@�jn�\f�R�����;��L�	�S�_�o4�,W�;�3���[C?���������0E������ODvLFt����u$�-�G��A��d��\�cr��;�jE�<���#��G��0���Q�/c�
H����83�G� �0�HLyy�>�p�`��H�I�TOaG2b)��-A��D�kg's5���������.VF����9������5���������b
0Wp�l_c^���N?N����~��7������U���������xc<��}��Y���`�B��N�U�]\��4����3��s��Zj��M&=^�.$�}�E�	�]�?���'���;��2e|�1 �r���(wlf����@�F4����,���.���������A=�����V�$�����@0���&��{�F��i�C�t���z�=����� ����(��E\e�t����%�8��d�)�����g��d+���akH�D<�������jV�`&~��0������	��?o�I9������'$�����/��{g���{���dBk	.���������_���tf$2�g�B87�K��\�����V.����~�0��v*�Q>9E�9�V8'aL�.�bxf$[`_����}��Q"��0)��������V)y�;N��u����~�X�Oa�Rr-k��w�\�	��]������Z*�!�X�)@O�/�R��?�.5i�T�Lm��7�W����h�$���?�6N�F��v����!����1-
��m��#"FB�Q`�+�A)�(	�tF���N��"U�iJ��d�wJ�(���o|���>��dyQi"��c�\��1��,�ef
����$�G�n@P����9U��=��&�y�k�!�!��+�HF���4�`w�����I�*?
�OQ�"a�+!
Q�����"�t1��}ai�#�V���u�Y�Gq���nrX��R�o0e��!6c��=,�����NO8����E��U���
��-����k����E~��������&����|x�=�������.;�W�<LBa&���7�f4�tO)���k����������3K��h��y�P6wP6�
���O�P��$�-R�E4�
u��C�fPM��w��c;���d7	��_I��FL�u�@�6�����d`�$���|r�L"Vy�G�^S���yD@NL/z������PK��Q��n{2��/����w1Q��ml]�"��OQ��yLW��2��+l�����u�����i����S[#c$R�u
%k��}Gc�t�/�4]J�KB�pi�~goz,@~����[3D ���z��\4"h)<37��	4����j���g��	�m���d�7���!Z������t��?�����uS\[�D�	�+dDMf~�R�U&r0��N�t8T:I))�h��:
������'�}���HZ�!�,Y������"�|8�b�i��@q���@v8�C���5�����&�������K���3wj,9�dI�
�R)=y[�������n�T	{���$%�J�$^	�
�!�;�(�!���r�����z�#�����*�9�V���O���\���(���4�����B����s��&���O���J��y�aQ���S�~�\!���y��X��<��Dv�P�����TYH\����ee�9JG�C�q�:j�{G)|J"�f5u�U#�|����v����,���������������x�L��j���U�L�+

�����P�1<��
��3gQ<��!��2�*��#�?D���<�C�X�P���Y�,:fk���)��U�6l�������9y�m5
D�J���?b�{��sF�e����$��=gUdCJF}7�j�������
M��
�rf�
9r��N
����ET�D�@P�����Qsj����:0bGT�������=���cL��I�>�4�����I�H��<�v����>��jMhvo��XDHIM4���7�r����$�:P�Vv�#�����,$[.&?������)��%�Ey$I��3�����^_I	�0!N�����o��& �1m�Cs"7���vo���w���������%�-�	��b�k��$����3B���&=�4kCk��D.e��P���c�v8}Hi����B���L�D}�v�i��:����in�l���+�!j"���P�����.����-�QW�a�����T92nX��'���J�K��V���Ey@R�yQq���)��#���r����D
�V���3�J�x�oI[�uc��s�t���iv�Z�CW�r���	�g�������uNw�j���iw����j���W�������{��q
�&��.V�wdn[����_��StSK��P��/LZ��F�
0dz����oQS�%J.���R�����c^�9�R�a�PS�p�B/��?(3*���9�
����p&�6_�
6�K����/��qO%�63��N�<`�s`6�A�6���c&�[?����\����F[6��C5G0,�����"J�����`�#"��G���JD�4�w{m+;���F���*�	����((�!2&�l�N���%p������c�sdg�)+dX��7���h}�����&�BV`=W���$���}Th� ]�f����U5F>��ak�4���2�.&��"L�����RW��A��K�mJK[H*�a��!%9[Wq,e��Z�@�
_��8��@�Ug�>A����<����1 $e��~q�����D��#[�5H ���p��N�@����� q�'�'�YA���(�UX��bdulrc��5����h�}e�m��k��yNw����C�jBq5�I!�}�+��r%�n=o"6�fU�O/��8���$K�dOL#I�.YM"V�[������>^92���^������`5��g��>�QOQ��R������Z�))��f����U�DP�k���W�l���L�q�)�r�H&�����w�����Be��KS����6����H�'Ro���9�� �g�mkyT��[�xx��?]�1=��*�~��1L��[����2�*�*�'?�����R�l��(�tJ2K:��7��������A�
'���!#+��?y�g�?H�T����E�"�����}81�n��Q��m��m��O�&�C�[e��[�����x�,�
2���������~�Q��F�
j�.������"����
��<����GC�1%�#lW��K���02�9���a��a���_e$�
�x\��q!�~���I��Y��T%t%�v&G�T�c���YP������B�I��p�W��I8�X�����U�,LtO�����+	���G�v��[77onm�\�[�1Q�;��^�S��������W^�~S��H!8]��Ye1V%��1���W�R/��5�PBc3���PV��0yL��s��:����%Ij��nk���o�K���d�$v��)�e�����{�N>����=�$7�ch���-i�%�Z���4��~G���Yz��q |
�(n���,��{��C Q(�M���ig�����:n�q��s�9�F�����M��,�U�V-;�c�9�(6�O�����b��3���|����sC>:�+�x>��lr��������$A%�������y7m����%P)CnL������]^w�G_�`�������:��o�^�Fn��6$��[N��@�u��<��1u�������,<��<c���;r�Q�sh�\��s9e�q�D�z���XS�#��AJ��E��R�8z���g)���s��I;8G<�9j"��
2�*�eJ��'}���)�S}����]�B)V�'8��/�0c(t^jU6����D#q��Q@�o��oHE$�X*��j4S���4sw�s���������mocR}�g:������������`��7���R��YZy�x{�G��AZ��`00���`���q��0��,
�P�[	��RO�T��S����d]"eW��DL�aw:�xQ�1��	EC��S���*Z�_\����1�Wx6����$���>�"�#H8"2q��	HRpd1W^?��[U��:��V}����'��G1�__�����0)<�2�3�z�i��"�^�����fs���~�O����Y�~N�95��"�z��J�����z{]m����[�/���7<	HW������v��w�xG�G�;����I�~\w�Oz@]�?��'��z������{������������^]w������O���y��89���'�W�xrsE��O:WG������k��K������i���+��{
��r�==?H��_������T���C%�J���5t�p8<��{v��_�\r������N�e��dk�����
jEw���n�D^���^��Ix]�wY8����X��<?1\�R�ZU����-�qu}xz���\���\^���_�N:�g�v)c�~M�������?<;>���?�e��������'���
mMX��1t�{�����N�?u�.l��.��vs}����U��������L8��t#��u�Q?������.;���g����/�����p���O|����	�~;��u��3r����^��G�:gWP�������5���=���w�KjN�J����9����O0�]��r����$����6����n�~M����g�����������:����"<�l���4L3�~|�={
���Zs���������������!����t�?t:g�~�8�{�=��^R��C�It�c$�gG��3�<�M�w�Vh��l��>��Qd�����v���q�]89�������@�v?�_P����x��(�����x1f'X\��f����[�]���8���>k�^x��z���C�+*7��;x������d�"F��'z ��a�)mM$�z�Uo�z���+��U-~ �>]�����om��R��k�dPya��vW�����K��T�{i}�����~�o��S������
q��4�A�^�4[;@g�������?H�M�������Xi"�#����-O�*��Vh�a������T����./��Wo�y����1����R�O��x��J�$���)�Rm�g�g�Flq�ew(
����_YH���X3��w�Q����Gh�������=�"�� �@E�'�$��>�%���F�������3��O��}_Hs�������X,������[��f�����{�VK����{���?�������9�Fq�o����sX)�Y�"��[F��j�v�Z6���n{���j�PfN��Ye=�.����is&�>��M��+V$px�e����������X������P�o9�2��2���
�`�����E��T{�{u%,+�g+5X�D���#?O�i�6��&;��A��q����]��G�s�Us8��b������898��ee�����9+����~���@~q�`E@9<��+��$���#�	O!�Y��{H H3�?Ec�E��A�u��C�@?m����U+=������k]H(x���Q�d�w�y���*h�<�R���0���)]�fd�7��F��w���^�������z�] ���&(Vc�������Q�C�"��9���/V��=���eu���O�'����w��b
��"�x�b�����N�&.FQz�B���?������r����6�Jj4���a���B�6?c��#}5V�8����u��s�)7I>�0io�K��C���L}��#eE�������=�q���#���B;"�����-O�>�UU!j)����d��^0��������S�;�s�^���]yo��W�;��4t��:U�"�s�O��h��}�m4iB������H���N�!�����'L���y1?��
D���x�������8��n�a<�0��nBzn~a�����/�������k�d$���8���YT�~�[V�~�j�,.-������1/m�i�C������9�e	�����
��b�T�����A������\�Z�����6CYGH����xp]16E&J$�#������4��N�=y[����1k��D�l��1�8)�	���~��G�K�e*|������kj�sx�'�CX��=q+;�@�0�^7��]����{k�8{�����)�7�m���2�"fN�_�`A�NCwP�^��Z��	,�`��Z�F\�/��Q����#����&RTXL&G���#Pg�Gr��S��R�@:����7s��t�������E3�X�Y<�0`�2����"�cX�{2yz#������p,v!�{?���������b����'�vV��'���%j�{)�T���yQ��B_^�W�������x�f6������r�
M	�](q�;�|��b��S�=N���)��{�w�
M#�G�{�(���9q������S��v�"�������mg��7mA�����������<�C|�i�%��~X��������~�����*�sH�k8M�+�#��?�����tz��������{������o����W���L�I�v��9af�`sR�VL�����T�\�W2���qq��a����n�n�3o5����r��"���������-w2HT�����"Y�W8a�cY������	��<}�$�tZ0��Gm)Q<(? +U�s�����������qRUOL<�u��[8�9�X��c �]��b%�nJ���������2�o�"J��Rr\)���0���M}��%����]�\��s�	L��	��6�BE���u=f3���\�4�	D���\
�-�=/0�L$��JZ��	c����x;b����H���i�CE�b��Y�r`Z -�%$&}��/2g�^���������qm�5��V�[���CD������O��}�����+�
�Wa�qK6B)��V�;��s���n�$a��G���%�QX(%E�w��qel5X/�N��a�p����.([6cgd���YB�zZ�����9n�5n�c�,��|#%��S�8�F�H��%���b�%~xg�q;��D�n�� �6�pp�W��P���u�%4���%���K��~�>�V�acww�Z�\��Ye/�9=I������l�-rr�����tR�I+����_'6�g�W�~�Z+��'6C
3g4*���X�v�,U��)���SM���
=�b�0����0��-���������AsT����������e�����P�`���v�����v>+�pz��H/��K��x������H?�/F���2�#��?��iJ��;�;MJ�zP����q�F�dog�X��uzO|D�������������)�������fy�pn4
��������sn�[1L����l*���Z)���j:yr~��[���d<�,*��c���g��S�9�L7Lo�}�xk�*��Y(�i���b����2��b-�;X9z��X�N���k	6����K��{s��-�8z�I�z�	�R�J_�T�_��>��O��B�t������8�0+���(Aq��������L�$�f���
D��gU�-#Z�Tx-��8�f�x�I7VBs���5�>�;�|�!��[���v��6���#�����s�����km>��k�Z���Z?��������uc�j��1�����juw��wG��8�m�w�kq�h�F�]��W���dk9o��q���b����a(���+J�\�?��R�'���X��j�:a:�v�4��xo�@��/g	��p�
�!I?�5&5��d��V��]��s���]_����K�M@�s��J�.-;+�	1�=��_�<o��p��R|��j��:V����?l��6��X���	9��u���o�����������+$S��EH��������1N�%���y�# uH�2�V�d5�
������kbH	������(�A����dG��"���nr����P��SR�oZ� ��3q�9�I���!����]�f)}� ��IldP��OO��V��;u��!��_�j���y����uy3f���j4�"�+�F��1�N��m��=����>����=�.8���c]%��5���oQ��t�>�IP-NR:�:��j_[�U5���W**���
2���Ejs&�u���X����FJ6������z��+ml�����m��s&�#�1��jh�MT��MWP��������I�$-�	�E����)���p���w&)ZH��|���m�.�������I�>p��0B������������G!��?&�r����y{~vo��9��[���i�cK=����#��uiE��]S�)��w����������I�����2q.�	�(��c�0�!j��z�!��r���;l��Q�����a��1�%p�NE�0�4
N
�z��<b�Y��c�O��3�OU��Er�������!�N,���V@p���\����@����C���:@J��o�7}f���W_�c��1Bf-��1.Iw��X���(�.�/g=�_�+)�]'�7��0��.g)����j��?��b�����@���7Jn �/lR0;���t�=}#�	�|�Q1W�p���������v�^i�~;�����u�g���?H>�tJ�i
���Lef�2�Y������n����T*�����sgXx��.+?��������U7b���D[+^+1�XYo�}�QU�J�\B?�	��y���F�0L%�0���I��s�����Dj�������i�"��K�eh'9=�����_�tvsj�����;|8rJ��������=<g�����D������;���L$����xc���|31^<{N���>���6�f��&��P�59�~���i�
Aq1����/����~}es���*��nMECN������[%!�.�&�z\��9:0�����X��h�	����m�-�%�y�Z���������E4%����9k�30y[���N�3)y���b�?6��Z��W���e�����t�E�TC8����f���F�`�	���oBATna�5�m�X���W�8��b�E���^��>���"����U�)�j�H���1�]�Aa�=��$����]�m��bL���4�od������L1�_����{�'@�;S��e$���;?��
��J��,0K�e�� J���F�P,��r��(�V/�FME>��=B�~�'
D��KT<gF�J��OFW�iG+J��W8��G���y�Rs&��]��������2�������>����$��S�8����V9�Q.�h��(���Oy��<=�����
�,�{$+Rn�5�J��tz,TT���h�W�Q�=%�y1#�t�c����w��	����EwF��XZ@���v!9�����
9��r,���������ms��������*4_X���|�h���!sL*O����6�{x��0�da��8��W[�j��������i�����s��[�(c�	�mv��7��Fj������j�41��3���W��=���Rv��$��q����H�����,�a�{sn�����>%lm�����@���<���@a*#�
��#��n+�s!m�*'�Cy����'��S�a�YL����h��b��C��W���U@�Od��n�-+���m�����o�[5[��b��m�'J|g�./� T�/A�
����N��"nT?������/�����L��$m!%���}�<Orw0=��������������+�)� �l
����S%��EE>���r!0r��gF�SUJ�4�/�7x�|��iSI�`m�N��e�rb��B��6�Onc�'����PN ��_���Vk{���1,�z���2�y���j��O��Q4U�a>��#���D.��Q��b�/�x[�(���'}��IR�(���,��bd�`8/��}��VE�~�Fc_m�H��vr?W����G>�����I7����dOW�#��")o�`�lE?��[��GL��� ��Q]>Up��U2A!E����d�8o��h�p>N���s^�����(
�#���3�����Q���4 	2���]��.�HE	��8/�T>N�v���MT������^��i~��N�'I��c�\G�?�X�9�[�?�.������������FL�C�8���r��|b5�l21��RB�����f��Qz���'�wj/MT��F��(�CoF~�1�'w��^%���6e��61�5ij
w1N�`L�<�WM!$��J��0K��OA0�������G�~�$#��B�h��P`l4�@����1^���B��~?�^����3�6���D��1����&��n0Q~�HaB�xZ0)���z
�*>a��ZI^�'[+V&�1���O���66��-�iw����u��5���(+�y� $�42s��/=��qKr�6d*��>���F��'v8��
�6/��u�U��'�w�1������`N�7��9�m�[�=�O������|w�	��0��/����gPH���&6>u�8�a��G����tWQ��7��\��Q^gn�2�����:M�3�pE�tM*R�x��I_o6������td�����	!��l7��K%\D���Z������;���OB���K���S���Q,��G}�z�g�
�A������jtD�F@ARw���������!� �������(���
�$sO���n��������	~�ND���m�
Q�����>��s�jp��6+a��M��il^�_P�5��0�%��[��.br*0Du�������h~=��Q��j���������roe�a����$�C$�"���~�`w��{P�6���.���F~3Y#�i�����(�y_1���1���;��"�3\l���$��Y�_���C~�9�1������\���G�S�%��?�I?l��o��3G�Z�d��L������Zm�7�����v)�g�M�n�S��N���E��+�s,&W ���c��m��8{�s�������� YwMR����S��������X��1W����o�GP��
���N��e�(TZO�?{��O����nG��!=�h]��m��Y�u�C������T�Mt=�~=��p�'~��{�Q������
���s	��A�n��R��H	�[#��(��KL��k�1uU�q�����:|��p	l�*�^�|��:h_��9�[m3e��A�;R��7�*(6��9#����`�Tf&�X��`�7���D�5q��[4�@��k����m����G���|q�-�S�:q�l�u����������7��h��e�e�N/�!h�4EC��{�:H�S�{)�J/FE3o�������4��o�~��^��J��!��T��=�%���_����e�=RZ�
�#�VMy�pk(�:�O/�2�!r�2G�g�!{��fW������H9@}����A�lq�U�&P5"�	%C��x��]��\ �	�=T����U�(����
��2����(�����:���e�B����\���W_:�pH�MU��621B9S��	o���U{��r����������>p�Y`~hSQ�Z�Q�n�[���,�J���U�J�i�����+d{{������J��%��]�e7aO>:z��~���4��]o���T�onU��j�����W���%9�`[�������q��@�\��O����;�UT���>��e-[��Dr�!'��&��c&����F��wWc�]��~���$��\���x$s�`���v�������q����q���{oP�G��h��D9�}����ob���k��n�)�?�B�!9�.�#o�o��9��o����nL5�U�3�t=�em2�<'��	h�j{�P�j+h�q��MY�0��7RY�=��-rN���x���Ns4h|Cg�#����J�^��P7��5E�\n���-;*L�"y�@W�L:E�3]�������hzaM�K�V���[5/9h�`f�a��	����x���r*���O����?>�2q���K|Y~$>w�VY~�?��J	�*��_�����}���7@�&Y����q�?����G���x�
��bRV��0G%c�������^�{�����e�����q��"u�A��3��Nf�-������b=5��7 �f��<�
s&)��"�:�:W����5���Q�8�g( ������fWg�0$��"^��G�#�2������s�,���7
F� ��Vj����W!�!��d���=_���C�4�8�"jg�n�1FL1��O�rL��<Px�FVN��$*��H83��R��0VU}w��:��~RD�`mn����)����y^��9�rg��M:���2[�wR�rD�\�V������{��?��7s��E����D�F����3Y`�^����E����F�K�V�/)����5���<����
.�z�����EyTnW����'ym�,��T
:�+�@�����Q�3��`�|A, �3�����Wv�wf��s&��e*����U�H��H4#��J��@PtL�t����M����W���t��0������Q�)��4L�*����o�pO��b�������,��u2s����Ul�+W��J_Q��e����������orL�WM�zG���R_-;�RD��6�?�j{x0:��\�������e��8�V��t1���#����!��f
��y��,bJ##A�2����k�X�K��d��j�u��pq�����6�����v-�sh�������g'�G4���
���N�\\��G?";����m��w�4/c���2�eH�`c#��
Y�S&_��y��"������PH��^�<a���6k���D��>!��I�<g��6�t�]R
d���pt�tz�Q����"<a`\Pf�k����r�f{���x	��������$��h$Sw�.���Z)��|����>��
7J: /�>.G�bz���� ���fDZ�kq�$A�>�L8�����uj�|���`T\�]w���b�����]MOS�
�DW��G8�$I��9:��9���*�]kJ�^�����3N�	�GI\�+
&!�RIWR�0*���J(q�Vo��j��?�{�)1U.����m�Dy�~�������a^�Uw����,��s�
ThJ����(����;e�P=�F����DO�FqaGH$:�0M����E������,�w0h�8��p��2��5w�9V�����?�W���J�&p�Q�_����HK"�2%�!�w=d����1��('���@������):��D5Am��x���%���~�,&A6h�Dg$����e��y��!�xF _��i\���.��JZ�<B�hpD-�CH�!C��
�0
��s�8��p2����De��^���d;Nz���Q"�'�$�����g|?����h
���8
1I���8qkq)�����3��X�##E1`;���H�Tv�U�9���^��gi��
7����"
�|W��s��R�y�������WB���X�N�pV��������<��`&O��������U������*�E�@��[Nb�_T*p�DS��1r�J�J��4��E���;4���zb�}I��roev|��<Je����~���NmcN�t
"����a��N�@�8>�����/[A|e?�t�]���q�.��3Z���RX��m�G�H'��`3
@��M�0�36Bm���H�}V�������:R�R��@#T�������$���c,,�
����q�&���Dyj�s���p���no���NS�<�m�c�����^c����������Q�!�=z�Y�\���s�Z!��!Z��k����r4z���1���Xx]HC�L(��b�$�
�=E���D�$�5GzT
��\��-s��������\�7'�D}gw�6I����D���`N<��e[������m����_X������XF�h���������z�%�P�N��p�0��d^�'���)��5�T< >Kl{x�;=�>z�Y"�U������[�H[kAO��������t�,�.A�$uv c=���r��a�J�����<�!��JEJOK^���,��P�mRj�*9�*]�*OYE�*v�a�$�
�~'T����]Z�*�p�p�)��
y���������br�/�Y`�/�U�)!+;����W�4��,^��6���W���v��[�����Bv����J8o�F�J����|d��)R��+������Z�:����5���0&�iPN���������"W$�K�"��L�NS7W�?�5��*s$=�f��&��mR����jK��U���F^{��Za��)p;>��u2^W�����������{������?^���Qa�Yi�X�l�7Tx�.�3������|��/���������}u9g��?��9�	��Gz���%�e�{B�w��a�N�W,�U������P-Z>�H�
:%�.�S2RV/e^I2M�sA���&���� 3	'+Y��omf��r���4�:_�����"��[����W\�`�=���V�V0<X�����)�e8��O";�����u>K66�@�@�m���&����)���&.�h���x@�GH����)`/U�
v���K�T������,��n��Oq�7t$�����9`�0��3�B:y}��A��p����7HH�	�����%����<Qe�����)~�Y,h;Mi�8|��_���z�(9wpw�loM�6�!�M���4�={�H�����e:�8E��N�2���@%�uAe}}7�O���l9�����X��?n [������!�X��K�Qj9��nQ���cR;���N8��J=�]�X#�M�=r$��}���[v9L��YL������v���_��� �|��c�i���������~�#� /�]���������S�CAG��h2d�q�E�e��v��{����\�D�P��b���������B��t.��fg4����R�n	��������^���c��Q�����]���5��%/�r��e�!��d>�{�	����o�U�I��
��%�Nj��v�Q�:�������;i�}d�E��}v��w
4P{����o��}���8�P�������+�9F>����|<�G���;���K�����.����?�F�@����i��)��T��J��>N�[��|�`�J�n�fX4�DSZc�V�zE����v��Bc_\�����0��=�T�~���?����e������+jAR�������6��
O�d����\���`V��G���=����N�u�M�J(<��7����z]�*p��88��jl��;N����f��%d�j2�UU8Z#o3N�)V\�RQ�!�G0y�����U�=�Zq�����������?@w�V��$Q[�D��t
�]�o���(N�,
G�wMQ��#���-�r!�L��������9oz���`v��49�3�C��-w�}q�������Q�[�D���9����R ���K����6�M�+1��n����}��f*�3�> g<����U" '�����z�Rl�����e��O�I@Hc%�G����M�P�������Fn�b�a�@��Z�9\����l�l
-?C�B�@�p-9�sP�9{�������RE�h�F}����?A�T���f��~Pi�9�R�3m�zi�,����P��8[/��ZSV�u��-���7�[�`���3����4�y�/V�Ld�������uC��r��6H��������N{ ��`��#��m���%��]fk������P=#�u����b��/S!��-�4������r��V�b4�V%��wZ������������"�?���L�.M�5������L!�q�f�W�����K����`9<b<�9����^�g2)�������m������_&st�)�Ybj>�F�17
�#��-� ��8���d�^}J�"��)�H�8$�b_d
1!Z'�]uN:G�;?��t��N��?���fd��DQ����K]9�$&l����mH���F���>��5�+E��g\�{���A��W��v{������m��=���F�c�eo��������l
j2ogsL���$ �N�%d�;y�P��d��
�}&,����FmJ�����rTm�!�����L�P!�+E���[e�����8L|n�|�����ptn�E����L������Y���7�kb4�����K>����������R7�j���d�f7�e��F�a���V���)���V���*C���n����������7�`'Q�#�O�?3��hv����:8����N�i�0}���=[�)9[3��%��F^B�{��0� �	�����g����`�[5c��jj_R�$���}���*+e�;f����?F��%�|�(��^MMs8�4�����3�����KC��������5�Wc��u��^��\�&�,S7���I8~pj7���R5[���I�A/hs����;?/�?z;�?�8,��p���Y��e���&���/	RCn`�5��^�m�9�9%T�Z�o��j�^���%>�����+���.#��ON���\��N8�fzwQ����_�.e2�*�~t����#�����B��r��p02��;vG%�'\J-=v�O��J;�'MO.��w�^'A��{�Co{���|�
��@,������	Z����SoD�E�vS��&��P���A�]�����}��u��M���\�����M$�����6�l��Z�&~��&��.��M ��$�A��j��K���a���]�	�[8��0�
l6A��;��J�b0��Ro�|�)�4�xe�8^:a�iMp��s�?��������[Y�
�����O�H�2�"�Vv����j��v����t��fH�`hVr@l��w��:�|8�<.m���f�c���wsv���\b��*?�������u�x�z�x6~��gG7������_*��/���������i����C���x�}wsy����=�J�G�L�"��^���'���~��K��yu}y�=�o�]/q*^��x�}����g��.�Z�??Yg��@��7���5��CD����v�a5����Wo�5��u.��Fq��I��^��~�UN�?u��`:[4�;=�w�;�|k2Y��z7��oN
��"����z�p�	��[����Z���a�����Z)$$�r����0�%_ry<& �)]`+��4o$������yC����(V68M������^������J������5��vd�_w5?��N)�,���q%�P&47J�?�0��\�7�p�l�u��	|����~>���z������f�<p���/,+�N�9</����TS=���������3kY[���4�s��14��#u����%���I?:
�:B	SV�JH'�@�h��V,/C�D�Q���w���Ft��A�4#��*fN�GT2F�J�@U��QK�MO�$�i���c��d��ke
��@��f~�vfS��A;q=d��r$��w��%Mjm��d"!o���	EY��nF���������b��� r�y��&H�3-�=j�XKA9�e�(��VJ>�,���B
�B�l�$k@�yN�E6��1�G�c�KY@��$��>���������+����X���R�^o���������kE������=F�`Ea
9��]�deuQ^�5
����J�A��"���L�U����ak8�Vw����DtH����t	N��Y�����`w��y����!m���s��tG}N|C,�(d����Z�������&{�3O�������!ai���RN:��������nl\aHZN��'�Fc�#��tp]N�%�%	Hl���I���S]��{�Z��K�������aP�w��K����f%
����M��8����[�����){��Ew������w�U}�J%��RN�qD�^������2��a �����H��J_�o�F�|eS��T:i�h
�ZI;?�AX��Ov��B2�/����v��7@/��j*#���� X��!��R��IS:N,%	��u*�T3�!X*�f����C@�L����SIJVb�X�I
�!l�w�O	��2�0���A��X�m\j�"�]-�k��hU���)����������0�	@&��
�A�o�)lJ��
\�^*-tY1��7_��!��Z7l�X^���b,)�����T��2���E?4��E]}�����������V����h�S��+su��\�������#�Y��l<K%:���'e%E�������������CQ��`�9��q+�q�G����l^
����*���+�����`��$kh�o���T��4�nk?�Gek�~�������]m�;:9�����������q���i �y�'�t�KB��U����F��e����5�X�;�r�[8��=�9�j������N����j�*#�`�,J���9�I	����� ���b�\���K61Q���ZK���2�%�*�1�VD�=��-��
�@�*D���S��H�;���(.��Q�
*��*F�Q!Q�w���o
�v�Zm�F�fk��[��$�{>%|H\aF�����4S�����
�@����U�����x�*~�qgJ����t���3�A��&c�-�R�W��j_F �[������'UfT&���r2#JQ,_.9�S���UO�V�c��~��|���h��>�@v�3E3X��C"�`�pX� ��x&�W���^��D�W���W����5W
>Kr$@ �$�y�N�8�\� �HE�9iI5J:��	���[r���R
Q�0-p_��lk��~0B`+�W���d�tn��O��A�v��!z;d���S�<c$�J��I2���#�b�3��|�a,�+���+�%��P�s�I��<�|��>4�b&�
���u�JM�u�1n+�P[:�61�6��!�!3n��e��Gv���(���v������=�`����-�b��O�&7�H����1�#�/���}6\%_����Lq��_Q��C�^����{��9e��r���WW��H��E��|'L"��v�/3�e��O���fa��e�'�����C
B-1?�_��
�N4ig|���{��|�g�0�*�2��hbT�Yl�4A[��l0h�^f�_\ =|����?���f����
��1��
������c�=�>�X�S�����e�=�K���L�?+{�@��N���5��o����Z������k����P����� �~!���ry~J}��W��������9��y�%�W
�W�.��?dI|��������4���*Z��!��p?�g������VP����^�����B��S�cv9dWX���}��)��7��3�|FL��6F���Xd������{C���m��|���rC��F`;�����W����w`�>>���r��&E��e�T����
�V��\�����E�[����9�p~���f
�`1������7
�����O_����mh;1����2j,�DUw ��%O!���U����0���L�iX�g(�Q)���Eq��d��j�yS{*f�l�vxH��������S��C��}��0��J;���V+����fR�t?����{�u��O���;���9��7��	�[!B$.K�M�o���{��5)4�<��1�c/5��wJ��C)�Lt�r#`�9w�5�F���!�A����m[8aX�\��g���O>
sDx���c���X0'j�t��^���0?���{k������������g+��8�tif���N�+^��z�p}���r����,ZX���[����n F�u*���&\��0I�1�V��?�\�*�(^��)TB�w�T� ��Qe���$;�%a�h��&*#q��4������]�{���s�r�����������R�mm��R�W6�v��o`l��!hu\���W~�� �������A��a�hTvbI��cEGR��;���~�@����]*e��u./�/_x�.�b�9���~�Q�'Q�B�c+�sG�,�
�F��9DTXT�w���?rd�v�����k��[���t.�.N�V��p$��?�aWn��cBP����DyJ4��U�G%u�������HTO�JM�~��W����*�Y��_k���F6!���<Z�b���+���f=�"Z:�@��D������K���CO��ZcGhn�M���=��]����HG�sB��V��1���)�|��a�����kx��]}T��|~����S����5�: �b��,���L-'C�(S����H����b�,�Z�w=���������T��h�t������<�%�5W�����O4@9�����R�y%�^���[��F���K��f�Eb��f��������R������#J+y0��j0�D�����I�Q���������<#�P�G0�
�_��Br�������(C�wA�����G���� �FI��[N�����>
��h���0�����O�����S���o�V�q�ae�d�S
y�P��S�`���&&��Fg>8�9i"��	d-�15�i��3�����4�HT!����Sr�g��	��@)�p6��?c�[�iO���+#��bXsL�)��+%<���6��B��8��U���[���z'�6\Pv2�P�#�1�JJ����Wb�[r�!�L]qP�����D���cm��!��w&����?��c��E�K�Ms�p�����QJ[,���h��"R[�����e!|�X����a���V�����R�	��
��"����t���W��p��� ��1�?������=z��S�]��-3��L�	8�%��G�A�@�����r�+^�f,��W'�z$�j�)m�v@{�u��>����l�_�oh�W������kE��1]Ef$<���y��:'9��w����q�������o���)����O5C�'��v�>Nt)��O�;��c���a��m�E�|��|��q�wj/=��@s��g����E����8������h�~�����]��I�g0]cn�"76w~\j`i5���{ v��,	'��xP��DJ:��j�������N�La;`��?�
��o
o��p@���cJcJ[�r�-z�F���j�����#HT�kD�F���e
�������Y8��f���{�4�g9|v�O��1�[��go��hu��o���.�z�����O�Z���oR�F}����)�}@�|�Xo������Sc��:@Ax	����i�x��g�8��aM����:�w��<��}&���=!b�~�D*��z�.c
-�C�>!\�r��z�������tR_��Wh������0�ZM��?���C()C�^�u�)	��_?t��k8��bm^|��;���|�#7�6+�~����%o�B7�{�^g���M�|��:1�f�"��g?��+r��x�����Xt)��V���_�6��Zc�]�����U�,�$G�7�~r���)������8��1�{������b����Q?�(i0�;�
�g��0
j��TU1�4��D24���8���0�w��Z+��F����������E<h��M�F��
��CQ#"��p�q��K[�s�{x�����{���;x�r��������19���B
7��WJb�{<�
O��\/����`�`�pOm:tz��� o�W�9��_����g����[A�?

p�\���r�����k�'�?J����6�W��Wo���Ie[G5"��&NL��^���yo����s�M���8��
����e��F����2��
>���������P;����J=io7��A�k���YeSD<�>a�"Z��WO���������=Y����2gfz���'F���N PY�AP"P��\�u��skl�:�R�}��x���9�����=�������r�{}�Q���k�{~<��-�������T,���lO���
���$��P�qy��k�q�P�a_[T9��=@���C($��&�/A�������)���V���3lR=d$�0�(6_�wUL�v_�xJ~���J(�OO�����%�8�=�|���wxr
������!~xt~rsz���b|�
A���<{��4����.?O��>#*tNOa!�	i�]��kA�����}E3�%[������t�wn�h �]�=��gnQ�+B��A����"}4���'a�z2Z�^���a0�C1c�1*�H,m]�����h�lU��nO����S/���>��l��%��o����(��fcw��U���j�Z:��F
�+]���Q��VK����8���F����q�����A~��"��z�5�	P�!;�7��A�
���w�;y�����Q������r�}����`o��-����)��v�/����wp;������y������{��S���c��V�Q=���_�T�z���w���vo��S}�?�����O�7yU���M�}<|P�N4Kt���(���v��Wak{�f}��ib�J�BE�.'���w���[�W��x_�<SPi|���d��V��[{���p�hG������Q��u[��+NX����i����������XMh��KHNNQe0�
���A����k{y���jl	�+N�������E��h�+SyK�W��'>�`�$���������c�78F(�A]�����_S�
>��Z�����[�1
0004-RLS-support-for-ON-CONFLICT-UPDATE.patch.gzapplication/x-gzip; name=0004-RLS-support-for-ON-CONFLICT-UPDATE.patch.gzDownload
0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch.gzapplication/x-gzip; name=0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch.gzDownload
���4U0003-Support-INSERT-.-ON-CONFLICT-UPDATE.patch�\{s������8��)���e�u<um��9~Uv��;w2	Il(R%H;:�w_"e����Z�]�.~����(\��;��Nw|t���Cqr4s�/N�b:}~��D���{k�;f��)����n�zlN���E�~�b)<`gk�`/����w��|�	W����){JD�
�-��S���{���i�����n�zLf�	'>e��p�t��
:��c��zF1��{�L��m����]������|b�.�&����$��-c�<r
���D
�}?|�������X� f<��a$Y2��7�X����X�L<�C��	�,����{q���d3�x^�(���U��1^��/����ib+��$V����`a���A����0�q��@�l������=AbW|i!W��yZ}�������a1�,4+5��a6_<�g���t�eQ��e�R�y2��H�n�x��	�$���l�k w��RV��+���p-A����v&�MOZH_����$������Ze>�n`�U�(K��9 0���k:����E���Y:�����A`�L?�H�!P������H����d���	��x�EF����NZ��d�����/��������8$N�D������
��^?=M�`�B_����w�y��h�l��6&XD�
B�,"R��=��pC\v
���?]^2��\�1����<�G������f]�������!f��RI[5/��P���a�����y$���8�����O$X�C�� ���gp��� ��K�&5�����X��!��������/K<���y���'���W�Q[h�0�)hS�p���-r����# `��&���+�����8��@������HA����Q�M|���FBJ28D���&�&�1F�8/p�(�+�����
��@�B�,*%�T������g�`��w�0&�Ax#F��p�&scS�����a�k����gO���5O�x����������4�}(!�D����F1������S&3��{1�)��92r�Y�@w�Q�<� K�b-�W?|Sp%'l��-�@A�0���b������g#�3c��~Nm�CH	��lh��h��y��E�`�(LK���m!0��8����]���5�p(��`�8O��!`��Cd�� [I��������D"/J�u��1�D�������H��1#&g�l��jj8J���Q���b- �O�L�x*�Zk���TD�������Atj�U�����
���&=	�������)|�Fi��S���!����a2����(^������0�C��q�"�������($dC��������������z����)�d���r��k����'k�r�z�"���
�����e�g�1	|��$�0���-�3-n�����\�����
���z.&�F�6��%!�E�K�<t�o"������1kZ���b��I@	g�����m����B�m(��1AapZG�*]���H��p'_��
�LNE����X6��H�JdWp�`�\*�(�{&�ib��g�X|�K�H�D�j-@��h^c�8s����m/C�(�O k�DdQ��c|��a�I���{T�@e��J#$��+g�q 	���%�Tl8TZ�Q@�D��0�
�D�k4�b���*5�y��OP?�Y�%:1?��@�|I�lA�r�c��|�<2��.MN�LN�V�������#����SRw�B}��CRY�h�&�����/k�+ �u��7Y�h�{
i�~��!�>~� �sn����a��+6%�����$���2����/BK�VGT�-q�ci�B�������>F#��"di���P���-�Gz5c[�v���yl�����K�������s�0	i���K��p��B��ji$4V�i����w�H��=�uC�##���;����Gy��q[�0!,�����]"^=;��#q��#����{��{#��DuP��
�(�C'|ehy�M�����)��J�j8�h'1%�jj��]���XTO�$~�D��P���U��
�������lvH]�a86@��3,�����,_�/��v-E���21�%����Os0�i�_�Z����QbL��`������L�<S�vpM{;����1����?nke�����i-����!�,qE�����6��s���l0���Q�e*���f�N��!����"��������W�����<�-*j�;��52������L�co��WD�P;�����,N�C!�����2�6�J������W��Ph��u�������jmt��h�8!<��m�����-c�@<��mpk�E�mSC����[�����I/;+�(��� U��n{��f�E���'�F�����]�l��5��}!���H��Y�$��q�e��q3:}�6�s����\���e���G��^@E�	I%�/{Y/w�@�H���<���k��s����_���H��:v��3c��k�0V1��nI�m�V��m�v�P�O�l}�m�v?]���|s��6�(%N_:��F1�FF�a��8�}��R���J{���Y�9�V3�S3����z4f������j�n��;�4�e�}2<2��+���8�9n�=_,���#���]�t�8s�j�,���q��l�����#j���7Y7Z=��5r^�zY51�&U��j�!�����������5���z���0�+�8_G���	���m>�-�jx��V$�_'�Z]J|R�9�f@���+��g�Cg�^'wee���I5��Sz���:sQ����x��n�:K,��e8g���q�lA��c.�P]i7-�jg����^�;���^���b6��}
�=r���j���R�,X��Y���b�;_��8�Z
K��u���;������
����RX��?!�w��v�?:nA��������Qi��^H��?��������$Ku9��7������]i��e/i���n+���0�����4����+YGS|�g���G�����z��p���2��Z�t����F"N"|�{����Q��wE?����wt��jx���=�J
��g�u����x|T��X���As0�4�s���/������<�5�^�9�&����������=�����-����gi�&�39nI�_M
�b���~z���|�x��KQlZ�\/�W��-��?�����t���1�]�;]��Yb/GP�g���%��AsR���_QU��}�<1g���7�������_���?���������oj����������4���{b��j{�bA8�YVO�������&O�7��=����CG�g%K�h� ���� �.U����G4(���q��S����p4:����R�x������p������~�N��.��y
_�#�-���xP;���<do�0�[C��Vr�8����5
y����5������!��N�Je��;��0����pd�_��cq��B��������Y|����f���[������]��}
���$z7���'��M�p�E$���aQ�]e�Lg�uj4������N�%�p� /ha�\��:�a��}�S7N���-l���,�������Ek T���.����@��;�����z�Un;\�<�xp<D��1b���i2eO��L��#D[���+l{�{|�^@`�Ev�ar���}v��u�r��n��c�Yl�T��G��?���B��`��A�0H~�^�|�mS^��~V}]�9v����m���;t����T ��G�����*'���I��o.�6oZ����������7:���y�cT�����Y�|����f��m�trwq;��,������9O|jT���U�I��K\L���ww�kN�3m���Q��
��P��jb
�|�9$U8�Q�6����=����$lxi;+O{�014��.�kh���h�n��"�����p�AD
a/�V6�tr}w5�%?w�����os�B�����{F-�������h��r#����&��/7��v��^0nw$F�-�G'��x��T8C��5�����I>���u��(�g}���+��r��"�6�-6C��<�=l����S`�uA��������K�&
_����<)&�����I���6����u7�Jp�j�6/UD[m��]=(������X����k�*��sR��S��:i�����(��A��#�<�_��s��EB��;����iVsCCr~r2����p��p����H+����Z�?�Z>X�Z[:�3���b��N��L�N����H��!��Tp�B�:��F���Y<�x�SGjo
S�n��^���n�m)�55+��aL��K��$ES�l10k��[lgwi�6\#��F�d���1��1:O"��f(\^%��������$�A!l���<2�#o��#���	��_1����Z��?���Nx+M8�3f�SP`��:��4�Yh�SK��La��n�DQ�+V�%.4���cN��
�
�j��>�C�#S��bz$G!���?f�S�=�QC<���x���^�@��Nqb
:�u��2T�e0oR9��&yi[�,���U��
�sqE��P9�pznI%�@`��<�0S��mj����s���=
�0��a����p����_�x����bH}�Y��n�w�9yD&��'V?�'4@2���>QtR@���#��*l�t�q�2��0#���u��>��g!X��|A�S����K��T������8�O|�dxv&��T����t�F���cdIDGgo��|C8��Ex�@�eGuIGf
wd�B������0��3��m�?�qgG�,PW���}Z���,������mrw�k��|�T�ARGBaI ����]��(��GU<[�T�r�J;���k�i��<���{�ch�����:�Ns�n}_W@t�����B��"����g�h3�Ne1�<�30v���c�M�z��7<c�u���r2�?�t���o�:���3�����U}�C��D��m�C�W�`r/�\��%�;��{�0�������3���&�<AIN���Qj�C�i�\<���Y�
�������B+<�|p����N��sv�U4>g-�����������4�T��zs���+^����3�p�q~7�� ����U/�v��1��hco~Q.;MV#f���������aVS��Z�����I�]j/��AFX	)����{���f��i2�h/5����Z�&�{�l)fa�4����'�\�|��\���Vv�O4%v<�������Qe���6�UE���6�]z��[�3�a���y��s��a���|0�z�2���t�����~��[E��r��3�� S��si���5���}U-������(���>}�cT�B�����k����>�42@�C�����!��0�`,m�U9d{!�m��@{Y��
T9�;�\80T�X�����
x�������.�� \x�J������7���7�21�|�����a[���,wzn1������`,>��DT������4`��f���S9�����UG��WDMO�?h���2�O=��t��`��	���V��s���'`�����z�� o�moN�T����]���������@��[)��8��Wf���}ft_�������O�y�����E{_����������z3����Wg���+�05-��l�VZq�`�o*�L�\��-�m�R�2~����0];�������������7	�H���qf467���87/O�����HjY-�����:[-�Hb/������k9u���c�\>{�Li��n������a�^�4����^c	���%��e�C�|c.���G��?��%��;=:�x��.�+]`�4wbb��.e��40P���|��ck���T�C�l��9*+�2���n�����O�E�_�?��1������6��bG@/ P�h"�"&�=
y�8=�z^46���L��tB!��e��J��1�E$!J��:|
Z��	�R�F��,�c��E��>����JH���w%����4�_&�dg(�O���A��wp�
~��k��
�n���!���z�?������<=������;=?V?����]u��;���O�����������_v^�dB��w��;;��:��y@������h��w`���jl�0�^�I.���W`1q7IH&#�����X�=��jW�����KK���H���%dc5�P ?��@�T�Z����jw��T��H�m��6��iZ��}
^�(j�����]�U��E���k�H*1������F\*m(+����
F�>�(�F�$�#)e�9Y���x}j��!�u�Q�D�����"�	\�g�XM�Q����0SMNo�/�V;G�_y�Wj��v�l�n��J���O.��?�{�������}���+�;S�9c"�A���r*�1����0��Tb3 ��W��~04�J[���H��q1�,?+�*��-8#%�iKy��t>��g�+Q�#1���������(�>D��`����]���K�+�#
%^���"g�r���Kr��+~4��(�K�,�}�Y�1�s���Po���r��5���\�1EW��GQ�+|X��,so�����a�l�U�
��j.p$��]�����}�3i���Jt�a�B�,���I��c���v����ebo"�������B'�a�����Zk�����b�gp�?�E6��\�������%�<�h,���3Yn���m��=pE�����F:vH$��b�C�)�\�I[���d6B?&��I(��[��+�b�Ur�����p���'a1�_�"����oZ-�?JB�d�N�ki�{����T��a��,H4�A{�� gn���Y�5����E�{�������'W_�����'��V�B!��bnSB-�gBG������]!"Q&i���O����~4Z�'��X�0�����������D����h�w+�0�'^����g����O���Z�������
��;J���W����l��Z]��f�P�i�Q��/[=:��N���X�h4��������W,/N����S�)�`�/b,�/��������=!�jK,D��<�����3����������W��s�q�/��k�^^����g����?-t�9��&1�l��N
BJ�Yyq?��/��l7��DS�X��?��$��I�o�k�dR�s\\�$LZ���taxjAH�W� �+��%���h�uU��m��C&�{���i����/��:J���2�'$�EY�`��w1�I����rg��l� �� |�Q���f�R�8���c�!L�����r��B����+
"�/��`�������1q8:�����������C��^0�DRsD0J��Mx}c"���������U����"��w����l`���A���:��	t�a�'��W�t����?�[?�&j^�s-��b���P�a?�&|��4�������'�i����0���h�����\*=���ab�9���Ct�+�t��/��d\�����=���;��W��\���:��0��UC������'3�p:�4�x"�@t����`$zd���-^��a<�F; vS� 7�,�z@.������ �C��Cp��~��t$��C3������/;&2#t�K�Y,��`s!��*1eB!W��;�]�3]��&V61_�G���9C�JN���"�xZY8�b�b[Tr����s��f-�^���ir��R1����f���\@����i"
m�z#^�[����P�]O����%k��uZ+�E+�liJ�������E���X�G)F4����,�w�)��R����qq��	-F�p���V�1RRT	CZdV��R�[0@�������uKY�����<S�Gc���s�+$�y�(�*��y��:��'	���O���@H�\T3�����g��&��;�����b��,u�,W�e�����Hzg�|���;~q�9������8n\Lb��6��L�Fj[,���'�0"]��+��D��Iz�)�W%�����0���=�����Y~�<�k���>�n�N��NM��v�CC5rD!�VD�I�A��?�FA�b�1��J�IY�8�[���dD��Z�X����FZ�9�S��5�g�k �����id-�d�@��8O��^7x	��&���q�Pey��)v��t0���$r�A���K�
�2|��8���T�D+0�
�Onv1(�}�aY0)C��l�����M
��%��G�sF��"z3�A�xXJ=��R2�
jO���
P��/���1�Y�x�d��(X�*�����8����I���%��Mf���#���2/�m�����;�^�x�/����I���Q�b��#*;�f<�����Eea��T�s�����/��	K/@?Z`m*�T�
kZz��������!���Cn��	�7�K��X�D
5�F�Xk���8���!������6����������,*�6���6��x�IL��H�\��Y��F�D��{�D�&��v�L����\z���B�;R��9?5�~�P�
a� $�����ia�5z������K��4���lp�,\��\?s���T65����
0��?�Sq9�r�-�v�\����x���Y�HR�n�Q�Z0�V���~��e�H�&�����	�)=����:�tP���VP�x��+�a
R�dgxz?�M���s���x������&4���n�@QO�w��01��>��"���=J�A�(Z:�p���������-m�
;2%�p��c#A��)�w�F\�*���
����/��0M�� PMDS���c;�k��*�Xo�����
����(���HJ���|���K�i���*H�~��1�����{���7�UCZ���`����.C���k-���^�t�q^�F��DI�\����B'�f��V@�zb��C������xdmr��.R��#��^�<�4P�?��X��c�5�7�2ql�+$�H0U�	�>�������'�W/G-��v�5@!e1�+�F��}o��V��i)���`?�l����%I���W�@��ard�peA�~�	�w���
�u��)��������rw4�#��;�G�
m�mD�j&�/2w�����0�������FfZB��h}"�uq9�����a?�������SA2�A�����-V�xt�(���v���D�@l<����:	��l���a���s:WZz��P3^������L9�y<��#Gt��t�a�]v<�"�������>���1j����.�e]h��#�6<��[������*~��� P[�\>Rv�S�2�����3�^z���,��Q���BS8��s�tT5����$��OC�S���6��PT��!0rTt<�#��<l������OpYp�N�q�]\(�ivT3�U2 �Y�����X�I�a+^z<�F���H|��.��Yi8Q�J	���A���Hl��z�m�(�YQ�����Mn�����d�'�f�p?��v0�~��h��v�a���������������"	T+�ys���k��9�O�z#��3��f;#X1����k��j��i%��y�Z��;�&�������.��)��C�d����$�R���2���l�
[@&�%6��7���k�z�+���r=U��y��3�3�`����^��2�s1�'s�>�����X�-cft����45Pbm������k� 1�s�5���H�ZT#1��{K�G�h�-����x����D���`�Kf��r}x/.v��a Q�1�@j��a�s�f�e.:�W]r�����g~M����>?��?�Kr]����>�~�dd����c�(����@�lr���=��!a��Q6�����u��/5A�$�2&jB��.>�gK����������Ic��l~�������d�XVg$3V��0�h�67��W;�:��a�J��(v$�x%��DUZ�W���
���B�+-�����b���>��(�u�EY������+1�{sq��p�<����'�k�$���MaL�R-H����
�d���M6�M���l�*R����r���k�ad��2�����$�Z�I/�N�d�s��Y�����a\�vHZP��m�0���z������
m��Z��"�#��������N���N�|���e*�\/���+�;��T��5��%,v�����o��O�s�l]�=����D�7qN�x�7�~c4p1��k;i�:����l����jU��A����o�DuP���Ew��\{���V�U'+����k9E�d,�B4�W ������q~PX�V$.��s�vv���d[\!����
]�}��jM��;y��8�>���������#���Ud��<�w�G���'o�����~W�n���=�/�0���]��������
��"������SKa��(5�J�C��; ?7v�G�����~�Ri�����2��U�)H�f2aK�cO`���mW�pj!�\,��Mfz]"A��a�� �����3��=?����5�&!D��s�c| �.��-���R2����n�rP�Mt��*�����b�W��%�Z_��c�vX�e'c���+�����=T���9v����g�9�����(��j/�������
�/��+` ��uy``����Q��Bh�����ejhv<rK0'$y��<h�i�l6]	���$�GY=3d��9WL���_'�O'`�����#z�
��DH�h�#�����%�F�<�(d���qG���QXv�,?����z�a�9���>���m�t�e0��&�
�����-'���]����4��w4�t��0C�$m��b�H��L�D�I�����5y>�5>J|�P~L�C)�R����6:P�h��lg��P4$H-9�a���S�������0�
pJq@�m��Xk�����:.�����!dB��b�H<�xv��ib�%����s:aS��x{�h�UO�v7k���8	�7�Mz�~dC��:�*����	��+�F�6�1�CE�!�8�����[����O�os`^��Z��4����y�h]��m��@=X.V[2��Q�(�L��!(O����R�1-��07�z���E<a�Hr��a��������U��a���I��D�	�x���s�9����O&�+��u�B�Y��Q��.���	,'z>yT<$�
��6���A��}�Q�D���s��s����|s���s��x���#�&�
��[��Y%%1P7��Y|?�E����%`<���/.��d7�T����%�[W����?����a��7�?���c,����������~��?�6�v�oe'���l��om�N�/��@����;/Q~wmt��S8U������`���qX21��������{#B;(��%|��������{�%�0`������������3�������i1E�|%|MJv
�!w��h�"�7��'�>�<E����|�������X�@p��@�t����4��{<��).��Zz���X��$��
[4H\�Q���Y5�m����]�q�2v5�~?���
j��-��h��-��|�&��?T<���~�n_=��0.��6�6�`�����p�S��I�t&Ab����~8�^��r|��� ��D��������/����]�.�������=v��w���^��B�F�p������j�V�	��(\F���dU4'��.�=��S�����U���<�(��Ot���2�jk�xL�f��_�W|����k-�S��.n@�sf2�^e�%�:�qsgc��^~@�P��BIZ����,B� :T�O9XG����Vj�oSt;+{_���s�==?�{����m�5���j�:k���J�Q�^�LRq�S��{�s�?��J��tI��`�%�������6��	Q�a6��������>a�'�\B��'��?v��*�3�jg~���gl��T0��N�^�Jo��V(��>����'.��fR��Q?����J������/����pnS
.=@HF��>x���6.�7JA"�2��b�F���TL��BeI*5���d��G�rs�)<���SM���I���]�����Qk�����+�#|rG=Y�x������lll��ol������N����
T�x���F��k� ���S��c��k�c0O.���? [S_���i�+<�"/')q1�@C�~-8*a����~{	������t#*���zE����a1���b^����x.�=�:Pw�(.��r#���_(��M�12�A8+ofu\a`R~�^�%Q�����EC�����b�<~^����n�ug���p��b������(M��K��(6.G{@��x����a�I�B�� ���e`���[{1�G;S\��`�P	���G��z�u�zw���h�����PB��;o�	\.z�wb���=��./D�M8]�f���7�w
�7�{���b�3���\X�6��v���U�+_�G/�5�����_��V)b1]�n��Q�������.��������~��X�!��6@������^����Z06i���X���x�v���,�?������U�h:U����YR;k�>~���u�o7`�������� {O�����!`����x���-��+���;�������?�����,�����`��(�F=;�������Z���W��!_���)IJ8��j�^S3�k���GOqcc�S����
k[��[d��Kd�9���C����G���Mt�#�|����]���-f����z'M���h�nH,X�
����!�`i7�����
'a'������l�\�Y���5�����)��f,9��m���6��?2#Nm���;��������n �mbg.�u��]Q���������~�_@Y���Lg�R��
�� 5�{O/d�{��S�����U���,�aT��	���1�����<do%t,@q�"C�l?Kv
�R:�y��6����w�KB��>�P�1m����_�����gp=HiIo^6�1\F��f��Ob�>��������W
���h|�������e�<�jI�I� �L9�2��������Eu�|�m Ri���@�4������j��~ �����
����$_F��Y���(��/g�bZ����r�aK���P�^z
����d+	��CC_��
	=��JX���orw�`[jJ�k7�9��q4�EJ��1�{���c���*��G#l:E����2d����"P���:��@{�T�l�(5w�L�V�t��i�����[j=��3"��h�|tk6=�,k:�u�d��>�l�g+'s>{�\�g��TJ��5g:��
����������f�Fu��K��bq�#x�g��e���Cc�5fc�����O������w�7!3���Fa�V�;��n:�gtw7� xj�0+_U����X�h��X�LY��)��C��kg$K��o�����F�*\�"�E��^��[a��[L����p���[b�J�[������:�~�����������c���y��n��\��#����:9����zZ�����WH�s�Z>�3t�{H��PR��a),Iu��w��[8�����������G2�-��0��A��f����w���X�`�B�z���C��6K���Nt������#S�jM����|1&���#�s�#���-N���4&����X����hM�;� ��������nA@j)����[t��~����g��z��V���i�_�B��+��w�w1o�����8�%W�~O�^%���h�w�u�	��\�������>W<{�m`X�����R�0E9
��"�)��	j	��v�/v���B�B�����K�)6=���3���J uDW���u n:�L95�o���q���5u�CD��'�?<:��U��6��X����q*c�����f
�@��{2	�;*8�]v�)&/�_������|��U_�����`����+�
�����u��_�=��^u��B�0�z#��������hT����b�8���T���/qq(	�\cc:��<!L���g�I|���%��f��c��\t�cb�/'r@�J�:�\�o$���j$��{�����#��8�����nb2�O�c *�X�&_�;����uu��Z�l�����h���e�C���g�\�^*���B�y�����#7�M�E�W�Er���@ZzY�q��Z.:k�2;��U�8�N��h�K�B	�t�[v}�����Ri5���]����~�d��j�K����D����;Y�`���	F������#��E�u`����I0�p���J����2
�+(�O5��Au����nH��}po�����{�h�dB+I�I.��To(�}P��MJ1�d�`:7/���+���	s���8�8��zL�n;�;�������;�����'��(�-����d|��R6S�'�|h�h^��%~�Z��7�'X��R�C�D����+#�
T��wz�c9��@���q�P���&+Aa�b�{��v\�'JvI �;�s, b00������Z8�f�7sJ��R�J��) t~��
�����~��0�V�<9���%�Yg�_x��t�y����Y��]C}���M@��?��E
72p1���oO��:�7K�����:��pT,�"�8_L���Tp�P��/����������C�aPE/��6Kk���F�p(��	q��A�������G��t0��E ��Q��� 	q#�>>�	u���)Y�uo!B�y�q����.��]�8��l8q��g��r��E[�V�]���� ���&����w%�y�T���
@����v����iu���1��`��7��)Z�]\@�L
�M��!n�t�j�T����������~���X���t���v`��PU�T�BC���"��������t�`!���� c{�Vv
�����@8�L3����D�-�]w�=�������P�J��a �����R� 86K="udovY	�P]=����j��������"���Al��ZC�)�SV�������^S�A�6�&��^�W������1�`��I �j(������:���6U����u���U�g�����=��P�q��SnM�YH�������q���������Q�K�`8J�tU�xLe�x��RW
��(��K�rg9�,��3D�RFp������9��O���#�m�E;�Tf���x�<�d�`��e��8Da(��H�X<�SH�E0���Vl2�!{&���?TIH�1�n��]�dN4s�I�a|CR����i#�9�}������cA�!�z���������z���zK)�-�Z������1��)W#�Q�f	�}�����}�,0�,�b@���Pia�p5'�"���
]Gta�y�*�X�Y*d�o����)��<�,�DH V���$�^���/D����C�Eb����l�prF�Li��}���$V���������{�#�c�_9�T�bJ������`^DzC����>C&��+��[(|���	@�I�	��Fq�_��:��*Y4bA��,Q�>	�VAY:T��x�?����4�4��9P��/y}t��AA ��A�e���!���S�1�p��=�)�p	�
9h����n7�[VO������	.!32�1_H&�&f�In�q������I������}�9�����>��� ��KO!Sq�4N��x��g��H_��H1W�/�8��
�m���t���$=�i=�f�I�=,��|
���������(r�H�bg���������������,��h��l[
���n2��A�^A��LsO�H��^*�q��!�<A�Di���A�Rw�g�ena��Pz1���M�1�w�k��(��b��(��*�B���
g>�.������tf�T���Gba8��I)����[x������E�T8c���h�)��^�y��R�{���R�H5�
�F��S�Ry�(VHEi�TG�_01��<��,��J���#��4o 
$Z��p-2����2_��Re3E��2�AC�/�/����T����k/Es��R�y���6����RH��"wC�;���C���Y#���(��\c97�t>��}���J^S@`��"COLio���<a)g]L/&�Q��b�3��A��)�]��p�w�)xb2t��	�^��-	�C]��&�� +���Z���=\S'��,aV�P���%N��UI����AUOb)0HaD�dd����7���|x�4>$�������S7�v���J��N<���M4��HS2>�/'!K=���������'Y�G�
sY�%�x%�������F��}����J+9����<T���,	N�u�u!*Q�Qv������Ne~W{O���Y��-��rP����z�2�
�as����c�'�j�rI��B�$�DXd���	� Y�h�� /8��*� k��@y�qK�%��a`��kd�s�����K.5
S�Bu�����8�1F���F���X�n�I�0D4�),�L=1U�5��wd���YT�&L$����4�&A��K�*KC��6���GP�����A�U�@(NV��Q��te�N�*�lm��:�_�o��4�yg�N~�=���c��"{���6$���/{\b�����
T�*�S�<������
(�X}�����x��������6�KsR����7avyC���pf!���C��C.!�
��@�H�lA4����,]���X���������t�,4���S�
�i���4�������#������>�R��_F(N�m��sn���D�����~��C�Yw-�<��+'�9O���	{��6�
j��_H2�0�#��Vp	V#�@bz@�7�3tR ocHN�"[=d��Yd��]������%�9Pf�z������G�a���T�A��?\�������[aN�>Fc��+���?_�=��A��q��AG)1�����|�H���E�)A�Ff�����@}
�[�����I<���W�
esf�p�|�D��O�in����R��;�^�_����:sF���j��
�B����h`:���.78����i�z�+�F�1�x��4�a����Rz��@srS����y��}�W��yKE[�n� �Crq��o,R������h�t�P����x��3���]/*�KDHe���t'�{P|��u!�������[c����TK��<V9�/��d^B�#�(����s� �F%<c�)��.��� ��v
p��`�����:�;�O�����8���#��:��(=|�Gw�Z��s��1,+�0�"��m4��	�aM[l�hx��;t#.��N�'��\,���=����=nPt;�bBC4L��1h!���$�/�R����\4(�8��Jl��fc�'a	L��Y�x�\�T��`������������Ei,X+U�-jA��	�a\���3��`�����1,{�>V`n��>��
��3g��mLIhUZ�FUx����5+��'\�(����W����x�0�Q�2'�:\$T~��D�����o�i���>j19�N��hh��E<��y�}��5�D#I�R����/�Z��z�7>����Ng�\v�?���Rf���N��:�o�.��bE�c����~.�
Z��u3t*aE2�J�Z<�h�1)�{!���&��0f b����A�(H��cW��u���}�lq�w3�����tW���I8����h�&���#EL�4�� _0��Z���uq��k���V����2�h2A6�;��~�2����g�W�a���� di��'L�"2�ui7q��i� C�)�G2�K������b�/�`�s��|�s>��Z��v@��������X�����as�Ri�~u�ZC�I��D�I�D��U/��J�����M}���6�h����AV���{X����P���6������y|�$G)w;X����Nx=���$V+g���4s�e��]d���k����eIJ�6�i�:�(
�<�;��p��4:
��k�Z}/�T���{�`[F��\�m�2j���������o-��(�����r��������e���Ux�N w����S�T'�!��1\;����|�Q�������}O���"����oH���S�$��+�:��/H�U��L�M�Q2���H"��Ie0HTVe���(�
��.o1T+�s�A�gb��a�j]P���1�S�����K���H��l����A����C�p�������g�IQ����wUq<��*�%'�U���*5j-��Zc�/ �����8jo��H,����8C�r]#C��9 ��3�70���������dC��$�z7	'���"M����U�C	���wDU8�G.��P�z�{�Yl8�&�>W��;��=jO�QD��a����0)�a
;�����b�"�s�je�6�U(�Zj4j�&���"��q4�C�ul�-
H�x������������8�f�/�
�D6R�WZ�9��E���0���Z�m_A��STJ���k���7�	�}en�(1������MP��S���yX���qX�����g������������
��8�
������
[��^�1�DC��j�����}9��`D��T�@��'!�)PH��H�[�F����I���a�+d6�����ysw�9~��L+'���I��uk�X)%��SM��V�	��N�"�~)�i����I���e�@{�����V���u!g�Q����dQS����B_y��)+���E-=��@/B��\�lD���'�?����`�����a@�Pw��j���"��z'�V�J����V��Ct(�w�Sm>����v�j��-t���^���8U"�Y='Wt)�I��������������~l�5�e'���EL��M���13��c�lb�6V����f������~t�A����NZ������Y�2���c�-�[���OFxW>��	���P��6���a��!J��C���z�}8��Mc!$v���~4����X��]�j���W�����T�g���?��,�>���,%����si�!N��AF�O�LIk\���a��QEIL2@S�AI���n@b/V����c�'��!�O]A DbB��=������C=�9RZ#aL"������e��b������`����n�@l�qW��b��[��lR���=����}�������K.jb=�W�"��e���:��a!�>�����7�����c%��S
�	�v�'�[L���-e��R��?��j���6��=���J���8+zC�"bbF:A��m��]��sV��d�	w �@���������GC��v�{t����J�	�U��������Gq�������
�r�c"�`XC�;L����1K&	�XU���+X����DG��r�kQ��t�V�A���K�VJ���zI�b)\�w>��	�:K���Dw7�����;�j��{�Pp]�C����1)S�D�c�:%iK��b�lt���m��i
�r�I����oaI��~��)�$;��@v����d�tH�+RR�r����M>.�iL�(Q��������4a>���
u�b��'S��=���S����`�"�{}
�`v�D*XM���A�B�vV$���n��Fa-��0��h=$<���A��E:Q�'1;��O���m5�d�V%���f}�_\���t��.^����\���������1��P�i_�2$ZY��N������t�4��zXN��~����� ����:��T��)�b�[E�����AX��j��G��]��.,�K]Wvq����'�[���H���5��!
$�p!f���&\���	�i3�5�:m\`�9M��a���f�`f�D�	�	�W,��h��yb���5��W@}(j�0G���3�X43AU��G�=l����B"X�����u�W�Q&)LF)C6�%E��j�n�'������`�@&��I)�}@*��l�	��4�@"}�J	�;��DJ������l�i!�7�J��!j
���:9	�e����uq7,3��>`�q��H�k�Q@�V`	H"*�V�* ~��?G��m�>��=$�������X������kO�m
��X�W���#2�����_u��H��������:�@���x�A���z�O������;�T�J^f��"�E��O��ds��4���a�,��������U�F�/��Z��?f�#X��	I�5���s��V�d�;������7��]��[V�����IC(��`���5vr���	K'/lq��8���'�!_M\���K��`��l����(�L���N�{�`'v�VO�<1�\�7-�,hq2��!���4?��B|���zS5�@���K�����z�=�H7���/���&>U�2*�����,��]�*C)c�bW��A��,C�xq�q��a1-�wo�'4�-T����:�"��PW�
�Q�%����afK�������	�.O��K�bla~���?���)X`F�e;��&_;�N�H� )�����J����D�p\&BJC��&v �r(q��i�IFR����@����%�!�����UK���g9^!}�9����EH0yk�_`G�������`����n�,�_�����!m�Ya58�M�D[��W"�a.=0+�N���'>f�8��,����7E�,�����G��|*&AB@���W^�U4^*,P�2h�WI���*b�rh�/z+�[L9$�p6
lZ�H=:=���$�0!�;H�cH8�cG�^�b_��I	h3�^�r8)8�3M6����,{y$hD�,��
��l;� R��FA���3���b���"�1��Q	��P����^����1e���i�E`���g;�"�j���9�9� =������V�R��N0ORJ,��A6����$^�&7xV�pDP�^Of��������:qF#�
�\���Z��C1�`�cL%7CH��}H7�f�M�:�:/	-�B"��b
�b��l�Y�9b_%�:
���F�AqFhRt"x�!��������l�HY���]�������1��Q���ci����V2��
�g�1��B0bN�V���i�0)/(�2�&�������J,�+k�i����ti�DJ\��c��f1S�.!��9f5�u]]|���#�y��xJ�fvnQr��kVg+�Br./L�>�S{�\�g�U.�v*9�I�����,1F:�S���J{d�pK�� /3Bh����2WZ�8c�lrR��Tr�nG�q�zL�L��p]*t��g�l���v�9�C�c	��]�#����t��\���>�zM�ke;��o^�{�U�����K:��5cT�!���������s����_u/��z=9:=�?��w������dX���[��@0�	����#��k��G2((�{�m��������#?7����E�(	� ����	�tx����$��|_r��.�����}?w�`������i�b\nJ�e�k�)�sa�Gg��Y$A�=��#��i�[D����gd��~�d�I�$i\���tJe��)��N��H��>gV�L���u�t�Q���6�.�-,�A��d�}��pgXh@�5Z{��R�g<�A0�p~o��j
����l�W�W�;�`�����X��F�R��73�e�$�
�d���|�q0
�{�nfHM�����9b6[?��h����Fa	�����4,���z$��b����!5���n|tK<��v~����&$@i���`���LRo��%�6�i���s���*�@yM���=1�������*�#�O(����`�~L`��vA������x��:`K�������p)Da���&�|�^��0@��I��L���������75w b���K����p��'N���Kh�D��	��)Mp�.���_[e�c�?#?O�>4)����5����W�����v�L�%EE6��:R�5(�|y_\^q���<�����b�TA1�qI���#*<d� ��Q�y��jq�"������RY�k3JC�u���o\5)\�V����o��h�u� $��]|>{��G������v~������Mb��p��E���{Hz�;4x��xpu?
��n��N,=���x�D�C�~�\[x_"�4V�u�u�&�U�{�4�����<#���5 ���l�t��nC��Cx���"�Q�t�6�j/D2CkG�U��$��g�����2�5��Ad�9��=�M7���<�Q�6�,��h4`�\R���;�G�o���L�����$:H��p�_g��tK�=����2��5m_�����\R��O#�({��A�{��{��z|�5�m�y��#��{1��b��.$6�EvUQ�^��������2;&-���Ak�����z���W�y�3���nH���%�F��ne
S_I�UOn."�:G��Y��A�vJs����uY��z�=1����Y����%
���;�]+�;�^����(�&^0��I�gc��c�d@O�Q���5���H����GKM8,!l��|0L�{R	;���D}%��7&�%e=5���K�l�a�>�3�1�z=��C�-X8td�	��1E�����Y�R��
���Jz���NnoL8�
��EK<;�����.�Rm������r��mc�t�~L5���i��Vy���X70>���R�2�R#�b����M-���
�a���q�����BX�	�?�C-9�X�x�]z��`I��eT�uF��������=4�P<�1L���b�~��^&-)���>���SQ��5V�^�|�+;i5�o ��J����E)�9��T?x�b����L'����@q���P����:;�<�b������5b�6oQ����D�����&�BJ�R��?��o$&f�_A�����(��Iz-���T���(L�/M&_�?�y�t~�HK��oA�3�'+S>:�B
�j�PQ���R5�5Q�o�����g�/f��6�W�B�X�6=��.���(
����B@U
��J�������|��ti��t�<��f�	L�����!zFjX���w�P���N��I��u�����������d��Jd��M�l����z{�#��4�8��v@�7���s/
p��9���/�_q�\QZ*�3�X�oi�����s� ]���R�9��mG���A��Ip�l/��<�-�c;w�����pl'R��3
�ng�+������nR��ZD'{|�A����z5sI�"2���g�q]
�O���;��"2�sF{rQp^�������y}
��t���J��8|X`1�i�|����������Z]8�%C�Z)�����lT���P.�*�=��xYvL��`�9��-5�[�H9����kc0�=9��;*��aW]�����4���K]:�n	�_�V�9h�U*�9�����k��"��p�6���������/mg���e/����E'���]��t���=f���o�]t�r4v5#;���3��y`���i�M;����Z���� �_��(�K��^��{9����w����G�M���Zd1�T�4I5I��z������^������/�{��RI���WR_��j���BJ%�x��{�&�P���>���������������cxi��rOg��%=����f	3���R��V��oz��j�����=��R�
nf���G
!��3����D4��_��������������b�`6�d�E�F��7��h�Eo�h������(����5��72(�2�P�l�7�Z�Mi���*�����p��^Gc?�|������0a����9z-~���Y]}�=J���y��w��#{ef� y��F�����Y���\��>f�xz�w�g9���XE���&L
c�Fd+~�i����p:���!�����*�W|�Ot�����K�>k/��iX�����c��eV��:T�i[���������,r�S+Q7#�j�_s���I=s�^0�)y�v�OR���V^H���Ug��-R:*��]6���9?L�������2�*{J��s���&A/cZU��������p�W�R�2XL�kYlc�09gx��L7�����u���(N�Vi�a=��YoH����F�M
�9h��Pi����Yt��c0��'��Xj6K�-wQ(��-��ue��RPs���1P�b
��U��ej����0M�Gh3go�K^\R6�J��E	�YW���\YGh����M��4�@�yJ1�l)�Y+��z�������G+Z�������2��P�n�Q���.��Y9^(%7JS���'V-O;����j���7�GW`j*�e�TE�K�4����!�N�`�v��)�(���x�4�8
���?��d�Q��{������k����1�93�$��q����0��d�}�v�����g��e~h����}�2>��%��8g��������of���6�\��%b�,S�S=f<�C��F������]�!���D<�w>GK0��tbE����"�����8�%F�q4A1��y��/3t�����_�
��J����k
��Ts��<��j�;BF�A�%���
��C9����[R���.N�:��1��s���eb*#.:��V��^�T�7Y�M��,�2�Q�j��)k�"��-1�et�K��p��R�bt���?����]�>���6lM���:�-�Y�b�p��_�b
N��Qe�%i�01�������\t��;����-1|6(H\�<0�I��7����c����&��q^c5^B�k[v���8� e-n��hfo�u�/���4{��p��M�3_��H�4v��a��%�F���^2�3���4��fG�<�e,��^"z���Y�T�J���W�d��<�l�!w1IA5����H6�
�o���x@���ni�40��l� ���i�N�H�u��U�jT�T�lJ(��K��2N��z@�A�9�I��0J�a�^f��������Y���D��IPk�h-��=��Jq:��BX����+z�m�-��k@��J����u��B����7�o�]l��Z�����H-���ES��m����Y��a�w��a��!�;JGW���tVS��r�H�
���`�����p?S#���lj��,Wo7���0�o�v�f�M)8�-%��|�u�LA���.fG���&9�=����`��%	m�BK�������"F�TPr���a�!N����{�qH�}��A�Rh���	��+��sqt
X�t�z�=���C���]at�z�=��
��}1#�-�
�a HL��D8�++�����s��a��I� _������j7�l<\�=J^��eO�$SG�V���2����!l�l�?$��8c-��s�1��RN�wb�B87��bAG���G<^��f�x�p���E��?.�\����G;�cD�?4E��U;B����
�p������������0�>-S��:�)�S��XO�=��QT��92J
�d?xF��?���w���;��E�'�@g�����Ou��H����u*w��A���$[2�i�����@1�a�l�kk1�T_KyN�5�%�k-%���'�P���b_3X��G�L�hpt�A!_�c��JiK�$����t������8L����B9��[��"������������"�]���'����Q���1���EI��<�)e?���2��x��G.bI^\o+��(��,�����Ep.�CW_�J'Xh!+'��$-j��e�y���!w/���X
y��	��DGH����j�Y��� ���T*�V��W[�����Sh�b�Z
|%��������N���Q�S�����Y����2�?�$y��/�xX)��c��qZ��t����.A9u��{%��wn���Wl�=NV�B��_������\�R����>��
�&���H���iG���:��^��r�N����)���Td6&9���������-O�Z640�xM�c:����A	��C�T��l w�v����4/�44�vR����Y'����g����L0HI1KY���`/�5���B5���H0�M��4����Hf�q�-;$���Y�������s	�\r,���d��=J�&�gN���9�)�3�-g?L�*�&� �I�Q$h��"��s�k��)a����.fS�t��������1`�P9fz����X��>1WuD��L�<��0�#�c+���|���rl����z�
fj�Z��z������0�Pz���}��"�����d�
������W�a���@C�� �F���0����<���0�KP4l'Ou��������`�	h�Y����4V"�	Q���/�M�8$��d���qb:i�`��$�,��C���P�����]X���{����GHS���1f�4����K8�s��[J�R
��c�����v���,��7+�����h�cxH���
h��/hwH6�O2n}�'Rk��%@�#�"�Q0�V�pf��4��&��"3��Z��,>D�WK�u2Ra��`1VL��\�>��2N��g�^4 �!�Y���Vja�k���A�&����y��_*����>��)N1C<'s hi�J��0�*jA��N��o��HK����;3���HH2�%�J�z��7~|�b(�N�*a��X+�������[o�RN�-�Vi����K���R]"�f��)At��R}��s0e�R*t3q
�����_���V���k+���J�i
���_�4s��@�5nWe�I�����l�X�z���CI� ��.�I�%��`+ r���Av��jHv�	T�3�����_�K�1������b���}!9L~�VD(��[��6�>�u<�p�j�5N��R
?D��NB�H���W���t]J[s��Z��
�-"��u8 �Q�2&U�S�t4(��*)��w5�������P��(�c�5�j�����T�W��^��?N��\ts�����������&;�����d�����	�|�-_��n�{wqY����T��sC���3�r2?iFH�A4�;[��}C`v^��2��`C�U��o�������z��)h���p�������P����$3�j����S������pP��+���� 8X�W[]���Vc
�hP��IA�X�C=�t��c��J���O�^@W\���(l��l/�/%B��7��Y�%^�`,���NAt�at9*~�:��+��/s%����QB�-!N���)A�FI�(=������%X��j*R��wJq�%4�6����kB�V���
���DG��'����D���'�4���VC�" ���F��|��P
XIy�(�O�X��0���
p.'��h&X������x��6��Qt��A��*�|�~�W�!�Qz�0>m9�9T��U�m�������:��)?���'������N��>�n���-��D���E����f�p���{�
�3�Ux����Y\v ����~�	����%������W�$�����uw�*�#��7�D I��^�}tJ�Yd(RO#�Y��+�n�3��%Q]UM���x�.{2�������q��"��A�d���f��@�C�YC��;����� �V��2X7��5z�O�6PI=K����1�2nnq0M���|�����"��!H1�������j�p_��~o�:�� ��RM04n!
�*�s�;
���&|9���A���2(���{�����ax5	u�������G��B����'����q�6�;���V�> ����p]���G�_.z���\�U���V�j��;��M6����R'ob=��������k��2��tE����h�]b�g��~k��#��������
)����WR�������X�������@�k���,�jjM�$�	����������4��`��uV�Z�
84�-���X����y�4Q�<�1"�#�� =�a|�FX�9\:�$a��/�D�
��q����.;����������������\�f�j�1�����Uk	j�y&`�u6�:�p�Q�+�N
�'�A����eI���&9�X�L��e��M�uh$�w��C��!#�����}RXk�9�t\`�:�����	s��r��W`�!T��*u��E7���,?��O
t$���M� ��j���3��1J���|o9�4���r��Zaf:��X>I��m�1�(�G��f
�IV�#J�{��qL`�<p�	o&^��{��"�v��Pb����2O�Ga����K�2``�^�'_��\���P�����.o+�h��I�
vjBQo������u���$���Q�� ���&�]n�B	�H66�����tP������9��wa�a'�j���'�l��<���e�q��Wm0[%��"�y���rem��VBLD;���GJg�$6��.�*��h?k�Y��<��Zd�%���P��I # ������CH��z/V-.����PrwKTrG�tAY�`��h3T�TX|��%��V��-TmWb�%�or�����^��kd�8h��}B/#a��.���,#�+�D��!�s����YxMw�)��o�#X�p�Ln�u�L����p�B�@�$�����h\F��\,"[E�bl�O�I������u�	f�Ii�
,
p���z����)���@�J�����H������p�z_���:����������7�=W�"p���Gbi��D��{~v�f����,���
�	���dA�t��������q%K8�' ]�[����#&$�������g��+�.JQW���!��[�~�W��C6J��5Z+�-�t������i�1q�%���L$��q��m�M���V����"P�$DU�@g�kL�� �g���o��A~��t����.��u�om��\|�A�`�
�}�%��d�p%�w�5���F�.�|�9H<s�(�<uO�Y,���Y��a-R�j�jhzP�����/a&]���JF�tI�3�I��1m��������������v\��.(^��XP�aAb�1mH${���aj	��]��N�������O�	�F��� �}G��Q+VM�[6��n��t���M��d��'o�E����(~O%����A���w�ag/q�@�B>ZAe�
,B���^�Gr�__���e�
C>�g:r���3�|�Tb������M,���E1vh	�����cxbK,��������=P���^_���p�KCm�DnA��Z�I|�D%����R��KIy��C��~���?�=�����vy��L��C7�\+��Q<\��\pOy�{���H�{u�T*�����[����%�^I���2����Y�����k����l����j��uF�
M��mS��Q�}��(���j����Pcv�
 ����,@K������	�.�SqS~)��VK�`�q�q���-G
,-H}
��
��������EFS=fTt�U(���5��fR"��/R�[w����m��4��!����<C:�~8����S:V���^�"�q/i�����:���c��,U5���k��)�c�l�(e����).B��t�8ZU�!`�mH\0��~���l;�^/ t����j����d5��R��<^�$��FR�lmK�A�y���`Eg�6�!
|���Ge�/����Ya�0Dg��S��@�,���
�h�t�i4�q����Q�Ns�L��<������0�1&��
��O�H+&B�$fS4F�hN+��n"bY�-����2������A>UC,
[�F
E�2�bZQ}���@����}��;wj�C�u��	�.�4CK�:lH��h���@���%R�?o������gP�;=u��uJ��qHD�q�e�	��������y����O�R>-aP��ba�^&�B�)u0�!��:%x�p��@8�o2dZ���$���>����)�T�=LB�spy0�8�K��7���M|s��8�� Y^*����2l�>�F�Pv�@��o��CA]i�R�AH�D��F"�����5o�s�>S���Q��629�2t��c|Dy�KN(R���O����S��sE����������,�e���������3��O+�R�'�2c�6���$��"2�F��/�bOO>���$�[A�?���Y�/��I�0��1����
dn�;+c�������!��k@zJ����1�p��������(*W�'�����d[�]�tv~�y�m������1���gj���TM]�s���x8��:2�H������3�!���z�q��(_5�n��2�,@P
>��xw8�P�_8��v68����r�����p�[j����N��H4�����h`���Za[=���B�g&t�W?�-�EBNWc��p/Zp\�q���J`�o��h�^^{uaM�:��`��r�5�Q$����'�@�-f�,M�e�l��P��)B������K����J�;|C��qyj�w�����I��$�����Aj6��������������c��(�����W�$p���x�u��c��y�F���r�b^4a�;�$��^������vR"|B?Q �y�<���8f��Jal�������K%}Y%�lU[�C��6��^)���x����=^���
}�!��Ig'�`L	
���FxL�%��dQi�����(�
����b��c(��C����
�n��_�����������H�
��'_Pt���s��w�����o��F�O5���-�.���/t4&q�X�t�0�U;��3�����D�_W��p���ltx+�(9m�rV�O91�9�+���8�]E�b��7T���k��[���bP�m����& ��Ri����t���8����B���Z�j�Ri���~�:���c�)�[!oo�jh	���n(	9
��H�;�_lx7L��P�@B*���w�To�d��v�����7)K��1\���@����.��n�'��@WbG$���@�6�,�1�~��G-�~{��^���b����(���ic�H(Q<��W�cf9xb�
���C��E�����f-i�~���F�dp���������\#��pYj5d�]����Vp�u`���Ux�,U�uy���3�t���ca�����w���P�Z���,h�,���Z���
+���9������c���VXP�K��T+iMo�dJ�e���Q���%eL)��-�2�@D�xKGO����]�h��P�*8[�{DgA��%�;c0���#�v������i���). �$z��p&�*�n�fsF���KM�H�YfTJ.2f�|q^IAl	w
i�lsI�t�@~~�����%��������V������%�KnGi��mJYRT���H!|A���|������O$Z����jy 6n*Tw�	�T�c�B|z���N���<���@	.1(���l7��A,_%����C�=�5S�u�8*�Dz�~f?�����������������O�J	N���b����W0�o��B~���r��@q�X�lo�4��a�N.f��8�����G5��_���K���[����nb
�����8uY��*u}�
:
Z(QL��2�:�R�}D
�b���>/X-���������(�6Q�plWc����1�����Vw�=
"��R�D�����y���3����	�Z% �Z����X>id���J&�.�t���"�w�9� �A�@@`;�/{��`�e6���f�$�SRu�Z���GA����l�u2K4EV��)�e��� Ae�����:�H?m�?�e������p�@Pg�~^Tw��c^���&��I�fm�w�g�]��*\0;?}M����q��3��$5��yl��`Z�-&���,�G[F`@[���{l>7K	R:��.��<=6��u�OiO�$=�DBN))����N��[�Hb4�>��D=�'k�Pl�"l���LR�7ob��~���K����I?����8���@�c�@-<;�������z~_�}�Y+��&�m.��l����a��N2]D�� (5��0Q�	&�!��ee��o�����^�	��j� ��@��p
9���n>GT�j4�
�D������GH��{�^]3���$��A�����8���y��#����e���������C5(g�O����h�����ev�6
����
'_t��_t���:k�n\C�}�4�A�H���(J'����(�L��Cw�98z��m�l��`	��S?W���Dk�M�����q:J���`����K�R��aY�B���A��1	�1��a���<�d�v�Bg�����LR�L.�Z�I��B���$@8���+��	g&��>O���TH�}��:t��Vg������4�Q�`��L���S�����B(���m(�P�I�Y��J�i���[�m���BPJ�)�E�)!"F7���D�������h��z"��t6F2!��h1g������~.�%��8 �R��p�Q���
���}�g��	I�������UV�T$W��a�~@u(�*@����^8��R��w��J�<C�-��6�,��N��W��\.Y�-T��(����8d��Z���#����k^���T���m��%�����hS&�����Z��u}�"bu	{���[��>���������vJ��J^C��]�UKz��>R�v--�,#���7��
���A������	\�����~�b"U������|�����nmO���7G�'��������u/:\�&��a��q+��UW�p���������s��yw���Yt�r����x�r�c�~4���!is�c3�A���}p�����%�-����/�{4��S����n�~Iu&4��5��� )�(����������p����J;,2}�~f�d��L�F�N���V"��5���u����VW��cu��em�w�/�-��Io	������JTZ����|���B!5E��K�A�������$>�a�>��=� ���a�Q�	�,��[�S�}���d�y���a��U<Q��d>tI������N;W'�g���/�_�sy�)<�y[,��eA���XgJ��_b��X�3p^��m1f�K~�VY�����n?�c/�H��V�`�]c��;�����b����M��@��W���:�I����9#���	�����<]p�L[s���k��fw&�(�LWA.������#5.7w��~�" v
wT����\PB0����q�b]h�vo������H~Q�5vp]dr��� p�"��S�Q��r:�N3��
��H2��<9�!"��'��Q�(0E�.
!�0�1Dw��������g�uDz���<�zxc�3��z����k.�����?���#���V����F���{\���$	���gA�nU�,,����Y<M=����7?��ZN�sZJ���]���P��5�|���'�z�|
���"t��b����`�F]�+%nd�a��~�k�j�J���)�c?_���%-~d��D��H����E����-��&<ub���:�F�R<P�`IMb$r���L1O��:������Q��\:dU��v:�t�jx�I,���P�K�n��K���;����t
hVH���Ql����"�9�dJl���jU���m����&@& J�Z�����O���s@���������5���[9�1PZ2�u0��=��&��T@mU_r���%�B�
c1V:*��P�#����5o�]/b2W���a���#���!���
�_���^�!�j8��� z�������������Y?��^G�������������'��r
�Rt��?��� ~)��l�fw�[H^`��3��n��m����>�9��;������Z>2W{���x%��������:kH�/��V���=�T���3��c����h�}���
`����b-�A(8��[���{���N��N�����-i���pS����z�a��.l� ���F��I��f�u&�vk�%~}%H�~�0�Z�Y����K��$�v�H��kj�W,Bq[[\K��J�9������Hq0�
~	'�����.�m�U�2��O�J���T��7��{l�I�RM}�qWM(]?(��a,8�����K.��&��/���V��<����
��T��'�[���zd��'���]t��_���}Y��E����/b�%������=r?�����G��k��%���3�c���	#���6�cr�md5fa�i��7F�q��,����fWR�Jo8A��e�����Un��r�gb������W�����v���{0D��A�"x��Yt+�e>S������<[�^�t���pC`�����]
�����������&R]�$�aV�s��a*��z`7���@l������-f%g�p/��Co��z�>��J��<�iR�z�?�����B4��Y�\��buj�����I{����b<��J�c%SDl�~�W-����G6�G����>�uH������K�:w�����$����������������p���1R��
e�(�I"N��?��rK�����j��w��B���#�U�|Y@�����/.���/D4!%)��i����$7g%Y�A\�1��Z��A�rP���������8��5jm
;���BrH� q(�Z-O�(]�/l!�>q�g0(��(5��p%tF��>�H%��$W�"�9�^�'����=[�FWAL���1��L���<a��}y��9�����F�����\z��A������=0b��o��pQ�������!�����
�Bg��sg��������dQ�-:��3��=C`(K���z�>���&��u��S�������'.y�+h����
����
�z4��J�]kC�����t�w��Fl�&�b+��8t�YS $]��t��X��]�D]�A	\��n�E�M��r�`�����W���5������8{��pa����uH�|�l^��z�7�a3�n����lg.��N��UlE�������
��F����&�����@�9P�!���{��x�BY��zX�����Sh5�,��D�m�T�<9�<�L<�M�;5PH�q�]
�Y�c�����p�@�f�+��FlM/)�����4�L��!##���\�=Q����@SL�w��C+�iJi�DV��`������i�o2t��HHcL��5Gsh�w�2�f������~oOq��?�����{�enV���P��R�����|B�H�b=QJU~��S����L��\-��rA*�/	[��[#�&�|�a�2�����Qe�8�>��m8>�e�l�IRm�Ym����� �|�����v�[8o�1��Ud���dk��K����J���k��V���C.�Zm��5	�~R�`����\�������cO��$��6�#_[Q�v�J�	$�����j����'	?Bu)��V$��c@e�&�;�����0z+|C�9;��H;Z\���?���)YU�m� ��>��T�93�83���h��(�����(����R� �#�=J��H=�+������b�r���TJ
��o�h<�E#����nS�K�������g���3��������r�-:�> ,�����@o�Z��M�W���0K�m��m��%���/�5Z4�����G���v�=lj���'���}�J��Z����o4�
��B_�������n�8�+B�@���vpA�I�`#�:�
���	���Id���Q��z�@��<���->�L��H�9er��3��bO����/���'�+���Z,���n���'i6	�P<�A��W0C�;��E��HtPp$���B�0�#Zs�m	���\������oq��Dc��9�|��.�F��)#�)$�(h��d~`o��;��`a�]+��P���@�F�
������R,kr��(�>}�*��NBJ����L�NG�x4d'xp�7����g��W����p�L�	�����z :�y�QS�G���S��%H��T����;���L�����pJ@��'��O������4_�_�����)�"�W�&��-��q�����@#��K�R?�/.o-A����"�f����j����]�/�8�x
�i��y��M����W��������u�c��g�p����q�����U��yP�A�0�	.u�������kC�����~e`s��C��������F�����I�}A�%�����a\(r�l���9�����G�!'�xe�{HV
��F����[�[����[-��]��J�4����Iu��(�*����@�������!���S��W��f��_��������5��f�wn!�G���U[���a9�.f��	�-US�!@p���$wa������mr����
���Y����G�P���������D@�8���w��#���P}�D�z�#~2��,��A2dbY

+gj���(�X.�QZ�D���i�Q�6z{�J��k�W�A>
e��&��V���{~p-�)F��2��W�$�!�R��?��?�9���M���j��������yh��-R�|7�#�iW�]���1�����+�s�{����R7Qs!�%���_���[��0�-��������|��a��e(dV���a��[���E����,%pf��!1\����E���{��z��ux(�J������]�+e�?�������W��7G��;���7Q����m���EGK+��.Ee�
����(\v������/;W�����my���an:��?d���������������gw>�����s�D_-�����'W�i}�i���K�T���G��U0V�������*�B!�!�����3�:�M���������_����E������g���~�`0��U���7R��D�O�����y/k�b��:�x�k�����A4E����N��''�Be�-Qh�0y��~��a1��wt���g�z���l^T4��9�����'��Z�
u���3�^>29�������j2����i�QK��=�Z��;��������l_�8|�sE�5�H��>f5��w�?���������J��,i�p?�b���n�MnI��lO�7���s�+����������\���z����oSl��GE������[X=fn[���B�@RR�#�k��0"���oH��/��O,��K
O��o����e��t;	��7Z{�f�2����j��ouO�2�-!9�3�z�g^��}�Q;����%E��FOe}3����,6�%�qx=�����-���������n����T+H*���|���/��`���h�F��{�G�o4��%s[�xp����������:����f
�����;�J��^��@3����z�{i� _H�&����]��R�����w�':D�F=�6�@{�Nl{������!tvw���=�`�%����b-s�c�x�����w|��Q]}q�av���C'%x�a��Z,x`���g'_���?X*�^K�(�y��Jj�7��8��WC>�Co+�m��������wryN���i����������U�5���n�/�-����	��c����Y��}���/�\�gY������5����
c��B���g/l=�����O�����R/z=�"���������4���G������@��O#��X��sW����L0)����#������*�=\_���Q\��mc:x:-��:2��������~�I�N���Nc�[������Yn�F��i<��9G�Z�X��}���z���1^7!��2u��~�tH�a��S�1a���AE�;�8�3�v��l'��SE�s��2{�pb�8��@�!9��qP�7�~b�H<�:�X���^@@�<��BN����p�Ag�'��p�%0��,���Bu�&�9GU��� I���������7?�@��x~=.�>eT���w�e���y+�q���'a<N ���5�(f��A�-2�����d/��6OVvHV����G��q/�k`(�LA�������V&����.s�p<�5�t���kDc���A����%��[�x'������
��+FP�91	Mg����t\��"e�jB
�����r���)����S������������iOD��q�r	�)dSuQ�kAWf�[C?Ir�:1������G3��^A*��$d���Qf���� ��)��S���� ���Y��c���=�[����;��T���z�������r�'+�,�;�����nOe|���uA��jtC�����f�����rW�4��.�Z ��\{��2E�6��"�aV���G���hi�@@��j-���@�R8�7�w'o�.�����vx�ta����N�@�����lC�E0��Jo>�rN���5���T2��:u�)u�?|<��H�������3=D��s�N���7<���D7��.��(����������7rx���f_��^���i4]P�$�!����+��G����ip[��1��8��.�V��&5#Q
�)�+�=2��FbJ<Y�����PA���y�����^�7��p`�����$�������M�U?�����m����c�iX(
�0��+HG�u
��I��}�������D�^x����5m[Z�!\������U}r��o����}9�����B������	��U�R2)�����?��JW��T�tRU��5����x=�h���Yj\C�F��^�X�=���6�~�R�6}��g�-��c���������&����u�v��);�4��5���1#v����5��:������.	��<�<�7K}($�px�&��	E�,�I<���������:���i��Q������L�J�)y��*H����f���P�!w��O�.��BQ�+�U�������}GiVY��5���V%���|�Rq6�H#8���_���v;�{�l����2���w��C12�w�T�0V�r�
&�����R��|�dH��z�+o*fsr#}��W����7���{���&?�K�@k���z�^��b+��Ch����G�%���
@8V/}:$>����z������hj�=�D�]�"���|y$�:�6�6���T�����5��=�-�,bk6��3��,=�;�&��r��mO��y/r�j9M=���{��G�9�J8����m��d��ZO��l!$/���G�����r�3�R�����j�|F��=St�������9�2<�-�j�;��I��?�����W�zV��n��R��7w��'���x�%��t��p��@����g~�]�O;�<�I<��O������g����3k�\�b���uU#�a.��x��U��z���|�<��;�3y�����^jp���������f]� ���<�K��q���P�0H\�����5���Mc��.3�!��F; a{0 ���)~zr��j/!��)�Q��KDk���p������`?}��W��|8S�1�6�MeO���;Aq����zEnv|�}�D��E�M*�!A���h�!o����,N���f)���
b<!(^ <n�w����,}5��\�"16��������'��t_�#Ry1�&Bs�b���L���s��2�g9O_)�P3��}����g��yhF��d��TG�K�q�{xf����>�3F�r"h��<U��H��$����ID��g�T3|�NQ���}f~�Sl�)�cu����{�][�������&'I��4���\-��V!���8O����0�B�Jw
�����'_�{&���<��A�b�w��Y~��}
s<��?Q��hi��{�jU�����	I����%|�;��Yw� 6�V]-c���]u`6�3�$Lj�pd�ep���n^$
cA�yLu��w�a��-���?�Z��=�������|�SXZ�AAf�l��?B`�5�����\���B/�?y�s�7<A�����T��u7�p�v������@�{N?K��]^Q���i�w8A���/���J�9�^$�<]������_OW��}'�m��qp���J����x�EZ���h���F��N�cNl�b����L����p�����?���O�-�8����r���"����'�\�B|��;��tO��Zu���3]&�P�V�zo��<-������z��{�������$���$�w��$[U��b������$u���hMu0��}E+a���f'����I�����{g�d'�.�~�@�vp��N*������U\��m��kJ<��������U�l���,L���}���a��Y��$�8!5���4��`{fb�e*�����j���4��d*�
�S�*8����'�P��$��zS����D���,��\���n��f*xU��=�hipO��+���Y���4�Y0��Zu���N;�e��Y���i���.��i����-E"[��X�V6e=@~�q!Y[�I��P�T��f��`V��gU��#{����?
?��_$�3�s�@`��\kI����0�3��"Rt�u�O.�B�����[��~�u�ff�s�7,���w?���;�^��`F���7<����
r[&_x3��d�"��8�.�v���o1;f+U��=B�lL�uv�D�<kE������m��V�K�7���Wj���6Vt�$<(|��W�i8�G1���Kw����.��}���������^���otJ����������8�@�i����3�^�k���
������$@��C��N�fQ������Yw�y�V��7H�v�[jX�z���N��4%��6���;��'{?�`8WSP��P�;c��r��_7�.���>�_�}��V��'�z|�F���q4�Q���A�aZ�7[��'J5w��M��t��:��^b���P�"$T�����e����� ���`�]^������J�������E�<e��7m3cKVm�z��{�O�7P�3����{��������[��*6����_��������Q�������M,���>bQ
=�E8n�����M5u4��}������X�Ob���T6��R�S���t���#���>��-�D�E������a
��vLt�+�.�;���>�d�����yKt��v�vx�>zj�Q����b_/�8����k?�z`��"��C>�Q@@d\Tw�&�e� X4��AWK[�D��������C��u����5E%��@q��(g���&���G�u���p�
���WP������z_��*0�Ds�+��(�H?�.X�����C1rx��������u2�gT]�`6�'\��!n	9l�)�G������G���&&�\v./����W�'�G9��j��;~���
�1��|��/7PAU�
�����+�}�K?��z]o���|yqtv%km�e�)��_l�.����uunO��;;:=��r�6q�m���5�s����2������a������V���*s��G���g�g�h��e��F���k$c�Uq���w�\1u�
���/��&I� cj>l�����k�C�]t�9�{'�,��d�u"�j:e83��v�������h
�[1w�7��z�U�4�^}�.w6��b��%��F�G��-�B4���Aw��y����@a��W7_{��,C�eZ����.D��K�h���nWu��"�1�~�~w����"��������h�E��|9�g
�����8���g��P\���Gd���WrW�0��/B'4����%���A���]�d�mz1��o	A��:�7Q�8�Z��GJ�S�4�|T�6Z�*���o��J5����k�����c��k��3k��w���f�l���8{
@1<����bV�gT{�p��l����y�$��m���@NQ��C}�F�E��L���(�W�1	�����,1^���+����Yc��l���J���7�fs]�����=���E��!�G��c��W�j����0��dm�=�&k�2�r���t�)��X����!�s.&�}0H�1�����ok�� ��Zc�k~�>��m����������F����N��cEZ�"]�^)��&���X���J�~�8�%��5�i�R��?� Xm/�>�4[���� I���p[�p�[$�TVq�~RJ���-pp�P�p3����~~���B��Cz��5^�C[Y�������5qg~?
��l���H�}��7�F�<�h�_{���:�+:��9V\%�u��W�ps������.����:���/��R�������R���j{]>�t���:�Q��k���?�R���������> �EX��2���8����Q�G�u���0W�����~C1��'�p��������?V"����*����ju��U��w�p��{E�G�� �m����cTkx���b�/�Zs����R>�N���|�e������w#���$_Z�����G�	�������]^���(���m���z�������������T1�n<������_�|�e���V�0b�.t���R��e�fc����[��m��"Rl�y�/UE+��,7B�R�,k=}����@Y��B��O-?�Y�.���Xbq8@"R���9�{�����R�p5�A��pZz6:?�g�W'��8�����
**�5���o�b!���_tt��V�)������H�������?�����R���iR��� P�7�����ys�������`U��J�CA���9�@�C�H��������BMU���l���i���8A����W��=�s�>�2|j���F'����T�j�tt
������N�������������?�� /�� YWr����A��K2�O~|��j�Vn@5�f�|�������Q�.��oa�8�d8]O����n+����p|���
#(��%��������,��R������b��$��.o�5�������w���}��f���,���pO�C�p�eW��R,h��P*����
�$(o�*�W�F��K�/�eO�Vy���O*���=�~2���he�_�K�������Ql�7����{�J�Z�j����1����"�������r�e�P��Es����J{
�����C�YC+B.��(�A8&8j
��pQ��|���Y�I���J���S:�i2M/����J���D�����h�=�U�-v^����n�������-R�i[�f�
�A��K��Gg'g_��)�t�����v����"�fX����^Vo*.o���B������Z=�����"��<���_�F�^����]c	2�xNF���_����|{�N@��5���%6�Y�w=�([�-|��d��`�r@��t1[��Y��U���A��,C6i�q����W?���G��#�nww�->�������^yz0��?��{���%Vc��Z�8�Y�l��3���"�_���A�n@*�}0�	����2��A�yP���L�K�\��������C'��v��D�5�_�w�hY-2�����d�6�9Pm���F�[V=}W��Q\2�������qn��n�$�=�B�j����W*{��~��v,dV��<"Y������q������oB���94��3�=��X�:�y=pW��)nFot-��;|}��gm�n1\��`��;��z�9���/��/:����>��*����z�b�|'���O����������NoK�|KCs_EJ��C��x��r{��,��P�O��
����I�A���a��������^��G!*���������kI������o��m�b��>;������uv����;��BJ����
M��CNy����3]u�]����v���{��h����|��2��S?5+���e([;��������/JaJ���CZ_2d�Z�D�CD���Qj<��m=��~m�����������l�,w�N��M�����%���y��������#&��Z[]��������
� ���P�=�=.���q�����KF��Lw��|�������������O>�5��K�����lW�%=[����B��R����B�F|���Z�o4+�`P6kkd�nV]&�!^%��6H��s������4��&^u+k�����R;��|8��J�+���A�NT�Q��
���e���v����[�u0�:[AW	!Y��~�k����vR�"�1�q�(���������M�2_\���R��C��V�i�T���HQ���QJ��b���Bw����+�to�W>���+�w~�Z�������B��o�����
���{�E��O^5��Oi�gJ����+�������(V2�������.�[����*���`F�C6z�mBy��]a����/�^k�����q�i�p�j����GZN�����{�G��F�_5��O��Z�i����T�I=�tZ9��@��j{�6�V�F������]�8gF�
@j�bt���)�������E��n�����>�2���h�����t��t]��Y2��U�}���|�������'��O�D/W����yH����������-c�i���O\55���Y�� �g%�mVo�����.VH�;=�2��eW�����k�fb���W���_Kx��B��7A�#�6g�	Ag���/2��T�+4�
��7����.��y�>a����!�6V����t1�� F*q����,)>�-+�Z{�nU+�Zo���[e_��-G��mOA�u
�s���B$�w��8����#��M,���07v��8�~���������3�ax���`G��K��C���g?�tz��m�eUy���g��f�U����*�=�����5�sS3�m`\�e���;��:�7��RL�?
���8A����j�����!�������f�VV�y	~�k	�����/z|�8<�zx��2scu�!��^����]��
y���PF�
�Lb��[m�Q�9���
�Z@>���6�P�59��_Y����W���Z?W����2�������-(B��1p������T��]d>����J>�Sd������A��OS���M����t�=�����3�|�����\4�n�Ko�J�D�p3XXNS���V���+�A�?��j+h^g9�3�90�V1A�G���83;���(�Ud'�U���K%���*i�&��]��pX�>�4�s+p=��� ��P����R>�2��gaoA�8�y0�&�b%�?��������l&�������7��?@��Nd{a!�H�P�q<e.8�cu��>���0��H�NN�����5%����;S�
����j&[����������uYz�wM~���u=7��
�ML��x]N���f�|��?��?�������s��\���%�]�H��</D��%
���dBs�����}=����5�q�F�[c;H���Y��1^��;����n	dN��W^A3�\����!����2/��X$1M�C|-H�e�������t_y����B�/���.���i���Vm[���>k��9j�����/�[l����_]��W���.3y�����������g..������[O>�W�{�>_��u��<���}�s��^����F�������~�/[A����~W?o�u_�l%��W���1�G���E�?���o�����}�����y��>c���Y��?
��n
���c}x9��XA�?J�.V�d�����"��N���P������u<���G���j�|���z����������r���wU��7[����3���V?Y����=����`��k��D5%}�����gI�S�%��*���J���c�A=��X�I�����d�/?gbg���
a��L��
�=�2�����/T;�G����V31�������_�k4j�J�=�
����t��k�F@��JR4?���uT������ �L2��?���z`��:Z���S��W%j��1�����f/��j�R������f��q",�^a���z�x�j\9'l��a��jV���?�>�
u�r�~g��>M��5������D�m�hp���p�5���_��8�N/K���e��>V
�+*=g	 �Z�Pk�V���	�~���W������Q���+W�a-A���:iA�k��tm���F���,*�����~��/��F��s�����,��z�]�
Wg���g��B�Z~'Z����_���������,����g�X����-����g�t����-����g�����g��_���z���&a����MY]�WkU�YU��^��������9�9��{X�U�w/��0��.��V������4�!���;�<���^��
d��F��5�B���{�Z}�_�T��pm��"���y ��k��Fk# ]zs��[_Tz��2�a�'��fV�x_}O���������^�����`���hH��������+`��*`��
��c����"��."�K���^�V�TZ���~c��a	�m�``� 8�$�3�+�*���
����@d��\����!`���*�N0{�h�I�����f��fU���A��������
0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patch.gzapplication/x-gzip; name=0002-Make-UPDATE-privileges-distinct-from-INSERT-privileg.patch.gzDownload
#50Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#49)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Sun, Apr 19, 2015 at 9:37 PM, Peter Geoghegan <pg@heroku.com> wrote:

Attached patch, V3.4, implements what I believe you and Heikki have in
mind here.

Any plans to look at this, Svenne? You are signed up as a reviewer on
the commitfest app. A review of the documentation, and interactions
with other features like inheritance, updatable views and postgres_fdw
would be useful at this point. Obviously I've gone to a lot of effort
to document how things fit together at a high level on the UPSERT wiki
page, but these aspects have not been commented on all that much.

Thanks
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#49)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-19 21:37:51 -0700, Peter Geoghegan wrote:

Attached patch, V3.4, implements what I believe you and Heikki have in
mind here.

I'm not 100% sure Heikki and I am on exactly the same page here :P

I'm looking at git diff $(git merge-base upstream/master HEAD).. where
HEAD is e1a5822d164db0.

* The logical stuff looks much saner.

* Please add tests for the logical decoding stuff. Probably both a plain
regression and and isolationtester test in
contrib/test_decoding. Including one that does spooling to disk.

* I don't like REORDER_BUFFER_CHANGE_INTERNAL_INSERT/DELETE as names. Why not
_SPECINSERT and _SPECDELETE or such?

* Iff we're going to have the XLOG_HEAP_AFFIRM record, I'd rather have
that guide the logical decoding code. Seems slightly cleaner.

* Still not a fan of the name 'arbiter' for the OC indexes.

* Gram.y needs a bit more discussion:
* Can anybody see a problem with changing the precedence of DISTINCT &
ON to nonassoc? Right now I don't see a problem given both are
reserved keywords already.
The reason the conflict exists AFAICS is because something like
INSERT INTO foo SELECT DISTINCT ON CONFLICT IGNORE;
is allowed by the grammar. The need for the nonassoc could be
avoided by requiring DISTINCT to be followed by a column. We
currently *do* enforce that, just not in the parser (c.f.
transformDistinctClause). That requires one more production in
simple_select, and a nonoptional distinct clause.
I've queued up a commit cleaning this up in my repo, feel free to
merge and polish.
* UpdateInsertStmt is a horrible name. OnConflictUpdateStmt maybe?
* '(' index_params where_clause ')' is imo rather strange. The where
clause is inside the parens? That's quite different from the
original index clause.
* SPEC_IGNORE, /* INSERT of "ON CONFLICT IGNORE" */ looks like
a wrongly copied comment.
* The indentation in RoerderBufferCommit is clearly getting out of hand,
I've queued up a commit cleaning this up in my repo, feel free to merge.
* I don't think we use the term 'ordinary table' in error messages so
far.
* I still think it's unacceptable to redefine
XLOG_HEAP_LAST_MULTI_INSERT as XLOG_HEAP_SPECULATIVE_TUPLE like you
did. I'll try to find something better.
* I wonder if we now couldn't avoid changing heap_delete's API - we can
always super delete if we find a speculative insertion now. It'd be
nice not to break out of core callers if not necessary.
* breinbaas on IRC just mentioned that it'd be nice to have upsert as a
link in the insert. Given that that's the pervasive term that doesn't
seem absurd.

I think this is getting closer to a commit. Let's get this done.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#51)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-21 16:57:45 +0200, Andres Freund wrote:

* I still think it's unacceptable to redefine
XLOG_HEAP_LAST_MULTI_INSERT as XLOG_HEAP_SPECULATIVE_TUPLE like you
did. I'll try to find something better.

I think we should "just" split this into different flag values for
insert/update/delete.

I.e. something like

/* flags for heap insert and multi insert */
#define XLH_INSERT_ALL_VISIBLE_CLEARED
#define XLH_INSERT_LAST_MULTI_INSERT
#define XLH_INSERT_IS_SPECULATIVE
#define XLH_INSERT_CONTAINS_NEW_TUPLE

/* flags for update */
#define XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED
#define XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED
#define XLH_UPDATE_CONTAINS_OLD_TUPLE
#define XLH_UPDATE_CONTAINS_OLD_KEY
#define XLH_UPDATE_CONTAINS_NEW_TUPLE
#define XLH_UPDATE_PREFIX_FROM_OLD
#define XLH_UPDATE_SUFFIX_FROM_OLD

/* flags for delete */
#define XLH_DELETE_ALL_VISIBLE_CLEARED
#define XLH_DELETE_CONTAINS_OLD_TUPLE
#define XLH_DELETE_CONTAINS_OLD_KEY

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#51)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Tue, Apr 21, 2015 at 7:57 AM, Andres Freund <andres@anarazel.de> wrote:

I'm not 100% sure Heikki and I am on exactly the same page here :P

I'm looking at git diff $(git merge-base upstream/master HEAD).. where
HEAD is e1a5822d164db0.

Merged your stuff into my Github branch. Heikki is pushing changes
there directly now.

* The logical stuff looks much saner.

Cool.

* Please add tests for the logical decoding stuff. Probably both a plain
regression and and isolationtester test in
contrib/test_decoding. Including one that does spooling to disk.

Working on it...hope to push that to Github soon.

* I don't like REORDER_BUFFER_CHANGE_INTERNAL_INSERT/DELETE as names. Why not
_SPECINSERT and _SPECDELETE or such?

Changed that on Github.

* Iff we're going to have the XLOG_HEAP_AFFIRM record, I'd rather have
that guide the logical decoding code. Seems slightly cleaner.

I thought that you didn't think that would always work out? That in
some possible scenario it could break?

* Still not a fan of the name 'arbiter' for the OC indexes.

What do you prefer? Seems to describe what we're talking about
reasonably well to me.

* Gram.y needs a bit more discussion:
* Can anybody see a problem with changing the precedence of DISTINCT &
ON to nonassoc? Right now I don't see a problem given both are
reserved keywords already.

Why did you push code that solved this in a roundabout way, but
without actually reverting my original nonassoc changes (which would
now not result in shift/reduce conflicts?). What should we do about
that? Seems your unsure (otherwise you'd have reverted my thing). I
don't like that you seem to have regressed diagnostic output [1]https://github.com/petergeoghegan/postgres/commit/75d96c23676fd91568e9ec638470250c8b5e5f1b.
Surely it's simpler to use the nonassoc approach? I think this works
by giving the relevant keywords an explicit priority lower than '(',
so that a rule with ON CONFLICT '(' will shift rather than reducing a
conflicting rule (CONFLICT is an unreserved keyword). I wrote the code
so long ago that I can't really remember why I thought it was the
right thing, though.

* UpdateInsertStmt is a horrible name. OnConflictUpdateStmt maybe?

Done on Github.

* '(' index_params where_clause ')' is imo rather strange. The where
clause is inside the parens? That's quite different from the
original index clause.

I don't know. Maybe I was lazy about fixing shift/reduce conflicts. :-)

I'll look at this some more.

* SPEC_IGNORE, /* INSERT of "ON CONFLICT IGNORE" */ looks like
a wrongly copied comment.

Not sure what you mean here. Please clarify.

* The indentation in RoerderBufferCommit is clearly getting out of hand,
I've queued up a commit cleaning this up in my repo, feel free to merge.

Done on Github.

* I don't think we use the term 'ordinary table' in error messages so
far.

Fixed on Github.

* I still think it's unacceptable to redefine
XLOG_HEAP_LAST_MULTI_INSERT as XLOG_HEAP_SPECULATIVE_TUPLE like you
did. I'll try to find something better.

I did what you suggested in your follow-up e-mail (changes are on Github [2]https://github.com/petergeoghegan/postgres/commit/0cfde636bf1ffca438418fa0c02043805e99f30f -- Peter Geoghegan).

* I wonder if we now couldn't avoid changing heap_delete's API - we can
always super delete if we find a speculative insertion now. It'd be
nice not to break out of core callers if not necessary.

Maybe, but if there is a useful way to break out only a small subset
of heap_delete(), I'm not seeing it. Most of the callers that need a
new NULL argument are heap_insert() callers, actually. There are now 3
heap_delete() callers, up from 2.

* breinbaas on IRC just mentioned that it'd be nice to have upsert as a
link in the insert. Given that that's the pervasive term that doesn't
seem absurd.

Not sure what you mean. Where would the link appear? It is kind of
hard to categorize that text so that we're strictly either talking
about INSERT or UPSERT. Might be possible, though.

I think this is getting closer to a commit. Let's get this done.

Great!

The blockers to committing the IGNORE patch I see are:

* We need to tweak some of the logical decoding stuff a bit more, I
feel. Firm up on the details of whether we treat a confirmation record
as a logical decoding change that is involved in the new dance during
transaction reassembly.

* We need to sort out those issues with the grammar, since that only
really applies to the inference specification. Maybe the WHERE clause
that the inference specification accepts can be broken out. No ON
CONFLICT UPDATE specific issues left there, AFAICT though.

That really seems totally doable in just a couple of days.

The blockers for committing the ON CONFLICT UPDATE patch are larger, but doable:

* We need to figure out the tuple lock strength details. I think this
is doable, but it is the greatest challenge to committing ON CONFLICT
UPDATE at this point. Andres feels that we should require no greater
lock strength than an equivalent UPDATE. I suggest we get to this
after committing the IGNORE variant. We probably need to discuss this
some more.

* At the very least, I need to rebase my RLS patch onto what Stephen
just pushed (Dean's work). It would be nice to get Stephen and/or Dean
to review those aspects, as subject matter experts.

* It would also be nice to get someone like Tom to take a quick look
at what I'm doing in the optimizer, and in particular the rewriter.
Maybe he'd feel that the normalization that I'm doing (the
ExcludedExpr stuff) would be better suited to the optimizer. This
doesn't have to be a blocker to commit - it's just a suggestion.

Do you agree with my assessment? Did I miss a blocker? I'd like to
hear what Heikki has to say here too, now that he is pushing code to
Github. What needs more work before we can commit 1) ON CONFLICT
IGNORE, and 2) ON CONFLICT UPDATE?

Thanks

[1]: https://github.com/petergeoghegan/postgres/commit/75d96c23676fd91568e9ec638470250c8b5e5f1b
[2]: https://github.com/petergeoghegan/postgres/commit/0cfde636bf1ffca438418fa0c02043805e99f30f -- Peter Geoghegan
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#54Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#53)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Wed, Apr 22, 2015 at 3:23 PM, Peter Geoghegan <pg@heroku.com> wrote:

* We need to sort out those issues with the grammar, since that only
really applies to the inference specification. Maybe the WHERE clause
that the inference specification accepts can be broken out. No ON
CONFLICT UPDATE specific issues left there, AFAICT though.

I pushed some code that deals with the predicate being within parenthesis:

https://github.com/petergeoghegan/postgres/commit/358854645279523310f998dfc9cb3fe3e165ce1e

(it now follows the attributes/expressions indexes, in the style of
CREATE INDEX).

We still need to reconcile these changes to the grammar with your own,
Andres. I'm going to wait to hear back on what you think about that.
Note that this removal:

-%nonassoc DISTINCT
-%nonassoc ON

was incidental to the commit (this is the code you could have removed
when you modified the grammar, adding a new production, but didn't).
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#55Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#53)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-22 15:23:16 -0700, Peter Geoghegan wrote:

On Tue, Apr 21, 2015 at 7:57 AM, Andres Freund <andres@anarazel.de> wrote:

* Iff we're going to have the XLOG_HEAP_AFFIRM record, I'd rather have
that guide the logical decoding code. Seems slightly cleaner.

I thought that you didn't think that would always work out? That in
some possible scenario it could break?

I don't think there's a real problem. You obviously have to do it right
(i.e. only abort insertion if there's a insert/update/delete or commit).

Speaking of commits: Without having rechecked: I think you're missing
cleanup of th pending insertion on commit.

* Gram.y needs a bit more discussion:
* Can anybody see a problem with changing the precedence of DISTINCT &
ON to nonassoc? Right now I don't see a problem given both are
reserved keywords already.

Why did you push code that solved this in a roundabout way, but
without actually reverting my original nonassoc changes (which would
now not result in shift/reduce conflicts?).

I pushed the changes to a separate repo so you could polish them ;)

What should we do about that?

I'm prety sure we should not do the %nonassoc stuff.

I don't like that you seem to have regressed
diagnostic output [1].

Meh^5. This is the same type of errors we get in literally hundreds of
other syntax errors. And in contrast to the old error it'll actually
have a correct error possition.

Surely it's simpler to use the nonassoc approach?

I think it's much harder to forsee all consequences of that.

* SPEC_IGNORE, /* INSERT of "ON CONFLICT IGNORE" */ looks like
a wrongly copied comment.

Not sure what you mean here. Please clarify.

I'm not sure either ;)

* I wonder if we now couldn't avoid changing heap_delete's API - we can
always super delete if we find a speculative insertion now. It'd be
nice not to break out of core callers if not necessary.

Maybe, but if there is a useful way to break out only a small subset
of heap_delete(), I'm not seeing it.

I think you misread my statement: I'm saying we don't need the new
argument anymore, even if we still do the super-deletion in
heap_delete(). Now that the speculative insertion will not be visible
(as in seen on a tuple they could delete) to other backends we can just
do the super deletion if we see that the tuple is a promise one.

* breinbaas on IRC just mentioned that it'd be nice to have upsert as a
link in the insert. Given that that's the pervasive term that doesn't
seem absurd.

Not sure what you mean. Where would the link appear?

The index, i.e. it'd just be another indexterm. It seems good to give
people a link.

* We need to figure out the tuple lock strength details. I think this
is doable, but it is the greatest challenge to committing ON CONFLICT
UPDATE at this point. Andres feels that we should require no greater
lock strength than an equivalent UPDATE. I suggest we get to this
after committing the IGNORE variant. We probably need to discuss this
some more.

I want to see a clear way forward before we commit parts. It doesn't
have to be completely iron-clad, but the way forward should be pretty
clear. What's the problem you're seeing right now making this work? A
couple days on jabber you seemed to see a way doing this?

The reason I think this has to use the appropriate lock level is that
it'll otherwise re-introduce the deadlocks that fk locks resolved. Given
how much pain we endured to get fk locks, that seems like a bad deal.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#54)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-22 16:40:07 -0700, Peter Geoghegan wrote:

On Wed, Apr 22, 2015 at 3:23 PM, Peter Geoghegan <pg@heroku.com> wrote:

* We need to sort out those issues with the grammar, since that only
really applies to the inference specification. Maybe the WHERE clause
that the inference specification accepts can be broken out. No ON
CONFLICT UPDATE specific issues left there, AFAICT though.

I pushed some code that deals with the predicate being within parenthesis:

https://github.com/petergeoghegan/postgres/commit/358854645279523310f998dfc9cb3fe3e165ce1e

And the way you've used nonassoc here doesn't look correct. You're
hiding legitimate ambiguities in the grammar. UPDATE is a unreserved
keyword, so for

... ON CONFLICT '(' index_params ')' where_clause OnConflictUpdateStmt

it won't be able to discern whether an UPDATE in the WHERE clause is
part of the where_clause or OnConflictUpdate.

This is legal:
SELECT * FROM (SELECT true as update) f WHERE update;
i.e. 'update' can be the last part of a WHERE clause.

Essentially what you're trying to do with the nonassic is hiding that
UPDATE and IGNORE need to be reserved keywords with the syntax you're
proposing. We can either make them reserved or change the syntax.

One way to avoid making them reserved keywords - which would be somewhat
painful - is to add a 'DO' before the IGNORE/UPDATE. I.e. something like

ON CONFLICT opt_conflict_expr DO OnConflictUpdateStmt
| ON CONFLICT opt_conflict_expr DO IGNORE

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57Geoff Winkless
pgsqladmin@geoff.dj
In reply to: Andres Freund (#56)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

Apologies for butting in but can I (as a user) express a preference as a
user against DO?

Firstly, it looks horrible. And what's to stop me having "SELECT true AS
do" in the where clause (as per your UPDATE objection)?

Shouldn't UPDATE be a reserved keyword anyway? AIUI ANSI suggests so.

http://developer.mimer.se/validator/sql-reserved-words.tml

I had always assumed it was; anyone who produced a query for me that
contained update in an unusual context would get slapped heavily.

Geoff

On 23 April 2015 at 11:54, Andres Freund <andres@anarazel.de> wrote:

Show quoted text

On 2015-04-22 16:40:07 -0700, Peter Geoghegan wrote:

On Wed, Apr 22, 2015 at 3:23 PM, Peter Geoghegan <pg@heroku.com> wrote:

* We need to sort out those issues with the grammar, since that only
really applies to the inference specification. Maybe the WHERE clause
that the inference specification accepts can be broken out. No ON
CONFLICT UPDATE specific issues left there, AFAICT though.

I pushed some code that deals with the predicate being within

parenthesis:

https://github.com/petergeoghegan/postgres/commit/358854645279523310f998dfc9cb3fe3e165ce1e

And the way you've used nonassoc here doesn't look correct. You're
hiding legitimate ambiguities in the grammar. UPDATE is a unreserved
keyword, so for

... ON CONFLICT '(' index_params ')' where_clause OnConflictUpdateStmt

it won't be able to discern whether an UPDATE in the WHERE clause is
part of the where_clause or OnConflictUpdate.

This is legal:
SELECT * FROM (SELECT true as update) f WHERE update;
i.e. 'update' can be the last part of a WHERE clause.

Essentially what you're trying to do with the nonassic is hiding that
UPDATE and IGNORE need to be reserved keywords with the syntax you're
proposing. We can either make them reserved or change the syntax.

One way to avoid making them reserved keywords - which would be somewhat
painful - is to add a 'DO' before the IGNORE/UPDATE. I.e. something like

ON CONFLICT opt_conflict_expr DO OnConflictUpdateStmt
| ON CONFLICT opt_conflict_expr DO IGNORE

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Petr Jelinek
petr@2ndquadrant.com
In reply to: Geoff Winkless (#57)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 23/04/15 14:34, Geoff Winkless wrote:

Apologies for butting in but can I (as a user) express a preference as a
user against DO?

Firstly, it looks horrible. And what's to stop me having "SELECT true AS
do" in the where clause (as per your UPDATE objection)?

DO is already reserved keyword. There is also some precedence for this
in CREATE RULE. But I agree that it's not ideal syntax.

Shouldn't UPDATE be a reserved keyword anyway? AIUI ANSI suggests so.

http://developer.mimer.se/validator/sql-reserved-words.tml

I had always assumed it was; anyone who produced a query for me that
contained update in an unusual context would get slapped heavily.

Postgres currently has UPDATE as unreserved keyword and more importantly
IGNORE is not keyword at all so making it a new reserved keyword is not
nice at all.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#59Andres Freund
andres@anarazel.de
In reply to: Geoff Winkless (#57)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On April 23, 2015 3:34:07 PM GMT+03:00, Geoff Winkless <pgsqladmin@geoff.dj> wrote:

Apologies for butting in but can I (as a user) express a preference as
a
user against DO?

Sure. If you propose an alternative ;)

Firstly, it looks horrible. And what's to stop me having "SELECT true
AS
do" in the where clause (as per your UPDATE objection)?

A syntax error. DO is a reserved keyword. Update is just unreserved (and thus can be used as a column label). Ignore is unreserved with the patch and was unreserved before. We obviously can make both reserved, but of so we have to do it for real, not by hiding the conflicts

Shouldn't UPDATE be a reserved keyword anyway? AIUI ANSI suggests so.

http://developer.mimer.se/validator/sql-reserved-words.tml

It's not one right now. And ignore isn't a keyword at all atm.

(Please don't top post)

Andres

--- 
Please excuse brevity and formatting - I am writing this on my mobile phone.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#60Geoff Winkless
pgsqladmin@geoff.dj
In reply to: Andres Freund (#59)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 23 April 2015 at 13:51, Andres Freund <andres@anarazel.de> wrote:

On April 23, 2015 3:34:07 PM GMT+03:00, Geoff Winkless <
pgsqladmin@geoff.dj> wrote:

​>​
And what's to stop me having "SELECT true

AS

do" in the where clause (as per your UPDATE objection)?

A syntax error. DO is a reserved keyword. Update is just unreserved (and
thus can be used as a column label). Ignore is unreserved with the patch
and was unreserved before. We obviously can make both reserved, but of so
we have to do it for real, not by hiding the conflicts

Sorry, I misunderstood: so it's not the fact that it can't be used as a
column label (because it can) but the fact that it can't then be referenced
within a WHERE clause without quoting
. Which is in itself utterly horrible, but that's a separate argument and I
can at least now understand your point.​

So I could end up with

INSERT INTO mytable (somevalue) VALUES (999) ON CONFLICT ('myindex') WHERE
update UPDATE update=1

but I would have to do

INSERT INTO mytable (somevalue) VALUES (999) ON CONFLICT ('myindex') WHERE
"do" UPDATE "do"=1

?

Apologies for butting in but can I (as a user) express a preference as
a


user against DO?

Sure. If you propose an alternative ;)

​Maybe I'm misreading it, but isn't index_predicate meant to be inside the
brackets?

http://postgres-benchmarks.s3-website-us-east-1.amazonaws.com/on-conflict-docs/sql-insert.html

certainly states that.

It's not one right now. And ignore isn't a keyword at all atm.

​As I said, it's my personal belief that anyone using keywords (of any
kind) unquoted deserves what they get, but I see your point.​

I think I object to the fact that you're talking about adding extra
syntactic sugar to work around a parser-implementation problem, not an
actual syntax problem (since UPDATE SET is unambiguous, isn't it?).

(Please don't top post)

Mea culpa. I blame google :)​

FWIW "DO IGNORE" just reads disgustingly. If you do finally go down the DO
route, perhaps "DO NOTHING"? :)

Geoff

#61Andres Freund
andres@anarazel.de
In reply to: Geoff Winkless (#60)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-23 14:34:02 +0100, Geoff Winkless wrote:

A syntax error. DO is a reserved keyword. Update is just unreserved (and
thus can be used as a column label). Ignore is unreserved with the patch
and was unreserved before. We obviously can make both reserved, but of so
we have to do it for real, not by hiding the conflicts

Sorry, I misunderstood: so it's not the fact that it can't be used as a
column label (because it can) but the fact that it can't then be referenced
within a WHERE clause without quoting

Meh. You can use any keyword in quotes - because then they're not
keywords anymore.

INSERT INTO mytable (somevalue) VALUES (999) ON CONFLICT ('myindex') WHERE
update UPDATE update=1

but I would have to do

INSERT INTO mytable (somevalue) VALUES (999) ON CONFLICT ('myindex') WHERE
"do" UPDATE "do"=1

Yes.

​Maybe I'm misreading it, but isn't index_predicate meant to be inside the
brackets?

http://postgres-benchmarks.s3-website-us-east-1.amazonaws.com/on-conflict-docs/sql-insert.html

That has changed since. And for good reason: It's pretty to have the
WHERE clause inside the brackets when that's not the case for CREATE
INDEX. But more importantly with multiple columns for stuff like ON
CONFLICT (a, b WHERE foo) it's unclear where the WHERE is actually
attached to. We have that problem with aggregates and it repeatedly
caused confusion.

​As I said, it's my personal belief that anyone using keywords (of any
kind) unquoted deserves what they get, but I see your point.​

Given that IGNORE isn't even a keyword right now (9.5 master) that
policy isn't particularly meaningful anyway.

I think I object to the fact that you're talking about adding extra
syntactic sugar to work around a parser-implementation problem, not an
actual syntax problem (since UPDATE SET is unambiguous, isn't it?).

I fail to see the point of such an objection. We have an LALR parser
(generated by bison). That implies a certain expressiveness. You're
suggesting that we change to a different kind of parser?

I don't think it's necessarily unambiguous. I'm not particularly
motivated to prove it though - the point is that we rely on bison to
prevent ambiguities. That only works if we're listening. And not if
we're silencing warnings about ambiguities over the whole grammar.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#62Geoff Winkless
pgsqladmin@geoff.dj
In reply to: Andres Freund (#61)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 23 April 2015 at 14:50, Andres Freund <andres@anarazel.de> wrote:

​Maybe I'm misreading it, but isn't index_predicate meant to be inside

the

brackets?

http://postgres-benchmarks.s3-website-us-east-1.amazonaws.com/on-conflict-docs/sql-insert.html

That has changed since.

​Oh, helpful. :)​

​I'll shut up. I have a feeling that my objection is really with the very
idea of unreserved keywords and I have a feeling that there will be rather
more people shouting me down if I go off on that particular rant; meanwhile
it's 20 years since I used yacc in earnest and it's too hazy to be able to
argue about what it is or isn't capable of.

When I set out I was really only hoping to express a preference as a user;
on balance I would really rather not have DO IGNORE, if it were possible to
avoid, because it's really ugly, but DO UPDATE/DO NOTHING I could just
about cope with (and means you don't need to add IGNORE as a keyword,
win!), although it still mildly pains me that there's an additional
unnecessary word.

But I certainly don't object enough to hold up you guys doing the actual
work for my benefit (among others, obviously!).

G

#63Andres Freund
andres@anarazel.de
In reply to: Geoff Winkless (#62)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-23 15:52:40 +0100, Geoff Winkless wrote:

When I set out I was really only hoping to express a preference as a user;
on balance I would really rather not have DO IGNORE, if it were possible to
avoid, because it's really ugly, but DO UPDATE/DO NOTHING I could just
about cope with (and means you don't need to add IGNORE as a keyword,
win!), although it still mildly pains me that there's an additional
unnecessary word.

Yea, DO NOTHING is a good alternative. And I do like we're adding one
keyword less (which is also good for the parser's
size/performance).

DO {UPDATE ... | NOTHING | LOCK} doesn't sound too bad to me (yes, LOCK
doesn't exist yet, except by writing UPDATE .. WHERE false ;)).

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#64Bruce Momjian
bruce@momjian.us
In reply to: Andres Freund (#63)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 05:02:19PM +0200, Andres Freund wrote:

On 2015-04-23 15:52:40 +0100, Geoff Winkless wrote:

When I set out I was really only hoping to express a preference as a user;
on balance I would really rather not have DO IGNORE, if it were possible to
avoid, because it's really ugly, but DO UPDATE/DO NOTHING I could just
about cope with (and means you don't need to add IGNORE as a keyword,
win!), although it still mildly pains me that there's an additional
unnecessary word.

Yea, DO NOTHING is a good alternative. And I do like we're adding one
keyword less (which is also good for the parser's
size/performance).

No question that DO IGNORE sounds awkward. DO NOTHING also matches
CREATE RULE --- another plus.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#65Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#49)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/20/2015 07:37 AM, Peter Geoghegan wrote:

if (wco->commandType == CMD_INSERT)
command = "INSERT-applicable ";
else if (wco->commandType == CMD_UPDATE)
command = "UPDATE-applicable ";
else if (wco->commandType == CMD_DELETE)
command = "DELETE-applicable ";
else if (wco->commandType == CMD_SELECT)
command = "SELECT-applicable ";

ereport(ERROR,
(errcode(ERRCODE_WITH_CHECK_OPTION_VIOLATION),
errmsg("new row violates %sWITH CHECK OPTION %sfor \"%s\"",
command ? command : "",
wco->secBarrier ? "(originally security barrier) ":"",
wco->viewname),
val_desc ? errdetail("Failing row contains %s.", val_desc) :
0));

That code in ExecWithCheckOptions is not translatable. See style guide:
http://www.postgresql.org/docs/devel/static/nls-programmer.html#NLS-GUIDELINES

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#66Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#65)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 9:02 AM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

That code in ExecWithCheckOptions is not translatable. See style guide:
http://www.postgresql.org/docs/devel/static/nls-programmer.html#NLS-GUIDELINES

It's probably going to need to change when I rebase on top of
Dean's/Stephen's work, anyway.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#55)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 12:55 AM, Andres Freund <andres@anarazel.de> wrote:

I think you misread my statement: I'm saying we don't need the new
argument anymore, even if we still do the super-deletion in
heap_delete(). Now that the speculative insertion will not be visible
(as in seen on a tuple they could delete) to other backends we can just
do the super deletion if we see that the tuple is a promise one.

I disagree. I think it's appropriate that the one and only "super"
heap_delete() caller make a point of indicating that that's what it's
doing. The cost is only that the two other such callers must say that
they're not doing it. Super deletion is a special thing, that logical
decoding knows all about for example, and I think it's appropriate
that callers ask explicitly.

* breinbaas on IRC just mentioned that it'd be nice to have upsert as a
link in the insert. Given that that's the pervasive term that doesn't
seem absurd.

Not sure what you mean. Where would the link appear?

The index, i.e. it'd just be another indexterm. It seems good to give
people a link.

Oh, okay. Sure. Done on Github.

* We need to figure out the tuple lock strength details. I think this
is doable, but it is the greatest challenge to committing ON CONFLICT
UPDATE at this point. Andres feels that we should require no greater
lock strength than an equivalent UPDATE. I suggest we get to this
after committing the IGNORE variant. We probably need to discuss this
some more.

I want to see a clear way forward before we commit parts. It doesn't
have to be completely iron-clad, but the way forward should be pretty
clear. What's the problem you're seeing right now making this work? A
couple days on jabber you seemed to see a way doing this?

I was really just identifying it as the biggest problem the patch now
faces, and I want to face those issues down ASAP. Of course, that's
good, because as you say it is a small problem in an absolute sense.
The second most significant open item is rebasing on top of the recent
RLS changes, IMV.

I can see yourself and Heikki continuing to change small stylistic
things, which I expect to continue for a little while. That's fine,
but naturally I want to be aggressive about closing off these open
issues that are not just general clean-up, but have some small level
of risk of becoming more significant blockers.

The reason I think this has to use the appropriate lock level is that
it'll otherwise re-introduce the deadlocks that fk locks resolved. Given
how much pain we endured to get fk locks, that seems like a bad deal.

Right.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#67)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-23 12:45:59 -0700, Peter Geoghegan wrote:

On Thu, Apr 23, 2015 at 12:55 AM, Andres Freund <andres@anarazel.de> wrote:

I think you misread my statement: I'm saying we don't need the new
argument anymore, even if we still do the super-deletion in
heap_delete(). Now that the speculative insertion will not be visible
(as in seen on a tuple they could delete) to other backends we can just
do the super deletion if we see that the tuple is a promise one.

I disagree. I think it's appropriate that the one and only "super"
heap_delete() caller make a point of indicating that that's what it's
doing. The cost is only that the two other such callers must say that
they're not doing it. Super deletion is a special thing, that logical
decoding knows all about for example, and I think it's appropriate
that callers ask explicitly.

Unconvinced. Not breaking an API has its worth.

The second most significant open item is rebasing on top of the recent
RLS changes, IMV.

Not sure I agree. That part seems pretty mechanical to me.

* The docs aren't suitable for endusers. I think this will take a fair
amount of work.

* We're not yet sure about the syntax yet. In addition to the keyword
issue I'm unconvinced about having two WHERE clauses in the same
statement. I think that'll end up confusing users a fair bit. Might
still be the best way forward.
* The executor integration still isn't pretty, although Heikki is making
strides there
* I don't think anybody seriously has looked at the planner side yet.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#69Peter Geoghegan
pg@heroku.com
In reply to: Andres Freund (#68)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 12:53 PM, Andres Freund <andres@anarazel.de> wrote:

Unconvinced. Not breaking an API has its worth.

Yeah, and I acknowledge that - but it's not something that it's
appropriate to encapsulate IMV.

Let's just leave it to Heikki...I'd say he has the deciding vote,
especially as the reviewer that is more in charge of the executor
stuff than anything else.

The second most significant open item is rebasing on top of the recent
RLS changes, IMV.

Not sure I agree. That part seems pretty mechanical to me.

Hopefully so.

* The docs aren't suitable for endusers. I think this will take a fair
amount of work.

It's hard to explain why something like the collation field in the
inference specification matters to users...because it's only supported
at all due to forwards compatibility concerns. It's hard to document
certain things like that in an accessible way. In general, much of the
effort of the last year was making the feature play nice, and
considering a bunch of usability edge cases that are unlikely to come
up, but still must be documented.

* We're not yet sure about the syntax yet. In addition to the keyword
issue I'm unconvinced about having two WHERE clauses in the same
statement. I think that'll end up confusing users a fair bit. Might
still be the best way forward.

My previous approach to allowing an index predicate did at least
clearly show that the predicate belonged to the inference
specification, since it appeared between parenthesis. Maybe we should
do that after all? I don't think it much matters if the inference
specification is not identical to CREATE INDEX. I don't want to give
up inference of partial indexes, and I don't want to give up allowing
the UPDATE to have a limited WHERE clause, and we can't just spell
those differently here. So what alternative does that leave?

Anyone else have an opinion?

* The executor integration still isn't pretty, although Heikki is making
strides there

That's just clean-up, though. I'm not worried about the risk of Heikki
not succeeding at that.

* I don't think anybody seriously has looked at the planner side yet.

Good point. That certainly needs to be looked at (and I include the
rewriter parts in that). It's really not that much code, but (ideally)
a subject matter expert should look into the whole ExcludedExpr dance
in particular.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#70Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Andres Freund (#68)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/23/2015 10:53 PM, Andres Freund wrote:

On 2015-04-23 12:45:59 -0700, Peter Geoghegan wrote:

On Thu, Apr 23, 2015 at 12:55 AM, Andres Freund <andres@anarazel.de> wrote:

I think you misread my statement: I'm saying we don't need the new
argument anymore, even if we still do the super-deletion in
heap_delete(). Now that the speculative insertion will not be visible
(as in seen on a tuple they could delete) to other backends we can just
do the super deletion if we see that the tuple is a promise one.

I disagree. I think it's appropriate that the one and only "super"
heap_delete() caller make a point of indicating that that's what it's
doing. The cost is only that the two other such callers must say that
they're not doing it. Super deletion is a special thing, that logical
decoding knows all about for example, and I think it's appropriate
that callers ask explicitly.

Unconvinced. Not breaking an API has its worth.

The heapam API is not that stable, we've added arguments to those
functions every once in a while, and I don't recall any complaints. So
I'm with Peter that super-deletion should be requested explicitly by the
caller.

That said, I'd actually like to see a separate heap_super_delete()
function for that, rather than piggybacking on heap_delete(). It's a
quite different operation. There'll be some duplication, but seems
better than a maze of if-else's in heap_delete.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#71Andres Freund
andres@anarazel.de
In reply to: Heikki Linnakangas (#70)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 2015-04-23 23:08:34 +0300, Heikki Linnakangas wrote:

The heapam API is not that stable, we've added arguments to those functions
every once in a while, and I don't recall any complaints.

I heard some, but not too many, that's true. I know that I've written
code that'd be broken/needed even more ifdefs ;)

That said, I'd actually like to see a separate heap_super_delete() function
for that, rather than piggybacking on heap_delete(). It's a quite different
operation. There'll be some duplication, but seems better than a maze of
if-else's in heap_delete.

+many. I've requested that changes a couple times now.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#72Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#70)
1 attachment(s)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 1:08 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

That said, I'd actually like to see a separate heap_super_delete() function
for that, rather than piggybacking on heap_delete(). It's a quite different
operation. There'll be some duplication, but seems better than a maze of
if-else's in heap_delete.

I found a bug that seems to be down to commit
e3144183562d08e347f832f0b29daefe8bac617b on Github:

"""
commit e3144183562d08e347f832f0b29daefe8bac617b
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu Apr 23 18:38:11 2015 +0300

Minor cleanup of check_exclusion_or_unique_constraint.

To improve readability.

"""

At least, that's what a "git bisect" session showed.

Basically, at high client counts (128 clients only), after a few
iterations of the B-Tree test, the latest version of jjanes_upsert
would see this happen (error originates fromExecOnConflictUpdate(),
with custom instrumentation added to identify invisible tuple):

2015-04-23 15:10:48 PDT [ txid: 0 ]: LOG: database system was shut
down at 2015-04-23 15:10:09 PDT
2015-04-23 15:10:48 PDT [ txid: 0 ]: LOG: database system is ready
to accept connections
2015-04-23 15:12:55 PDT [ txid: 472418 ]: ERROR: attempted to lock
invisible tuple (140,39)
2015-04-23 15:12:55 PDT [ txid: 472418 ]: STATEMENT: insert into
upsert_race_test (index, filler, count) values ('3896',
random_characters(), '1') on conflict (index)
update set count=TARGET.count + EXCLUDED.count, filler =
EXCLUDED.filler
where TARGET.index = EXCLUDED.index
returning count

This seemed to only show up when fsync = on on my laptop, whereas in
the past some race conditions that I've found were easier to recreate
with fsync = off.

I attach some notes from my bisect/debugging session, including
pg_xlogdump output (from a psql session - I like to manipulate
pg_xlogdump output using SQL). That's probably not all that
interesting, but I attach it all the same. Hopefully this is something
that Heikki can easily debug, since the commit implicated here only
concerned readability. A simple oversight? If you want, Heikki, I can
try and debug it, but it seems like something we're better off having
you sign off on if possible.

Thanks
--
Peter Geoghegan

Attachments:

invisible_bug.txttext/plain; charset=US-ASCII; name=invisible_bug.txtDownload
#73Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#67)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 12:45 PM, Peter Geoghegan <pg@heroku.com> wrote:

* We need to figure out the tuple lock strength details. I think this
is doable, but it is the greatest challenge to committing ON CONFLICT
UPDATE at this point. Andres feels that we should require no greater
lock strength than an equivalent UPDATE. I suggest we get to this
after committing the IGNORE variant. We probably need to discuss this
some more.

I want to see a clear way forward before we commit parts. It doesn't
have to be completely iron-clad, but the way forward should be pretty
clear. What's the problem you're seeing right now making this work? A
couple days on jabber you seemed to see a way doing this?

I was really just identifying it as the biggest problem the patch now
faces, and I want to face those issues down ASAP. Of course, that's
good, because as you say it is a small problem in an absolute sense.
The second most significant open item is rebasing on top of the recent
RLS changes, IMV.

OK, I pushed out code to do this a while ago. I must admit that I
probably significantly over-estimated the difficulty of doing this.

With Heikki's problematic commit reverted this works fine (I have not
actually reverted it myself...I'll leave it to Heikki to clean that up
when he gets around to it). The usually jjanes_upsert stress tests
show up no problems.

Curious about what you think about my proposal to go back to putting
the inference specification WHERE clause within the parenthesis, since
this solves several problems, including clarifying to users that the
predicate is part of the inference clause.
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#74Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Peter Geoghegan (#72)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On 04/24/2015 02:52 AM, Peter Geoghegan wrote:

I found a bug that seems to be down to commit
e3144183562d08e347f832f0b29daefe8bac617b on Github:

"""
commit e3144183562d08e347f832f0b29daefe8bac617b
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu Apr 23 18:38:11 2015 +0300

Minor cleanup of check_exclusion_or_unique_constraint.

To improve readability.

"""

At least, that's what a "git bisect" session showed.

Ok, I see now that I totally screwed up the conditions on "waitMode" in
that commit. I just pushed a fix to your github repository.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#75Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#74)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 24, 2015 at 12:31 AM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Ok, I see now that I totally screwed up the conditions on "waitMode" in that
commit. I just pushed a fix to your github repository.

I can confirm that the commit you just pushed prevents the
implementation from attempting to lock an invisible tuple, where
previously it would reliably fall given the same testcase.

Thanks
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#76Stephen Frost
sfrost@snowman.net
In reply to: Peter Geoghegan (#43)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

Peter,

* Peter Geoghegan (pg@heroku.com) wrote:

I came up with something that is along the lines of what we discussed.
I'll wait for you to push Dean's code, and rebase on top of that
before submitting what I came up with. Maybe this can be rolled into
my next revision if I work through Andres' most recent feedback
without much delay.

This is done (finally!). Please take a look and let me know if you have
any questions or concerns. Happy to chat again also, of course.

Thanks!

Stephen

#77Peter Geoghegan
pg@heroku.com
In reply to: Stephen Frost (#76)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Fri, Apr 24, 2015 at 5:50 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Peter Geoghegan (pg@heroku.com) wrote:

I came up with something that is along the lines of what we discussed.
I'll wait for you to push Dean's code, and rebase on top of that
before submitting what I came up with. Maybe this can be rolled into
my next revision if I work through Andres' most recent feedback
without much delay.

This is done (finally!). Please take a look and let me know if you have
any questions or concerns. Happy to chat again also, of course.

Great, thanks.

I didn't actually wait for you (as earlier indicated) before posting
the new approach to RLS in V3.4. However, I have some decent tests for
the new behaviors (I did test-driven development), so I think that
when I rebase on top of the new RLS stuff tomorrow, I'll find that it
won't be too difficult.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#78Stephen Frost
sfrost@snowman.net
In reply to: Peter Geoghegan (#77)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

* Peter Geoghegan (pg@heroku.com) wrote:

On Fri, Apr 24, 2015 at 5:50 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Peter Geoghegan (pg@heroku.com) wrote:

I came up with something that is along the lines of what we discussed.
I'll wait for you to push Dean's code, and rebase on top of that
before submitting what I came up with. Maybe this can be rolled into
my next revision if I work through Andres' most recent feedback
without much delay.

This is done (finally!). Please take a look and let me know if you have
any questions or concerns. Happy to chat again also, of course.

Great, thanks.

I didn't actually wait for you (as earlier indicated) before posting
the new approach to RLS in V3.4. However, I have some decent tests for
the new behaviors (I did test-driven development), so I think that
when I rebase on top of the new RLS stuff tomorrow, I'll find that it
won't be too difficult.

Fantastic, glad to hear it!

Thanks!

Stephen

#79Peter Geoghegan
pg@heroku.com
In reply to: Peter Geoghegan (#73)
Re: INSERT ... ON CONFLICT IGNORE (and UPDATE) 3.0

On Thu, Apr 23, 2015 at 6:07 PM, Peter Geoghegan <pg@heroku.com> wrote:

Curious about what you think about my proposal to go back to putting
the inference specification WHERE clause within the parenthesis, since
this solves several problems, including clarifying to users that the
predicate is part of the inference clause.

I've *provisionally* pushed code that goes back to the old way,
Andres: https://github.com/petergeoghegan/postgres/commit/2a5d80b27d2c5832ad26dde4651c64dd2004f401

Perhaps this is the least worst way, after all.
--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers