!127 UPSERT 功能

Merge pull request !127 from gentle_hu/master
This commit is contained in:
opengauss-bot 2020-08-25 19:25:59 +08:00 committed by Gitee
commit ed27420caa
110 changed files with 9002 additions and 402 deletions

View File

@ -154,6 +154,7 @@ enable_copy_server_files|bool|0,0|NULL|NULL|
enable_sonic_hashjoin|bool|0,0|NULL|NULL|
enable_sonic_hashagg|bool|0,0|NULL|NULL|
enable_sonic_optspill|bool|0,0|NULL|NULL|
enable_upsert_to_merge|bool|0,0|NULL|Enable transform INSERT ON DUPLICATE KEY UDPATE statement to MERGE statement.|
enable_codegen|bool|0,0|NULL|NULL|
enable_codegen_print|bool|0,0|NULL|Enable dump for llvm function|
enable_delta_store|bool|0,0|NULL|NULL|

View File

@ -1814,7 +1814,13 @@ IndexInfo* BuildIndexInfo(Relation index)
/* fetch exclusion constraint info if any */
if (indexStruct->indisexclusion) {
RelationGetExclusionInfo(index, &ii->ii_ExclusionOps, &ii->ii_ExclusionProcs, &ii->ii_ExclusionStrats);
}
}
/* not doing speculative insertion here */
ii->ii_UniqueOps = NULL;
ii->ii_UniqueProcs = NULL;
ii->ii_UniqueStrats = NULL;
return ii;
}
@ -1865,6 +1871,42 @@ IndexInfo* BuildDummyIndexInfo(Relation index)
return ii;
}
void BuildSpeculativeIndexInfo(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) {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("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.

View File

@ -45,7 +45,7 @@ CatalogIndexState CatalogOpenIndexes(Relation heapRel)
resultRelInfo->ri_RelationDesc = heapRel;
resultRelInfo->ri_TrigDesc = NULL; /* we don't fire triggers */
ExecOpenIndices(resultRelInfo);
ExecOpenIndices(resultRelInfo, false);
return resultRelInfo;
}

View File

@ -1427,7 +1427,7 @@ void gtt_create_storage_files(Oid relid)
InitResultRelInfo(resultRelInfo, rel, 1, 0);
if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
resultRelInfo->ri_IndexRelationDescs == NULL) {
ExecOpenIndices(resultRelInfo);
ExecOpenIndices(resultRelInfo, false);
}
init_gtt_storage(CMD_UTILITY, resultRelInfo);
relation_close(rel, NoLock);

View File

@ -283,6 +283,11 @@ static ModifyTable* _copyModifyTable(const ModifyTable* from)
COPY_NODE_FIELD(mergeSourceTargetList);
COPY_NODE_FIELD(mergeActionList);
COPY_SCALAR_FIELD(upsertAction);
COPY_NODE_FIELD(updateTlist);
COPY_NODE_FIELD(exclRelTlist);
COPY_SCALAR_FIELD(exclRelRTIndex);
return newnode;
}
@ -3264,6 +3269,7 @@ static RangeTblEntry* _copyRangeTblEntry(const RangeTblEntry* from)
COPY_SCALAR_FIELD(relhasbucket);
COPY_SCALAR_FIELD(isbucket);
COPY_NODE_FIELD(buckets);
COPY_SCALAR_FIELD(isexcluded);
return newnode;
}
@ -3341,6 +3347,28 @@ static WithClause* _copyWithClause(const WithClause* from)
return newnode;
}
static UpsertClause* _copyUpsertClause(const UpsertClause* from)
{
UpsertClause* newnode = makeNode(UpsertClause);
COPY_NODE_FIELD(targetList);
COPY_LOCATION_FIELD(location);
return newnode;
}
static UpsertExpr* _copyUpsertExpr(const UpsertExpr* from)
{
UpsertExpr* newnode = makeNode(UpsertExpr);
COPY_SCALAR_FIELD(upsertAction);
COPY_NODE_FIELD(updateTlist);
COPY_NODE_FIELD(exclRelTlist);
COPY_SCALAR_FIELD(exclRelIndex);
return newnode;
}
static CommonTableExpr* _copyCommonTableExpr(const CommonTableExpr* from)
{
CommonTableExpr* newnode = makeNode(CommonTableExpr);
@ -3981,6 +4009,7 @@ static Query* _copyQuery(const Query* from)
COPY_NODE_FIELD(mergeSourceTargetList);
COPY_NODE_FIELD(mergeActionList);
COPY_NODE_FIELD(upsertQuery);
COPY_NODE_FIELD(upsertClause);
COPY_SCALAR_FIELD(isRowTriggerShippable);
COPY_SCALAR_FIELD(use_star_targets);
COPY_SCALAR_FIELD(is_from_full_join_rewrite);
@ -3998,6 +4027,7 @@ static InsertStmt* _copyInsertStmt(const InsertStmt* from)
COPY_NODE_FIELD(selectStmt);
COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(withClause);
COPY_NODE_FIELD(upsertClause);
return newnode;
}
@ -6164,6 +6194,9 @@ void* copyObject(const void* from)
case T_FromExpr:
retval = _copyFromExpr((FromExpr*)from);
break;
case T_UpsertExpr:
retval = _copyUpsertExpr((UpsertExpr *)from);
break;
case T_PartitionState:
retval = _copyPartitionState((PartitionState*)from);
break;
@ -6701,6 +6734,9 @@ void* copyObject(const void* from)
case T_WithClause:
retval = _copyWithClause((WithClause*)from);
break;
case T_UpsertClause:
retval = _copyUpsertClause((UpsertClause *)from);
break;
case T_CommonTableExpr:
retval = _copyCommonTableExpr((CommonTableExpr*)from);
break;

View File

@ -680,6 +680,15 @@ static bool _equalMergeAction(const MergeAction* a, const MergeAction* b)
return true;
}
static bool _equalUpsertExpr(const UpsertExpr* a, const UpsertExpr* b)
{
COMPARE_SCALAR_FIELD(upsertAction);
COMPARE_NODE_FIELD(updateTlist);
COMPARE_NODE_FIELD(exclRelTlist);
COMPARE_SCALAR_FIELD(exclRelIndex);
return true;
}
/*
* Stuff from relation.h
*/
@ -818,6 +827,7 @@ static bool _equalQuery(const Query* a, const Query* b)
COMPARE_NODE_FIELD(mergeSourceTargetList);
COMPARE_NODE_FIELD(mergeActionList);
COMPARE_NODE_FIELD(upsertQuery);
COMPARE_NODE_FIELD(upsertClause);
COMPARE_SCALAR_FIELD(isRowTriggerShippable);
COMPARE_SCALAR_FIELD(use_star_targets);
COMPARE_SCALAR_FIELD(is_from_full_join_rewrite);
@ -832,6 +842,7 @@ static bool _equalInsertStmt(const InsertStmt* a, const InsertStmt* b)
COMPARE_NODE_FIELD(selectStmt);
COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(withClause);
COMPARE_NODE_FIELD(upsertClause);
return true;
}
@ -2310,6 +2321,7 @@ static bool _equalRangeTblEntry(const RangeTblEntry* a, const RangeTblEntry* b)
COMPARE_SCALAR_FIELD(relhasbucket);
COMPARE_SCALAR_FIELD(isbucket);
COMPARE_NODE_FIELD(buckets);
COMPARE_SCALAR_FIELD(isexcluded);
return true;
}
@ -2387,6 +2399,13 @@ static bool _equalWithClause(const WithClause* a, const WithClause* b)
return true;
}
static bool _equalUpsertClause(const UpsertClause* a, const UpsertClause* b)
{
COMPARE_NODE_FIELD(targetList);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool _equalCommonTableExpr(const CommonTableExpr* a, const CommonTableExpr* b)
{
COMPARE_STRING_FIELD(ctename);
@ -2892,6 +2911,9 @@ bool equal(const void* a, const void* b)
case T_FromExpr:
retval = _equalFromExpr((FromExpr*)a, (FromExpr*)b);
break;
case T_UpsertExpr:
retval = _equalUpsertExpr((UpsertExpr*)a, (UpsertExpr*)b);
break;
case T_JoinExpr:
retval = _equalJoinExpr((JoinExpr*)a, (JoinExpr*)b);
break;
@ -3429,6 +3451,9 @@ bool equal(const void* a, const void* b)
case T_WithClause:
retval = _equalWithClause((WithClause*)a, (WithClause*)b);
break;
case T_UpsertClause:
retval = _equalUpsertClause((UpsertClause*)a, (UpsertClause*)b);
break;
case T_CommonTableExpr:
retval = _equalCommonTableExpr((CommonTableExpr*)a, (CommonTableExpr*)b);
break;

View File

@ -1372,6 +1372,9 @@ int exprLocation(const Node* expr)
case T_WithClause:
loc = ((const WithClause*)expr)->location;
break;
case T_UpsertClause:
loc = ((const UpsertClause*)expr)->location;
break;
case T_CommonTableExpr:
loc = ((const CommonTableExpr*)expr)->location;
break;
@ -1781,6 +1784,11 @@ bool expression_tree_walker(Node* node, bool (*walker)(), void* context)
return true;
}
} break;
case T_UpsertExpr: {
UpsertExpr* upsertClause = (UpsertExpr*)node;
if (p2walker(upsertClause->updateTlist, context))
return true;
} break;
case T_JoinExpr: {
JoinExpr* join = (JoinExpr*)node;
@ -1880,6 +1888,9 @@ bool query_tree_walker(Query* query, bool (*walker)(), void* context, int flags)
if (p2walker((Node*)query->mergeActionList, context)) {
return true;
}
if (p2walker((Node*)query->upsertClause, context)) {
return true;
}
if (p2walker((Node*)query->returningList, context)) {
return true;
}
@ -2468,6 +2479,14 @@ Node* expression_tree_mutator(Node* node, Node* (*mutator)(Node*, void*), void*
}
return (Node*)resultlist;
} break;
case T_UpsertExpr: {
UpsertExpr* upsertClause = (UpsertExpr*)node;
UpsertExpr* newnode = NULL;
FLATCOPY(newnode, upsertClause, UpsertExpr, isCopy);
MUTATE(newnode->updateTlist, upsertClause->updateTlist, List*);
return (Node*)newnode;
} break;
case T_FromExpr: {
FromExpr* from = (FromExpr*)node;
FromExpr* newnode = NULL;
@ -2586,6 +2605,7 @@ Query* query_tree_mutator(Query* query, Node* (*mutator)(Node*, void*), void* co
MUTATE(query->targetList, query->targetList, List*);
MUTATE(query->mergeSourceTargetList, query->mergeSourceTargetList, List*);
MUTATE(query->mergeActionList, query->mergeActionList, List*);
MUTATE(query->upsertClause, query->upsertClause, UpsertExpr*);
MUTATE(query->returningList, query->returningList, List*);
MUTATE(query->jointree, query->jointree, FromExpr*);
MUTATE(query->setOperations, query->setOperations, Node*);
@ -2853,6 +2873,9 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context)
if (p2walker(stmt->withClause, context)) {
return true;
}
if (p2walker(stmt->upsertClause, context)) {
return true;
}
} break;
case T_DeleteStmt: {
DeleteStmt* stmt = (DeleteStmt*)node;
@ -3144,6 +3167,8 @@ bool raw_expression_tree_walker(Node* node, bool (*walker)(), void* context)
} break;
case T_WithClause:
return p2walker(((WithClause*)node)->ctes, context);
case T_UpsertClause:
return p2walker(((UpsertClause*)node)->targetList, context);
case T_CommonTableExpr:
return p2walker(((CommonTableExpr*)node)->ctequery, context);
default:

View File

@ -735,8 +735,30 @@ static void _outModifyTable(StringInfo str, ModifyTable* node)
WRITE_INT_FIELD(mergeTargetRelation);
WRITE_NODE_FIELD(mergeSourceTargetList);
WRITE_NODE_FIELD(mergeActionList);
WRITE_ENUM_FIELD(upsertAction, UpsertAction);
WRITE_NODE_FIELD(updateTlist);
WRITE_NODE_FIELD(exclRelTlist);
WRITE_INT_FIELD(exclRelRTIndex);
}
static void _outUpsertClause(StringInfo str, const UpsertClause* node)
{
WRITE_NODE_TYPE("UPSERTCLAUSE");
WRITE_NODE_FIELD(targetList);
WRITE_INT_FIELD(location);
}
static void _outUpsertExpr(StringInfo str, const UpsertExpr* node)
{
WRITE_NODE_TYPE("UPSERTEXPR");
WRITE_ENUM_FIELD(upsertAction, UpsertAction);
WRITE_NODE_FIELD(updateTlist);
WRITE_NODE_FIELD(exclRelTlist);
WRITE_INT_FIELD(exclRelIndex);
}
static void _outMergeWhenClause(StringInfo str, const MergeWhenClause* node)
{
WRITE_NODE_TYPE("MERGEWHENCLAUSE");
@ -3317,6 +3339,8 @@ static void _outInsertStmt(StringInfo str, InsertStmt* node)
WRITE_NODE_FIELD(selectStmt);
WRITE_NODE_FIELD(returningList);
WRITE_NODE_FIELD(withClause);
WRITE_NODE_FIELD(upsertClause);
}
static void _outUpdateStmt(StringInfo str, UpdateStmt* node)
@ -3771,6 +3795,7 @@ static void _outQuery(StringInfo str, Query* node)
WRITE_NODE_FIELD(mergeSourceTargetList);
WRITE_NODE_FIELD(mergeActionList);
WRITE_NODE_FIELD(upsertQuery);
WRITE_NODE_FIELD(upsertClause);
WRITE_BOOL_FIELD(isRowTriggerShippable);
WRITE_BOOL_FIELD(use_star_targets);
WRITE_BOOL_FIELD(is_from_full_join_rewrite);
@ -3986,6 +4011,7 @@ static void _outRangeTblEntry(StringInfo str, RangeTblEntry* node)
WRITE_BOOL_FIELD(isbucket);
WRITE_NODE_FIELD(buckets);
}
WRITE_BOOL_FIELD(isexcluded);
}
/*
@ -5116,7 +5142,9 @@ static void _outNode(StringInfo str, const void* obj)
case T_MergeAction:
_outMergeAction(str, (MergeAction*)obj);
break;
case T_UpsertExpr:
_outUpsertExpr(str, (UpsertExpr*)obj);
break;
case T_Path:
_outPath(str, (Path*)obj);
break;
@ -5289,6 +5317,9 @@ static void _outNode(StringInfo str, const void* obj)
case T_WithClause:
_outWithClause(str, (WithClause*)obj);
break;
case T_UpsertClause:
_outUpsertClause(str, (UpsertClause*)obj);
break;
case T_CommonTableExpr:
_outCommonTableExpr(str, (CommonTableExpr*)obj);
break;

View File

@ -1304,6 +1304,9 @@ static Query* _readQuery(void)
IF_EXIST(upsertQuery) {
READ_NODE_FIELD(upsertQuery);
}
IF_EXIST(upsertClause) {
READ_NODE_FIELD(upsertClause);
}
IF_EXIST(isRowTriggerShippable) {
READ_BOOL_FIELD(isRowTriggerShippable);
}
@ -2659,6 +2662,10 @@ static RangeTblEntry* _readRangeTblEntry(void)
READ_NODE_FIELD(buckets);
}
IF_EXIST(isexcluded) {
READ_BOOL_FIELD(isexcluded);
}
READ_DONE();
}
@ -3392,6 +3399,44 @@ static ModifyTable* _readModifyTable(ModifyTable* local_node)
READ_NODE_FIELD(mergeActionList);
}
IF_EXIST(upsertAction) {
READ_ENUM_FIELD(upsertAction, UpsertAction);
}
IF_EXIST(updateTlist) {
READ_NODE_FIELD(updateTlist);
}
IF_EXIST(exclRelTlist) {
READ_NODE_FIELD(exclRelTlist);
}
IF_EXIST(exclRelRTIndex) {
READ_INT_FIELD(exclRelRTIndex);
}
READ_DONE();
}
static UpsertExpr* _readUpsertExpr(void)
{
READ_LOCALS(UpsertExpr);
READ_ENUM_FIELD(upsertAction, UpsertAction);
READ_NODE_FIELD(updateTlist);
READ_NODE_FIELD(exclRelTlist);
READ_INT_FIELD(exclRelIndex);
READ_DONE();
}
static UpsertClause* _readUpsertClause(void)
{
READ_LOCALS(UpsertClause);
READ_NODE_FIELD(targetList);
READ_INT_FIELD(location);
READ_DONE();
}
@ -5197,6 +5242,10 @@ Node* parseNodeString(void)
return_value = _readAddPartitionState();
} else if (MATCH("ROWNUM", 6)) {
return_value = _readRownum();
} else if (MATCH("UPSERTEXPR", 10)) {
return_value = _readUpsertExpr();
} else if (MATCH("UPSERTCLAUSE", 12)) {
return_value = _readUpsertClause();
} else {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),

View File

@ -82,6 +82,8 @@ THR_LOCAL post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
static Query* transformDeleteStmt(ParseState* pstate, DeleteStmt* stmt);
static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt);
static void checkUpsertTargetlist(Relation targetTable, List* updateTlist);
static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upsertClause, RangeVar* relation);
static int count_rowexpr_columns(ParseState* pstate, Node* expr);
static Query* transformSelectStmt(
ParseState* pstate, SelectStmt* stmt, bool isFirstNode = true, bool isCreateView = false);
@ -90,6 +92,7 @@ static Query* transformSetOperationStmt(ParseState* pstate, SelectStmt* stmt);
static Node* transformSetOperationTree(ParseState* pstate, SelectStmt* stmt, bool isTopLevel, List** targetlist);
static void determineRecursiveColTypes(ParseState* pstate, Node* larg, List* nrtargetlist);
static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt);
static List* transformUpdateTargetList(ParseState* pstate, List* qryTlist, List* origTlist, RangeVar* stmtrel);
static List* transformReturningList(ParseState* pstate, List* returningList);
static Query* transformDeclareCursorStmt(ParseState* pstate, DeclareCursorStmt* stmt);
static Query* transformExplainStmt(ParseState* pstate, ExplainStmt* stmt);
@ -1002,6 +1005,7 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
ListCell* icols = NULL;
ListCell* attnos = NULL;
ListCell* lc = NULL;
AclMode targetPerms = ACL_INSERT;
/* There can't be any outer WITH to worry about */
AssertEreport(pstate->p_ctenamespace == NIL, MOD_OPT, "para should be NIL");
@ -1068,7 +1072,10 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
* mentioned in the SELECT part. Note that the target table is not added
* to the joinlist or namespace.
*/
qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, ACL_INSERT);
if (stmt->upsertClause != NULL && stmt->upsertClause->targetList != NIL) {
targetPerms |= ACL_UPDATE;
}
qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, targetPerms);
if (pstate->p_target_relation != NULL &&
((unsigned int)RelationGetInternalMask(pstate->p_target_relation) & INTERNAL_MASK_DINSERT)) {
ereport(ERROR,
@ -1076,6 +1083,23 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
errmsg("Un-support feature"),
errdetail("internal relation doesn't allow INSERT")));
}
if (pstate->p_target_relation != NULL && stmt->upsertClause != NULL) {
/* non-supported upsert cases */
if (!u_sess->attr.attr_sql.enable_upsert_to_merge && RelationIsColumnFormat(pstate->p_target_relation)) {
ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table."))));
}
if (RelationIsForeignTable(pstate->p_target_relation)) {
ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on foreign table."))));
}
if (RelationIsView(pstate->p_target_relation)) {
ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on VIEW."))));
}
}
/* data redistribution for DFS table.
* check if the target relation is being redistributed(insert mode).
@ -1439,6 +1463,11 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
attnos = lnext(attnos);
}
/* Process DUPLICATE KEY UPDATE, if any. */
if (stmt->upsertClause) {
qry->upsertClause = transformUpsertClause(pstate, stmt->upsertClause, stmt->relation);
}
/*
* 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
@ -1480,6 +1509,153 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
return qry;
}
static void checkUpsertTargetlist(Relation targetTable, List* updateTlist)
{
List* index_list = RelationGetIndexInfoList(targetTable);
if (check_unique_constraint(index_list)) {
ListCell* target = NULL;
ListCell* index = NULL;
IndexInfo* index_info = NULL;
Bitmapset* target_attrs = NULL; /* attr bitmap according to targetlist */
Bitmapset* index_attrs = NULL; /* attr bitmap according to index */
TargetEntry* tle = NULL;
foreach (target, updateTlist) {
tle = (TargetEntry*)lfirst(target);
target_attrs = bms_add_member(target_attrs, tle->resno);
}
foreach (index, index_list) {
index_info = (IndexInfo*)lfirst(index);
for (int i = 0; i < index_info->ii_NumIndexAttrs; i++) {
int attrno = index_info->ii_KeyAttrNumbers[i];
if (attrno > 0) {
index_attrs = bms_add_member(index_attrs, attrno);
}
}
}
if (bms_overlap(index_attrs, target_attrs)) {
ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key."))));
}
}
}
List* BuildExcludedTargetlist(Relation targetrel, Index exclRelIndex)
{
List* result = NIL;
int attno;
Var* var;
TargetEntry* te;
/*
* Note that resnos of the tlist must correspond to attnos of the
* underlying relation, hence we need entries for dropped columns too.
*/
for (attno = 0; attno < RelationGetNumberOfAttributes(targetrel); attno++) {
Form_pg_attribute attr = targetrel->rd_att->attrs[attno];
char* name;
if (attr->attisdropped) {
/*
* can't use atttypid here, but it doesn't really matter what type
* the Const claims to be.
*/
var = (Var*)makeNullConst(INT4OID, -1, InvalidOid);
name = NULL;
} else {
var = makeVar(exclRelIndex, attno + 1, attr->atttypid, attr->atttypmod,
attr->attcollation, 0);
name = pstrdup(NameStr(attr->attname));
}
te = makeTargetEntry((Expr*)var, attno + 1, name, false);
result = lappend(result, te);
}
/*
* Add a whole-row-Var entry to support references to "EXCLUDED.*". Like
* the other entries in the EXCLUDED tlist, its resno must match the Var's
* varattno, else the wrong things happen while resolving references in
* setrefs.c. This is against normal conventions for targetlists, but
* it's okay since we don't use this as a real tlist.
*/
var = makeVar(exclRelIndex, InvalidAttrNumber, targetrel->rd_rel->reltype,
-1, InvalidOid, 0);
te = makeTargetEntry((Expr*)var, InvalidAttrNumber, NULL, true);
result = lappend(result, te);
return result;
}
static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upsertClause, RangeVar* relation)
{
UpsertExpr* result = NULL;
List* updateTlist = NIL;
RangeTblEntry* exclRte = NULL;
int exclRelIndex = 0;
List* exclRelTlist = NIL;
UpsertAction action = UPSERT_NOTHING;
Relation targetrel = pstate->p_target_relation;
#ifdef ENABLE_MULTIPLE_NODES
if (targetrel->rd_rel->relhastriggers) {
ereport(WARNING,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("INSERT ON DUPLICATE KEY UPDATE will ignore triggers.")));
}
#endif
if (upsertClause->targetList != NIL) {
pstate->p_is_insert = false;
action = UPSERT_UPDATE;
exclRte = addRangeTableEntryForRelation(pstate, targetrel, makeAlias("excluded", NIL), false, false);
exclRte->isexcluded = true;
exclRelIndex = list_length(pstate->p_rtable);
/*
* Build a targetlist for the EXCLUDED pseudo relation. Out of
* simplicity we do that here, because expandRelAttrs() happens to
* nearly do the right thing; specifically it also works with views.
* It'd be more proper to instead scan some pseudo scan node, but it
* doesn't seem worth the amount of code required.
*
* The only caveat of this hack is that the permissions expandRelAttrs
* adds have to be reset. markVarForSelectPriv() will add the exact
* required permissions back.
*/
exclRelTlist = BuildExcludedTargetlist(targetrel, exclRelIndex);
exclRte->requiredPerms = 0;
exclRte->selectedCols = NULL;
/*
* Add EXCLUDED and the target RTE to the namespace, so that they can
* be used in the UPDATE statement.
*/
addRTEtoQuery(pstate, exclRte, false, true, true);
addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true);
updateTlist = transformTargetList(pstate, upsertClause->targetList);
updateTlist = transformUpdateTargetList(pstate, updateTlist, upsertClause->targetList, relation);
/* We can't update primary or unique key in upsert, check it here */
if (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) {
checkUpsertTargetlist(pstate->p_target_relation, updateTlist);
}
}
/* Finally, build DUPLICATE KEY UPDATE [NOTHING | ... ] expression */
result = makeNode(UpsertExpr);
result->updateTlist = updateTlist;
result->exclRelIndex = exclRelIndex;
result->exclRelTlist = exclRelTlist;
result->upsertAction = action;
return result;
}
/*
* Prepare an INSERT row for assignment to the target table.
*
@ -2656,13 +2832,10 @@ void fixResTargetListWithTableNameRef(Relation rd, RangeVar* rel, List* clause_l
static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
{
Query* qry = makeNode(Query);
RangeTblEntry* target_rte = NULL;
Node* qual = NULL;
ListCell* origTargetList = NULL;
ListCell* tl = NULL;
qry->commandType = CMD_UPDATE;
pstate->p_is_update = true;
pstate->p_is_insert = false;
/* set io state for backend status for the thread, we will use it to check user space */
pgstat_set_io_state(IOSTATE_READ);
@ -2714,7 +2887,6 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
transformFromClause(pstate, stmt->fromClause);
qry->targetList = transformTargetList(pstate, stmt->targetList);
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
qry->returningList = transformReturningList(pstate, stmt->returningList);
@ -2752,6 +2924,24 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
* Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the UPDATE target columns.
*/
qry->targetList = transformUpdateTargetList(pstate, qry->targetList, stmt->targetList, stmt->relation);
assign_query_collations(pstate, qry);
return qry;
}
/*
* transformUpdateTargetList -
* handle SET clause in UPDATE/INSERT ... DUPLICATE KEY UPDATE
*/
static List* transformUpdateTargetList(ParseState* pstate, List* qryTlist, List* origTlist, RangeVar* stmtrel)
{
List* tlist = NIL;
RangeTblEntry* target_rte = NULL;
ListCell* orig_tl = NULL;
ListCell* tl = NULL;
tlist = qryTlist;
/* Prepare to assign non-conflicting resnos to resjunk attributes */
if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) {
@ -2760,9 +2950,9 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
/* Prepare non-junk columns for assignment to target table */
target_rte = pstate->p_target_rangetblentry;
origTargetList = list_head(stmt->targetList);
orig_tl = list_head(origTlist);
foreach (tl, qry->targetList) {
foreach (tl, tlist) {
TargetEntry* tle = (TargetEntry*)lfirst(tl);
ResTarget* origTarget = NULL;
int attrno;
@ -2778,14 +2968,16 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
tle->resname = NULL;
continue;
}
if (origTargetList == NULL) {
if (orig_tl == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("UPDATE target count mismatch --- internal error")));
}
origTarget = (ResTarget*)lfirst(origTargetList);
origTarget = (ResTarget*)lfirst(orig_tl);
AssertEreport(IsA(origTarget, ResTarget), MOD_OPT, "Node type inconsistant here");
fixResTargetNameWithTableNameRef(pstate->p_target_relation, stmt->relation, origTarget);
if (stmtrel != NULL) {
fixResTargetNameWithTableNameRef(pstate->p_target_relation, stmtrel, origTarget);
}
attrno = attnameAttNum(pstate->p_target_relation, origTarget->name, true);
if (attrno == InvalidAttrNumber) {
@ -2812,16 +3004,14 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
/* Mark the target column as requiring update permissions */
target_rte->updatedCols = bms_add_member(target_rte->updatedCols, attrno - FirstLowInvalidHeapAttributeNumber);
origTargetList = lnext(origTargetList);
orig_tl = lnext(orig_tl);
}
if (origTargetList != NULL) {
if (orig_tl != NULL) {
ereport(
ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("UPDATE target count mismatch --- internal error")));
}
assign_query_collations(pstate, qry);
return qry;
return tlist;
}
/*

View File

@ -239,6 +239,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
/* PGXC_END */
ForeignPartState *foreignpartby;
MergeWhenClause *mergewhen;
UpsertClause *upsert;
}
%type <node> stmt schema_stmt
@ -447,7 +448,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
/* INSERT */
%type <istmt> insert_rest
%type <node> duplicate_update_clause
%type <node> upsert_clause
%type <mergewhen> merge_insert merge_update
@ -640,7 +641,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
DROP DUPLICATE DISCONNECT
EACH ELASTIC ELSE ENABLE_P ENCODING ENCRYPTED END_P ENFORCED ENUM_P ERRORS ESCAPE EOL ESCAPING EVERY EXCEPT EXCHANGE
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXCLUDE EXCLUDED EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FAST FENCED FETCH FILEHEADER_P FILL_MISSING_FIELDS FIRST_P FIXED_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORMATTER FORWARD
@ -13067,80 +13068,79 @@ InsertStmt: opt_with_clause INSERT INTO qualified_name insert_rest returning_cla
$5->withClause = $1;
$$ = (Node *) $5;
}
| opt_with_clause INSERT INTO qualified_name insert_rest duplicate_update_clause returning_clause
| opt_with_clause INSERT INTO qualified_name insert_rest upsert_clause returning_clause
{
/* It is a INSERT ON DUPLICATE KEY UPDATE statement */
if ($7 != NIL)
{
if ($7 != NIL) {
ereport(ERROR,
(errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.")));
}
if ($1 != NULL)
{
if ($1 != NULL) {
ereport(ERROR,
(errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.")));
}
if ($5 != NULL && $5->cols != NIL)
{
ListCell *c = NULL;
List *cols = $5->cols;
foreach (c, cols)
{
ResTarget *rt = (ResTarget *)lfirst(c);
if (rt->indirection != NIL)
{
ereport(ERROR,
(errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Subfield name or array subscript of column \"%s\" "
"is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.",
rt->name),
errhint("Try assign a composite or an array expression to column \"%s\".", rt->name)));
if (unlikely(u_sess->attr.attr_sql.enable_upsert_to_merge)) {
if ($5 != NULL && $5->cols != NIL) {
ListCell *c = NULL;
List *cols = $5->cols;
foreach (c, cols) {
ResTarget *rt = (ResTarget *)lfirst(c);
if (rt->indirection != NIL) {
ereport(ERROR,
(errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Subfield name or array subscript of column \"%s\" "
"is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.",
rt->name),
errhint("Try assign a composite or an array expression to column \"%s\".", rt->name)));
}
}
}
MergeStmt *m = makeNode(MergeStmt);
m->is_insert_update = true;
/* for UPSERT, keep the INSERT statement as well */
$5->relation = $4;
$5->returningList = $7;
$5->withClause = $1;
m->insert_stmt = (Node *) copyObject($5);
/* fill a MERGE statement*/
m->relation = $4;
Alias *a1 = makeAlias(($4->relname), NIL);
$4->alias = a1;
Alias *a2 = makeAlias("excluded", NIL);
RangeSubselect *r = makeNode(RangeSubselect);
r->alias = a2;
r->subquery = (Node *) ($5->selectStmt);
m->source_relation = (Node *) r;
MergeWhenClause *n = makeNode(MergeWhenClause);
n->matched = false;
n->commandType = CMD_INSERT;
n->cols = $5->cols;
n->values = NULL;
m->mergeWhenClauses = list_make1((Node *) n);
if ($6 != NULL)
m->mergeWhenClauses = list_concat(list_make1($6), m->mergeWhenClauses);
$$ = (Node *)m;
} else {
$5->relation = $4;
$5->returningList = $7;
$5->withClause = $1;
$5->upsertClause = (UpsertClause *)$6;
$$ = (Node *) $5;
}
MergeStmt *m = makeNode(MergeStmt);
m->is_insert_update = true;
/* for UPSERT, keep the INSERT statement as well */
$5->relation = $4;
$5->returningList = $7;
$5->withClause = $1;
m->insert_stmt = (Node *) copyObject($5);
/* fill a MERGE statement*/
m->relation = $4;
Alias *a1 = makeAlias(($4->relname), NIL);
$4->alias = a1;
Alias *a2 = makeAlias("__unnamed_subquery_source__", NIL);
RangeSubselect *r = makeNode(RangeSubselect);
r->alias = a2;
r->subquery = (Node *) ($5->selectStmt);
m->source_relation = (Node *) r;
MergeWhenClause *n = makeNode(MergeWhenClause);
n->matched = false;
n->commandType = CMD_INSERT;
n->cols = $5->cols;
n->values = NULL;
m->mergeWhenClauses = list_make2(($6), (Node *) n);
/* for UPSERT, keep the INSERT statement as well */
$5->relation = $4;
$5->returningList = $7;
$5->withClause = $1;
m->insert_stmt = (Node *) copyObject($5);
$$ = (Node *)m;
}
;
@ -13188,16 +13188,47 @@ returning_clause:
| /* EMPTY */ { $$ = NIL; }
;
duplicate_update_clause:
ON DUPLICATE KEY UPDATE set_clause_list
{
MergeWhenClause *n = makeNode(MergeWhenClause);
n->matched = true;
n->commandType = CMD_UPDATE;
n->targetList = $5;
$$ = (Node *) n;
}
;
upsert_clause:
ON DUPLICATE KEY UPDATE set_clause_list
{
if (unlikely(u_sess->attr.attr_sql.enable_upsert_to_merge)) {
MergeWhenClause *n = makeNode(MergeWhenClause);
n->matched = true;
n->commandType = CMD_UPDATE;
n->targetList = $5;
$$ = (Node *) n;
} else {
/* check subquery in set clause*/
ListCell* cell = NULL;
ResTarget* res = NULL;
foreach (cell, $5) {
res = (ResTarget*)lfirst(cell);
if (IsA(res->val,SubLink)) {
ereport(ERROR,
(errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Update with subquery is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.")));
}
}
UpsertClause *uc = makeNode(UpsertClause);
uc->targetList = $5;
uc->location = @1;
$$ = (Node *) uc;
}
}
| ON DUPLICATE KEY UPDATE NOTHING
{
if (unlikely(u_sess->attr.attr_sql.enable_upsert_to_merge)) {
$$ = NULL;
} else {
UpsertClause *uc = makeNode(UpsertClause);
uc->targetList = NIL;
uc->location = @1;
$$ = (Node *) uc;
}
}
;
/*****************************************************************************
*
@ -13288,7 +13319,7 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
{
UpdateStmt *n = makeNode(UpdateStmt);
n->relation = $3;
n->targetList = $5;
n->targetList = $5;
n->fromClause = $6;
n->whereClause = $7;
n->returningList = $8;
@ -13313,6 +13344,30 @@ single_set_clause:
$$ = $1;
$$->val = (Node *) $3;
}
/* this is only used in ON DUPLICATE KEY UPDATE col = VALUES(col) case
* for mysql compatibility
*/
| set_target '=' VALUES '(' columnref ')'
{
ColumnRef *c = NULL;
int nfields = 0;
if (IsA($5, ColumnRef)) {
c = (ColumnRef *) $5;
} else if (IsA($5, A_Indirection)) {
c = (ColumnRef *)(((A_Indirection *)$5)->arg);
}
nfields = list_length(c->fields);
/* only allow col.*, col[...], col */
if (nfields > 1) {
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("only allow column name within VALUES"), parser_errposition(@5)));
}
c->fields = lcons((Node *)makeString("excluded"), c->fields);
$$ = $1;
$$->val = (Node *) $5;
}
;
multiple_set_clause:
@ -17755,6 +17810,7 @@ SignedIconst: Iconst { $$ = $1; }
ColId: IDENT { $$ = $1; }
| unreserved_keyword { $$ = pstrdup($1); }
| col_name_keyword { $$ = pstrdup($1); }
| EXCLUDED { $$ = pstrdup($1); }
;
/* Type/function identifier --- names that can be type or function names.
@ -18312,6 +18368,7 @@ reserved_keyword:
| ELSE
| END_P
| EXCEPT
| EXCLUDED
| FALSE_P
| FETCH
| FOR

View File

@ -459,8 +459,8 @@ static bool assign_collations_walker(Node* node, assign_collations_context* cont
case T_FromExpr:
case T_SortGroupClause:
case T_MergeAction:
case T_UpsertExpr:
(void)expression_tree_walker(node, (bool (*)())assign_collations_walker, (void*)&loccontext);
/*
* When we're invoked on a query's jointree, we don't need to do
* anything with join nodes except recurse through them to process
@ -786,4 +786,4 @@ static void assign_ordered_set_collations(Aggref* aggref, assign_collations_cont
assign_expr_collations(loccontext->pstate, (Node*)tle);
}
}
}
}

View File

@ -56,7 +56,8 @@ static void check_system_column_node(Node* node, bool is_insert_update);
static void check_system_column_reference(List* joinVarList, List* mergeActionList, bool is_insert_update);
static void checkTargetTableSystemCatalog(Relation targetRel);
static void check_insert_action_targetlist(List* merge_action_list, List* source_targetlist);
static bool check_unique_constraint(List*& index_list);
static bool check_update_action_targetlist(List* update_action_list, List* source_targetlist);
bool check_unique_constraint(List*& index_list);
static bool find_valid_unique_constraint(Relation relation, List* colnames, List*& index_list);
static bool var_in_list(Var* var, List* list);
static Bitmapset* get_relation_attno_bitmap_by_names(Relation relation, List* colnames);
@ -1180,6 +1181,16 @@ Query* transformMergeStmt(ParseState* pstate, MergeStmt* stmt)
Assert(stmt->insert_stmt != NULL);
Assert(IsA(stmt->insert_stmt, InsertStmt));
if (check_update_action_targetlist(mergeActionList, qry->mergeSourceTargetList)) {
if (u_sess->attr.attr_sql.enable_upsert_to_merge) {
return qry;
}
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("Invalid column reference in the UPDATE target values"),
errhint("Only allow to reference target table's column in the UPDATE clause")));
}
Query* insert_query = NULL;
ParseState* insert_pstate = make_parsestate(NULL);
insert_pstate->p_resolve_unknowns = pstate->p_resolve_unknowns;
@ -1768,7 +1779,7 @@ static int count_target_columns(Node* query)
* @out index_list: unique index list
* @return: true if there is any primary or unique index
*/
static bool check_unique_constraint(List*& index_list)
bool check_unique_constraint(List*& index_list)
{
/* There are no indexes */
if (index_list == NIL) {
@ -1790,10 +1801,48 @@ static bool check_unique_constraint(List*& index_list)
return true;
}
bool check_action_targetlist_condition(List* action_targetlist, List* source_targetlist, bool condition_in)
{
ListCell* var_cell = NULL;
/* oops, not insert action found, noting to do */
if (action_targetlist == NIL) {
return true;
}
/* let's do the hard work */
List* vars_list =
pull_var_clause((Node*)action_targetlist, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS);
List* source_vars_list =
pull_var_clause((Node*)source_targetlist, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS);
foreach (var_cell, vars_list) {
Var* var = (Var*)lfirst(var_cell);
if (var_in_list(var, source_vars_list) != condition_in) {
return false;
}
}
return true;
}
bool check_update_action_targetlist(List* merge_action_list, List* source_targetlist)
{
ListCell* action_cell = NULL;
List* update_action_targetlist = NIL;
/* first let's locate the insert action */
foreach (action_cell, merge_action_list) {
MergeAction* action = (MergeAction*)lfirst(action_cell);
if (action->commandType == CMD_UPDATE) {
update_action_targetlist = action->targetList;
break;
}
}
return !check_action_targetlist_condition(update_action_targetlist, source_targetlist, false);
}
/* report error if vars in insert_action_targetlist not found in source_targetlist */
void check_insert_action_targetlist(List* merge_action_list, List* source_targetlist)
{
ListCell* var_cell = NULL;
ListCell* action_cell = NULL;
List* insert_action_targetlist = NIL;
@ -1806,25 +1855,10 @@ void check_insert_action_targetlist(List* merge_action_list, List* source_target
break;
}
}
/* oops, not insert action found, noting to do */
if (insert_action_targetlist == NIL) {
return;
}
/* let's do the hard work */
List* insert_vars_list =
pull_var_clause((Node*)insert_action_targetlist, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS);
List* source_vars_list =
pull_var_clause((Node*)source_targetlist, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS);
foreach (var_cell, insert_vars_list) {
Var* var = (Var*)lfirst(var_cell);
/* INSERT can only reference source rel's targetlist */
if (!var_in_list(var, source_vars_list)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("Invalid column reference in the INSERT VALUES Clause"),
errhint("You may have referenced target table's column")));
}
if (!check_action_targetlist_condition(insert_action_targetlist, source_targetlist, true)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("Invalid column reference in the INSERT VALUES Clause"),
errhint("You may have referenced target table's column")));
}
}

View File

@ -479,7 +479,7 @@ CommonTableExpr* GetCTEForRTE(ParseState* pstate, RangeTblEntry* rte, int rtelev
* Side effect: if we find a match, mark the RTE as requiring read access
* for the column.
*/
Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, int location)
Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, int location, bool omit_excluded)
{
Node* result = NULL;
int attnum = 0;
@ -501,6 +501,9 @@ Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, in
*/
foreach (c, rte->eref->colnames) {
attnum++;
if (omit_excluded && rte->isexcluded) {
continue;
}
if (strcmp(strVal(lfirst(c)), colname) == 0) {
if (result != NULL) {
ereport(ERROR,
@ -607,7 +610,7 @@ Node* colNameToVar(ParseState* pstate, char* colname, bool localonly, int locati
Node* newresult = NULL;
/* use orig_pstate here to get the right sublevels_up */
newresult = scanRTEForColumn(orig_pstate, rte, colname, location);
newresult = scanRTEForColumn(orig_pstate, rte, colname, location, true);
if (newresult != NULL) {
if (final_rte != NULL) {
*final_rte = rte;
@ -1087,6 +1090,7 @@ RangeTblEntry* addRangeTableEntry(ParseState* pstate, RangeVar* relation, Alias*
rte->rtekind = RTE_RELATION;
rte->alias = alias;
rte->isexcluded = false;
if (pstate == NULL) {
ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("pstate can not be NULL")));
@ -1205,6 +1209,7 @@ RangeTblEntry* addRangeTableEntryForRelation(ParseState* pstate, Relation rel, A
rte->relkind = rel->rd_rel->relkind;
rte->ispartrel = RELATION_IS_PARTITIONED(rel);
rte->relhasbucket = RELATION_HAS_BUCKET(rel);
rte->isexcluded = false;
/*
* In cases that target relation's rd_refSynOid is valid, it has been referenced from one synonym.
* thus, alias name is used to take its raw relname away in order to form the refname.

View File

@ -335,12 +335,12 @@ static void markTargetListOrigin(ParseState* pstate, TargetEntry* tle, Var* var,
/*
* transformAssignedExpr()
* This is used in INSERT and UPDATE statements only. It prepares an
* expression for assignment to a column of the target table.
* This includes coercing the given value to the target column's type
* (if necessary), and dealing with any subfield names or subscripts
* attached to the target column itself. The input expression has
* already been through transformExpr().
* This is used in INSERT and UPDATE (including DUPLICATE KEY UPDATE)
* statements only. It prepares an expression for assignment to a column
* of the target table. This includes coercing the given value
* to the target column's type (if necessary), and dealing with
* any subfield names or subscripts attached to the target column itself
* The input expression has already been through transformExpr().
*
* pstate parse state
* expr expression to be modified
@ -496,11 +496,11 @@ Expr* transformAssignedExpr(ParseState* pstate, Expr* expr, char* colname, int a
/*
* updateTargetListEntry()
* This is used in UPDATE statements only. It prepares an UPDATE
* TargetEntry for assignment to a column of the target table.
* This includes coercing the given value to the target column's type
* (if necessary), and dealing with any subfield names or subscripts
* attached to the target column itself.
* This is used in UPDATE (and DUPLICATE KEY UPDATE) statements only.
* It prepares an UPDATE TargetEntry for assignment to a column of
* the target table. This includes coercing the given value to the
* target column's type (if necessary), and dealing with any subfield
* names or subscripts attached to the target column itself.
*
* pstate parse state
* tle target list entry to be modified

View File

@ -222,6 +222,8 @@ static void get_with_clause(Query* query, deparse_context* context);
static void get_select_query_def(Query* query, deparse_context* context, TupleDesc resultDesc);
static void get_insert_query_def(Query* query, deparse_context* context);
static void get_update_query_def(Query* query, deparse_context* context);
static void get_update_query_targetlist_def(
Query* query, List* targetList, RangeTblEntry* rte, deparse_context* context);
static void get_delete_query_def(Query* query, deparse_context* context);
static void get_utility_query_def(Query* query, deparse_context* context);
static void get_basic_select_query(Query* query, deparse_context* context, TupleDesc resultDesc);
@ -3738,6 +3740,10 @@ static void set_deparse_planstate(deparse_namespace* dpns, PlanState* ps)
* For a SubqueryScan, pretend the subplan is INNER referent. (We don't
* use OUTER because that could someday conflict with the normal meaning.)
* Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
* For DUPLICATE KEY UPDATE we just need the inner tlist to point to the
* excluded expression's tlist. (Similar to the SubqueryScan we don't want
* to reuse OUTER, it's used for RETURNING in some modify table cases,
* although not INSERT ... ON DUPLICATE KEY UPDATE).
*/
if (IsA(ps, SubqueryScanState))
dpns->inner_planstate = ((SubqueryScanState*)ps)->subplan;
@ -3749,6 +3755,7 @@ static void set_deparse_planstate(deparse_namespace* dpns, PlanState* ps)
ModifyTableState* mps = (ModifyTableState*)ps;
dpns->outer_planstate = mps->mt_plans[0];
dpns->inner_planstate = ps;
/*
* For merge into, we should deparse the inner plan, since the targetlist and qual will
* reference sourceTargetList, which comes from outer plan of the join (source table)
@ -3762,6 +3769,11 @@ static void set_deparse_planstate(deparse_namespace* dpns, PlanState* ps)
} else
dpns->inner_planstate = innerPlanState(ps);
#ifdef ENABLE_MULTIPLE_NODEX
if (IsA(ps, ModifyTableState))
dpns->inner_tlist = ((ModifyTableState*)ps)->mt_upsert->us_excludedtlist;
else
#endif
if (dpns->inner_planstate != NULL)
dpns->inner_tlist = dpns->inner_planstate->plan->targetlist;
else
@ -5564,18 +5576,9 @@ static void get_insert_query_def(Query* query, deparse_context* context)
}
/*
* select_rte and values_rte are not required by INSERT queries in XC
* Both these should stay null for INSERT queries to work corretly
* Consider an example
* create table tt as values(1,'One'),(2,'Two');
* This query uses values_rte, but we do not need them in XC
* because it gets broken down into two queries
* CREATE TABLE tt(column1 int4, column2 text)
* and
* INSERT INTO tt (column1, column2) VALUES ($1, $2)
* Note that the insert query does not need values_rte
*
* Now consider another example
* select_rte are not required by INSERT queries in XC
* it should stay null for INSERT queries to work corretly
* consider an example
* insert into tt select * from tt
* This query uses select_rte, but again that is not required in XC
* Again here the query gets broken down into two queries
@ -5600,7 +5603,35 @@ static void get_insert_query_def(Query* query, deparse_context* context)
}
select_rte = rte;
}
}
#ifdef PGXC
}
#endif
/*
* values_rte will be required by INSERT queries in XC
* only when the relation is located on a single node
* requested by FQS
* Consider an example
* CREATE NODE GROUP ng WITH (datanode2);
* CREATE TABLE tt (column1 int4, column2 text) TO GROUP ng;
* INSERT INTO tt (column1) VALUES(1), (2)
*
* for other cases values_rte should stay null
* create table tt as values(1,'One'),(2,'Two');
* This query uses values_rte, but we do not need them in XC
* because it gets broken down into two queries
* CREATE TABLE tt(column1 int4, column2 text)
* and
* INSERT INTO tt (column1, column2) VALUES ($1, $2)
* Note that the insert query does not need values_rte
*/
#ifdef PGXC
if (context->is_fqs || !(IS_PGXC_COORDINATOR && !IsConnFromCoord())) {
#endif
foreach (l, query->rtable) {
rte = (RangeTblEntry*)lfirst(l);
if (rte->rtekind == RTE_VALUES) {
if (values_rte != NULL) {
ereport(ERROR, (errcode(ERRCODE_RESTRICT_VIOLATION), errmsg("too many values RTEs in INSERT")));
@ -5611,6 +5642,7 @@ static void get_insert_query_def(Query* query, deparse_context* context)
#ifdef PGXC
}
#endif
if ((select_rte != NULL) && (values_rte != NULL)) {
ereport(ERROR, (errcode(ERRCODE_RESTRICT_VIOLATION), errmsg("both subquery and values RTEs in INSERT")));
}
@ -5758,21 +5790,37 @@ static void get_insert_query_def(Query* query, deparse_context* context)
appendStringInfo(buf, "DEFAULT VALUES");
}
/* it is an UPSERT statement, add ON DUPLICATE KEY UPDATE expression */
/* for MERGE INTO statement for UPSERT, add ON DUPLICATE KEY UPDATE expression */
if (query->mergeActionList) {
appendStringInfo(buf, " ON DUPLICATE KEY UPDATE ");
ListCell* l = NULL;
bool update = false;
foreach (l, query->mergeActionList) {
MergeAction* mc = (MergeAction*)lfirst(l);
/* only deparse the update clause */
if (mc->commandType == CMD_UPDATE) {
update = true;
get_set_target_list(mc->targetList, rte, context);
} else {
continue;
}
}
if (!update) {
appendStringInfoString(buf, "NOTHING");
}
}
/* for INSERT statement for UPSERT, add ON DUPLICATE KEY UPDATE expression */
if (query->upsertClause != NULL && query->upsertClause->upsertAction != UPSERT_NONE) {
appendStringInfoString(buf, " ON DUPLICATE KEY UPDATE ");
UpsertExpr* upsertClause = query->upsertClause;
if (upsertClause->upsertAction == UPSERT_NOTHING) {
appendStringInfoString(buf, "NOTHING");
} else {
get_update_query_targetlist_def(query, upsertClause->updateTlist, rte, context);
}
}
/* Add RETURNING if present */
@ -5789,9 +5837,7 @@ static void get_insert_query_def(Query* query, deparse_context* context)
static void get_update_query_def(Query* query, deparse_context* context)
{
StringInfo buf = context->buf;
char* sep = NULL;
RangeTblEntry* rte = NULL;
ListCell* l = NULL;
/* Insert the WITH clause if given */
get_with_clause(query, context);
@ -5810,10 +5856,38 @@ static void get_update_query_def(Query* query, deparse_context* context)
appendStringInfo(buf, " %s", quote_identifier(rte->alias->aliasname));
}
appendStringInfoString(buf, " SET ");
/* Deparse targetlist */
get_update_query_targetlist_def(query, query->targetList, rte, context);
/* Add the FROM clause if needed */
get_from_clause(query, " FROM ", context);
/* Add a WHERE clause if given */
if (query->jointree->quals != NULL) {
append_context_keyword(context, " WHERE ", PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false);
}
/* Add RETURNING if present */
if (query->returningList) {
append_context_keyword(context, " RETURNING", PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_target_list(query, query->returningList, context, NULL);
}
}
/* ----------
* get_update_query_targetlist_def - Parse back an UPDATE targetlist
* ----------
*/
static void get_update_query_targetlist_def(Query* query, List* targetList,
RangeTblEntry* rte, deparse_context* context)
{
StringInfo buf = context->buf;
ListCell* l;
const char* sep;
/* Add the comma separated list of 'attname = value' */
sep = "";
foreach (l, query->targetList) {
foreach (l, targetList) {
TargetEntry* tle = (TargetEntry*)lfirst(l);
Node* expr = NULL;
@ -5890,21 +5964,6 @@ static void get_update_query_def(Query* query, deparse_context* context)
get_rule_expr((Node*)tle->expr, context, false);
}
}
/* Add the FROM clause if needed */
get_from_clause(query, " FROM ", context);
/* Add a WHERE clause if given */
if (query->jointree->quals != NULL) {
append_context_keyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false);
}
/* Add RETURNING if present */
if (query->returningList) {
append_context_keyword(context, " RETURNING", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_target_list(query, query->returningList, context, NULL);
}
}
/* ----------

View File

@ -1389,6 +1389,20 @@ static void init_configure_names_bool()
NULL,
NULL
},
{
{
"enable_upsert_to_merge",
PGC_USERSET,
QUERY_TUNING_METHOD,
gettext_noop("Enable transform INSERT ON DUPLICATE KEY UDPATE statement to MERGE statement."),
NULL
},
&u_sess->attr.attr_sql.enable_upsert_to_merge,
false,
NULL,
NULL,
NULL
},
#ifdef ENABLE_MULTIPLE_NODES
{
{

View File

@ -520,6 +520,14 @@ bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
if (!HeapTupleHeaderXminCommitted(tuple)) {
if (HeapTupleHeaderXminInvalid(tuple))
return false;
/*
* An invalid Xmin can be left behind by a speculative insertion that
* is cancelled by super-deleting the tuple. We shouldn't see any of
* those in TOAST tables, but better safe than sorry.
*/
if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(BufferGetPage(buffer), tuple)))
return false;
}
/* otherwise assume the tuple is valid for TOAST. */
@ -552,8 +560,11 @@ bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
* the case where the tuple is share-locked by a MultiXact, even if the
* MultiXact includes the current transaction. Callers that want to
* distinguish that case must test for it themselves.)
*
* HeapTupleSelfCreated: the tuple didn't exist at all when the scan started, it
* was created during the current CommandId (scan)
*/
HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer, bool self_visible)
{
bool needSync = false;
HeapTupleHeader tuple = htup->t_data;
@ -580,8 +591,10 @@ restart:
return HeapTupleInvisible;
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(page, tuple))) {
if (HeapTupleHeaderGetCmin(tuple, page) >= curcid)
if (HeapTupleHeaderGetCmin(tuple, page) > curcid)
return HeapTupleInvisible; /* inserted after scan started */
else if (HeapTupleHeaderGetCmin(tuple, page) == curcid && !self_visible)
return HeapTupleSelfCreated; /* inserted during the scan */
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HeapTupleMayBeUpdated;

View File

@ -3490,7 +3490,7 @@ static uint64 CopyFrom(CopyState cstate)
1, /* dummy rangetable index */
0);
ExecOpenIndices(resultRelInfo);
ExecOpenIndices(resultRelInfo, false);
init_gtt_storage(CMD_INSERT, resultRelInfo);
resultRelationDesc = resultRelInfo->ri_RelationDesc;
@ -4066,7 +4066,7 @@ static uint64 CopyFrom(CopyState cstate)
estate,
isPartitionRel ? heaprel : NULL,
isPartitionRel ? partition : NULL,
bucketid);
bucketid, NULL);
/* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, partitionid, bucketid, tuple, recheckIndexes);
@ -4413,7 +4413,7 @@ static void CopyFromUpdateIndexAndRunAfterRowTrigger(EState* estate, ResultRelIn
estate,
ispartitionedtable ? actualHeap : NULL,
ispartitionedtable ? partition : NULL,
bucketid);
bucketid, NULL);
ExecARInsertTriggers(estate, resultRelInfo, partitionOid, bucketid, bufferedTuples[i], recheckIndexes);
list_free(recheckIndexes);
}
@ -4484,7 +4484,7 @@ static void CopyFromInsertBatch(Relation rel, EState* estate, CommandId mycid, i
estate,
ispartitionedtable ? actualHeap : NULL,
ispartitionedtable ? partition : NULL,
bucketId);
bucketId, NULL);
ExecARInsertTriggers(estate, resultRelInfo, partitionOid, bucketId, bufferedTuples[i], recheckIndexes);
list_free(recheckIndexes);
}

View File

@ -187,6 +187,7 @@ static void ExplainProperty(const char* qlabel, const char* value, bool numeric,
static void ExplainOpenGroup(const char* objtype, const char* labelname, bool labeled, ExplainState* es);
static void ExplainCloseGroup(const char* objtype, const char* labelname, bool labeled, ExplainState* es);
static void ExplainDummyGroup(const char* objtype, const char* labelname, ExplainState* es);
static void show_on_duplicate_info(ModifyTableState* mtstate, ExplainState* es);
#ifdef PGXC
static void ExplainExecNodes(const ExecNodes* en, ExplainState* es);
static void ExplainRemoteQuery(RemoteQuery* plan, PlanState* planstate, List* ancestors, ExplainState* es);
@ -2326,6 +2327,12 @@ static void ExplainNode(
}
}
} else {
/* upsert cases */
ModifyTableState* mtstate = (ModifyTableState*)planstate;
if (mtstate->mt_upsert != NULL &&
mtstate->mt_upsert->us_action != UPSERT_NONE && mtstate->resultRelInfo->ri_NumIndices > 0) {
show_on_duplicate_info(mtstate, es);
}
/* non-merge cases */
foreach (elt, mt->remote_plans) {
if (lfirst(elt)) {
@ -7912,6 +7919,39 @@ static void ExplainTargetRel(Plan* plan, Index rti, ExplainState* es)
}
}
/*
* Show extra information for upsert info
*/
static void show_on_duplicate_info(ModifyTableState* mtstate, ExplainState* es)
{
ResultRelInfo* resultRelInfo = mtstate->resultRelInfo;
IndexInfo* indexInfo = NULL;
List* idxNames = NIL;
/* Gather names of ON CONFLICT Arbiter indexes */
for (int i = 0; i < resultRelInfo->ri_NumIndices; ++i) {
indexInfo = resultRelInfo->ri_IndexRelationInfo[i];
if (!indexInfo->ii_Unique && !indexInfo->ii_ExclusionOps) {
continue;
}
Relation indexRelation = resultRelInfo->ri_IndexRelationDescs[i];
char* indexName = RelationGetRelationName(indexRelation);
idxNames = lappend(idxNames, indexName);
}
ExplainPropertyText("Conflict Resolution",
mtstate->mt_upsert->us_action == UPSERT_NOTHING ? "NOTHING" : "UPDATE",
es);
/*
* Don't display arbiter indexes at all when DO NOTHING variant
* implicitly ignores all conflicts
*/
if (idxNames != NIL) {
ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
}
}
#ifndef PGXC
/*
* Show extra information for a ModifyTable node

View File

@ -18804,7 +18804,7 @@ static void checkValidationForExchangeCStore(Relation partTableRel, Relation ord
// init cstore partition insert
resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, partTableRel, 1, 0);
ExecOpenIndices(resultRelInfo);
ExecOpenIndices(resultRelInfo, false);
resultRelInfo->ri_junkFilter = makeNode(JunkFilter);
resultRelInfo->ri_junkFilter->jf_junkAttNo = tididx;
resultRelInfo->ri_junkFilter->jf_xc_part_id = tableoidIdx;

View File

@ -2616,6 +2616,11 @@ static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRe
LockTupleExclusive,
false);
switch (test) {
case HeapTupleSelfCreated:
ReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("attempted to lock invisible tuple")));
break;
case HeapTupleSelfUpdated:
/* treat it as deleted; do not process */
ReleaseBuffer(buffer);

View File

@ -8298,11 +8298,11 @@ static void deparallelize_modifytable(List* subplans)
#ifdef STREAMPLAN
Plan* make_modifytable(PlannerInfo* root, CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList, bool isDfsStore)
List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause, bool isDfsStore)
#else
ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList, bool isDfsStore)
List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause, bool isDfsStore)
#endif
{
ModifyTable* node = makeNode(ModifyTable);
@ -8377,6 +8377,16 @@ ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRel
node->mergeTargetRelation = mergeTargetRelation;
node->mergeSourceTargetList = mergeSourceTargetList;
node->mergeActionList = mergeActionList;
if (upsertClause != NULL) {
node->upsertAction = upsertClause->upsertAction;
node->updateTlist = upsertClause->updateTlist;
node->exclRelTlist = upsertClause->exclRelTlist;
node->exclRelRTIndex = upsertClause->exclRelIndex;
} else {
node->upsertAction = UPSERT_NONE;
node->updateTlist = NIL;
node->exclRelTlist = NIL;
}
#ifdef STREAMPLAN
node->plan.exec_nodes = exec_nodes;
@ -8507,11 +8517,11 @@ ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRel
*/
Plan* make_modifytables(PlannerInfo* root, CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList)
List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause)
#else
ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList)
List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause)
#endif
{
if (isDfsStore) {
@ -8542,6 +8552,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
0,
NULL,
NULL,
upsertClause,
isDfsStore);
/*
* We must adjust the plan tree. Because the make_modifytable function would make a
@ -8593,6 +8604,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
0,
NULL,
NULL,
upsertClause,
isDfsStore);
appendSubPlans = lappend(appendSubPlans, (void*)mtplan);
#endif
@ -8627,6 +8639,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
mergeTargetRelation,
mergeSourceTargetList,
mergeActionList,
upsertClause,
isDfsStore);
if (IS_STREAM_PLAN)
return mt_stream_plan;
@ -8644,6 +8657,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
mergeTargetRelation,
mergeSourceTargetList,
mergeActionList,
upsertClause
isDfsStore);
return pgxc_make_modifytable(root, (Plan*)mtplan);
#endif
@ -8659,6 +8673,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
mergeTargetRelation,
mergeSourceTargetList,
mergeActionList,
upsertClause,
isDfsStore);
#endif
}
@ -8952,7 +8967,7 @@ List* process_agg_targetlist(PlannerInfo* root, List** local_tlist)
* We are about to change the local_tlist, check if we have already
* copied original local_tlist, if not take a copy
*/
if ((orig_local_tlist == NULL) && (IsA(expr, Aggref) || context.aggs))
if ((orig_local_tlist == NIL) && (IsA(expr, Aggref) || context.aggs))
orig_local_tlist = (List*)copyObject(*local_tlist);
/*

View File

@ -1332,6 +1332,10 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
parse->mergeSourceTargetList =
(List*)preprocess_expression(root, (Node*)parse->mergeSourceTargetList, EXPRKIND_TARGET);
if (parse->upsertClause) {
parse->upsertClause->updateTlist = (List*)
preprocess_expression(root, (Node*)parse->upsertClause->updateTlist, EXPRKIND_TARGET);
}
root->append_rel_list = (List*)preprocess_expression(root, (Node*)root->append_rel_list, EXPRKIND_APPINFO);
/* Also need to preprocess expressions for function and values RTEs */
@ -1521,6 +1525,7 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
parse->mergeTarget_relation,
parse->mergeSourceTargetList,
parse->mergeActionList,
parse->upsertClause,
isDfsStore);
#else
plan = (Plan*)make_modifytable(parse->commandType,
@ -1534,6 +1539,7 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
parse->mergeTarget_relation,
parse->mergeSourceTargetList,
parse->mergeActionList,
parse->upsertClause,
isDfsStore);
#endif
#ifdef PGXC
@ -2014,6 +2020,7 @@ static Plan* inheritance_planner(PlannerInfo* root)
isDfsStore,
0,
NULL,
NULL,
NULL);
#else
return make_modifytables(parse->commandType,
@ -2027,6 +2034,7 @@ static Plan* inheritance_planner(PlannerInfo* root)
isDfsStore,
0,
NULL,
NULL,
NULL);
#endif
}
@ -2445,6 +2453,11 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
/* Preprocess targetlist */
tlist = preprocess_targetlist(root, tlist);
if (parse->upsertClause) {
UpsertExpr* upsertClause = parse->upsertClause;
upsertClause->updateTlist =
preprocess_upsert_targetlist(upsertClause->updateTlist, parse->resultRelation, parse->rtable);
}
/*
* Locate any window functions in the tlist. (We don't need to look
* anywhere else, since expressions used in ORDER BY will be in there

View File

@ -830,6 +830,15 @@ static Plan* set_plan_refs(PlannerInfo* root, Plan* plan, int rtoffset)
}
}
if (splan->updateTlist != NIL) {
indexed_tlist* itlist;
itlist = build_tlist_index(splan->exclRelTlist);
splan->updateTlist = fix_join_expr(root, splan->updateTlist, NULL,
itlist, linitial_int(splan->resultRelations), rtoffset);
}
splan->exclRelRTIndex += rtoffset;
foreach (l, splan->resultRelations) {
lfirst_int(l) += rtoffset;
}

View File

@ -2578,6 +2578,7 @@ static Bitmapset* finalize_plan(PlannerInfo* root, Plan* plan, Bitmapset* valid_
valid_params = bms_add_member(bms_copy(valid_params), locally_added_param);
scan_params = bms_add_member(bms_copy(scan_params), locally_added_param);
(void)finalize_primnode((Node*)mtplan->returningLists, &context);
(void)finalize_primnode((Node*)mtplan->updateTlist, &context);
finalize_plans(root, &context, mtplan->plans, valid_params, scan_params);
} break;
#ifdef PGXC

View File

@ -1056,6 +1056,10 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
*/
parse->targetList = (List*)pullup_replace_vars((Node*)parse->targetList, &rvcontext);
parse->returningList = (List*)pullup_replace_vars((Node*)parse->returningList, &rvcontext);
if (parse->upsertClause != NULL) {
parse->upsertClause->updateTlist = (List*)
pullup_replace_vars((Node*)parse->upsertClause->updateTlist, &rvcontext);
}
replace_vars_in_jointree((Node*)parse->jointree, &rvcontext, lowest_outer_join);

View File

@ -555,5 +555,10 @@ static List* add_distribute_column(List* tlist, Index result_relation, List* ran
return tlist;
}
List* preprocess_upsert_targetlist(List* tlist, int result_relation, List* range_table)
{
return expand_targetlist(tlist, CMD_UPDATE, result_relation, range_table);
}
#endif

View File

@ -57,7 +57,8 @@ static bool acquireLocksOnSubLinks(Node* node, void* context);
static Query* rewriteRuleAction(
Query* parsetree, Query* rule_action, Node* rule_qual, int rt_index, CmdType event, bool* returning_flag);
static List* adjustJoinTreeList(Query* parsetree, bool removert, int rt_index);
static void rewriteTargetListIU(Query* parsetree, Relation target_relation, List** attrno_list);
static List* rewriteTargetListIU(List* targetList, CmdType commandType,
Relation target_relation, int result_rtindex, List** attrno_list);
static TargetEntry* process_matched_tle(TargetEntry* src_tle, TargetEntry* prior_tle, const char* attrName);
static Node* get_assignment_input(Node* node);
static void rewriteValuesRTE(RangeTblEntry* rte, Relation target_relation, List* attrnos);
@ -613,9 +614,9 @@ static List* adjustJoinTreeList(Query* parsetree, bool removert, int rt_index)
* order of the original tlist's non-junk entries. This is needed for
* processing VALUES RTEs.
*/
static void rewriteTargetListIU(Query* parsetree, Relation target_relation, List** attrno_list)
static List* rewriteTargetListIU(List* targetList, CmdType commandType, Relation target_relation,
int result_rtindex, List** attrno_list)
{
CmdType commandType = parsetree->commandType;
TargetEntry** new_tles;
List* new_tlist = NIL;
List* junk_tlist = NIL;
@ -639,7 +640,7 @@ static void rewriteTargetListIU(Query* parsetree, Relation target_relation, List
new_tles = (TargetEntry**)palloc0(numattrs * sizeof(TargetEntry*));
next_junk_attrno = numattrs + 1;
foreach (temp, parsetree->targetList) {
foreach (temp, targetList) {
TargetEntry* old_tle = (TargetEntry*)lfirst(temp);
if (!old_tle->resjunk) {
@ -737,8 +738,7 @@ static void rewriteTargetListIU(Query* parsetree, Relation target_relation, List
Node* new_expr = NULL;
new_expr = (Node*)makeVar(
(unsigned int)(parsetree->resultRelation), attrno, att_tup->atttypid, att_tup->atttypmod,
att_tup->attcollation, 0);
result_rtindex, attrno, att_tup->atttypid, att_tup->atttypmod, att_tup->attcollation, 0);
new_tle = makeTargetEntry((Expr*)new_expr, (int16)attrno, pstrdup(NameStr(att_tup->attname)), false);
}
@ -748,8 +748,8 @@ static void rewriteTargetListIU(Query* parsetree, Relation target_relation, List
}
pfree_ext(new_tles);
parsetree->targetList = list_concat(new_tlist, junk_tlist);
targetList = list_concat(new_tlist, junk_tlist);
return targetList;
}
/*
@ -2303,15 +2303,28 @@ static List* RewriteQuery(Query* parsetree, List* rewrite_events)
List* attrnos = NIL;
/* Process the main targetlist ... */
rewriteTargetListIU(parsetree, rt_entry_relation, &attrnos);
parsetree->targetList =
rewriteTargetListIU(parsetree->targetList, parsetree->commandType,
rt_entry_relation, parsetree->resultRelation, &attrnos);
/* ... and the VALUES expression lists */
rewriteValuesRTE(values_rte, rt_entry_relation, attrnos);
} else {
/* Process just the main targetlist */
rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
parsetree->targetList =
rewriteTargetListIU(parsetree->targetList, parsetree->commandType,
rt_entry_relation, parsetree->resultRelation, NULL);
}
if (parsetree->upsertClause != NULL &&
parsetree->upsertClause->upsertAction == UPSERT_UPDATE) {
parsetree->upsertClause->updateTlist =
rewriteTargetListIU(parsetree->upsertClause->updateTlist, CMD_UPDATE,
rt_entry_relation, parsetree->resultRelation, NULL);
}
} else if (event == CMD_UPDATE) {
rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
parsetree->targetList =
rewriteTargetListIU(parsetree->targetList, parsetree->commandType,
rt_entry_relation, parsetree->resultRelation, NULL);
rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation);
} else if (event == CMD_MERGE) {
/*

View File

@ -2762,6 +2762,11 @@ HeapTuple EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, Ite
ReleaseBuffer(buffer);
switch (test) {
case HeapTupleSelfCreated:
ReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("attempted to lock invisible tuple")));
break;
case HeapTupleSelfUpdated:
/* treat it as deleted; do not process */
ReleaseBuffer(buffer);

View File

@ -64,6 +64,8 @@ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo);
static bool index_recheck_constraint(
Relation index, Oid* constr_procs, Datum* existing_values, const bool* existing_isnull, Datum* new_values);
static void ShutdownExprContext(ExprContext* econtext, bool isCommit);
static bool check_violation(Relation heap, Relation index, IndexInfo *indexInfo, ItemPointer tupleid, Datum *values,
const bool *isnull, EState *estate, bool newIndex, bool errorOK, CheckWaitMode waitMode, ItemPointer conflictTid);
/* ----------------------------------------------------------------
* Executor state and memory management functions
@ -1100,7 +1102,7 @@ Partition ExecOpenScanParitition(EState* estate, Relation parent, PartitionIdent
* resultRelInfo->ri_RelationDesc.
* ----------------------------------------------------------------
*/
void ExecOpenIndices(ResultRelInfo* resultRelInfo)
void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
{
Relation resultRelation = resultRelInfo->ri_RelationDesc;
List* indexoidlist = NIL;
@ -1156,6 +1158,13 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo)
/* extract index key information from the index's pg_index info */
ii = BuildIndexInfo(indexDesc);
/*
* If the indexes are to be used for speculative insertion, add extra
* information required by unique index entries.
*/
if (speculative && ii->ii_Unique) {
BuildSpeculativeIndexInfo(indexDesc, ii);
}
relationDescs[i] = indexDesc;
indexInfoArray[i] = ii;
i++;
@ -1195,6 +1204,164 @@ void ExecCloseIndices(ResultRelInfo* resultRelInfo)
*/
}
/* ----------------------------------------------------------------
* ExecCheckIndexConstraints
*
* This routine checks if a tuple violates any unique or
* exclusion constraints. Returns true if there is no no conflict.
* 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. But this can be used for a pre-check
* before insertion.
* ----------------------------------------------------------------
*/
bool ExecCheckIndexConstraints(TupleTableSlot* slot, EState* estate,
Relation targetRel, Partition p, int2 bucketId, ItemPointer conflictTid)
{
ResultRelInfo* resultRelInfo = NULL;
RelationPtr relationDescs = NULL;
int i = 0;
int numIndices = 0;
IndexInfo** indexInfoArray = NULL;
Relation heapRelationDesc = NULL;
Relation actualHeap = NULL;
ExprContext* econtext = NULL;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
ItemPointerData invalidItemPtr;
bool isPartitioned = false;
List* partitionIndexOidList = NIL;
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;
heapRelationDesc = resultRelInfo->ri_RelationDesc;
actualHeap = targetRel;
if (RELATION_IS_PARTITIONED(heapRelationDesc)) {
Assert(p != NULL && p->pd_part != NULL);
isPartitioned = true;
if (!p->pd_part->indisusable) {
return false;
}
}
/*
* 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 index tuple and check if it satisfies the
* constraint.
*/
for (i = 0; i < numIndices; i++) {
Relation indexRelation = relationDescs[i];
IndexInfo* indexInfo;
bool satisfiesConstraint;
Relation actualIndex = NULL;
Oid partitionedindexid = InvalidOid;
Oid indexpartitionid = InvalidOid;
Partition indexpartition = NULL;
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;
if (!indexRelation->rd_index->indimmediate)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("INSERT ON DUPLICATE KEY UPDATE does not support deferrable"
" unique constraints/exclusion constraints.")));
if (isPartitioned) {
partitionedindexid = RelationGetRelid(indexRelation);
if (!PointerIsValid(partitionIndexOidList)) {
partitionIndexOidList = PartitionGetPartIndexList(p);
if (!PointerIsValid(partitionIndexOidList)) {
// no local indexes available
return false;
}
}
indexpartitionid = searchPartitionIndexOid(partitionedindexid, partitionIndexOidList);
searchFakeReationForPartitionOid(estate->esfRelations,
estate->es_query_cxt,
indexRelation,
indexpartitionid,
actualIndex,
indexpartition,
RowExclusiveLock);
/* skip unusable index */
if (indexpartition->pd_part->indisusable == false) {
continue;
}
} else {
actualIndex = indexRelation;
}
if (bucketId != InvalidBktId) {
searchHBucketFakeRelation(estate->esfRelations, estate->es_query_cxt, actualIndex, bucketId, actualIndex);
}
/* 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_violation(actualHeap, actualIndex, indexInfo, &invalidItemPtr, values, isnull,
estate, false, true, CHECK_WAIT, conflictTid);
if (!satisfiesConstraint) {
return false;
}
}
return true;
}
/* ----------------------------------------------------------------
* ExecInsertIndexTuples
*
@ -1215,8 +1382,8 @@ void ExecCloseIndices(ResultRelInfo* resultRelInfo)
* Should we change the API to make it safer?
* ----------------------------------------------------------------
*/
List* ExecInsertIndexTuples(
TupleTableSlot* slot, ItemPointer tupleid, EState* estate, Relation targetPartRel, Partition p, int2 bucketId)
List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* estate,
Relation targetPartRel, Partition p, int2 bucketId, bool* conflict)
{
List* result = NIL;
ResultRelInfo* resultRelInfo = NULL;
@ -1362,6 +1529,8 @@ List* ExecInsertIndexTuples(
*/
if (!indexRelation->rd_index->indisunique) {
checkUnique = UNIQUE_CHECK_NO;
} else if (conflict != NULL) {
checkUnique = UNIQUE_CHECK_PARTIAL;
} else if (indexRelation->rd_index->indimmediate) {
checkUnique = UNIQUE_CHECK_YES;
} else {
@ -1397,9 +1566,13 @@ List* ExecInsertIndexTuples(
/*
* The tuple potentially violates the uniqueness or exclusion
* constraint, so make a note of the index so that we can re-check
* it later.
* it later. Speculative inserters are told if there was a
* speculative conflict, since that always requires a restart.
*/
result = lappend_oid(result, RelationGetRelid(indexRelation));
if (conflict != NULL) {
*conflict = true;
}
}
}
@ -1433,6 +1606,13 @@ List* ExecInsertIndexTuples(
*/
bool check_exclusion_constraint(Relation heap, Relation index, IndexInfo* indexInfo, ItemPointer tupleid, Datum* values,
const bool* isnull, EState* estate, bool newIndex, bool errorOK)
{
return check_violation(heap, index, indexInfo, tupleid, values, isnull,
estate, newIndex, errorOK, errorOK ? CHECK_NOWAIT : CHECK_WAIT, NULL);
}
bool check_violation(Relation heap, Relation index, IndexInfo* indexInfo, ItemPointer tupleid, Datum* values,
const bool* isnull, EState* estate, bool newIndex, bool errorOK, CheckWaitMode waitMode, ItemPointer conflictTid)
{
Oid* constr_procs = indexInfo->ii_ExclusionProcs;
uint16* constr_strats = indexInfo->ii_ExclusionStrats;
@ -1459,6 +1639,13 @@ bool check_exclusion_constraint(Relation heap, Relation index, IndexInfo* indexI
}
}
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;
}
/*
* Search the tuples that are in the index for any violations, including
* tuples that aren't visible yet.
@ -1503,7 +1690,7 @@ 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 */
ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED),
@ -1527,33 +1714,44 @@ 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;
}
/*
* At this point we have either a conflict or a potential conflict.
* 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 ---
* this case probably doesn't happen often enough to be worth trying
* harder, and anyway we don't want to hold any index internal locks
* while waiting.
* tuple, we need to wait for it to complete and then recheck (unless
* the caller requested not to). For simplicity we do rechecking by
* just restarting the whole scan --- this case probably doesn't
* happen often enough to be worth trying harder, and anyway we don't
* want to hold any index internal locks while waiting.
*/
xwait = TransactionIdIsValid(DirtySnapshot.xmin) ? DirtySnapshot.xmin : DirtySnapshot.xmax;
if (TransactionIdIsValid(xwait)) {
if (TransactionIdIsValid(xwait) && waitMode == CHECK_WAIT) {
index_endscan(index_scan);
/* for speculative insertion (INSERT ON DUPLICATE KEY UPDATE),
* we only need to wait the speculative token lock to be release,
* which happens when the tuple is speculative inserted by other
* running transction, and has done it's insertion (eithter
* finished or aborted).
*/
XactLockTableWait(xwait);
goto retry;
}
/*
* We have a definite conflict. Report it.
* We have a definite conflict (or a potential one, but the caller
* didn't want to wait). If we're not supposed to raise error, just
* return to the caller.
*/
if (errorOK) {
conflict = true;
if (conflictTid != NULL)
*conflictTid = tup->t_self;
break;
}
/*
* We have a definite conflict (or a potential one, but the caller
* didn't want to wait). Report it.
*/
error_new = BuildIndexValueDescription(index, values, isnull);
error_existing = BuildIndexValueDescription(index, existing_values, existing_isnull);
@ -1578,7 +1776,7 @@ retry:
/*
* Ordinarily, at this point the search should have found the originally
* inserted tuple, unless we exited the loop early because of conflict.
* inserted tuple (if any), unless we exited the loop early because of conflict.
* 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.

View File

@ -184,6 +184,10 @@ lnext:
bucket_rel, &tuple, &buffer, &update_ctid, &update_xmax, estate->es_output_cid, lock_mode, erm->noWait);
ReleaseBuffer(buffer);
switch (test) {
case HeapTupleSelfCreated:
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("attempted to lock invisible tuple")));
break;
case HeapTupleSelfUpdated:
/* treat it as deleted; do not process */
goto lnext;

View File

@ -248,6 +248,277 @@ static TupleTableSlot* ExecProcessReturning(
return ExecProject(projectReturning, NULL);
}
static void ExecCheckHeapTupleVisible(EState* estate, HeapTuple tuple, Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
return;
if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
static void ExecCheckTIDVisible(EState* estate, Relation rel, ItemPointer tid)
{
Buffer buffer;
HeapTupleData tuple;
/* check isolation level to tell if tuple visibility check is needed */
if (!IsolationUsesXactSnapshot()) {
return;
}
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL)) {
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("failed to fetch conflicting tuple for DUPLICATE KEY UPDATE")));
}
ExecCheckHeapTupleVisible(estate, &tuple, buffer);
ReleaseBuffer(buffer);
}
static bool ExecConflictUpdate(ModifyTableState* mtstate, ResultRelInfo* resultRelInfo, ItemPointer conflictTid,
TupleTableSlot* planSlot, TupleTableSlot* excludedSlot, EState* estate, Relation targetRel,
Oid oldPartitionOid, int2 bucketid, bool canSetTag, TupleTableSlot** returning)
{
ExprContext* econtext = mtstate->ps.ps_ExprContext;
Relation relation = targetRel;
UpsertState* upsertState = mtstate->mt_upsert;
HeapTupleData tuple;
HTSU_Result test;
Buffer buffer;
ItemPointerData update_ctid;
TransactionId update_xmax;
tuple.t_self = *conflictTid;
test = heap_lock_tuple(relation, &tuple, &buffer,
&update_ctid, &update_xmax,
estate->es_output_cid, LockTupleExclusive, false);
checktest:
switch (test) {
case HeapTupleMayBeUpdated:
/* success */
break;
case HeapTupleSelfCreated:
/*
* This can occur when a just inserted tuple is updated again in
* the same command. E.g. because multiple rows with the same
* conflicting key values are inserted using STREAM:
* INSERT INTO t VALUES(1),(1) ON DUPLICATE KEY UPDATE ...
*
* This is somewhat similar to the ExecUpdate()
* HeapTupleSelfUpdated case. We do not want to proceed because
* it would lead to the same row being updated a second time in
* some unspecified order, and in contrast to plain UPDATEs
* there's no historical behavior to break.
*
* It is the user's responsibility to prevent this situation from
* occurring. 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.
*
* However, in order by be compatible with SQL, we have to break the
* rule and update the same row which is created within the command.
*/
ReleaseBuffer(buffer);
#ifdef ENABLE_MULTIPLE_NODES
if (!(u_sess->attr.attr_sql.sql_compatibility & DB_CMPT_C)) {
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("ON DUPLICATE KEY UPDATE command cannot affect row a second time"),
errhint("Ensure that no rows proposed for insertion within"
"the same command have duplicate constrained values.")));
}
#endif
test = heap_lock_tuple(relation, &tuple, &buffer, &update_ctid, &update_xmax,
estate->es_output_cid, LockTupleExclusive, false, true);
Assert(test != HeapTupleSelfCreated);
goto checktest;
break;
case HeapTupleSelfUpdated:
ReleaseBuffer(buffer);
/*
* This state should never be reached. As a dirty snapshot is used
* to find conflicting tuples, speculative insertion wouldn't have
* seen this row to conflict with.
*/
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("unexpected self-updated tuple")));
break;
case HeapTupleUpdated:
ReleaseBuffer(buffer);
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.
* It does not make sense to use the usual EvalPlanQual() style
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
return false;
case HeapTupleBeingUpdated:
ReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("unexpected concurrent update tuple")));
break;
default:
ReleaseBuffer(buffer);
elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
break;
}
/*
* Success, the tuple is locked.
*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous cycle.
*/
ResetExprContext(econtext);
/* NOTE: we rely on ExecUpdate() to do MVCC snapshot check, thus projection is
* done here although the final ExecUpdate might be failed.
*/
ExecCheckHeapTupleVisible(estate, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, upsertState->us_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
* ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
* the target's existing tuple is installed in the scantuple. EXCLUDED has
* been made to reference INNER_VAR in setrefs.c, but there is no other redirection.
*/
econtext->ecxt_scantuple = upsertState->us_existing;
econtext->ecxt_innertuple = excludedSlot;
econtext->ecxt_outertuple = NULL;
ExecProject(resultRelInfo->ri_updateProj, NULL);
*returning = ExecUpdate(conflictTid, oldPartitionOid, bucketid, NULL,
upsertState->us_updateproj, planSlot, &mtstate->mt_epqstate,
mtstate, canSetTag, false);
ReleaseBuffer(buffer);
return true;
}
static Oid ExecUpsert(ModifyTableState* state, TupleTableSlot* slot, TupleTableSlot* planSlot, EState* estate,
bool canSetTag, HeapTuple tuple, TupleTableSlot** returning, bool* updated)
{
Oid newid = InvalidOid;
bool specConflict;
List* recheckIndexes = NIL;
ResultRelInfo* resultRelInfo = NULL;
Relation resultRelationDesc = NULL;
Relation heaprel = NULL; /* actual relation to upsert index */
Relation targetrel = NULL; /* actual relation to upsert tuple */
Oid partitionid = InvalidOid; /* bucket id for bucket hash table */
Partition partition = NULL; /* partition info for partition table */
int2 bucketid = InvalidBktId;
ItemPointerData conflictTid;
UpsertState* upsertState = state->mt_upsert;
*updated = false;
/*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
heaprel = resultRelationDesc;
if (unlikely(RelationIsCUFormat(resultRelationDesc))) {
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ON DUPLICATE KEY UPDATE is not supported on column orientated table"))));
}
if (unlikely(RelationIsPAXFormat(resultRelationDesc))) {
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ON DUPLICATE KEY UPDATE is not supported on DFS table"))));
}
if (RelationIsPartitioned(resultRelationDesc)) {
partitionid = heapTupleGetPartitionId(resultRelationDesc, tuple);
searchFakeReationForPartitionOid(estate->esfRelations,
estate->es_query_cxt,
resultRelationDesc,
partitionid,
heaprel,
partition,
RowExclusiveLock);
}
targetrel = heaprel;
if (RELATION_OWN_BUCKET(resultRelationDesc)) {
bucketid = computeTupleBucketId(resultRelationDesc, tuple);
if (unlikely(bucketid != InvalidBktId)) {
searchHBucketFakeRelation(estate->esfRelations, estate->es_query_cxt, heaprel, bucketid, targetrel);
}
}
vlock:
specConflict = false;
if (!ExecCheckIndexConstraints(slot, estate, targetrel, partition, bucketid, &conflictTid)) {
/* committed conflict tuple found */
if (upsertState->us_action == UPSERT_UPDATE) {
/*
* In case of DUPLICATE KEY UPDATE, execute the UPDATE part.
* Be prepared to retry if the UPDATE fails because
* of another concurrent UPDATE/DELETE to the conflict tuple.
*/
*returning = NULL;
if (ExecConflictUpdate(state, resultRelInfo, &conflictTid, planSlot, slot, estate, targetrel, partitionid,
bucketid, canSetTag, returning)) {
InstrCountFiltered2(&state->ps, 1);
*updated = true;
return InvalidOid;
} else {
goto vlock;
}
} else {
/*
* In case of DUPLICATE UPDATE NOTHING, do nothing.
* However, verify that the tuple is visible to the
* executor's MVCC snapshot at higher isolation levels.
*/
Assert(upsertState->us_action == UPSERT_NOTHING);
ExecCheckTIDVisible(estate, targetrel, &conflictTid);
InstrCountFiltered2(&state->ps, 1);
*updated = true;
return InvalidOid;
}
}
/* insert the tuple */
newid = heap_insert(targetrel, tuple, estate->es_output_cid, 0, NULL);
/* insert index entries for tuple */
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate, heaprel,
partition, bucketid, &specConflict);
/* other transaction commit index insertion before us,
* then abort the tuple and try to find the conflict tuple again
*/
if (specConflict) {
heap_abort_speculative(targetrel, tuple);
list_free(recheckIndexes);
goto vlock;
}
return newid;
}
/* ----------------------------------------------------------------
* ExecInsert
*
@ -307,9 +578,12 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
if (result_relation_desc->rd_rel->relhasoids)
HeapTupleSetOid(tuple, InvalidOid);
/* BEFORE ROW INSERT Triggers */
if (state->operation != CMD_MERGE && result_rel_info->ri_TrigDesc &&
result_rel_info->ri_TrigDesc->trig_insert_before_row) {
/* BEFORE ROW INSERT Triggers
* Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an except
* for a MERGE or INSERT ... ON DUPLICATE KEY UPDATE statement.
*/
if (state->operation != CMD_MERGE &&
result_rel_info->ri_TrigDesc && result_rel_info->ri_TrigDesc->trig_insert_before_row) {
slot = ExecBRInsertTriggers(estate, result_rel_info, slot);
if (slot == NULL) /* "do nothing" */
return NULL;
@ -318,9 +592,12 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
tuple = ExecMaterializeSlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
if (state->operation != CMD_MERGE && result_rel_info->ri_TrigDesc &&
result_rel_info->ri_TrigDesc->trig_insert_instead_row) {
/* INSTEAD OF ROW INSERT Triggers
* Note: We fire INSREAD OF ROW TRIGGERS for every attempted insertion except
* for a MERGE or INSERT ... ON DUPLICATE KEY UPDATE statement.
*/
if (state->operation != CMD_MERGE &&
result_rel_info->ri_TrigDesc && result_rel_info->ri_TrigDesc->trig_insert_instead_row) {
slot = ExecIRInsertTriggers(estate, result_rel_info, slot);
if (slot == NULL) /* "do nothing" */
return NULL;
@ -449,6 +726,14 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
}
ExecDropSingleTupleTableSlot(tmp_slot);
} else if (state->mt_upsert->us_action != UPSERT_NONE && result_rel_info->ri_NumIndices > 0) {
TupleTableSlot* returning = NULL;
bool updated = false;
new_id = InvalidOid;
new_id = ExecUpsert(state, slot, planSlot, estate, canSetTag, tuple, &returning, &updated);
if (updated) {
return returning;
}
} else {
/*
* insert the tuple
@ -524,7 +809,7 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
estate,
RELATION_IS_PARTITIONED(result_relation_desc) ? heap_rel : NULL,
RELATION_IS_PARTITIONED(result_relation_desc) ? partition : NULL,
bucket_id);
bucket_id, NULL);
}
}
@ -539,7 +824,10 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
setLastTid(&(tuple->t_self));
}
/* AFTER ROW INSERT Triggers */
/* AFTER ROW INSERT Triggers
* Note: We fire AFTER ROW TRIGGERS for every attempted insertion except
* for a MERGE or INSERT ... ON DUPLICATE KEY UPDATE statement.
*/
if (state->operation != CMD_MERGE && !useHeapMultiInsert)
ExecARInsertTriggers(estate, result_rel_info, partition_id, bucket_id, tuple, recheck_indexes);
@ -938,6 +1226,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
#ifdef PGXC
RemoteQueryState* result_remote_rel = NULL;
#endif
bool allow_update_self = (node->mt_upsert != NULL &&
node->mt_upsert->us_action != UPSERT_NONE) ? true : false;
/*
* abort the operation if not running transactions
@ -1049,7 +1339,6 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
return NULL;
}
/* FDW might have changed tuple */
tuple = ExecMaterializeSlot(slot);
} else {
@ -1102,7 +1391,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
&update_xmax,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */);
true /* wait for commit */,
allow_update_self);
switch (result) {
case HeapTupleSelfUpdated:
/* can not update one row more than once for merge into */
@ -1212,7 +1502,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
* If it's a HOT update, we mustn't insert new index entries.
*/
if (result_rel_info->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
recheck_indexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate, NULL, NULL, bucketid);
recheck_indexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate,
NULL, NULL, bucketid, NULL);
} else {
/* for partitioned table */
bool row_movement = false;
@ -1300,7 +1591,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
&update_xmax,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */);
true /* wait for commit */,
allow_update_self);
switch (result) {
case HeapTupleSelfUpdated:
/* can not update one row more than once for merge into */
@ -1403,7 +1695,7 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
* delete index entries for tuple
*/
recheck_indexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate,
fake_part_rel, partition, bucketid);
fake_part_rel, partition, bucketid, NULL);
}
} else {
/* row movement */
@ -1432,7 +1724,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
&update_xmax,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */);
true /* wait for commit */,
allow_update_self);
switch (result) {
case HeapTupleSelfUpdated:
/* can not update one row more than once for merge into */
@ -1541,7 +1834,7 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
if (result_rel_info->ri_NumIndices > 0) {
recheck_indexes = ExecInsertIndexTuples(
slot, &(tuple->t_self), estate, fake_part_rel, insert_partition, bucketid);
slot, &(tuple->t_self), estate, fake_part_rel, insert_partition, bucketid, NULL);
}
}
}
@ -1602,6 +1895,9 @@ static void fireBSTriggers(ModifyTableState* node)
switch (node->operation) {
case CMD_INSERT:
ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
if (node->mt_upsert->us_action == UPSERT_UPDATE) {
ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
}
break;
case CMD_UPDATE:
ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
@ -1629,6 +1925,9 @@ static void fireASTriggers(ModifyTableState* node)
switch (node->operation) {
case CMD_INSERT:
ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
if (node->mt_upsert->us_action == UPSERT_UPDATE) {
ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
}
break;
case CMD_UPDATE:
ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
@ -1755,7 +2054,7 @@ TupleTableSlot* ExecModifyTable(ModifyTableState* node)
#endif
if (operation == CMD_INSERT) {
if (node->ps.type == T_ModifyTableState ||
if (node->ps.type == T_ModifyTableState || node->mt_upsert->us_action != UPSERT_NONE ||
(result_rel_info->ri_TrigDesc != NULL && (result_rel_info->ri_TrigDesc->trig_insert_before_row ||
result_rel_info->ri_TrigDesc->trig_insert_instead_row)))
ExecInsert = ExecInsertT<false>;
@ -2048,6 +2347,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
ResultRelInfo* result_rel_info = NULL;
TupleDesc tup_desc = NULL;
Plan* sub_plan = NULL;
UpsertState* upsertState = NULL;
ListCell* l = NULL;
int i;
#ifdef PGXC
@ -2110,6 +2410,13 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
mt_state->mt_arowmarks = (List**)palloc0(sizeof(List*) * nplans);
mt_state->mt_nplans = nplans;
upsertState = (UpsertState*)palloc0(sizeof(UpsertState));
upsertState->us_action = node->upsertAction;
upsertState->us_existing = NULL;
upsertState->us_excludedtlist = NIL;
upsertState->us_updateproj = NULL;
mt_state->mt_upsert = upsertState;
/* set up epqstate with dummy sub_plan data for the moment */
EvalPlanQualInit(&mt_state->mt_epqstate, estate, NULL, NIL, node->epqParam);
mt_state->fireBSTriggers = true;
@ -2160,7 +2467,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
result_rel_info->ri_IndexRelationDescs == NULL) {
if (result_rel_info->ri_FdwRoutine == NULL || result_rel_info->ri_FdwRoutine->GetFdwType == NULL ||
result_rel_info->ri_FdwRoutine->GetFdwType() != MOT_ORC)
ExecOpenIndices(result_rel_info);
ExecOpenIndices(result_rel_info, node->upsertAction != UPSERT_NONE);
}
init_gtt_storage(operation, result_rel_info);
/* Now init the plan for this result rel */
@ -2281,6 +2588,43 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
mt_state->ps.ps_ExprContext = NULL;
}
/*
* If needed, Initialize target list, projection and qual for DUPLICATE KEY UPDATE
*/
result_rel_info = mt_state->resultRelInfo;
if (node->upsertAction == UPSERT_UPDATE) {
ExprContext* econtext;
ExprState* setexpr;
TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
Assert(nplans = 1);
/* already exists if created by RETURNING processing above */
if (mt_state->ps.ps_ExprContext == NULL) {
ExecAssignExprContext(estate, &mt_state->ps);
}
econtext = mt_state->ps.ps_ExprContext;
/* initialize slot for the existing tuple */
upsertState->us_existing = ExecInitExtraTupleSlot(mt_state->ps.state);
ExecSetSlotDescriptor(upsertState->us_existing, result_rel_info->ri_RelationDesc->rd_att);
upsertState->us_excludedtlist = node->exclRelTlist;
/* create target slot for UPDATE SET projection */
tupDesc = ExecTypeFromTL((List*)node->updateTlist, result_rel_info->ri_RelationDesc->rd_rel->relhasoids);
upsertState->us_updateproj = ExecInitExtraTupleSlot(mt_state->ps.state);
ExecSetSlotDescriptor(upsertState->us_updateproj, tupDesc);
/* build UPDATE SET expression and projection state */
setexpr = ExecInitExpr((Expr*)node->updateTlist, &mt_state->ps);
result_rel_info->ri_updateProj =
ExecBuildProjectionInfo((List*)setexpr, econtext,
upsertState->us_updateproj, result_rel_info->ri_RelationDesc->rd_att);
}
/*
* If we have any secondary relations in an UPDATE or DELETE, they need to
* be treated like non-locked relations in SELECT FOR UPDATE, ie, the

View File

@ -1078,7 +1078,7 @@ bool InsertFusion::execute(long max_rows, char* completionTag)
m_estate->es_result_relation_info = result_rel_info;
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info);
ExecOpenIndices(result_rel_info, false);
}
CommandId mycid = GetCurrentCommandId(true);
@ -1112,7 +1112,7 @@ bool InsertFusion::execute(long max_rows, char* completionTag)
/* insert index entries for tuple */
List* recheck_indexes = NIL;
if (result_rel_info->ri_NumIndices > 0) {
recheck_indexes = ExecInsertIndexTuples(m_reslot, &(tuple->t_self), m_estate, NULL, NULL, bucketid);
recheck_indexes = ExecInsertIndexTuples(m_reslot, &(tuple->t_self), m_estate, NULL, NULL, bucketid, NULL);
}
list_free_ext(recheck_indexes);
@ -1391,7 +1391,7 @@ bool UpdateFusion::execute(long max_rows, char* completionTag)
m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info);
ExecOpenIndices(result_rel_info, false);
}
/*********************************
@ -1446,7 +1446,8 @@ bool UpdateFusion::execute(long max_rows, char* completionTag)
/* done successfully */
nprocessed++;
if (result_rel_info->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tup)) {
recheck_indexes = ExecInsertIndexTuples(m_reslot, &(tup->t_self), m_estate, NULL, NULL, bucketid);
recheck_indexes = ExecInsertIndexTuples(m_reslot, &(tup->t_self), m_estate,
NULL, NULL, bucketid, NULL);
list_free_ext(recheck_indexes);
}
break;
@ -1577,7 +1578,7 @@ bool DeleteFusion::execute(long max_rows, char* completionTag)
m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info);
ExecOpenIndices(result_rel_info, false);
}
/********************************
@ -1784,7 +1785,7 @@ bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info);
ExecOpenIndices(result_rel_info, false);
}
/**************************************
@ -1853,6 +1854,10 @@ bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
}
switch (result) {
case HeapTupleSelfCreated:
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("attempted to lock invisible tuple")));
break;
case HeapTupleSelfUpdated:
/* already deleted by self; nothing to do */
break;

View File

@ -208,6 +208,11 @@ const char *getBypassReason(FusionType result)
break;
}
case NOBYPASS_UPSERT_NOT_SUPPORT: {
return "Bypass not support INSERT INTO ... ON DUPLICATE KEY UPDATE statement";
break;
}
default: {
Assert(0);
ereport(ERROR,
@ -757,6 +762,9 @@ FusionType getInsertFusionType(List *stmt_list, ParamListInfo params)
if (base->plan.lefttree != NULL || base->plan.initPlan != NIL || base->resconstantqual != NULL) {
return NOBYPASS_NO_SIMPLE_INSERT;
}
if (node->upsertAction != UPSERT_NONE) {
return NOBYPASS_UPSERT_NOT_SUPPORT;
}
/* check relation */
Index res_rel_idx = linitial_int(plannedstmt->resultRelations);

View File

@ -2547,7 +2547,7 @@ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, Bu
}
xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self);
xlrec.flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
xlrec.flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
Assert(ItemPointerGetBlockNumber(&heaptup->t_self) == BufferGetBlockNumber(buffer));
/*
@ -2556,7 +2556,7 @@ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, Bu
* image. (XXX We could alternatively store a pointer into the FPW).
*/
if (RelationIsLogicallyLogged(relation)) {
xlrec.flags |= XLOG_HEAP_CONTAINS_NEW_TUPLE;
xlrec.flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
bufflags |= REGBUF_KEEP_DATA;
}
@ -2605,6 +2605,7 @@ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, Bu
*/
CacheInvalidateHeapTuple(relation, heaptup, NULL);
/* Note: speculative insertions are counted too, even if aborted later */
pgstat_count_heap_insert(relation, 1);
/*
@ -2619,7 +2620,145 @@ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, Bu
return HeapTupleGetOid(tup);
}
/**
/*
* heap_abort_speculative - kill a speculatively inserted tuple
*
* Marks a tuple that was speculatively inserted in the same command as dead,
* by setting its xmin as invalid. That makes it immediately appear as dead
* to all transactions, including our own. In particular, it makes
* HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
* inserting a duplicate key value won't unnecessarily wait for our whole
* transaction to finish (it'll just wait for our speculative insertion to
* finish).
*
* Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
* that arise due to a mutual dependency that is not user visible. By
* definition, unprincipled deadlocks cannot be prevented by the user
* reordering lock acquisition in client code, because the implementation level
* lock acquisitions are not under the user's direct control. If speculative
* inserters did not take this precaution, then under high concurrency they
* could deadlock with each other, which would not be acceptable.
*
* This is somewhat redundant with heap_delete, but we prefer to have a
* dedicated routine with stripped down requirements.
*
* This routine does not affect logical decoding as it only looks at
* confirmation records.
*/
void heap_abort_speculative(Relation relation, HeapTuple tuple)
{
TransactionId xid = GetCurrentTransactionId();
ItemPointer tid = &(tuple->t_self);
ItemId lp;
HeapTupleData tp;
Page page;
BlockNumber block;
Buffer buffer;
Assert(ItemPointerIsValid(tid));
block = ItemPointerGetBlockNumber(tid);
buffer = ReadBuffer(relation, block);
page = BufferGetPage(buffer);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
/*
* Page can't be all visible, we just inserted into it, and are still
* running.
*/
Assert(!PageIsAllVisible(page));
lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
Assert(ItemIdIsNormal(lp));
tp.t_tableOid = RelationGetRelid(relation);
tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
tp.t_len = ItemIdGetLength(lp);
tp.t_self = *tid;
/*
* Sanity check that the tuple really is a speculatively inserted tuple,
* inserted by us.
*/
if (HeapTupleHeaderGetXmin(page, tp.t_data) != xid) {
ereport(ERROR,
(errmsg("attempted to kill a tuple inserted by another transaction: %lu, %lu",
HeapTupleGetRawXmin(&tp), xid)));
}
Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
/*
* No need to check for serializable conflicts here. There is never a
* need for a combocid, either. No need to extract replica identity, or
* do anything special with infomask bits.
*/
START_CRIT_SECTION();
/*
* The tuple will become DEAD immediately. Flag that this page
* immediately is a candidate for pruning by setting xmin to
* RecentGlobalXmin. That's not pretty, but it doesn't seem worth
* inventing a nicer API for this.
*/
PageSetPrunable(page, xid);
/* store transaction information of xact deleting the tuple */
tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_XMAX_IS_MULTI |
HEAP_IS_LOCKED | HEAP_MOVED);
/*
* Set the tuple header xmin to InvalidTransactionId. This makes the
* tuple immediately invisible everyone. (In particular, to any
* transactions waiting on the speculative token, woken up later.)
*/
HeapTupleHeaderSetXmin(page, tp.t_data, InvalidTransactionId);
MarkBufferDirty(buffer);
/*
* XLOG stuff
*
* The WAL records generated here match heap_delete(). The same recovery
* routines are used.
*/
if (RelationNeedsWAL(relation)) {
xl_heap_delete xlrec;
XLogRecPtr recptr;
xlrec.flags = XLH_DELETE_IS_SUPER;
xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
XLogBeginInsert();
XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
/* No replica identity & replication origin logged */
recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
PageSetLSN(page, recptr);
}
END_CRIT_SECTION();
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
if (HeapTupleHasExternal(&tp))
toast_delete(relation, &tp, HEAP_INSERT_SPECULATIVE);
/*
* Never need to mark tuple for invalidation, since catalogs don't support
* speculative insertion
*/
/* Now we can release the buffer */
ReleaseBuffer(buffer);
/* count deletion, as we counted the insertion too */
pgstat_count_heap_delete(relation);
}
/*
* @Description: Find minimum and maximum short transaction ids which occurs in the page.
* @in: page, heap page
* @in: multi, Whether multixact
@ -3422,7 +3561,7 @@ int heap_multi_insert(Relation relation, Relation parent, HeapTuple* tuples, int
/* the rest of the scratch space is used for tuple data */
tuple_data = scratchptr;
xlrec->flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
xlrec->ntuples = nthispage;
/* xlog: write the dictionary between header and tuples */
@ -3472,7 +3611,7 @@ int heap_multi_insert(Relation relation, Relation parent, HeapTuple* tuples, int
Assert((scratchptr - scratch) < BLCKSZ);
if (need_tuple_data) {
xlrec->flags |= XLOG_HEAP_CONTAINS_NEW_TUPLE;
xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
}
/*
@ -3481,7 +3620,7 @@ int heap_multi_insert(Relation relation, Relation parent, HeapTuple* tuples, int
* decoding so it knows when to cleanup temporary data.
*/
if (ndone + nthispage == ntuples) {
xlrec->flags |= XLOG_HEAP_LAST_MULTI_INSERT;
xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
}
/*
@ -3620,7 +3759,7 @@ Oid simple_heap_insert(Relation relation, HeapTuple tup)
* (t_xmax is needed to verify that the replacement tuple matches.)
*/
HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, TransactionId* update_xmax, CommandId cid,
Snapshot crosscheck, bool wait)
Snapshot crosscheck, bool wait, bool allow_delete_self)
{
HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
@ -3694,11 +3833,16 @@ HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, Tr
HeapTupleCopyBaseFromPage(&tp, page);
l1:
result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
result = HeapTupleSatisfiesUpdate(&tp, cid, buffer, allow_delete_self);
if (result == HeapTupleInvisible) {
UnlockReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to delete invisible tuple")));
} else if (result == HeapTupleSelfCreated) {
UnlockReleaseBuffer(buffer);
/* if allow self delete, HeapTupleSelfCreated status will never be reached */
Assert(!allow_delete_self);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to delete self created tuple")));
} else if (result == HeapTupleBeingUpdated && wait) {
TransactionId xwait;
uint16 infomask;
@ -3866,7 +4010,7 @@ l1:
(void)log_heap_new_cid(relation, &tp);
}
xlrec.flags = all_visible_cleared ? XLOG_HEAP_ALL_VISIBLE_CLEARED : 0;
xlrec.flags = all_visible_cleared ? XLH_DELETE_ALL_VISIBLE_CLEARED : 0;
xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
if (old_key_tuple != NULL) {
@ -3890,9 +4034,9 @@ l1:
}
if (relreplident == REPLICA_IDENTITY_FULL) {
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_TUPLE;
xlrec.flags |= XLH_DELETE_CONTAINS_OLD_TUPLE;
} else {
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_KEY;
xlrec.flags |= XLH_DELETE_CONTAINS_OLD_KEY;
}
}
@ -3940,7 +4084,7 @@ l1:
/* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(&tp));
} else if (HeapTupleHasExternal(&tp))
toast_delete(relation, &tp);
toast_delete(relation, &tp, allow_delete_self ? HEAP_INSERT_SPECULATIVE : 0);
/*
* Mark tuple for invalidation from system caches at next command
@ -3976,11 +4120,12 @@ l1:
* on the relation associated with the tuple). Any failure is reported
* via ereport().
*/
void simple_heap_delete(Relation relation, ItemPointer tid)
void simple_heap_delete(Relation relation, ItemPointer tid, int options)
{
HTSU_Result result;
ItemPointerData update_ctid;
TransactionId update_xmax;
bool allow_delete_self = (options & HEAP_INSERT_SPECULATIVE) ? true : false;
result = heap_delete(relation,
tid,
@ -3988,7 +4133,8 @@ void simple_heap_delete(Relation relation, ItemPointer tid)
&update_xmax,
GetCurrentCommandId(true),
InvalidSnapshot,
true /* wait for commit */);
true /* wait for commit */,
allow_delete_self);
switch (result) {
case HeapTupleSelfUpdated:
/* Tuple was already updated in current command? */
@ -4042,8 +4188,9 @@ void simple_heap_delete(Relation relation, ItemPointer tid)
* tuple was updated, and t_ctid is the location of the replacement tuple.
* (t_xmax is needed to verify that the replacement tuple matches.)
*/
HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid, HeapTuple newtup,
ItemPointer ctid, TransactionId* update_xmax, CommandId cid, Snapshot crosscheck, bool wait)
HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid,
HeapTuple newtup, ItemPointer ctid, TransactionId* update_xmax, CommandId cid,
Snapshot crosscheck, bool wait, bool allow_update_self)
{
HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
@ -4144,10 +4291,15 @@ HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer
l2:
HeapTupleCopyBaseFromPage(&oldtup, BufferGetPage(buffer));
result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer, allow_update_self);
if (result == HeapTupleInvisible) {
UnlockReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to update invisible tuple")));
} else if (result == HeapTupleSelfCreated) {
UnlockReleaseBuffer(buffer);
/* if allow self update, HeapTupleSelfCreated status will never be reached */
Assert(!allow_update_self);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to update self created tuple")));
} else if (result == HeapTupleBeingUpdated && wait) {
TransactionId xwait;
uint16 infomask;
@ -4386,7 +4538,8 @@ l2:
*/
if (need_toast) {
/* Note we always use WAL and FSM during updates */
heaptup = toast_insert_or_update(relation, newtup, &oldtup, 0, page);
heaptup = toast_insert_or_update(relation, newtup, &oldtup,
allow_update_self ? HEAP_INSERT_SPECULATIVE : 0, page);
new_tup_size = MAXALIGN(heaptup->t_len);
} else {
heaptup = newtup;
@ -5064,7 +5217,7 @@ void simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
* conflict for a tuple, we don't incur any extra overhead.
*/
HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer, ItemPointer ctid,
TransactionId* update_xmax, CommandId cid, LockTupleMode mode, bool nowait)
TransactionId* update_xmax, CommandId cid, LockTupleMode mode, bool nowait, bool allow_lock_self)
{
HTSU_Result result;
ItemPointer tid = &(tuple->t_self);
@ -5122,7 +5275,7 @@ HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer,
l3:
HeapTupleCopyBaseFromPage(tuple, page);
result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer, allow_lock_self);
ereport(DEBUG1,
(errmsg("heap lock tuple ctid (%u,%d) cur_xid %lu xmin "
"%lu xmax %lu infomask %hu result %d",
@ -5137,6 +5290,25 @@ l3:
if (result == HeapTupleInvisible) {
UnlockReleaseBuffer(*buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to lock invisible tuple")));
} else if (result == HeapTupleSelfCreated) {
/*
* This is possible when the tuple is going to be updated twice in one command,
* which should be considered as invisible (same with HeapTupleInvisible) and
* throw an error.
*
* However, there is a special case: UPSERT multiple VALUES using a STREAM plan
* e.g INSERT values(1,x),(1,x) ON DUPLICATE KEY UPDATE..
* As we have to allow this case to be done in MYSQL compatibility,
* we return HeapTupleSelfCreated here rather than throwing an error in
* order to give UPSERT case the opportunity to throw a more specific error or
* allow to UPSERT
*
* NOTE: multiple VALUES UPSERT using a PGXC plan is not a problem because
* the optimizer will spilt the query into multiple commands, each of which only
* UPSERT one VALUES().
*/
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
return HeapTupleSelfCreated;
} else if (result == HeapTupleBeingUpdated) {
TransactionId xwait;
uint16 infomask;
@ -6040,18 +6212,18 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf, const ItemPointe
xlrec.new_offnum = ItemPointerGetOffsetNumber(&newtup->t_self);
xlrec.flags = 0;
if (all_visible_cleared) {
xlrec.flags |= XLOG_HEAP_ALL_VISIBLE_CLEARED;
xlrec.flags |= XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED;
}
if (new_all_visible_cleared) {
xlrec.flags |= XLOG_HEAP_NEW_ALL_VISIBLE_CLEARED;
xlrec.flags |= XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED;
}
if (need_tuple_data) {
xlrec.flags |= XLOG_HEAP_CONTAINS_NEW_TUPLE;
xlrec.flags |= XLH_UPDATE_CONTAINS_NEW_TUPLE;
if (old_key_tuple) {
if (reln->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_TUPLE;
xlrec.flags |= XLH_UPDATE_CONTAINS_OLD_TUPLE;
else
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_KEY;
xlrec.flags |= XLH_UPDATE_CONTAINS_OLD_KEY;
}
}
if (need_tuple_data) {
@ -6717,7 +6889,7 @@ static void heap_xlog_delete(XLogReaderState* record)
* The visibility map may need to be fixed even if the heap page is
* already up-to-date.
*/
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED) {
RelFileNode target_node;
BlockNumber blkno;
@ -6759,7 +6931,7 @@ static void heap_xlog_insert(XLogReaderState* record)
* The visibility map may need to be fixed even if the heap page is
* already up-to-date.
*/
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
heap_xlog_allvisiblecleared(target_node, blkno);
}
@ -6832,7 +7004,7 @@ static void heap_xlog_multi_insert(XLogReaderState* record)
* The visibility map may need to be fixed even if the heap page is
* already up-to-date.
*/
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
heap_xlog_allvisiblecleared(rnode, blkno);
}
@ -6902,12 +7074,11 @@ static void heap_xlog_update(XLogReaderState* record, bool hot_update)
oldblk = newblk;
}
/*
* The visibility map may need to be fixed even if the heap page is
* already up-to-date.
*/
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED) {
heap_xlog_allvisiblecleared(rnode, oldblk);
}
@ -6949,7 +7120,7 @@ static void heap_xlog_update(XLogReaderState* record, bool hot_update)
* The visibility map may need to be fixed even if the heap page is
* already up-to-date.
*/
if (xlrec->flags & XLOG_HEAP_NEW_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED) {
heap_xlog_allvisiblecleared(rnode, newblk);
}

View File

@ -45,7 +45,7 @@
#undef TOAST_DEBUG
static void toast_delete_datum(Relation rel, Datum value);
static void toast_delete_datum(Relation rel, Datum value, int options);
static Datum toast_save_datum(Relation rel, Datum value, struct varlena* oldexternal, int options);
static bool toastid_valueid_exists(Oid toastrelid, Oid valueid, int2 bucketid);
static struct varlena* toast_fetch_datum(struct varlena* attr);
@ -332,7 +332,7 @@ Size toast_datum_size(Datum value)
* Cascaded delete toast-entries on DELETE
* ----------
*/
void toast_delete(Relation rel, HeapTuple oldtup)
void toast_delete(Relation rel, HeapTuple oldtup, int options)
{
TupleDesc tuple_desc;
Form_pg_attribute* att = NULL;
@ -381,7 +381,7 @@ void toast_delete(Relation rel, HeapTuple oldtup)
if (toast_isnull[i])
continue;
else if (VARATT_IS_EXTERNAL_ONDISK_B(PointerGetDatum(value)))
toast_delete_datum(rel, value);
toast_delete_datum(rel, value, options);
else if (VARATT_IS_EXTERNAL_INDIRECT(PointerGetDatum(value)))
ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("attempt to delete tuple containing indirect datums")));
@ -931,7 +931,7 @@ HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtu
if (need_delold) {
for (i = 0; i < num_attrs; i++) {
if (toast_delold[i]) {
toast_delete_datum(rel, toast_oldvalues[i]);
toast_delete_datum(rel, toast_oldvalues[i], options);
}
}
}
@ -1461,7 +1461,7 @@ static Datum toast_save_datum(Relation rel, Datum value, struct varlena* oldexte
* Delete a single external stored value.
* ----------
*/
static void toast_delete_datum(Relation rel, Datum value)
static void toast_delete_datum(Relation rel, Datum value, int options)
{
struct varlena* attr = (struct varlena*)DatumGetPointer(value);
struct varatt_external toast_pointer;
@ -1501,7 +1501,7 @@ static void toast_delete_datum(Relation rel, Datum value)
/*
* Have a chunk, delete it
*/
simple_heap_delete(toastrel, &toasttup->t_self);
simple_heap_delete(toastrel, &toasttup->t_self, options);
if (u_sess->attr.attr_storage.enable_debug_vacuum)
elogVacuumInfo(toastrel, toasttup, "toast_delete_datum", u_sess->cmd_cxt.OldestXmin);

View File

@ -367,7 +367,7 @@ void heap_xlog_delete_operator_page(RedoBufferInfo* buffer, void* recorddata, Tr
/* Mark the page as a candidate for pruning */
PageSetPrunable(page, recordxid);
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
/* Make sure there is no forward chain link in t_ctid */
@ -445,7 +445,7 @@ void heap_xlog_insert_operator_page(RedoBufferInfo* buffer, void* recorddata, bo
PageSetLSN(page, buffer->lsn);
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
}
@ -548,7 +548,7 @@ void heap_xlog_multi_insert_operator_page(RedoBufferInfo* buffer, void* recoredd
PageSetLSN(page, buffer->lsn);
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
}
@ -592,7 +592,7 @@ void heap_xlog_update_operator_oldpage(RedoBufferInfo* buffer, void* recoreddata
/* Mark the page as a candidate for pruning */
PageSetPrunable(page, recordxid);
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
PageHeader oldPhdr = (PageHeader)page;
@ -708,7 +708,7 @@ void heap_xlog_update_operator_newpage(RedoBufferInfo* buffer, void* recorddata,
if (PageAddItem(page, (Item)htup, newlen, xlrec->new_offnum, true, true) == InvalidOffsetNumber)
ereport(PANIC, (errmsg("heap_update_redo: failed to add tuple")));
if (xlrec->flags & XLOG_HEAP_NEW_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
PageClearAllVisible(page);
if (freespace != NULL) {
*freespace = PageGetHeapFreeSpace(page);
@ -889,7 +889,7 @@ static XLogRecParseState* heap_xlog_insert_parse_block(XLogReaderState* record,
}
xlrec = (xl_heap_insert*)rec_data;
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
@ -915,7 +915,7 @@ static XLogRecParseState* heap_xlog_delete_parse_block(XLogReaderState* record,
XLogRecSetBlockDataState(record, HEAP_DELETE_ORIG_BLOCK_NUM, recordstatehead);
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
@ -973,7 +973,7 @@ static XLogRecParseState* heap_xlog_update_parse_block(XLogReaderState* record,
XLogRecSetAuxiBlkNumState(&blockstate->blockparse.extra_rec.blockdatarec, newblk, InvalidForkNumber);
// OLD BLOCK
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
@ -985,8 +985,8 @@ static XLogRecParseState* heap_xlog_update_parse_block(XLogReaderState* record,
}
}
if ((xlrec->flags & XLOG_HEAP_NEW_ALL_VISIBLE_CLEARED) ||
((oldblk == newblk) && (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED))) {
if ((xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED) ||
((oldblk == newblk) && (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED))) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {
@ -1279,7 +1279,7 @@ static XLogRecParseState* heap_xlog_multi_insert_parse_block(XLogReaderState* re
}
xlrec = (xl_heap_multi_insert*)rec_data;
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) {
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) {
(*blocknum)++;
XLogParseBufferAllocListFunc(record, &blockstate, recordstatehead);
if (blockstate == NULL) {

View File

@ -1214,7 +1214,7 @@ static void TrackVMPageModification(XLogReaderState* record)
recData += sizeof(TransactionId);
xl_heap_insert* xlrec = (xl_heap_insert*)recData;
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
(void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo1);
break;
@ -1222,7 +1222,7 @@ static void TrackVMPageModification(XLogReaderState* record)
case XLOG_HEAP_DELETE: {
xl_heap_delete* xlrec = (xl_heap_delete*)recData;
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
(void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo1);
break;
@ -1232,10 +1232,10 @@ static void TrackVMPageModification(XLogReaderState* record)
recData += sizeof(TransactionId);
xl_heap_update* xlrec = (xl_heap_update*)recData;
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
(void)XLogRecGetBlockTag(record, 1, &rNode, NULL, &heapBlkNo1);
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
(void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo2);
break;
@ -1249,7 +1249,7 @@ static void TrackVMPageModification(XLogReaderState* record)
recData += sizeof(TransactionId);
xl_heap_multi_insert* xlrec = (xl_heap_multi_insert*)recData;
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
(void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo1);
}
} else

View File

@ -675,7 +675,7 @@ static void DecodeInsert(LogicalDecodingContext* ctx, XLogRecordBuffer* buf)
rc = memcpy_s(&change->data.tp.relnode, sizeof(RelFileNode), &target_node, sizeof(RelFileNode));
securec_check(rc, "\0", "\0");
if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE) {
if (xlrec->flags & XLH_INSERT_CONTAINS_NEW_TUPLE) {
change->data.tp.newtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
DecodeXLogTuple(tupledata, tuplelen, change->data.tp.newtuple);
@ -741,11 +741,11 @@ static void DecodeUpdate(LogicalDecodingContext* ctx, XLogRecordBuffer* buf)
change->origin_id = XLogRecGetOrigin(r);
rc = memcpy_s(&change->data.tp.relnode, sizeof(RelFileNode), &target_node, sizeof(RelFileNode));
securec_check(rc, "", "");
if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE) {
if (xlrec->flags & XLH_UPDATE_CONTAINS_NEW_TUPLE) {
change->data.tp.newtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen_new);
DecodeXLogTuple(data_new, datalen_new, change->data.tp.newtuple);
}
if (xlrec->flags & XLOG_HEAP_CONTAINS_OLD) {
if (xlrec->flags & XLH_UPDATE_CONTAINS_OLD) {
change->data.tp.oldtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen_old);
DecodeXLogTuple(data_old, datalen_old, change->data.tp.oldtuple);
@ -790,7 +790,7 @@ static void DecodeDelete(LogicalDecodingContext* ctx, XLogRecordBuffer* buf)
securec_check(rc, "", "");
/* old primary key stored */
if (xlrec->flags & XLOG_HEAP_CONTAINS_OLD) {
if (xlrec->flags & XLH_DELETE_CONTAINS_OLD) {
Assert(XLogRecGetDataLen(r) > (SizeOfHeapDelete + SizeOfHeapHeader));
change->data.tp.oldtuple = ReorderBufferGetTupleBuf(ctx->reorder, datalen);
@ -848,7 +848,7 @@ static void DecodeMultiInsert(LogicalDecodingContext* ctx, XLogRecordBuffer* buf
* We decode the tuple in pretty much the same way as DecodeXLogTuple,
* but since the layout is slightly different, we can't use it here.
*/
if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE) {
if (xlrec->flags & XLH_INSERT_CONTAINS_NEW_TUPLE) {
HeapTupleHeader header;
xlhdr = (xl_multi_insert_tuple*)data;
data = ((char*)xlhdr) + SizeOfMultiInsertTuple;
@ -889,7 +889,7 @@ static void DecodeMultiInsert(LogicalDecodingContext* ctx, XLogRecordBuffer* buf
* xl_multi_insert_tuple record emitted by one heap_multi_insert()
* call.
*/
if ((xlrec->flags & XLOG_HEAP_LAST_MULTI_INSERT) && ((i + 1) == xlrec->ntuples)) {
if ((xlrec->flags & XLH_INSERT_LAST_IN_MULTI) && ((i + 1) == xlrec->ntuples)) {
change->data.tp.clear_toast_afterwards = true;
} else {
change->data.tp.clear_toast_afterwards = false;

View File

@ -119,6 +119,7 @@ extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate);
extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
extern bool heap_page_prepare_for_xid(
Relation relation, Buffer buffer, TransactionId xid, bool multi, bool pageReplication = false);
extern bool heap_change_xidbase_after_freeze(Relation relation, Buffer buffer);
@ -127,18 +128,19 @@ extern bool rewrite_page_prepare_for_xid(Page page, TransactionId xid, bool mult
extern int heap_multi_insert(Relation relation, Relation parent, HeapTuple* tuples, int ntuples, CommandId cid, int options,
BulkInsertState bistate, HeapMultiInsertExtraArgs* args);
extern HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, TransactionId* update_xmax,
CommandId cid, Snapshot crosscheck, bool wait);
CommandId cid, Snapshot crosscheck, bool wait, bool allow_delete_self = false);
extern HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid, HeapTuple newtup,
ItemPointer ctid, TransactionId* update_xmax, CommandId cid, Snapshot crosscheck, bool wait);
ItemPointer ctid, TransactionId* update_xmax, CommandId cid, Snapshot crosscheck,
bool wait, bool allow_update_self = false);
extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer, ItemPointer ctid,
TransactionId* update_xmax, CommandId cid, LockTupleMode mode, bool nowait);
TransactionId* update_xmax, CommandId cid, LockTupleMode mode, bool nowait, bool allow_lock_self = false);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTuple tuple, TransactionId cutoff_xid);
extern bool heap_tuple_needs_freeze(HeapTuple tuple, TransactionId cutoff_xid, Buffer buf);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_delete(Relation relation, ItemPointer tid, int options = 0);
extern void simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup);
extern void heap_markpos(HeapScanDesc scan);

View File

@ -691,23 +691,47 @@ static inline TransactionId HeapTupleGetRawXmax(HeapTuple tup)
#define XLOG_HEAP3_NEW_CID 0x00
#define XLOG_HEAP3_REWRITE 0x10
/* we used to put all xl_heap_* together, which made us run out of opcodes (quickly)
* when trying to add a DELETE_IS_SUPER operation. Thus we split the codes carefully
* for INSERT, UPDATE, DELETE individually. each has 8 bits available to use.
*/
/*
* xl_heap_* ->flag values, 8 bits are available
* xl_heap_insert/xl_heap_multi_insert flag values, 8 bits are available
*/
/* PD_ALL_VISIBLE was cleared */
#define XLOG_HEAP_ALL_VISIBLE_CLEARED (1 << 0)
#define XLH_INSERT_ALL_VISIBLE_CLEARED (1<<0)
#define XLH_INSERT_CONTAINS_NEW_TUPLE (1<<4)
#define XLH_INSERT_LAST_IN_MULTI (1<<7)
/*
* xl_heap_update flag values, 8 bits are available.
*/
/* PD_ALL_VISIBLE was cleared */
#define XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED (1<<0)
/* PD_ALL_VISIBLE was cleared in the 2nd page */
#define XLOG_HEAP_NEW_ALL_VISIBLE_CLEARED (1 << 1)
#define XLOG_HEAP_CONTAINS_OLD_TUPLE (1 << 2)
#define XLOG_HEAP_CONTAINS_OLD_KEY (1 << 3)
#define XLOG_HEAP_CONTAINS_NEW_TUPLE (1 << 4)
#define XLOG_HEAP_PREFIX_FROM_OLD (1 << 5)
#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)
#define XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED (1<<1)
#define XLH_UPDATE_CONTAINS_OLD_TUPLE (1<<2)
#define XLH_UPDATE_CONTAINS_OLD_KEY (1<<3)
#define XLH_UPDATE_CONTAINS_NEW_TUPLE (1<<4)
#define XLH_UPDATE_PREFIX_FROM_OLD (1<<5)
#define XLH_UPDATE_SUFFIX_FROM_OLD (1<<6)
/* convenience macro for checking whether any form of old tuple was logged */
#define XLOG_HEAP_CONTAINS_OLD (XLOG_HEAP_CONTAINS_OLD_TUPLE | XLOG_HEAP_CONTAINS_OLD_KEY)
#define XLH_UPDATE_CONTAINS_OLD \
(XLH_UPDATE_CONTAINS_OLD_TUPLE | XLH_UPDATE_CONTAINS_OLD_KEY)
/*
* xl_heap_delete flag values, 8 bits are available.
*/
/* PD_ALL_VISIBLE was cleared */
#define XLH_DELETE_ALL_VISIBLE_CLEARED (1<<0)
#define XLH_DELETE_IS_SUPER (1<<1)
#define XLH_DELETE_CONTAINS_OLD_TUPLE (1<<2)
#define XLH_DELETE_CONTAINS_OLD_KEY (1<<3)
/* convenience macro for checking whether any form of old tuple was logged */
#define XLH_DELETE_CONTAINS_OLD \
(XLH_DELETE_CONTAINS_OLD_TUPLE | XLH_DELETE_CONTAINS_OLD_KEY)
/* This is what we need to know about delete */
typedef struct xl_heap_delete {

View File

@ -165,7 +165,7 @@ extern HeapTuple toast_insert_or_update(
* Called by heap_delete().
* ----------
*/
extern void toast_delete(Relation rel, HeapTuple oldtup);
extern void toast_delete(Relation rel, HeapTuple oldtup, int options);
/* ----------
* heap_tuple_fetch_attr() -

View File

@ -48,11 +48,15 @@ typedef enum
#define REINDEX_CGIN_INDEX (1<<5)
#define REINDEX_ALL_INDEX (REINDEX_BTREE_INDEX|REINDEX_HASH_INDEX|REINDEX_GIN_INDEX|REINDEX_GIST_INDEX|REINDEX_CGIN_INDEX)
typedef enum CheckWaitMode
{
CHECK_WAIT,
CHECK_NOWAIT,
} CheckWaitMode;
extern void index_check_primary_key(Relation heapRel, IndexInfo *indexInfo, bool is_alter_table);
typedef struct
{
typedef struct {
Oid existingPSortOid;
bool isPartitionedIndex;
} IndexCreateExtraArgs;
@ -74,6 +78,8 @@ extern void index_drop(Oid indexId, bool concurrent);
extern IndexInfo *BuildIndexInfo(Relation index);
extern IndexInfo *BuildDummyIndexInfo(Relation index);
extern void BuildSpeculativeIndexInfo(Relation index, IndexInfo* ii);
extern void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull);
extern void index_build(Relation heapRelation, Partition heapPartition, Relation indexRelation,
Partition indexPartition, IndexInfo *indexInfo, bool isprimary,

View File

@ -367,13 +367,15 @@ extern void ExecCloseScanRelation(Relation scanrel);
extern Partition ExecOpenScanParitition(
EState* estate, Relation parent, PartitionIdentifier* partID, LOCKMODE lockmode);
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, Relation targetPartRel, Partition p, int2 bucketId);
TupleTableSlot* slot, ItemPointer tupleid, EState* estate, Relation targetPartRel,
Partition p, int2 bucketId, bool* conflict);
extern bool ExecCheckIndexConstraints(TupleTableSlot* slot, EState* estate,
Relation targetRel, Partition p, int2 bucketId, ItemPointer conflictTid);
extern bool check_exclusion_constraint(Relation heap, Relation index, IndexInfo* indexInfo, ItemPointer tupleid,
Datum* values, const bool* isnull, EState* estate, bool newIndex, bool errorOK);
extern void RegisterExprContextCallback(ExprContext* econtext, ExprContextCallbackFunction function, Datum arg);
extern void UnregisterExprContextCallback(ExprContext* econtext, ExprContextCallbackFunction function, Datum arg);
extern List* GetAccessedVarnoList(List* targetList, List* qual);

View File

@ -53,6 +53,7 @@ typedef struct knl_session_attr_sql {
bool enable_sonic_optspill;
bool enable_sonic_hashjoin;
bool enable_sonic_hashagg;
bool enable_upsert_to_merge;
bool enable_csqual_pushdown;
bool enable_change_hjcost;
bool enable_seqscan;

View File

@ -61,6 +61,9 @@ typedef struct UtilityDesc {
* 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?
@ -81,6 +84,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;
@ -396,6 +402,7 @@ typedef struct MergeState {
* ConstraintExprs array of constraint-checking expr states
* junkFilter for removing junk attributes from tuples
* projectReturning for computing a RETURNING list
* updateProj for computing a UPSERT update list
* ----------------
*/
typedef struct ResultRelInfo {
@ -434,6 +441,7 @@ typedef struct ResultRelInfo {
* ri_RangeTableIndex elsewhere.
*/
Index ri_mergeTargetRTI;
ProjectionInfo* ri_updateProj;
} ResultRelInfo;
/* bloom filter controller */
@ -1292,6 +1300,18 @@ typedef struct MergeActionState {
VectorBatch* scanBatch; /* scan batch for UPDATE */
} MergeActionState;
/* ----------------
* UpsertState information
* ----------------
*/
typedef struct UpsertState {
NodeTag type;
UpsertAction us_action; /* Flags showing DUPLICATE UPDATE NOTHING or SOMETHING */
TupleTableSlot* us_existing; /* slot to store existing target tuple in */
List* us_excludedtlist; /* the excluded pseudo relation's tlist */
TupleTableSlot* us_updateproj; /* slot to update */
} UpsertState;
/* ----------------
* ModifyTableState information
* ----------------
@ -1326,7 +1346,7 @@ typedef struct ModifyTableState {
TupleTableSlot* mt_insert_constr_slot; /* slot to store target tuple in for checking constraints */
TupleTableSlot* mt_mergeproj; /* MERGE action projection target */
uint32 mt_merge_subcommands; /* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */
UpsertState* mt_upsert; /* DUPLICATE KEY UPDATE evaluation state */
instr_time first_tuple_modified; /* record the end time for the first tuple inserted, deleted, or updated */
} ModifyTableState;

View File

@ -210,6 +210,7 @@ typedef enum NodeTag {
T_RangeTblRef,
T_JoinExpr,
T_FromExpr,
T_UpsertExpr,
T_IntoClause,
T_IndexVar,
#ifdef PGXC
@ -304,7 +305,7 @@ typedef enum NodeTag {
#endif /* PGXC */
T_StreamPath,
T_MergeAction,
T_UpsertState,
/*
* TAGS FOR MEMORY NODES (memnodes.h)
*/
@ -499,7 +500,7 @@ typedef enum NodeTag {
T_PruningResult,
T_Position,
T_MergeWhenClause,
T_UpsertClause,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
*/
@ -894,4 +895,10 @@ typedef enum JoinType {
(1 << JOIN_RIGHT_ANTI) | (1 << JOIN_LEFT_ANTI_FULL) | (1 << JOIN_RIGHT_ANTI_FULL))) != \
0)
typedef enum UpsertAction {
UPSERT_NONE, /* No "DUPLICATE KEY UPDATE" clause */
UPSERT_NOTHING, /* DUPLICATE KEY UPDATE NOTHING */
UPSERT_UPDATE /* DUPLICATE KEY UPDATE ... */
}UpsertAction;
#endif /* NODES_H */

View File

@ -234,6 +234,7 @@ typedef struct Query {
List* mergeSourceTargetList;
List* mergeActionList; /* list of actions for MERGE (only) */
Query* upsertQuery; /* insert query for INSERT ON DUPLICATE KEY UPDATE (only) */
UpsertExpr* upsertClause; /* DUPLICATE KEY UPDATE [NOTHING | ...] */
bool isRowTriggerShippable; /* true if all row triggers are shippable. */
bool use_star_targets; /* true if use * for targetlist. */
@ -942,6 +943,8 @@ typedef struct RangeTblEntry {
bool relhasbucket; /* the rel has underlying buckets, get from pg_class */
bool isbucket; /* the sql only want some buckets from the rel */
List* buckets; /* the bucket id wanted */
bool isexcluded; /* the rel is the EXCLUDED relation for UPSERT */
} RangeTblEntry;
/*
@ -1139,6 +1142,13 @@ typedef struct WithClause {
int location; /* token location, or -1 if unknown */
} WithClause;
typedef struct UpsertClause
{
NodeTag type;
List* targetList;
int location;
} UpsertClause;
/*
* CommonTableExpr -
* representation of WITH list element
@ -1194,6 +1204,7 @@ typedef struct InsertStmt {
Node* selectStmt; /* the source SELECT/VALUES, or NULL */
List* returningList; /* list of expressions to return */
WithClause* withClause; /* WITH clause */
UpsertClause* upsertClause; /* DUPLICATE KEY UPDATE clause */
} InsertStmt;
/* ----------------------

View File

@ -412,7 +412,13 @@ typedef struct ModifyTable {
Index mergeTargetRelation; /* RT index of the merge target */
List* mergeSourceTargetList;
List* mergeActionList; /* actions for MERGE */
OpMemInfo mem_info; /* Memory info for modify node */
UpsertAction upsertAction; /* DUPLICATE KEY UPDATE action */
List* updateTlist; /* List of UPDATE target */
List* exclRelTlist; /* target list of the EXECLUDED pseudo relation */
Index exclRelRTIndex; /* RTI of the EXCLUDED pseudo relation */
OpMemInfo mem_info; /* Memory info for modify node */
} ModifyTable;
/* ----------------

View File

@ -1359,4 +1359,14 @@ typedef struct {
bool indexpath;
} IndexVar;
typedef struct UpsertExpr {
NodeTag type;
UpsertAction upsertAction; /* DO NOTHING or UPDATE? */
/* DUPLICATE KEY UPDATE */
List* updateTlist; /* List of UPDATE TargetEntrys */
List* exclRelTlist; /* tlist of the 'EXCLUDED' pseudo relation */
int exclRelIndex; /* RT index of 'EXCLUDED' relation */
} UpsertExpr;
#endif /* PRIMNODES_H */

View File

@ -64,6 +64,7 @@ enum FusionType {
NOBYPASS_INVALID_SELECT_FOR_UPDATE,
NOBYPASS_INVALID_MODIFYTABLE,
NOBYPASS_NO_SIMPLE_INSERT,
NOBYPASS_UPSERT_NOT_SUPPORT,
NOBYPASS_NO_INDEXSCAN,
NOBYPASS_INDEXSCAN_WITH_ORDERBY,

View File

@ -109,21 +109,21 @@ extern int get_plan_actual_total_width(Plan* plan, bool vectorized, OpType type,
#ifdef STREAMPLAN
extern Plan* make_modifytable(PlannerInfo* root, CmdType operation, bool canSetTag, List* resultRelations,
List* subplans, List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList, bool isDfsStore = false);
List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause, bool isDfsStore = false);
extern Plan* make_modifytables(PlannerInfo* root, CmdType operation, bool canSetTag, List* resultRelations,
List* subplans, List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore,
Index mergeTargetRelation, List* mergeSourceTargetList, List* mergeActionList);
Index mergeTargetRelation, List* mergeSourceTargetList, List *mergeActionList, UpsertExpr *upsertClause);
extern Plan* make_redistribute_for_agg(PlannerInfo* root, Plan* lefttree, List* redistribute_keys, double multiple,
Distribution* distribution = NULL, bool is_local_redistribute = false);
extern Plan* make_stream_plan(PlannerInfo* root, Plan* lefttree, List* redistribute_keys, double multiple,
Distribution* target_distribution = NULL);
#else
extern ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList, bool isDfsStore = false);
extern ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList);
extern ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRelations,
List* subplans, List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause, bool isDfsStore = false);
extern ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRelations,
List* subplans, List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore,
Index mergeTargetRelation, List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause);
#endif
extern bool is_projection_capable_plan(Plan* plan);

View File

@ -58,7 +58,7 @@ extern Expr* canonicalize_qual(Expr* qual);
* prototypes for preptlist.c
*/
extern List* preprocess_targetlist(PlannerInfo* root, List* tlist);
extern List* preprocess_upsert_targetlist(List* tlist, int result_relation, List* range_table);
extern PlanRowMark* get_plan_rowmark(List* rowmarks, Index rtindex);
/*

View File

@ -19,4 +19,5 @@ extern Query* transformMergeStmt(ParseState* pstate, MergeStmt* stmt);
extern List* expandTargetTL(List* te_list, Query* parsetree);
extern List* expandActionTL(List* te_list, Query* parsetree);
extern List* expandQualTL(List* te_list, Query* parsetree);
extern bool check_unique_constraint(List*& index_list);
#endif

View File

@ -106,7 +106,6 @@ struct ParseState {
bool p_hasSubLinks;
bool p_hasModifyingCTE;
bool p_is_insert;
bool p_is_update;
bool p_locked_from_parent;
bool p_resolve_unknowns; /* resolve unknown-type SELECT outputs as type text */
bool p_hasSynonyms;

View File

@ -23,7 +23,7 @@ extern void checkNameSpaceConflicts(ParseState* pstate, List* namespace1, List*
extern int RTERangeTablePosn(ParseState* pstate, RangeTblEntry* rte, int* sublevels_up);
extern RangeTblEntry* GetRTEByRangeTablePosn(ParseState* pstate, int varno, int sublevels_up);
extern CommonTableExpr* GetCTEForRTE(ParseState* pstate, RangeTblEntry* rte, int rtelevelsup);
extern Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, int location);
extern Node* scanRTEForColumn(ParseState* pstate, RangeTblEntry* rte, char* colname, int location, bool omit = false);
extern Node* colNameToVar(
ParseState* pstate, char* colname, bool localonly, int location, RangeTblEntry** final_rte = NULL);
extern void markVarForSelectPriv(ParseState* pstate, Var* var, RangeTblEntry* rte);

View File

@ -120,7 +120,8 @@ typedef enum {
HeapTupleInvisible,
HeapTupleSelfUpdated,
HeapTupleUpdated,
HeapTupleBeingUpdated
HeapTupleBeingUpdated,
HeapTupleSelfCreated
} HTSU_Result;
#endif /* SNAPSHOT_H */

View File

@ -70,7 +70,7 @@ extern bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer bu
extern bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer);
extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer);
/* Special "satisfies" routines with different APIs */
extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer);
extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer, bool self_visible = false);
extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);

View File

@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_001 CASCADE;
ERROR: schema "test_insert_update_001" does not exist
CREATE SCHEMA test_insert_update_001;
SET CURRENT_SCHEMA TO test_insert_update_001;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test description
\h INSERT
Command: INSERT

View File

@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_002 CASCADE;
ERROR: schema "test_insert_update_002" does not exist
CREATE SCHEMA test_insert_update_002;
SET CURRENT_SCHEMA TO test_insert_update_002;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,

View File

@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_003 CASCADE;
ERROR: schema "test_insert_update_003" does not exist
CREATE SCHEMA test_insert_update_003;
SET CURRENT_SCHEMA TO test_insert_update_003;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,

View File

@ -4,6 +4,9 @@
-- initial
CREATE SCHEMA test_insert_update_008;
SET current_schema = test_insert_update_008;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
CREATE TABLE products_base
(
product_id INTEGER DEFAULT 0,
@ -58,9 +61,9 @@ EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge on test_insert_update_008.products_row
@ -84,9 +87,9 @@ INSERT INTO products_row
FROM newproducts_row, products_row
WHERE products_row.total + newproducts_row.total < 1000
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge on test_insert_update_008.products_row
@ -113,9 +116,9 @@ INSERT INTO products_row
SELECT product_id, product_name, category, total
FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge on test_insert_update_008.products_row
@ -137,9 +140,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------
Merge on products_row (actual rows=4 loops=1)
@ -162,9 +165,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
-- explain analyze
@ -173,9 +176,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------
Merge on products_row (actual rows=4 loops=1)
@ -198,9 +201,9 @@ EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge on test_insert_update_008.products_row
@ -221,9 +224,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------
Merge on products_row (actual rows=4 loops=1)
@ -245,9 +248,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------
Merge on products_row (actual rows=4 loops=1)
@ -270,9 +273,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = run;
BEGIN;
@ -280,9 +283,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = summary;
BEGIN;
@ -290,9 +293,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
CREATE TABLE item

View File

@ -3,6 +3,9 @@ ERROR: schema "test_insert_update_009" does not exist
CREATE SCHEMA test_insert_update_009;
SET CURRENT_SCHEMA TO test_insert_update_009;
SET enable_light_proxy=off;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,

View File

@ -2,6 +2,9 @@ DROP SCHEMA test_insert_update_010 CASCADE;
ERROR: schema "test_insert_update_010" does not exist
CREATE SCHEMA test_insert_update_010;
SET CURRENT_SCHEMA TO test_insert_update_010;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,

View File

@ -1,3 +1,6 @@
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%' ORDER BY name;
name | setting
-----------------------------------+---------
@ -74,12 +77,13 @@ SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%' ORDER BY name;
enable_thread_pool | off
enable_tidscan | on
enable_upgrade_merge_lock_mode | off
enable_upsert_to_merge | on
enable_user_metric_persistent | on
enable_valuepartition_pruning | on
enable_vector_engine | on
enable_wdr_snapshot | off
enable_xlog_prune | on
(78 rows)
(79 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);

View File

@ -0,0 +1,515 @@
DROP SCHEMA test_upsert_001 CASCADE;
ERROR: schema "test_upsert_001" does not exist
CREATE SCHEMA test_upsert_001;
SET CURRENT_SCHEMA TO test_upsert_001;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test description
\h INSERT
Command: INSERT
Description: create new rows in a table
Syntax:
[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT INTO table_name [ ( column_name [, ...] ) ]
{ DEFAULT VALUES | VALUES {( { expression | DEFAULT } [, ...] ) }[, ...] | query }
[ ON DUPLICATE KEY UPDATE { column_name = { expression | DEFAULT } } [, ...] ]
[ RETURNING {* | {output_expression [ [ AS ] output_name ] }[, ...]} ];
-- test permission
--- test with no sequence column
CREATE TABLE t00 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t00_pkey" for table "t00"
CREATE USER upsert_tester PASSWORD '123456@cc';
GRANT ALL PRIVILEGES ON SCHEMA test_upsert_001 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: permission denied for relation t00
RESET SESSION AUTHORIZATION;
---- error: only have INSERT permission
GRANT INSERT ON test_upsert_001.t00 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: permission denied for relation t00
RESET SESSION AUTHORIZATION;
---- success: have INSERT UPDATE permission
GRANT INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
RESET SESSION AUTHORIZATION;
--- have SELECT INSERT UPDATE permission
GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
ERROR: column "col3" of relation "t00" does not exist
LINE 1: ...t_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
^
RESET SESSION AUTHORIZATION;
--- test with sequnce column
CREATE TABLE t01 (col1 INT , col2 BIGSERIAL PRIMARY KEY, col3 INT) ;
NOTICE: CREATE TABLE will create implicit sequence "t01_col2_seq" for serial column "t01.col2"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t01_pkey" for table "t01"
---- error: don't have UPDATE permission on sequence table.
GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t01 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
ERROR: permission denied for sequence t01_col2_seq
CONTEXT: referenced column: col2
RESET SESSION AUTHORIZATION;
---- have SELECT INSERT UPDATE permission on target relation, and UPDATE permission on sequence table.
GRANT UPDATE ON test_upsert_001.t01_col2_seq TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
RESET SESSION AUTHORIZATION;
-- test ommit INSERT target column
CREATE TABLE t02 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT, col3 INT);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t02_pkey" for table "t02"
INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20;
SELECT * FROM t02 ORDER BY 1, 2;
col1 | col2 | col3
------+------+------
1 | 2 | 3
(1 row)
INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20;
SELECT * FROM t02 ORDER BY 1, 2;
col1 | col2 | col3
------+------+------
1 | 20 | 3
(1 row)
ALTER TABLE t02 DROP COLUMN col2;
ALTER TABLE t02 ADD COLUMN col4 INT;
INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col4 = 40;
SELECT * FROM t02 ORDER BY 1, 2;
col1 | col3 | col4
------+------+------
1 | 3 | 40
(1 row)
INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40;
SELECT * FROM t02 ORDER BY 1, 2;
col1 | col3 | col4
------+------+------
1 | 3 | 40
2 | 3 | 4
(2 rows)
INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40;
SELECT * FROM t02 ORDER BY 1, 2;
col1 | col3 | col4
------+------+------
1 | 3 | 40
2 | 3 | 40
(2 rows)
-- test restriction
--- test replication table
CREATE TABLE t03 (col1 int PRIMARY KEY, col2 INT, col3 smallserial) ;
NOTICE: CREATE TABLE will create implicit sequence "t03_col3_seq" for serial column "t03.col3"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t03_pkey" for table "t03"
--- error: not allowed volatile function as default value
INSERT INTO t03(col2) VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100;
ERROR: null value in column "col1" violates not-null constraint
DETAIL: Failing row contains (null, 1, 1).
ALTER TABLE t03 DROP COLUMN col3;
--- error: primary key are not allowed to update
INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col1 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- error: clause other than VALUSES are not allowed to use
INSERT INTO t03 SELECT * FROM t03 ON DUPLICATE KEY UPDATE col2 = 1;
--- success: expression index are supported
CREATE UNIQUE INDEX u_expr_index ON t03 USING btree (abs(col1));
INSERT INTO t03 VALUES(-10, 10) ON DUPLICATE KEY UPDATE col2 = 20;
DROP INDEX u_expr_index;
-- test with stream operator on
INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100;
SELECT * FROM t03;
col1 | col2
------+------
-10 | 10
1 |
(2 rows)
INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100;
SELECT * FROM t03;
col1 | col2
------+------
-10 | 10
1 | 100
(2 rows)
SELECT * FROM t03;
col1 | col2
------+------
-10 | 10
1 | 100
(2 rows)
--- test PBE
PREPARE p1 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100;
EXECUTE p1(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
col1 | col2
------+------
5 | 50
(1 row)
EXECUTE p1(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
col1 | col2
------+------
5 | 500
(1 row)
DELETE t03 WHERE col1 = 5;
---- test with primary key
INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200;
SELECT * FROM t03;
col1 | col2
------+------
-10 | 10
1 | 100
2 |
(3 rows)
INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200;
SELECT * FROM t03;
col1 | col2
------+------
-10 | 10
1 | 100
2 | 200
(3 rows)
SELECT * FROM t03;
col1 | col2
------+------
-10 | 10
1 | 100
2 | 200
(3 rows)
---- test with unique key without NOT NULL constraint
ALTER TABLE t03 DROP CONSTRAINT t03_pkey;
ALTER TABLE t03 ADD COLUMN col3 INT;
CREATE UNIQUE INDEX ON t03 (col1, col3);
---- unique constraints might contain NULL, depends on the plan
----- for cn light and fqs, it can be done
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
----- for stream or pgxc it can not be done
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
---- test with unique key with NOT NULL constraint, should success
CREATE UNIQUE INDEX ON t03 (col1);
ERROR: could not create unique index "t03_col1_idx"
DETAIL: Key (col1)=(3) is duplicated.
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
SELECT * FROM t03;
col1 | col2 | col3
------+------+------
-10 | 10 |
1 | 100 |
2 | 200 |
3 | |
3 | |
3 | |
(6 rows)
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
SELECT * FROM t03;
col1 | col2 | col3
------+------+------
-10 | 10 |
1 | 100 |
2 | 200 |
3 | |
3 | |
3 | |
3 | |
(7 rows)
SELECT * FROM t03;
col1 | col2 | col3
------+------+------
-10 | 10 |
1 | 100 |
2 | 200 |
3 | |
3 | |
3 | |
3 | |
(7 rows)
---- test PBE
PREPARE p2 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100;
EXECUTE p2(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
col1 | col2 | col3
------+------+------
5 | 50 |
(1 row)
EXECUTE p2(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
col1 | col2 | col3
------+------+------
5 | 50 |
5 | 50 |
(2 rows)
--- error: test with clause
WITH tmp(col1, col2) AS (SELECT * FROM t01)
INSERT INTO t01 SELECT * FROM tmp ON DUPLICATE KEY UPDATE col1 = 1;
ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
WITH RECURSIVE rq AS
(
SELECT col1, col2 FROM t00 WHERE col1 = 1
UNION ALL
SELECT origin.col1, rq.col2
FROM rq JOIN t00 AS origin ON origin.col1 = rq.col1
)
INSERT INTO t03 SELECT * FROM rq ON DUPLICATE KEY UPDATE col1 = rq.col1;
ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
--- error: test returning clause
INSERT INTO t01 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 1 RETURNING NOT(1::bool);
ERROR: RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
--- error: distribute key are not allowed to UPDATE
CREATE TABLE t04 (col1 INT, col2 INT) ;
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 5;
--- error: unique index referenced column are not allowed to UPDATE
CREATE UNIQUE INDEX t04_u_index ON t04(col1, col2);
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
DROP INDEX t04_u_index;
--- error: primary key referenced column are not allowed to UPDATE
ALTER TABLE t04 ADD PRIMARY KEY (col1, col2);
ERROR: column "col2" contains null values
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
--- error: invalid column
INSERT INTO t04 (col2, col3) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: column "col3" of relation "t04" does not exist
LINE 1: INSERT INTO t04 (col2, col3) VALUES (2, 3) ON DUPLICATE KEY ...
^
--- error: duplicate column
INSERT INTO t04 (col2, col2) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: column "col2" specified more than once
LINE 1: INSERT INTO t04 (col2, col2) VALUES (2, 3) ON DUPLICATE KEY ...
^
-- error: target column more than insert target
INSERT INTO t04 (col1, col2) VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT has more expressions than target columns
LINE 1: INSERT INTO t04 (col1, col2) VALUES (2, 3, 4) ON DUPLICATE K...
^
INSERT INTO t04 (col1, col2) VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT has more target columns than expressions
LINE 1: INSERT INTO t04 (col1, col2) VALUES (1) ON DUPLICATE KEY UPD...
^
INSERT INTO t04 (col1, col2) SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT has more target columns than expressions
LINE 1: INSERT INTO t04 (col1, col2) SELECT col1 FROM t04 ON DUPLICA...
^
INSERT INTO t04 (col1, col2) SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT has more expressions than target columns
LINE 1: INSERT INTO t04 (col1, col2) SELECT *, col1 FROM t04 ON DUPL...
^
INSERT INTO t04 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT has more expressions than target columns
LINE 1: INSERT INTO t04 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col...
^
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
ERROR: INSERT has more expressions than target columns
LINE 1: INSERT INTO t04 SELECT *, col1 FROM t04 ON DUPLICATE KEY UPD...
^
-- test DEFAULT VALUES
TRUNCATE t00;
TRUNCATE t01;
--- without sequence
----should insert
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
col1 | col2
------+------
1 |
(1 row)
---- should update
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
col1 | col2
------+------
1 | 1
(1 row)
--- test drop column
TRUNCATE t00;
ALTER TABLE t00 DROP COLUMN col2;
ALTER TABLE t00 ADD COLUMN col3 INT DEFAULT 100;
----should insert
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
col1 | col3
------+------
1 | 100
(1 row)
---- should update
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
col1 | col3
------+------
1 | 1
(1 row)
--- with sequence
----should insert
INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2;
INSERT INTO t01 (col2) SELECT col2 + 1 FROM t01 LIMIT 1;
SELECT * FROM t01 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
| 3 |
| 4 |
(2 rows)
---- should update
INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2;
SELECT * FROM t01 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
4 | 4 |
| 3 |
(2 rows)
-- test VALUES(DEFAULT)
CREATE TABLE t05 (col1 INT , col2 INT DEFAULT 1 PRIMARY KEY, col3 INT DEFAULT 100) ;
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t05_pkey" for table "t05"
--- should insert
INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
| 1 | 100
(1 row)
--- should update
INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
| 1 | 1000
(1 row)
-- test UPDATE DEFAULT
INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
2 | 2 | 2
| 1 | 1000
(2 rows)
INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
2 | 2 | 100
| 1 | 1000
(2 rows)
-- test VALUES (DEFAULT, ...)
TRUNCATE t05;
--- should insert
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
| 1 | 200
| 200 | 100
(2 rows)
--- should update
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
100 | 200 | 100
200 | 1 | 100
(2 rows)
--- test drop coulmn
BEGIN;
ALTER TABLE t05 ADD COLUMN col4 INT;
ALTER TABLE t05 ADD COLUMN col5 INT DEFAULT 500;
ALTER TABLE t05 DROP COLUMN col3;
TRUNCATE t05;
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col4 | col5
------+------+------+------
| 1 | | 600
(1 row)
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
col1 | col2 | col4 | col5
------+------+------+------
| 1 | | 500
(1 row)
ROLLBACK;
-- test schema
SET current_schema = public;
TRUNCATE test_upsert_001.t05;
--- should insert
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
| 1 | 200
| 200 | 100
(2 rows)
--- should update
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
100 | 200 | 100
200 | 1 | 100
(2 rows)
--- test using schema on update
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1;
SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3;
col1 | col2 | col3
------+------+------
101 | 1 | 100
101 | 200 | 100
(2 rows)
--- error: should not append schema
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE test_upsert_001.t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1;
ERROR: column "test_upsert_001.t05" of relation "t05" does not exist
LINE 2: ON DUPLICATE KEY UPDATE test_upsert_001.t05.col3 = DEFAULT, ...
^
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = test_upsert_001.t05.col3 + 1;
SET CURRENT_SCHEMA TO test_upsert_001;
DROP USER upsert_tester CASCADE;
DROP SCHEMA test_upsert_001 CASCADE;
NOTICE: drop cascades to 6 other objects
DETAIL: drop cascades to table t00
drop cascades to table t01
drop cascades to table t02
drop cascades to table t03
drop cascades to table t04
drop cascades to table t05

View File

@ -0,0 +1,545 @@
DROP SCHEMA test_upsert_002 CASCADE;
ERROR: schema "test_upsert_002" does not exist
CREATE SCHEMA test_upsert_002;
SET CURRENT_SCHEMA TO test_upsert_002;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
NOTICE: CREATE TABLE will create implicit sequence "t1_col5_seq" for serial column "t1.col5"
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3;
--- should always insert
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3;
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4;
--- appoint column list in insert clause, should always insert
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6;
--- multiple rows, should always insert
INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7;
SELECT * FROM t1 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 2 | 1 | 1
1 | 2 | 1 | 2
1 | 2 | 1 | 3
1 | | 3 | 4
1 | | 3 | 5
2 | 1 | 1 | 6
2 | 1 | 1 | 7
(7 rows)
--- test union, should insert
INSERT INTO t1 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3) * 10;
SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2;
col1 | col2 | col3
------+------+------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | | 1
2 | 1 | 1
2 | 1 | 1
(6 rows)
--- test subquery, should insert
INSERT INTO t1
(SELECT col1 || col2 || '00' FROM t1 ORDER BY col5)
ON DUPLICATE KEY UPDATE col3 = col1 * 100;
SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1;
col1 | col2 | col3
------+------+------
100 | | 1
100 | | 1
100 | | 1
1100 | | 1
1200 | | 1
1200 | | 1
1200 | | 1
1200 | | 1
1300 | | 1
2100 | | 1
2100 | | 1
2100 | | 1
(12 rows)
-- test t2 with one primary key
CREATE TABLE t2 (
col1 INT,
col2 INT PRIMARY KEY,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
NOTICE: CREATE TABLE will create implicit sequence "t2_col5_seq" for serial column "t2.col5"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2"
--- primary key or unique key are not allowed to update
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col2 = 100,
col3 = 100,
col4 = '2019-08-09'::TIMESTAMP,
col5 = 100;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- should insert
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30;
INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40;
INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2;
INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1;
INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 1 | 1 | 1
2 | 2 | 1 | 2
3 | 3 | 1 | 3
4 | 4 | 1 | 4
5 | 5 | 1 | 5
6 | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
(8 rows)
--- should update
INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1;
INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5;
INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
30 | 1 | 2 | 1
40 | 2 | 2 | 2
3 | 3 | 4 | 3
4 | 4 | 5 | 4
--?.*| 5 | 1 | 5
--?.*| 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
(8 rows)
-- primary key are not allowed to be null
INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10;
ERROR: null value in column "col2" violates not-null constraint
--?
--- appoint column list in insert clause
---- should insert
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5;
col1 | col2 | col3 | col4 | col5
------+------+------+---------------------------------+------
--? 30 | 1 | 2 | .* | 1
--? 40 | 2 | 2 | .* | 2
--? 3 | 3 | 4 | .* | 3
--? 4 | 4 | 5 | .* | 4
--? .* | 5 | 1 | .* | 5
--? .* | 6 | 1 | .* | 6
--? 7 | 7 | 1 | .* | 7
--? 8 | 8 | 1 | .* | 8
--? | 10 | 10 | .* | 10
--? | 9 | 9 | .* | 16
--? | 20 | 20 | .* | 20
--? | 30 | 30 | .* | 30
(12 rows)
---- should update
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col4 | col5
------+------+------+---------------------------------+------
--? 30 | 1 | 2 | .* | 1
--? 40 | 2 | 2 | .* | 2
--? 3 | 3 | 4 | .* | 3
--? 4 | 4 | 5 | .* | 4
--? .* | 5 | 1 | .* | 5
--? .* | 6 | 1 | .* | 6
--? 7 | 7 | 1 | .* | 7
--? 8 | 8 | 1 | .* | 8
--? 90 | 9 | 9 | .* | 16
100 | 10 | 100 | Tue Aug 20 00:00:00 2019 | 100
100 | 20 | 100 | Tue Aug 20 00:00:00 2019 | 100
100 | 30 | 100 | Tue Aug 20 00:00:00 2019 | 100
(12 rows)
--- test subquery
---- should insert
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+------+------
30 | 1 | 2 | 1
40 | 2 | 2 | 2
3 | 3 | 4 | 3
4 | 4 | 5 | 4
--? .* | 5 | 1 | 5
--? .* | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1 | 11
--? 40000 | 2001 | 1 | 12
--? | 40002 | 2000 | 13
--? | 3002 | 3000 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
---- should update
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+-------+------
30 | 1 | 2 | 1
40 | 2 | 2 | 2
3 | 3 | 4 | 3
4 | 4 | 5 | 4
--? .* | 5 | 1 | 5
--? .* | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1002 | 11
--? 40000 | 2001 | 2002 | 12
--? | 40002 | 40003 | 13
--? | 3002 | 3003 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
--- test union, some insert some update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1 ORDER BY 1, 2;
col1 | col2
------+------
1 | 1
1 | 2
1 | 3
2 | 1
100 | 1
1100 | 1
1200 | 1
1300 | 1
2100 | 1
(9 rows)
INSERT INTO t2 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+-------+------
30 | 1 | 219 | 1
40 | 2 | 44 | 2
3 | 3 | 10 | 3
4 | 4 | 5 | 4
--? .* | 5 | 1 | 5
--? .* | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1002 | 11
--? 40000 | 2001 | 2002 | 12
--? | 40002 | 40003 | 13
--? | 3002 | 3003 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+-------+------
30 | 1 | 226 | 1
40 | 2 | 45 | 2
3 | 3 | 11 | 3
4 | 4 | 5 | 4
--? 15 | 5 | 1 | 5
--? 2105 | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1002 | 11
--? 40000 | 2001 | 2002 | 12
--? | 40002 | 40003 | 13
--? | 3002 | 3003 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
reset behavior_compat_options;
-- test INTERSECT, should update
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1;
col1 | ?column?
------+----------
1 | 3
(1 row)
INSERT INTO t2 (col1, col2)
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2;
col1 | col2 | col3 | col5
------+------+------+------
3 | 3 | 12 | 3
(1 row)
-- test EXCEPT, should update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
col1 | col2
------+------
1 | 2
(1 row)
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
------+------+------+------
40 | 2 | 46 | 2
(1 row)
-- test unique index with not default value
ALTER TABLE t2 DROP CONSTRAINT t2_pkey;
CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5);
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
col1 | col2
------+------
1 | 2
(1 row)
INSERT INTO t2
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
------+------+------+------
40 | 2 | 46 | 2
1 | 2 | 1 | 46
(2 rows)
-- test t3 with one primary index with two columns
CREATE TABLE t3 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3)
) ;
NOTICE: CREATE TABLE will create implicit sequence "t3_col5_seq" for serial column "t3.col5"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t3_pkey" for table "t3"
--- column referenced by primary key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- should insert when not contains primary key and not all primary key referred columns have default value.
--- but will fail cause primary key should not be null
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
ERROR: null value in column "col2" violates not-null constraint
--?.*
--- should insert
--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence)
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 1 | 1 | 2
2 | 2 | 2 | 3
(2 rows)
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
10 | 1 | 1 | 2
2 | 2 | 2 | 20
(2 rows)
--- test subquery
---- should insert
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
10 | 1 | 1 | 2
| 3 | 3 | 6
2 | 2 | 2 | 20
(3 rows)
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
10 | 1 | 1 | 2
6 | 3 | 3 | 6
2 | 2 | 2 | 20
(3 rows)
-- test t3 with one unique index with two columns
TRUNCATE t3;
ALTER TABLE t3 DROP CONSTRAINT t3_pkey;
ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL;
CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3);
--- column referenced by unique key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- should insert cause not contains unique key and not all unique key referred columns have default value.
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
(2 rows)
--- should insert
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
1 | 1 | 1 | 10
2 | 2 | 2 | 11
(4 rows)
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
10 | 1 | 1 | 10
2 | 2 | 2 | 20
(4 rows)
--- test subquery
---- should insert
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
10 | 1 | 1 | 10
100 | | 2 | 14
100 | | 2 | 15
| 3 | 3 | 16
2 | 2 | 2 | 20
(7 rows)
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
10 | 1 | 1 | 10
100 | | 2 | 14
100 | | 2 | 15
6 | 3 | 3 | 16
2 | 2 | 2 | 20
(7 rows)
DROP SCHEMA test_upsert_002 CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table t1
drop cascades to table t2
drop cascades to table t3

View File

@ -0,0 +1,313 @@
DROP SCHEMA test_insert_update_003 CASCADE;
ERROR: schema "test_insert_update_003" does not exist
CREATE SCHEMA test_insert_update_003;
SET CURRENT_SCHEMA TO test_insert_update_003;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,
col2 INT DEFAULT 0,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3, col5)
) ;
NOTICE: CREATE TABLE will create implicit sequence "t4_col5_seq" for serial column "t4.col5"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t4_pkey" for table "t4"
--- should insert
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 0 | 1 | 1
1 | 0 | 1 | 2
2 | 2 | 2 | 3
100 | 100 | 100 | 100
(4 rows)
--- should update
INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 0 | 1 | 1
1 | 0 | 1 | 2
200 | 2 | 2 | 3
1000 | 100 | 100 | 100
(4 rows)
--- error: duplicate key update on (x, x, 20)
--- this is because current version is not inplace update but merge,
--- so when the subquery contains multiple same values, it will cause duplicate insert failure.
SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2;
col3 | ?column?
------+----------
1 | 20
2 | 20
100 | 1000
(3 rows)
INSERT INTO t4 (col1, col5)
(SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3)
ON DUPLICATE KEY UPDATE col1 = 3;
-- test t5 with sequence or default column with volatile function in constaint index
CREATE TABLE t5 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM() + 1
) ;
NOTICE: CREATE TABLE will create implicit sequence "t5_col3_seq" for serial column "t5.col3"
-- test t5 with sequence column in constaint index
CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3);
--- should insert
INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 1 | 1 | 1 | 1.58182
--? 1 | 1 | 2 | 1.00814
--? 1 | 1 | 3 | 1.50194
--? | 1 | 4 | 1.12955
(4 rows)
--- should update
INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10;
SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
1 | 10 | 1 | 1.00000
1 | 20 | 2 | 1.00000
1 | 30 | 3 | 1.00000
(3 rows)
--- should some insert some update
INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
col1 | col2 | col3
------+------+------
1 | 10 | 1
1 | 20 | 2
1 | 30 | 3
| 1 | 4
2 | 1 | 5
2 | 1 | 6
(6 rows)
INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
col1 | col2 | col3
------+------+------
1 | 10 | 1
1 | 20 | 2
1 | 30 | 3
| 1 | 4
2 | 5 | 5
2 | 6 | 6
2 | 1 | 7
(7 rows)
--- should INSERT and sequence starting from 7
INSERT INTO t5 VALUES (2), (2);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
col1 | col2 | col3
------+------+------
1 | 10 | 1
1 | 20 | 2
1 | 30 | 3
| 1 | 4
2 | 5 | 5
2 | 6 | 6
2 | 1 | 7
2 | 1 | 8
2 | 1 | 9
(9 rows)
-- test with volatile function as default column in constraint index
TRUNCATE t5;
DROP INDEX u_t5_index1;
CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2;
--- should insert
INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 1 | 10 | 1.24521
--? 3 | 1 | 11 | 1.80598
--? 3 | 1 | 12 | 1.86845
--? 4 | 1 | 13 | 1.65797
--? 4 | 1 | 14 | 1.07881
--? 4 | 1 | 15 | 1.69493
(6 rows)
--- should update
INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 125 | 10 | 1.24521
--? 3 | 181 | 11 | 1.80598
--? 3 | 187 | 12 | 1.86845
--? 4 | 1 | 13 | 1.65797
--? 4 | 1 | 14 | 1.07881
--? 4 | 1 | 15 | 1.69493
(6 rows)
--- test subquery
---- should insert
INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 167 | 10 | 1.67400
--? 3 | 132 | 11 | 1.31552
--? 3 | 129 | 12 | 1.29423
--? 4 | 1 | 13 | 1.44100
--? 4 | 1 | 14 | 1.37620
--? 4 | 1 | 15 | 1.51296
--? 4 | 1 | 16 | 1.37516
--? 4 | 1 | 17 | 1.21710
--? 4 | 1 | 18 | 1.50422
--? 3 | 132 | 19 | 1.30560
--? 3 | 167 | 20 | 1.62282
--? 3 | 129 | 21 | 1.76699
(12 rows)
---- should update
INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 1674 | 10 | 1.67400
--? 3 | 1316 | 11 | 1.31552
--? 3 | 1294 | 12 | 1.29423
--? 4 | 1441 | 13 | 1.44100
--? 4 | 1376 | 14 | 1.37620
--? 4 | 1513 | 15 | 1.51296
--? 4 | 1375 | 16 | 1.37516
--? 4 | 1217 | 17 | 1.21710
--? 4 | 1504 | 18 | 1.50422
--? 3 | 1306 | 19 | 1.30560
--? 3 | 1623 | 20 | 1.62282
--? 3 | 1767 | 21 | 1.76699
(12 rows)
-- test t6 with one more index
CREATE TABLE t6 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM(),
col6 INT,
col7 TEXT
) ;
NOTICE: CREATE TABLE will create implicit sequence "t6_col3_seq" for serial column "t6.col3"
ALTER TABLE t6 ADD PRIMARY KEY (col1, col3);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "t6_pkey" for table "t6"
CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6);
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5);
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
1 | 1 | 1 | |
2 | 1 | 2 | |
3 | 1 | 3 | |
4 | 1 | 4 | |
5 | 1 | 5 | |
(5 rows)
--- should not insert
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2);
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
1 | 1 | 1 | |
2 | 1 | 2 | |
3 | 1 | 3 | |
4 | 1 | 4 | |
5 | 1 | 5 | |
(5 rows)
--- should update because primary key matches
INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12);
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 11 | |
6 | 1 | 12 | |
(2 rows)
INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 11 | |
6 | 1 | 12 | |
(2 rows)
--- should update for those col6 is not null bacause they will match the unique index,
--- and insert for those col6 is null because the unique index containing null never matches,
--- also primary key will not match
--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows,
--- because those sequence have to be generated during the unique index join stage.
INSERT INTO t6 (col1, col5, col6)
(SELECT col1, col5, col6 FROM t6 WHERE col1 = 6)
ON DUPLICATE KEY UPDATE
col7 = col2 + 1;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 6 | |
6 | 1 | 7 | |
6 | 1 | 11 | |
6 | 1 | 12 | |
(4 rows)
--- should update because unique index and primary key both match
INSERT INTO t6 (col1, col3, col5, col6)
(SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL)
ON DUPLICATE KEY UPDATE
col7 = col7 * 10;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 6 | |
6 | 1 | 7 | |
6 | 1 | 11 | |
6 | 1 | 12 | |
(4 rows)
--- insert when col3 = 17 because constaints does not match,
--- but update when col3 = 18 because 18 has been inserted and will cause a match
INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100);
SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col5 | col6 | col7
------+------+------+-----------+------+------
7 | 1 | 18 | 100.00000 | 100 |
(1 row)
INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE
col7 = col3 * 100;
SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col5 | col6 | col7
------+------+------+-----------+------+------
7 | 1 | 8 | 10.00000 | 10 |
7 | 1 | 18 | 100.00000 | 100 | 1800
(2 rows)
DROP SCHEMA test_insert_update_003 CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table t4
drop cascades to table t5
drop cascades to table t6

View File

@ -0,0 +1,466 @@
--
-- INSERT UPDATE, test explain command, comes from merge_explain and merge_explain_pretty
--
-- initial
CREATE SCHEMA test_insert_update_008;
SET current_schema = test_insert_update_008;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
CREATE TABLE products_base
(
product_id INTEGER DEFAULT 0,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
INSERT INTO products_base VALUES (1501, 'vivitar 35mm', 'electrncs', 100);
INSERT INTO products_base VALUES (1502, 'olympus is50', 'electrncs', 100);
INSERT INTO products_base VALUES (1600, 'play gym', 'toys', 100);
INSERT INTO products_base VALUES (1601, 'lamaze', 'toys', 100);
INSERT INTO products_base VALUES (1666, 'harry potter', 'dvd', 100);
CREATE TABLE newproducts_base
(
product_id INTEGER DEFAULT 0,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
INSERT INTO newproducts_base VALUES (1502, 'olympus camera', 'electrncs', 200);
INSERT INTO newproducts_base VALUES (1601, 'lamaze', 'toys', 200);
INSERT INTO newproducts_base VALUES (1666, 'harry potter', 'toys', 200);
INSERT INTO newproducts_base VALUES (1700, 'wait interface', 'books', 200);
ANALYZE products_base;
ANALYZE newproducts_base;
--
-- row table
--
CREATE TABLE products_row
(
product_id INTEGER DEFAULT 0 PRIMARY KEY,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "products_row_pkey" for table "products_row"
CREATE TABLE newproducts_row
(
product_id INTEGER DEFAULT 0 PRIMARY KEY,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "newproducts_row_pkey" for table "newproducts_row"
INSERT INTO products_row SELECT * FROM products_base;
INSERT INTO newproducts_row SELECT * FROM newproducts_base;
ANALYZE products_row;
ANALYZE newproducts_row;
SET explain_perf_mode = normal;
-- explain verbose
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on test_insert_update_008.newproducts_row
Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total
(5 rows)
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT newproducts_row.product_id,
newproducts_row.product_name,
newproducts_row.category,
newproducts_row.total
FROM newproducts_row, products_row
WHERE products_row.total + newproducts_row.total < 1000
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Nested Loop
Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total
Join Filter: ((test_insert_update_008.products_row.total + newproducts_row.total) < 1000)
-> Seq Scan on test_insert_update_008.products_row
Output: test_insert_update_008.products_row.product_id, test_insert_update_008.products_row.product_name, test_insert_update_008.products_row.category, test_insert_update_008.products_row.total
-> Materialize
Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total
-> Seq Scan on test_insert_update_008.newproducts_row
Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total
(12 rows)
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total
FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on test_insert_update_008.newproducts_row
Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total
Filter: ((newproducts_row.product_id IS NOT NULL) AND (newproducts_row.product_name IS NOT NULL))
(6 rows)
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-----------------------------------------------------------
Insert on products_row (actual rows=4 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on newproducts_row (actual rows=4 loops=1)
--? Total runtime: .* ms
(5 rows)
ROLLBACK;
-- explain performance
\o insert_update_explain.txt
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-----------------------------------------------------------
Insert on products_row (actual rows=4 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on newproducts_row (actual rows=4 loops=1)
--? Total runtime: .* ms
(5 rows)
ROLLBACK;
-- pretty mode performance
SET explain_perf_mode = pretty;
-- explain verbose
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on test_insert_update_008.newproducts_row
Output: newproducts_row.product_id, newproducts_row.product_name, newproducts_row.category, newproducts_row.total
(5 rows)
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-----------------------------------------------------------
Insert on products_row (actual rows=4 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on newproducts_row (actual rows=4 loops=1)
--? Total runtime: .* ms
(5 rows)
ROLLBACK;
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
QUERY PLAN
-----------------------------------------------------------
Insert on products_row (actual rows=4 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Seq Scan on newproducts_row (actual rows=4 loops=1)
--? Total runtime: .* ms
(5 rows)
ROLLBACK;
-- explain performance
\o insert_update_explain_pretty.txt
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = run;
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = summary;
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
CREATE TABLE item
(
a INT DEFAULT 3,
item_id NUMERIC(18,10),
item_name VARCHAR(100),
item_level NUMERIC(39,0),
item_desc VARCHAR(250),
item_subclass_cd VARCHAR(50),
item_type_cd VARCHAR(50),
inventory_ind CHAR(300),
vendor_party_id SMALLINT,
commodity_cd VARCHAR(50),
brand_cd VARCHAR(50),
item_available CHAR(100),
CONSTRAINT u_item_index UNIQUE (item_subclass_cd, vendor_party_id)
)
PARTITION BY RANGE (vendor_party_id)
(
PARTITION item_1 VALUES LESS THAN (0),
PARTITION item_2 VALUES LESS THAN (1),
PARTITION item_3 VALUES LESS THAN (2),
PARTITION item_4 VALUES LESS THAN (3),
PARTITION item_5 VALUES LESS THAN (6),
PARTITION item_6 VALUES LESS THAN (8),
PARTITION item_7 VALUES LESS THAN (10),
PARTITION item_8 VALUES LESS THAN (15),
PARTITION item_9 VALUES LESS THAN (MAXVALUE)
) ENABLE ROW MOVEMENT;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "u_item_index" for table "item"
CREATE TABLE region
(
a INT DEFAULT 8,
region_cd VARCHAR(50),
region_name VARCHAR(100),
division_cd VARCHAR(50),
region_mgr_associate_id number(18,9)
);
CREATE TABLE associate_benefit_expense
(
a INT DEFAULT 44,
period_end_dt DATE,
associate_expns_type_cd VARCHAR(50),
associate_party_id INTEGER,
benefit_hours_qty decimal(38,11),
benefit_cost_amt number(38,4)
)
PARTITION BY RANGE (associate_expns_type_cd)
(
PARTITION associate_benefit_expense_1 VALUES LESS THAN ('B'),
PARTITION associate_benefit_expense_2 VALUES LESS THAN ('E'),
PARTITION associate_benefit_expense_3 VALUES LESS THAN ('G'),
PARTITION associate_benefit_expense_4 VALUES LESS THAN ('I'),
PARTITION associate_benefit_expense_5 VALUES LESS THAN ('L'),
PARTITION associate_benefit_expense_6 VALUES LESS THAN ('N'),
PARTITION associate_benefit_expense_7 VALUES LESS THAN ('P'),
PARTITION associate_benefit_expense_8 VALUES LESS THAN ('Q'),
PARTITION associate_benefit_expense_9 VALUES LESS THAN ('R'),
PARTITION associate_benefit_expense_10 VALUES LESS THAN ('T'),
PARTITION associate_benefit_expense_11 VALUES LESS THAN ('U'),
PARTITION associate_benefit_expense_12 VALUES LESS THAN ('V'),
PARTITION associate_benefit_expense_13 VALUES LESS THAN (MAXVALUE)
) ENABLE ROW MOVEMENT;
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.12, ' ' , 'A' , NULL, 'TGK' , 'A' , 2, 'A' , 'A' , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (1.3, 'B' , NULL, 'B' , 'B' , NULL, 1, 'B' , NULL , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (2.23, 'C' , 'C' , NULL, 'C' , 'C' , 2, 'C' , 'C' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (3.33, 'D' , 'D' , 'PT' , NULL, 'D' , 3, 'D' , 'D' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (4.98, ' ' , NULL, 'E' , 'E' , 'E' , 4, 'E' , 'E' , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (5.01, NULL, 'F' , ' ' , 'F' , 'F' , 5, 'F' , 'F' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (6, 'G' , 'G' , 'G' , '_D' , 'G' , 6, 'G' , NULL ,'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.7, NULL, NULL, NULL, 'H' , 'H' , 7, NULL, 'G' , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.08, 'I' , ' ' , ' T ' , NULL, 'I' , 8, 'I' , '' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (9.12, ' ' , 'J' , ' PP' , 'J' , 'J' , 9, 'J' , NULL , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (10.10, NULL, ' ' , 'A' , 'A' , 'A' , 2, NULL, 'A','Y');
--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (11.11, 'B' , 'B' , 'B' , 'BCDAA' , NULL, 1, 'B' , 'B','N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (12.02, 'D' , NULL, NULL, 'C' , 'C' , 2, 'C' , 'C','N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (13.99, NULL, ' ' , 'D' , 'D' , 'D' , 3, 'D' , 'D','Y');
--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (14, 'G' , 'E' , 'E' , NULL, 'E' , 4, 'E' , 'E','N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (15, 'F' , ' ' , 'C' , 'CLEANING' , 'F' , 5, 'F' , 'F','Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (16, '' , 'Z' , NULL, 'G' , 'G' , 6, 'G' , NULL,'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (17, NULL, '' , ' PAPER' , 'H' , '' , 7, NULL, NULL,'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (19, ' ' , 'B' , '' , '' , 'I' , 8, 'I' , NULL,'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (20 , 'A' , 'J' , 'J' , 'J' , NULL, 9, 'J' , 'G','Y');
/*--REGION--*/
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('A', 'A ', 'A', 0.123433);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('B', 'B', 'B', NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('C', 'C', 'C', 2.232008908);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('D', ' DD', 'D', 3.878789);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('E', 'A', 'E', 4.89060603);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('F', 'F', 'F', 5.82703827);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('G', 'G', 'TTT', NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('H', 'H', 'G', 7.3829083);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('I', 'C', 'M', 8.983989);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('J', 'J', 'G', NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('K', ' ', 'C', 2.232008908);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('L', 'D', 'X', 3.878789);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('M', 'TTTTTT ', 'D' , 4.89060603);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('N', 'G' , 'B' , NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('O' , 'G', 'F', 6.6703972);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1970-01-01', 'A', 5, 0.5 , 0.5);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1973-01-01', 'B', 1, NULL, 1.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1976-01-01', 'C', 2, 2.0 , NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1979-01-01', 'D', 3, 3.0 , 3.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1982-01-01', 'E', 4, 4.0 , 4.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-01-01', 'F', 5, 5.0 , 5.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1988-01-01', 'F', 6, NULL, 6.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1991-01-01', 'G', 6, NULL, NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1994-01-01', 'G', 15, 8.0 , 8.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-01-01', 'G', 16, 9.0 , 9.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1983-01-03', 'I', 14, 4.0 , 4.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1984-01-01', 'GO', 15, 5.0 , NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-05-01', 'I', 16, 6.0 , 6.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1990-01-01', 'TTT', 16, NULL, 7.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1992-02-01', 'A', 15, 8.0 , 8.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-02-01', 'G', 17, 9.0 , NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-05-01', 'G' , 17, 9.0 , NULL);
ANALYZE item;
ANALYZE region;
ANALYZE associate_benefit_expense;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO item (item_level, item_subclass_cd, item_desc, vendor_party_id)
SELECT Table_001.REGION_MGR_ASSOCIATE_ID Column_003,
Table_002.associate_expns_type_cd Column_004,
CAST(Table_001.region_name AS VARCHAR) Column_005,
10 Column_006
-- 'o' Column_007,
-- 'F' Column_008,
-- pg_client_encoding() Column_009
FROM region Table_001, associate_benefit_expense Table_002
ON DUPLICATE KEY UPDATE item_level = -1000;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Insert on test_insert_update_008.item
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: u_item_index
-> Nested Loop
Output: 3, NULL::numeric, NULL::character varying, table_001.region_mgr_associate_id, (table_001.region_name)::character varying(250), table_002.associate_expns_type_cd, NULL::character varying, NULL::bpchar, 10::smallint, NULL::character varying, NULL::character varying, NULL::bpchar
-> Partition Iterator
Output: table_002.a, table_002.period_end_dt, table_002.associate_expns_type_cd, table_002.associate_party_id, table_002.benefit_hours_qty, table_002.benefit_cost_amt
Iterations: 13
-> Partitioned Seq Scan on test_insert_update_008.associate_benefit_expense table_002
Output: table_002.a, table_002.period_end_dt, table_002.associate_expns_type_cd, table_002.associate_party_id, table_002.benefit_hours_qty, table_002.benefit_cost_amt
Selected Partitions: 1..13
-> Materialize
Output: table_001.region_mgr_associate_id, table_001.region_name
-> Seq Scan on test_insert_update_008.region table_001
Output: table_001.region_mgr_associate_id, table_001.region_name
(15 rows)
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
QUERY PLAN
-----------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Result
Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0
(5 rows)
SET enable_light_proxy=off;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
QUERY PLAN
-----------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Result
Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0
(5 rows)
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
QUERY PLAN
-----------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Result
Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0
(5 rows)
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
QUERY PLAN
-----------------------------------------------------------------------------------------
Insert on test_insert_update_008.products_row
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: products_row_pkey
-> Result
Output: 100, 'null'::character varying(60), 'unknown'::character varying(60), 0
(5 rows)
RESET enable_light_proxy;
DROP SCHEMA test_insert_update_008 CASCADE;
NOTICE: drop cascades to 7 other objects
DETAIL: drop cascades to table products_base
drop cascades to table newproducts_base
drop cascades to table products_row
drop cascades to table newproducts_row
drop cascades to table item
drop cascades to table region
drop cascades to table associate_benefit_expense

View File

@ -0,0 +1,548 @@
DROP SCHEMA test_insert_update_009 CASCADE;
ERROR: schema "test_insert_update_009" does not exist
CREATE SCHEMA test_insert_update_009;
SET CURRENT_SCHEMA TO test_insert_update_009;
SET enable_light_proxy=off;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
NOTICE: CREATE TABLE will create implicit sequence "t1_col5_seq" for serial column "t1.col5"
--- distribute key are not allowed to update
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3;
--- should always insert
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3;
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4;
--- appoint column list in insert clause, should always insert
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6;
--- multiple rows, should always insert
INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7;
SELECT * FROM t1 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 2 | 1 | 1
1 | 2 | 1 | 2
1 | 2 | 1 | 3
1 | | 3 | 4
1 | | 3 | 5
2 | 1 | 1 | 6
2 | 1 | 1 | 7
(7 rows)
--- test union, should insert
INSERT INTO t1 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2;
col1 | col2 | col3
------+------+------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | | 1
2 | 1 | 1
2 | 1 | 1
(6 rows)
--- test subquery, should insert
INSERT INTO t1
(SELECT col1 || col2 || '00' FROM t1 ORDER BY col5)
ON DUPLICATE KEY UPDATE col3 = col1 * 100;
SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1;
col1 | col2 | col3
------+------+------
100 | | 1
100 | | 1
100 | | 1
1100 | | 1
1200 | | 1
1200 | | 1
1200 | | 1
1200 | | 1
1300 | | 1
2100 | | 1
2100 | | 1
2100 | | 1
(12 rows)
-- test t2 with one primary key
CREATE TABLE t2 (
col1 INT,
col2 INT PRIMARY KEY,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
NOTICE: CREATE TABLE will create implicit sequence "t2_col5_seq" for serial column "t2.col5"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2"
--- distribute key are not allowed to update
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col2 = 100,
col3 = 100,
col4 = '2019-08-09'::TIMESTAMP,
col5 = 100;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- should insert
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30;
INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40;
INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2;
INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1;
INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 1 | 1 | 1
2 | 2 | 1 | 2
3 | 3 | 1 | 3
4 | 4 | 1 | 4
5 | 5 | 1 | 5
6 | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
(8 rows)
--- should update
INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1;
INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5;
INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
30 | 1 | 2 | 1
40 | 2 | 2 | 2
3 | 3 | 4 | 3
4 | 4 | 5 | 4
--?.*| 5 | 1 | 5
--?.*| 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
(8 rows)
-- primary key are not allowed to be null
INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10;
ERROR: null value in column "col2" violates not-null constraint
--?
--- appoint column list in insert clause
---- should insert
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5;
col1 | col2 | col3 | col4 | col5
------+------+------+---------------------------------+------
--? 30 | 1 | 2 | .* | 1
--? 40 | 2 | 2 | .* | 2
--? 3 | 3 | 4 | .* | 3
--? 4 | 4 | 5 | .* | 4
--? .* | 5 | 1 | .* | 5
--? .* | 6 | 1 | .* | 6
--? 7 | 7 | 1 | .* | 7
--? 8 | 8 | 1 | .* | 8
--? | 9 | 9 | .* | 10
--? | 10 | 10 | .* | 10
--? | 20 | 20 | .* | 20
--? | 30 | 30 | .* | 30
(12 rows)
---- should update
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col4 | col5
------+------+------+---------------------------------+------
--? 30 | 1 | 2 | .* | 1
--? 40 | 2 | 2 | .* | 2
--? 3 | 3 | 4 | .* | 3
--? 4 | 4 | 5 | .* | 4
--? .* | 5 | 1 | .* | 5
--? .* | 6 | 1 | .* | 6
--? 7 | 7 | 1 | .* | 7
--? 8 | 8 | 1 | .* | 8
--? 90 | 9 | 9 | .* | 10
100 | 10 | 100 | Tue Aug 20 00:00:00 2019 | 100
100 | 20 | 100 | Tue Aug 20 00:00:00 2019 | 100
100 | 30 | 100 | Tue Aug 20 00:00:00 2019 | 100
(12 rows)
--- test subquery
---- should insert
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+------+------
30 | 1 | 2 | 1
40 | 2 | 2 | 2
3 | 3 | 4 | 3
4 | 4 | 5 | 4
--? .* | 5 | 1 | 5
--? .* | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1 | 11
--? 40000 | 2001 | 1 | 12
--? | 40002 | 2000 | 13
--? | 3002 | 3000 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
---- should update
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+-------+------
30 | 1 | 2 | 1
40 | 2 | 2 | 2
3 | 3 | 4 | 3
4 | 4 | 5 | 4
--? 2 | 5 | 1 | 5
--? 2101 | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1002 | 11
--? 40000 | 2001 | 2002 | 12
--? | 40002 | 40003 | 13
--? | 3002 | 3003 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
--- test union, some insert some update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1 ORDER BY 1, 2;
col1 | col2
------+------
1 | 1
1 | 2
1 | 3
2 | 1
100 | 1
1100 | 1
1200 | 1
1300 | 1
2100 | 1
(9 rows)
INSERT INTO t2 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+-------+------
30 | 1 | 219 | 1
40 | 2 | 44 | 2
3 | 3 | 10 | 3
4 | 4 | 5 | 4
--? .* | 5 | 1 | 5
--? .* | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1002 | 11
--? 40000 | 2001 | 2002 | 12
--? | 40002 | 40003 | 13
--? | 3002 | 3003 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
-------+-------+-------+------
30 | 1 | 436 | 1
40 | 2 | 86 | 2
3 | 3 | 16 | 3
4 | 4 | 5 | 4
--? 15 | 5 | 1 | 5
--? 2105 | 6 | 1 | 6
7 | 7 | 1 | 7
8 | 8 | 1 | 8
90 | 9 | 9 | 16
--? 30000 | 1001 | 1002 | 11
--? 40000 | 2001 | 2002 | 12
--? | 40002 | 40003 | 13
--? | 3002 | 3003 | 14
100 | 10 | 100 | 100
100 | 20 | 100 | 100
100 | 30 | 100 | 100
(16 rows)
reset behavior_compat_options;
-- test INTERSECT, should update
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1;
col1 | ?column?
------+----------
1 | 3
(1 row)
INSERT INTO t2 (col1, col2)
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2;
col1 | col2 | col3 | col5
------+------+------+------
3 | 3 | 22 | 3
(1 row)
-- test EXCEPT, should update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
col1 | col2
------+------
1 | 2
(1 row)
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
------+------+------+------
40 | 2 | 128 | 2
(1 row)
-- test unique index with not default value
ALTER TABLE t2 DROP CONSTRAINT t2_pkey;
CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5);
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
col1 | col2
------+------
1 | 2
(1 row)
INSERT INTO t2
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
col1 | col2 | col3 | col5
------+------+------+------
40 | 2 | 128 | 2
1 | 2 | 1 | 46
(2 rows)
-- test t3 with one primary index with two columns
CREATE TABLE t3 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3)
) ;
NOTICE: CREATE TABLE will create implicit sequence "t3_col5_seq" for serial column "t3.col5"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t3_pkey" for table "t3"
--- column referenced by primary key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- should insert when not contains primary key and not all primary key referred columns have default value.
--- but will fail cause primary key should not be null
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
ERROR: null value in column "col2" violates not-null constraint
--?.*
--- should insert
--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence)
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 1 | 1 | 2
2 | 2 | 2 | 3
(2 rows)
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
10 | 1 | 1 | 2
2 | 2 | 2 | 20
(2 rows)
--- test subquery
---- should insert
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
10 | 1 | 1 | 2
| 3 | 3 | 6
2 | 2 | 2 | 20
(3 rows)
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
10 | 1 | 1 | 2
6 | 3 | 3 | 6
2 | 2 | 2 | 20
(3 rows)
-- test t3 with one unique index with two columns
TRUNCATE t3;
ALTER TABLE t3 DROP CONSTRAINT t3_pkey;
ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL;
CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3);
--- column referenced by unique key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--- should insert cause not contains unique key and not all unique key referred columns have default value.
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
SELECT * FROM t3 order by col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
(2 rows)
--- should insert
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
1 | 1 | 1 | 10
2 | 2 | 2 | 11
(4 rows)
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
10 | 1 | 1 | 10
2 | 2 | 2 | 20
(4 rows)
--- test subquery
---- should insert
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
10 | 1 | 1 | 10
100 | | 2 | 14
100 | | 2 | 15
| 3 | 3 | 16
2 | 2 | 2 | 20
(7 rows)
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | | 1 | 8
1 | | 1 | 9
10 | 1 | 1 | 10
100 | | 2 | 14
100 | | 2 | 15
6 | 3 | 3 | 16
2 | 2 | 2 | 20
(7 rows)
RESET enable_light_proxy;
DROP SCHEMA test_insert_update_009 CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table t1
drop cascades to table t2
drop cascades to table t3

View File

@ -0,0 +1,313 @@
DROP SCHEMA test_insert_update_010 CASCADE;
ERROR: schema "test_insert_update_010" does not exist
CREATE SCHEMA test_insert_update_010;
SET CURRENT_SCHEMA TO test_insert_update_010;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,
col2 INT DEFAULT 0,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3, col5)
) ;
NOTICE: CREATE TABLE will create implicit sequence "t4_col5_seq" for serial column "t4.col5"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t4_pkey" for table "t4"
--- should insert
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 0 | 1 | 1
1 | 0 | 1 | 2
2 | 2 | 2 | 3
100 | 100 | 100 | 100
(4 rows)
--- should update
INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
col1 | col2 | col3 | col5
------+------+------+------
1 | 0 | 1 | 1
1 | 0 | 1 | 2
200 | 2 | 2 | 3
1000 | 100 | 100 | 100
(4 rows)
--- error: duplicate key update on (x, x, 20)
--- this is because current version is not inplace update but merge,
--- so when the subquery contains multiple same values, it will cause duplicate insert failure.
SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2;
col3 | ?column?
------+----------
1 | 20
2 | 20
100 | 1000
(3 rows)
INSERT INTO t4 (col1, col5)
(SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3)
ON DUPLICATE KEY UPDATE col1 = 3;
-- test t5 with sequence or default column with volatile function in constaint index
CREATE TABLE t5 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM() + 1
) ;
NOTICE: CREATE TABLE will create implicit sequence "t5_col3_seq" for serial column "t5.col3"
-- test t5 with sequence column in constaint index
CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3);
--- should insert
INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 1 | 1 | 1 | 1.58182
--? 1 | 1 | 2 | 1.00814
--? 1 | 1 | 3 | 1.50194
--? | 1 | 4 | 1.12955
(4 rows)
--- should update
INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10;
SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
1 | 10 | 1 | 1.00000
1 | 20 | 2 | 1.00000
1 | 30 | 3 | 1.00000
(3 rows)
--- should some insert some update
INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
col1 | col2 | col3
------+------+------
1 | 10 | 1
1 | 20 | 2
1 | 30 | 3
| 1 | 4
2 | 1 | 5
2 | 1 | 6
(6 rows)
INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
col1 | col2 | col3
------+------+------
1 | 10 | 1
1 | 20 | 2
1 | 30 | 3
| 1 | 4
2 | 5 | 5
2 | 6 | 6
2 | 1 | 7
(7 rows)
--- should INSERT and sequence starting from 7
INSERT INTO t5 VALUES (2), (2);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
col1 | col2 | col3
------+------+------
1 | 10 | 1
1 | 20 | 2
1 | 30 | 3
| 1 | 4
2 | 5 | 5
2 | 6 | 6
2 | 1 | 7
2 | 1 | 8
2 | 1 | 9
(9 rows)
-- test with volatile function as default column in constraint index
TRUNCATE t5;
DROP INDEX u_t5_index1;
CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2;
--- should insert
INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 1 | 10 | 1.24521
--? 3 | 1 | 11 | 1.80598
--? 3 | 1 | 12 | 1.86845
--? 4 | 1 | 13 | 1.65797
--? 4 | 1 | 14 | 1.07881
--? 4 | 1 | 15 | 1.69493
(6 rows)
--- should update
INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 125 | 10 | 1.24521
--? 3 | 181 | 11 | 1.80598
--? 3 | 187 | 12 | 1.86845
--? 4 | 1 | 13 | 1.65797
--? 4 | 1 | 14 | 1.07881
--? 4 | 1 | 15 | 1.69493
(6 rows)
--- test subquery
---- should insert
INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 167 | 10 | 1.67400
--? 3 | 132 | 11 | 1.31552
--? 3 | 129 | 12 | 1.29423
--? 4 | 1 | 13 | 1.44100
--? 4 | 1 | 14 | 1.37620
--? 4 | 1 | 15 | 1.51296
--? 4 | 1 | 16 | 1.37516
--? 4 | 1 | 17 | 1.21710
--? 4 | 1 | 18 | 1.50422
--? 3 | 132 | 19 | 1.30560
--? 3 | 167 | 20 | 1.62282
--? 3 | 129 | 21 | 1.76699
(12 rows)
---- should update
INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000;
SELECT * FROM t5 ORDER BY col3;
col1 | col2 | col3 | col5
------+------+------+---------
--? 3 | 1674 | 10 | 1.67400
--? 3 | 1316 | 11 | 1.31552
--? 3 | 1294 | 12 | 1.29423
--? 4 | 1441 | 13 | 1.44100
--? 4 | 1376 | 14 | 1.37620
--? 4 | 1513 | 15 | 1.51296
--? 4 | 1375 | 16 | 1.37516
--? 4 | 1217 | 17 | 1.21710
--? 4 | 1504 | 18 | 1.50422
--? 3 | 1306 | 19 | 1.30560
--? 3 | 1623 | 20 | 1.62282
--? 3 | 1767 | 21 | 1.76699
(12 rows)
-- test t6 with one more index
CREATE TABLE t6 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM(),
col6 INT,
col7 TEXT
) ;
NOTICE: CREATE TABLE will create implicit sequence "t6_col3_seq" for serial column "t6.col3"
ALTER TABLE t6 ADD PRIMARY KEY (col1, col3);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "t6_pkey" for table "t6"
CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6);
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5);
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
1 | 1 | 1 | |
2 | 1 | 2 | |
3 | 1 | 3 | |
4 | 1 | 4 | |
5 | 1 | 5 | |
(5 rows)
--- should insert
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2);
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
1 | 1 | 1 | |
2 | 1 | 2 | |
3 | 1 | 3 | |
4 | 1 | 4 | |
5 | 1 | 5 | |
(5 rows)
--- should update because primary key matches
INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12);
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 11 | |
6 | 1 | 12 | |
(2 rows)
INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 11 | |
6 | 1 | 12 | |
(2 rows)
--- should update for those col6 is not null bacause they will match the unique index,
--- and insert for those col6 is null because the unique index containing null never matches,
--- also primary key will not match
--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows,
--- because those sequence have to be generated during the unique index join stage.
INSERT INTO t6 (col1, col5, col6)
(SELECT col1, col5, col6 FROM t6 WHERE col1 = 6)
ON DUPLICATE KEY UPDATE
col7 = col2 + 1;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 6 | |
6 | 1 | 7 | |
6 | 1 | 11 | |
6 | 1 | 12 | |
(4 rows)
--- should update because unique index and primary key both match
INSERT INTO t6 (col1, col3, col5, col6)
(SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL)
ON DUPLICATE KEY UPDATE
col7 = col7 * 10;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col6 | col7
------+------+------+------+------
6 | 1 | 6 | |
6 | 1 | 7 | |
6 | 1 | 11 | |
6 | 1 | 12 | |
(4 rows)
--- insert when col3 = 17 because constaints does not match,
--- but update when col3 = 18 because 18 has been inserted and will cause a match
INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100);
SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col5 | col6 | col7
------+------+------+-----------+------+------
7 | 1 | 18 | 100.00000 | 100 |
(1 row)
INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE
col7 = col3 * 100;
SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7;
col1 | col2 | col3 | col5 | col6 | col7
------+------+------+-----------+------+------
7 | 1 | 8 | 10.00000 | 10 |
7 | 1 | 18 | 100.00000 | 100 | 1800
(2 rows)
DROP SCHEMA test_insert_update_010 CASCADE;
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table t4
drop cascades to table t5
drop cascades to table t6

View File

@ -0,0 +1,19 @@
\c upsert
DROP SCHEMA upsert_test CASCADE;
NOTICE: drop cascades to 8 other objects
DETAIL: drop cascades to type upsert_test.atype
drop cascades to type upsert_test.btype
drop cascades to table upsert_test.t_grammer
drop cascades to table upsert_test.t_default
drop cascades to table upsert_test.t_data
drop cascades to table upsert_test.t_trigger
drop cascades to function upsert_test.upsert_before_func()
drop cascades to function upsert_test.upsert_after_func()
DROP SCHEMA upsert_test_unlog CASCADE;
NOTICE: drop cascades to 6 other objects
DETAIL: drop cascades to type upsert_test_unlog.atype
drop cascades to type upsert_test_unlog.btype
drop cascades to table upsert_test_unlog.t_hash_unlog_0
drop cascades to table upsert_test_unlog.t_hash_unlog_1
drop cascades to table upsert_test_unlog.t_rep_unlog_0
drop cascades to table upsert_test_unlog.t_rep_unlog_1

View File

@ -0,0 +1 @@
drop database upsert_etc;

View File

@ -0,0 +1,404 @@
--------------------------------------------------------------------------------------------
/*
* procedure
*/
--------------------------------------------------------------------------------------------
\c upsert;
SET CURRENT_SCHEMA TO upsert_test_procedure;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
drop table IF EXISTS t_proc;
NOTICE: table "t_proc" does not exist, skipping
create table t_proc(c1 int, c2 int, c3 int unique);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "t_proc_c3_key" for table "t_proc"
insert into t_proc select a,a,a from generate_series(1,20) as a;
select *from t_proc order by c3;
c1 | c2 | c3
----+----+----
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
9 | 9 | 9
10 | 10 | 10
11 | 11 | 11
12 | 12 | 12
13 | 13 | 13
14 | 14 | 14
15 | 15 | 15
16 | 16 | 16
17 | 17 | 17
18 | 18 | 18
19 | 19 | 19
20 | 20 | 20
(20 rows)
create or replace procedure mul_ups( c in INTEGER)
as
declare
val int;
begin
for val in select c3 from t_proc loop
insert into t_proc values(c,c, val) on duplicate key update c1 = c, c2 =c;
end loop;
end;
/
call mul_ups(100);
mul_ups
---------
(1 row)
select *from t_proc order by c3;
c1 | c2 | c3
-----+-----+----
100 | 100 | 1
100 | 100 | 2
100 | 100 | 3
100 | 100 | 4
100 | 100 | 5
100 | 100 | 6
100 | 100 | 7
100 | 100 | 8
100 | 100 | 9
100 | 100 | 10
100 | 100 | 11
100 | 100 | 12
100 | 100 | 13
100 | 100 | 14
100 | 100 | 15
100 | 100 | 16
100 | 100 | 17
100 | 100 | 18
100 | 100 | 19
100 | 100 | 20
(20 rows)
create or replace procedure mul_ups( c in INTEGER)
as
declare
val int;
begin
for val in select c3 from t_proc loop
insert into t_proc values(c,c, val) on duplicate key update c1 = $1, c2 =$1;
end loop;
end;
/
call mul_ups(200);
mul_ups
---------
(1 row)
select *from t_proc order by c3;
c1 | c2 | c3
-----+-----+----
200 | 200 | 1
200 | 200 | 2
200 | 200 | 3
200 | 200 | 4
200 | 200 | 5
200 | 200 | 6
200 | 200 | 7
200 | 200 | 8
200 | 200 | 9
200 | 200 | 10
200 | 200 | 11
200 | 200 | 12
200 | 200 | 13
200 | 200 | 14
200 | 200 | 15
200 | 200 | 16
200 | 200 | 17
200 | 200 | 18
200 | 200 | 19
200 | 200 | 20
(20 rows)
create or replace procedure mul_ups_00()
as
declare
val int;
begin
for val in select c3 from t_proc loop
insert into t_proc values(val,val,val) on duplicate key update c1 = 300, c2 =300;
end loop;
end;
/
call mul_ups_00();
mul_ups_00
------------
(1 row)
select *from t_proc order by c3;
c1 | c2 | c3
-----+-----+----
300 | 300 | 1
300 | 300 | 2
300 | 300 | 3
300 | 300 | 4
300 | 300 | 5
300 | 300 | 6
300 | 300 | 7
300 | 300 | 8
300 | 300 | 9
300 | 300 | 10
300 | 300 | 11
300 | 300 | 12
300 | 300 | 13
300 | 300 | 14
300 | 300 | 15
300 | 300 | 16
300 | 300 | 17
300 | 300 | 18
300 | 300 | 19
300 | 300 | 20
(20 rows)
declare
val int;
c int :=400;
begin
for val in select c3 from t_proc loop
insert into t_proc values(c,c, val) on duplicate key update c1 = c, c2 =c;
end loop;
end;
/
select *from t_proc order by c3;
c1 | c2 | c3
-----+-----+----
400 | 400 | 1
400 | 400 | 2
400 | 400 | 3
400 | 400 | 4
400 | 400 | 5
400 | 400 | 6
400 | 400 | 7
400 | 400 | 8
400 | 400 | 9
400 | 400 | 10
400 | 400 | 11
400 | 400 | 12
400 | 400 | 13
400 | 400 | 14
400 | 400 | 15
400 | 400 | 16
400 | 400 | 17
400 | 400 | 18
400 | 400 | 19
400 | 400 | 20
(20 rows)
create or replace procedure mul_ups( c in INTEGER)
as
declare
val int;
begin
for val in select c3 from t_proc loop
insert into t_proc values(c,c, val+100) on duplicate key update c1 = $1, c2 =$1;
end loop;
end;
/
call mul_ups(500);
mul_ups
---------
(1 row)
select *from t_proc order by c3;
c1 | c2 | c3
-----+-----+-----
400 | 400 | 1
400 | 400 | 2
400 | 400 | 3
400 | 400 | 4
400 | 400 | 5
400 | 400 | 6
400 | 400 | 7
400 | 400 | 8
400 | 400 | 9
400 | 400 | 10
400 | 400 | 11
400 | 400 | 12
400 | 400 | 13
400 | 400 | 14
400 | 400 | 15
400 | 400 | 16
400 | 400 | 17
400 | 400 | 18
400 | 400 | 19
400 | 400 | 20
500 | 500 | 101
500 | 500 | 102
500 | 500 | 103
500 | 500 | 104
500 | 500 | 105
500 | 500 | 106
500 | 500 | 107
500 | 500 | 108
500 | 500 | 109
500 | 500 | 110
500 | 500 | 111
500 | 500 | 112
500 | 500 | 113
500 | 500 | 114
500 | 500 | 115
500 | 500 | 116
500 | 500 | 117
500 | 500 | 118
500 | 500 | 119
500 | 500 | 120
(40 rows)
/*
* insert
*/
drop table if exists t_default;
NOTICE: table "t_default" does not exist, skipping
CREATE TABLE t_default (c1 INT PRIMARY KEY DEFAULT 10, c2 FLOAT DEFAULT 3.0, c3 TIMESTAMP DEFAULT '20200508');
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_default_pkey" for table "t_default"
create view v_default as select *from t_default order by c1;
-- insert src
---- insert values
truncate t_default;
insert into t_default values(DEFAULT, DEFAULT, DEFAULT);
insert into t_default values(1, 2, '20200630');
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 2 | Tue Jun 30 00:00:00 2020
10 | 3 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default values(DEFAULT, DEFAULT, DEFAULT) on duplicate key update c2 = 1;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 2 | Tue Jun 30 00:00:00 2020
10 | 1 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default values(1, 2, '20200630') on duplicate key update c2 = 2;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 2 | Tue Jun 30 00:00:00 2020
10 | 1 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default values(DEFAULT, DEFAULT, DEFAULT),(1, 2, '20200630') on duplicate key update c2 = 3;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 3 | Tue Jun 30 00:00:00 2020
10 | 3 | Fri May 08 00:00:00 2020
(2 rows)
---- insert subquery
insert into t_default select *from t_default order by c1 on duplicate key update c2 = 4;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 4 | Tue Jun 30 00:00:00 2020
10 | 4 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default select *from t_default order by c1 limit 1 on duplicate key update c2 = 5;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 5 | Tue Jun 30 00:00:00 2020
10 | 4 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default select *from t_default where c1=10 on duplicate key update c2 = 6;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 5 | Tue Jun 30 00:00:00 2020
10 | 6 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default select *from t_default order by c1 on duplicate key update c2 = 7;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 7 | Tue Jun 30 00:00:00 2020
10 | 7 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default select *from v_default order by c1 on duplicate key update c2 = 8;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 8 | Tue Jun 30 00:00:00 2020
10 | 8 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default select *from t_default union select c1+1,c2+1,c3+1 from t_default order by c1 on duplicate key update c2 = 9;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 9 | Tue Jun 30 00:00:00 2020
2 | 9 | Wed Jul 01 00:00:00 2020
10 | 9 | Fri May 08 00:00:00 2020
11 | 9 | Sat May 09 00:00:00 2020
(4 rows)
insert into t_default(c1,c2) select max(c1),c2 from t_default group by c2 on duplicate key update c2 = 10;
-- index
---- mul index
truncate t_default;
create unique index t_default_mul on t_default(c1,c3);
insert into t_default values(DEFAULT, DEFAULT, DEFAULT);
insert into t_default values(1, 2, '20200630');
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 2 | Tue Jun 30 00:00:00 2020
10 | 3 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default values(DEFAULT, DEFAULT, DEFAULT) on duplicate key update c2 = 1;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 2 | Tue Jun 30 00:00:00 2020
10 | 1 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default values(1, 2, '20200630') on duplicate key update c2 = 2;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 2 | Tue Jun 30 00:00:00 2020
10 | 1 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default values(DEFAULT, DEFAULT, DEFAULT),(1, 2, '20200630') on duplicate key update c2 = 3;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 3 | Tue Jun 30 00:00:00 2020
10 | 3 | Fri May 08 00:00:00 2020
(2 rows)
insert into t_default select *from t_default on duplicate key update c2 = 4;
select *from t_default order by c1;
c1 | c2 | c3
----+----+--------------------------
1 | 4 | Tue Jun 30 00:00:00 2020
10 | 4 | Fri May 08 00:00:00 2020
(2 rows)

View File

@ -0,0 +1,298 @@
--------------------------------------------------------------------------------------------
/*
* explain upsert
*/
--------------------------------------------------------------------------------------------
\c upsert;
SET CURRENT_SCHEMA TO upsert_test_explain;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
create temp table up_expl_temp(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_temp_c3_key" for table "up_expl_temp"
insert into up_expl_hash select a,a,a from generate_series(1,20) as a;
insert into up_expl_repl select a,a,a from generate_series(1,20) as a;
insert into up_expl_part select a,a,a from generate_series(1,20) as a;
insert into up_expl_temp select a,a,a from generate_series(1,20) as a;
insert into up_expl_unlog select a,a,a from generate_series(1,20) as a;
insert into up_expl_node select a,a,a from generate_series(1,20) as a;
insert into up_expl_repl2 select a,a,a,a,a from generate_series(1,20) as a;
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_hash
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_hash_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_hash (actual rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_hash_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN PERFORMANCE insert into up_expl_hash values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
--? Insert on upsert_test_explain.up_expl_hash (cost=.* rows=1 width=0) (actual time=.* rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_hash_c3_key
(Buffers: shared hit=4)
--? (CPU: ex c/r=.*, ex row=1, ex cyc=.*, inc cyc=.*)
--? -> Result (cost=.* rows=1 width=0) (actual time=.* rows=1 loops=1)
Output: 1, 1, 1
--? (CPU: ex c/r=.*, ex row=1, ex cyc=.*, inc cyc=.*)
--? Total runtime: .* ms
(9 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update c1 = 2;
QUERY PLAN
----------------------------------------------------------------------------
Insert on upsert_test_explain.up_expl_hash
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_hash_c3_key
-> Values Scan on "*VALUES*"
Output: "*VALUES*".column1, "*VALUES*".column2, "*VALUES*".column3
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update c1 = 2;
QUERY PLAN
---------------------------------------------------------
Insert on up_expl_hash (actual rows=2 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_hash_c3_key
-> Values Scan on "*VALUES*" (actual rows=2 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_repl
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_repl_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_repl (actual rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_repl_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_part
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_part_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_part (actual rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_part_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update c1 = 2;
--?.*QUERY PLAN.*
--?----.*
--? Insert on pg_temp_datanod.*.up_expl_temp
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_temp_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_temp (actual rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_temp_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
--------------------------------------------------
Insert on upsert_test_explain.up_expl_unlog
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_unlog_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
--------------------------------------------------
Insert on up_expl_unlog (actual rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_unlog_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_node
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_node_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update c1 = 2;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_node (actual rows=1 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_expl_node_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update nothing;
QUERY PLAN
----------------------------------------------------------------------------
Insert on upsert_test_explain.up_expl_hash
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_hash_c3_key
-> Values Scan on "*VALUES*"
Output: "*VALUES*".column1, "*VALUES*".column2, "*VALUES*".column3
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_hash values(1,1,1),(2,2,2)on duplicate key update nothing;
QUERY PLAN
---------------------------------------------------------
Insert on up_expl_hash (actual rows=0 loops=1)
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_hash_c3_key
-> Values Scan on "*VALUES*" (actual rows=2 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_repl
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_repl_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_repl values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_repl (actual rows=0 loops=1)
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_repl_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_part
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_part_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_part values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_part (actual rows=0 loops=1)
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_part_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update nothing;
--?.*QUERY PLAN.*
--?----.*
--? Insert on pg_temp_datanod.*.up_expl_temp
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_temp_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_temp values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_temp (actual rows=0 loops=1)
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_temp_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update nothing;
QUERY PLAN
--------------------------------------------------
Insert on upsert_test_explain.up_expl_unlog
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_unlog_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_unlog values(1,1,1) on duplicate key update nothing;
QUERY PLAN
--------------------------------------------------
Insert on up_expl_unlog (actual rows=0 loops=1)
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_unlog_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)
EXPLAIN (VERBOSE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_explain.up_expl_node
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_node_c3_key
-> Result
Output: 1, 1, 1
(5 rows)
EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1,1) on duplicate key update nothing;
QUERY PLAN
-------------------------------------------------
Insert on up_expl_node (actual rows=0 loops=1)
Conflict Resolution: NOTHING
Conflict Arbiter Indexes: up_expl_node_c3_key
-> Result (actual rows=1 loops=1)
--? Total runtime: .* ms
(5 rows)

View File

@ -0,0 +1,188 @@
\c upsert
SET CURRENT_SCHEMA TO upsert_test;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
-- support multiple set
INSERT INTO t_grammer VALUES(11, 1) ON DUPLICATE KEY UPDATE c2 = 2, c4 = ROW(10, 20), c3 = '{10, 20, 30}';
INSERT INTO t_grammer VALUES(12, 2) ON DUPLICATE KEY UPDATE c2 = 2, c3[1] = 3;
-- support const value and expr
INSERT INTO t_grammer VALUES(23) ON DUPLICATE KEY UPDATE c2 = 2;
INSERT INTO t_grammer VALUES(24) ON DUPLICATE KEY UPDATE c2 = length('test') + 100;
-- support without table name
INSERT INTO t_grammer VALUES(31, 1) ON DUPLICATE KEY UPDATE c2 = c1, c4.a = VALUES(c1);
INSERT INTO t_grammer VALUES(32, 2) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = VALUES(c1);
-- support with table name
INSERT INTO t_grammer VALUES(41, 1) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1, c4.a = VALUES(c1);
INSERT INTO t_grammer VALUES(42, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = VALUES(c1);
-- support EXCLUDED alone and with expr
INSERT INTO t_grammer VALUES(51, 1) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = EXCLUDED.c1;
INSERT INTO t_grammer VALUES(52, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = EXCLUDED.c1 + 5;
INSERT INTO t_grammer VALUES(53, 3) ON DUPLICATE KEY UPDATE c4.a = sqrt(EXCLUDED.c1 - EXCLUDED.c2 - 1) + 1, c2 = t_grammer.c1 + 5;
INSERT INTO t_grammer VALUES(54, 4) ON DUPLICATE KEY UPDATE c4.b = EXCLUDED.c1 + t_grammer.c1;
INSERT INTO t_grammer VALUES(55, 5) ON DUPLICATE KEY UPDATE c4.a = EXCLUDED.c2, c4.b = EXCLUDED.c1 + sqrt(c1 - 6);
-- support ARRAY
INSERT INTO t_grammer VALUES(61, 1) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]);
INSERT INTO t_grammer VALUES(62, 2) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2,3]);
INSERT INTO t_grammer VALUES(63, 3) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2:3]);
INSERT INTO t_grammer VALUES(64, 4, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c3[1];
INSERT INTO t_grammer VALUES(65, 5, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[2,3];
INSERT INTO t_grammer VALUES(66, 6, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[1:2];
INSERT INTO t_grammer VALUES(67, 7, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = c3[1];
INSERT INTO t_grammer VALUES(68, 8, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[2,3];
INSERT INTO t_grammer VALUES(69, 9, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[1:2];
-- support user defined type
INSERT INTO t_grammer VALUES(71, 1) ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1;
INSERT INTO t_grammer VALUES(72, 2, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1;
INSERT INTO t_grammer VALUES(73, 3, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1, c4.b = c3[2];
INSERT INTO t_grammer VALUES(74, 4, '{71, 72, 73}', ROW(74, 75)) ON DUPLICATE KEY UPDATE c4 = ROW(740, 750);
-- appoint INSERT target column
INSERT INTO t_grammer (c5, c1, c2) VALUES('{81,82}', 81, DEFAULT) ON DUPLICATE KEY UPDATE c2 = VALUES(c1);
INSERT INTO t_grammer (c3[1], c3[2], c1) VALUES(881, 882, 82) ON DUPLICATE KEY UPDATE c5 = VALUES(c3), c2 = VALUES(c3[1]);
INSERT INTO t_grammer (c1, c3, c4) VALUES(83, '{81, 82, 83}', ROW(810, 820)) ON DUPLICATE KEY UPDATE c4 = EXCLUDED.c4;
INSERT INTO t_grammer (c1, c3, c4.a) VALUES(84, '{81, 82, 83}', 850) ON DUPLICATE KEY UPDATE c4.b = c3[3];
-- support UPDATE NOTHING
INSERT INTO t_grammer VALUES(91, 1, '{91, 92, 93}', ROW(94, 95)) ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_grammer (c5, c1, c2, c4) VALUES('{91,92}', 92, DEFAULT, ROW(910, 920)) ON DUPLICATE KEY UPDATE NOTHING;
-- UPDATE target: unsupport with schema but support with tablename
INSERT INTO t_grammer VALUES(0, 0, '{0,0}', ROW(0, 0), '{107, 108}') ON DUPLICATE KEY UPDATE
upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c3[1:2] = c5[1:2], upsert_test.t_grammer.c4.a = c1;
ERROR: column "upsert_test.t_grammer" of relation "t_grammer" does not exist
LINE 2: upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c...
^
INSERT INTO t_grammer VALUES(101, 1, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE
t_grammer.c2 = c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = c1;
-- INSERT target: appoint schema
INSERT INTO upsert_test.t_grammer VALUES(102, 2, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE
t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1);
CREATE SCHEMA upsert_test_tmpschema;
SET CURRENT_SCHEMA TO upsert_test_tmpschema;
INSERT INTO upsert_test.t_grammer VALUES(103, 3, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE
t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1);
SET CURRENT_SCHEMA TO upsert_test;
DROP SCHEMA upsert_test_tmpschema;
-- support data type
INSERT INTO t_data VALUES(11, 11, 11, 11, 11, '11', '11', '11', '2020-04-23', '2020-04-23 11:00', '1:00') ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_data VALUES(12, 12, 12, 12, 12, '12', '12', '12', '2020-04-24', '2020-04-24 11:00', '2:00')
ON DUPLICATE KEY UPDATE
c_tiny = 13, c_smallint = 13, c_bigint = 13, c_numeric = 13,
c_var = '13', c_text = '13', c_bytea = '13', c_date = '2020-05-24', c_timestamp = '2020-05-24 11:00', c_time = '3:00';
INSERT INTO t_data VALUES(13, 13, 13, 13, 13, '13', '13', '13', '2020-04-25', '2020-04-25 11:00', '4:00')
ON DUPLICATE KEY UPDATE
c_tiny = EXCLUDED.c_tiny + 1, c_smallint = EXCLUDED.c_smallint + 1, c_bigint = EXCLUDED.c_bigint + 1, c_numeric = EXCLUDED.c_numeric + 1,
c_var = EXCLUDED.c_var || '3', c_text = EXCLUDED.c_text || '3', c_bytea = EXCLUDED.c_bytea || '3',
c_date = EXCLUDED.c_date + '1 day'::interval, c_timestamp = EXCLUDED.c_timestamp + '1 day'::interval, c_time = EXCLUDED.c_time + '1 hour'::interval;
-- support UPDATE to default
INSERT INTO t_default DEFAULT VALUES ON DUPLICATE KEY UPDATE c2 = 100, c3 = '2020-05-17';
INSERT INTO t_default VALUES(91, 0.91, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = DEFAULT, c3 = DEFAULT;
INSERT INTO t_default VALUES(92, DEFAULT, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = 10, c3 = DEFAULT;
-- unsupport alias
INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY UPDATE c2 = VALUES(c1);
ERROR: syntax error at or near "AS"
LINE 1: INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY ...
^
-- unsupport VALUES with expr
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2));
ERROR: syntax error at or near "("
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2));
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1;
ERROR: syntax error at or near "+"
LINE 1: ...VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1;
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1;
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1;
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c3[1]) + 1;
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer....
^
-- unsupport VALUES with table name
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c4.a);
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer....
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c2);
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer....
^
-- unsupport DEFAULT with expr
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1;
ERROR: syntax error at or near "+"
LINE 1: ...ammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1;
^
-- unsupport user defined typed column's element
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a);
ERROR: only allow column name within VALUES
LINE 1: ...mmer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a);
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c4.a;
ERROR: schema "excluded" does not exist
CONTEXT: referenced column: c2
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a;
ERROR: missing FROM-clause entry for table "c4"
LINE 1: ...TO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a;
^
CONTEXT: referenced column: c2
SELECT * FROM t_data ORDER BY 1;
c_int | c_tiny | c_smallint | c_bigint | c_numeric | c_var | c_text | c_bytea | c_date | c_timestamp | c_time | c_intarray | c_com
-------+--------+------------+----------+-----------+-------+--------+---------+--------------------------+--------------------------+----------+------------+-------
11 | 11 | 11 | 11 | 11 | 11 | 11 | \x3131 | Thu Apr 23 00:00:00 2020 | Thu Apr 23 11:00:00 2020 | 01:00:00 | |
12 | 12 | 12 | 12 | 12 | 12 | 12 | \x3132 | Fri Apr 24 00:00:00 2020 | Fri Apr 24 11:00:00 2020 | 02:00:00 | |
13 | 13 | 13 | 13 | 13 | 13 | 13 | \x3133 | Sat Apr 25 00:00:00 2020 | Sat Apr 25 11:00:00 2020 | 04:00:00 | |
(3 rows)
SELECT * FROM t_grammer ORDER BY 1;
c1 | c2 | c3 | c4 | c5
-----+------+---------------+-----------+-----------
11 | 1 | | |
12 | 2 | | |
23 | -100 | | |
24 | -100 | | |
31 | 1 | | |
32 | 2 | | |
41 | 1 | | |
42 | 2 | | |
51 | 1 | | |
52 | 2 | | |
53 | 3 | | |
54 | 4 | | |
55 | 5 | | |
61 | 1 | | |
62 | 2 | | |
63 | 3 | | |
64 | 4 | {10,20,30} | |
65 | 5 | {10,20,30} | |
66 | 6 | {10,20,30} | |
67 | 7 | {10,20,30} | |
68 | 8 | {10,20,30} | |
69 | 9 | {10,20,30} | |
71 | 1 | | |
72 | 2 | {71,72,73} | |
73 | 3 | {71,72,73} | |
74 | 4 | {71,72,73} | (74,75) |
81 | -100 | | | {81,82}
82 | -100 | {881,882} | |
83 | -100 | {81,82,83} | (810,820) |
84 | -100 | {81,82,83} | (850,) |
91 | 1 | {91,92,93} | (94,95) |
92 | -100 | | (910,920) | {91,92}
101 | 1 | {102,103,104} | (105,106) | {107,108}
102 | 2 | {102,103,104} | (105,106) | {107,108}
103 | 3 | {102,103,104} | (105,106) | {107,108}
(35 rows)
SELECT * FROM t_default ORDER BY 1;
--? c1 | c2 | c3
--?
--? 10 | .396464773453772 | Wed Apr 29 10:25:23.202729 2020
--? 91 | .91 | Sun May 17 00:00:00 2020
--? 92 | .840485369320959 | Sun May 17 00:00:00 2020
(3 rows)

View File

@ -0,0 +1,188 @@
\c upsert
SET CURRENT_SCHEMA TO upsert_test;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
-- support multiple set
INSERT INTO t_grammer VALUES(11, 1) ON DUPLICATE KEY UPDATE c2 = 2, c4 = ROW(10, 20), c3 = '{10, 20, 30}';
INSERT INTO t_grammer VALUES(12, 2) ON DUPLICATE KEY UPDATE c2 = 2, c3[1] = 3;
-- support const value and expr
INSERT INTO t_grammer VALUES(23) ON DUPLICATE KEY UPDATE c2 = 2;
INSERT INTO t_grammer VALUES(24) ON DUPLICATE KEY UPDATE c2 = length('test') + 100;
-- support without table name
INSERT INTO t_grammer VALUES(31, 1) ON DUPLICATE KEY UPDATE c2 = c1, c4.a = VALUES(c1);
INSERT INTO t_grammer VALUES(32, 2) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = VALUES(c1);
-- support with table name
INSERT INTO t_grammer VALUES(41, 1) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1, c4.a = VALUES(c1);
INSERT INTO t_grammer VALUES(42, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = VALUES(c1);
-- support EXCLUDED alone and with expr
INSERT INTO t_grammer VALUES(51, 1) ON DUPLICATE KEY UPDATE c2 = c1 + 5, c4.a = EXCLUDED.c1;
INSERT INTO t_grammer VALUES(52, 2) ON DUPLICATE KEY UPDATE c2 = t_grammer.c1 + 5, c4.a = EXCLUDED.c1 + 5;
INSERT INTO t_grammer VALUES(53, 3) ON DUPLICATE KEY UPDATE c4.a = sqrt(EXCLUDED.c1 - EXCLUDED.c2 - 1) + 1, c2 = t_grammer.c1 + 5;
INSERT INTO t_grammer VALUES(54, 4) ON DUPLICATE KEY UPDATE c4.b = EXCLUDED.c1 + t_grammer.c1;
INSERT INTO t_grammer VALUES(55, 5) ON DUPLICATE KEY UPDATE c4.a = EXCLUDED.c2, c4.b = EXCLUDED.c1 + sqrt(c1 - 6);
-- support ARRAY
INSERT INTO t_grammer VALUES(61, 1) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]);
INSERT INTO t_grammer VALUES(62, 2) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2,3]);
INSERT INTO t_grammer VALUES(63, 3) ON DUPLICATE KEY UPDATE c5 = VALUES(c3[2:3]);
INSERT INTO t_grammer VALUES(64, 4, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c3[1];
INSERT INTO t_grammer VALUES(65, 5, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[2,3];
INSERT INTO t_grammer VALUES(66, 6, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = EXCLUDED.c3[1:2];
INSERT INTO t_grammer VALUES(67, 7, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c2 = c3[1];
INSERT INTO t_grammer VALUES(68, 8, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[2,3];
INSERT INTO t_grammer VALUES(69, 9, '{10, 20, 30}') ON DUPLICATE KEY UPDATE c5 = c3[1:2];
-- support user defined type
INSERT INTO t_grammer VALUES(71, 1) ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1;
INSERT INTO t_grammer VALUES(72, 2, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1;
INSERT INTO t_grammer VALUES(73, 3, '{71, 72, 73}') ON DUPLICATE KEY UPDATE c4.a = c3[1] + c1, c4.b = c3[2];
INSERT INTO t_grammer VALUES(74, 4, '{71, 72, 73}', ROW(74, 75)) ON DUPLICATE KEY UPDATE c4 = ROW(740, 750);
-- appoint INSERT target column
INSERT INTO t_grammer (c5, c1, c2) VALUES('{81,82}', 81, DEFAULT) ON DUPLICATE KEY UPDATE c2 = VALUES(c1);
INSERT INTO t_grammer (c3[1], c3[2], c1) VALUES(881, 882, 82) ON DUPLICATE KEY UPDATE c5 = VALUES(c3), c2 = VALUES(c3[1]);
INSERT INTO t_grammer (c1, c3, c4) VALUES(83, '{81, 82, 83}', ROW(810, 820)) ON DUPLICATE KEY UPDATE c4 = EXCLUDED.c4;
INSERT INTO t_grammer (c1, c3, c4.a) VALUES(84, '{81, 82, 83}', 850) ON DUPLICATE KEY UPDATE c4.b = c3[3];
-- support UPDATE NOTHING
INSERT INTO t_grammer VALUES(91, 1, '{91, 92, 93}', ROW(94, 95)) ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_grammer (c5, c1, c2, c4) VALUES('{91,92}', 92, DEFAULT, ROW(910, 920)) ON DUPLICATE KEY UPDATE NOTHING;
-- UPDATE target: unsupport with schema but support with tablename
INSERT INTO t_grammer VALUES(0, 0, '{0,0}', ROW(0, 0), '{107, 108}') ON DUPLICATE KEY UPDATE
upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c3[1:2] = c5[1:2], upsert_test.t_grammer.c4.a = c1;
ERROR: column "upsert_test.t_grammer" of relation "t_grammer" does not exist
LINE 2: upsert_test.t_grammer.c2 = c2 * 10, upsert_test.t_grammer.c...
^
INSERT INTO t_grammer VALUES(101, 1, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE
t_grammer.c2 = c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = c1;
-- INSERT target: appoint schema
INSERT INTO upsert_test.t_grammer VALUES(102, 2, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE
t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1);
CREATE SCHEMA upsert_test_tmp;
SET CURRENT_SCHEMA TO upsert_test_tmp;
INSERT INTO upsert_test.t_grammer VALUES(103, 3, '{102, 103, 104}', ROW(105, 106), '{107, 108}') ON DUPLICATE KEY UPDATE
t_grammer.c2 = EXCLUDED.c2 * 10, t_grammer.c3[1:2] = c5[1:2], t_grammer.c4.a = VALUES(c1);
SET CURRENT_SCHEMA TO upsert_test;
DROP SCHEMA upsert_test_tmp;
-- support data type
INSERT INTO t_data VALUES(11, 11, 11, 11, 11, '11', '11', '11', '2020-04-23', '2020-04-23 11:00', '1:00') ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_data VALUES(12, 12, 12, 12, 12, '12', '12', '12', '2020-04-24', '2020-04-24 11:00', '2:00')
ON DUPLICATE KEY UPDATE
c_tiny = 13, c_smallint = 13, c_bigint = 13, c_numeric = 13,
c_var = '13', c_text = '13', c_bytea = '13', c_date = '2020-05-24', c_timestamp = '2020-05-24 11:00', c_time = '3:00';
INSERT INTO t_data VALUES(13, 13, 13, 13, 13, '13', '13', '13', '2020-04-25', '2020-04-25 11:00', '4:00')
ON DUPLICATE KEY UPDATE
c_tiny = EXCLUDED.c_tiny + 1, c_smallint = EXCLUDED.c_smallint + 1, c_bigint = EXCLUDED.c_bigint + 1, c_numeric = EXCLUDED.c_numeric + 1,
c_var = EXCLUDED.c_var || '3', c_text = EXCLUDED.c_text || '3', c_bytea = EXCLUDED.c_bytea || '3',
c_date = EXCLUDED.c_date + '1 day'::interval, c_timestamp = EXCLUDED.c_timestamp + '1 day'::interval, c_time = EXCLUDED.c_time + '1 hour'::interval;
-- support UPDATE to default
INSERT INTO t_default DEFAULT VALUES ON DUPLICATE KEY UPDATE c2 = 100, c3 = '2020-05-17';
INSERT INTO t_default VALUES(91, 0.91, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = DEFAULT, c3 = DEFAULT;
INSERT INTO t_default VALUES(92, DEFAULT, '2020-05-17') ON DUPLICATE KEY UPDATE c2 = 10, c3 = DEFAULT;
-- unsupport alias
INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY UPDATE c2 = VALUES(c1);
ERROR: syntax error at or near "AS"
LINE 1: INSERT INTO t_grammer AS alias VALUES (91) ON DUPLICATE KEY ...
^
-- unsupport VALUES with expr
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2));
ERROR: syntax error at or near "("
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = sqrt(VALUES(c2));
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1;
ERROR: syntax error at or near "+"
LINE 1: ...VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c3[1]) + 1;
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1;
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a) + 1;
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c3[1]) + 1;
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer....
^
-- unsupport VALUES with table name
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c4.a);
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer....
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer.c2);
ERROR: only allow column name within VALUES
LINE 1: ... VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(t_grammer....
^
-- unsupport DEFAULT with expr
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1;
ERROR: syntax error at or near "+"
LINE 1: ...ammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = DEFAULT + 1;
^
-- unsupport user defined typed column's element
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a);
ERROR: only allow column name within VALUES
LINE 1: ...mmer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = VALUES(c4.a);
^
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = EXCLUDED.c4.a;
ERROR: schema "excluded" does not exist
CONTEXT: referenced column: c2
INSERT INTO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a;
ERROR: missing FROM-clause entry for table "c4"
LINE 1: ...TO t_grammer VALUES(0, 0) ON DUPLICATE KEY UPDATE c2 = c4.a;
^
CONTEXT: referenced column: c2
SELECT * FROM t_data ORDER BY 1;
c_int | c_tiny | c_smallint | c_bigint | c_numeric | c_var | c_text | c_bytea | c_date | c_timestamp | c_time | c_intarray | c_com
-------+--------+------------+----------+-----------+-------+--------+----------+--------------------------+--------------------------+----------+------------+-------
11 | 11 | 11 | 11 | 11 | 11 | 11 | \x3131 | Thu Apr 23 00:00:00 2020 | Thu Apr 23 11:00:00 2020 | 01:00:00 | |
12 | 13 | 13 | 13 | 13 | 13 | 13 | \x3133 | Sun May 24 00:00:00 2020 | Sun May 24 11:00:00 2020 | 03:00:00 | |
13 | 14 | 14 | 14 | 14 | 133 | 133 | \x313333 | Sun Apr 26 00:00:00 2020 | Sun Apr 26 11:00:00 2020 | 05:00:00 | |
(3 rows)
SELECT * FROM t_grammer ORDER BY 1;
c1 | c2 | c3 | c4 | c5
-----+------+---------------+-----------+-----------
11 | 2 | {10,20,30} | (10,20) |
12 | 2 | {3} | |
23 | 2 | | |
24 | 104 | | |
31 | 31 | | (31,) |
32 | 37 | | (32,) |
41 | 41 | | (41,) |
42 | 47 | | (42,) |
51 | 56 | | (51,) |
52 | 57 | | (57,) |
53 | 58 | | (8,) |
54 | 4 | | (,108) |
55 | 5 | | (5,62) |
61 | | | |
62 | 2 | | |
63 | 3 | | |
64 | 10 | {10,20,30} | |
65 | 5 | {10,20,30} | | {20,30}
66 | 6 | {10,20,30} | | {10,20}
67 | 10 | {10,20,30} | |
68 | 8 | {10,20,30} | | {20,30}
69 | 9 | {10,20,30} | | {10,20}
71 | 1 | | (,) |
72 | 2 | {71,72,73} | (143,) |
73 | 3 | {71,72,73} | (144,72) |
74 | 4 | {71,72,73} | (740,750) |
81 | 81 | | | {81,82}
82 | 881 | {881,882} | | {881,882}
83 | -100 | {81,82,83} | (810,820) |
84 | -100 | {81,82,83} | (850,83) |
91 | 1 | {91,92,93} | (94,95) |
92 | -100 | | (910,920) | {91,92}
101 | 10 | {107,108,104} | (101,106) | {107,108}
102 | 20 | {107,108,104} | (102,106) | {107,108}
103 | 30 | {107,108,104} | (103,106) | {107,108}
(35 rows)
SELECT * FROM t_default ORDER BY 1;
--? c1 | c2 | c3
--?
--? 10 | 100 | Sun May 17 00:00:00 2020
--? 91 | .840485369320959 | Wed Apr 29 10:25:28.439693 2020
--? 92 | 10 | Wed Apr 29 10:25:28.442916 2020
(3 rows)

View File

@ -0,0 +1,92 @@
-- Note: about upsert test
-- test point:
-- grammer
-- table, unlogged table, temp table, matview
-- constraint(primary key, index, foreign key, null ...)
-- trigger
-- sequence
CREATE DATABASE upsert WITH TEMPLATE template0 ENCODING 'UTF8';
\c upsert
-- grammer test
CREATE SCHEMA upsert_test;
SET CURRENT_SCHEMA TO upsert_test;
CREATE TYPE atype AS(a int, b int);
CREATE TYPE btype AS(a int, b atype, c varchar[3]);
CREATE TABLE t_grammer (c1 INT PRIMARY KEY, c2 INT DEFAULT -100, c3 int[3], c4 atype, c5 int[2]);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_grammer_pkey" for table "t_grammer"
CREATE TABLE t_default (c1 INT PRIMARY KEY DEFAULT 10, c2 FLOAT DEFAULT random(), c3 TIMESTAMP DEFAULT current_timestamp);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_default_pkey" for table "t_default"
CREATE TABLE t_data (c_int INT PRIMARY KEY, c_tiny TINYINT, c_smallint SMALLINT, c_bigint BIGINT, c_numeric NUMERIC,
c_var VARCHAR, c_text TEXT, c_bytea BYTEA, c_date DATE, c_timestamp TIMESTAMP, c_time TIME,
c_intarray INT[3], c_com atype);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_data_pkey" for table "t_data"
CREATE TABLE t_trigger (key INT PRIMARY KEY, color TEXT);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_trigger_pkey" for table "t_trigger"
-- unlogged table
CREATE SCHEMA upsert_test_unlog;
SET CURRENT_SCHEMA TO upsert_test_unlog;
CREATE TYPE atype AS(a int, b int);
CREATE TYPE btype AS(a int, b atype, c varchar[3]);
CREATE UNLOGGED TABLE t_hash_unlog_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype);
CREATE UNLOGGED TABLE t_hash_unlog_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_hash_unlog_1_pkey" for table "t_hash_unlog_1"
CREATE UNLOGGED TABLE t_rep_unlog_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ;
CREATE UNLOGGED TABLE t_rep_unlog_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_rep_unlog_1_pkey" for table "t_rep_unlog_1"
-- temp table
-- temp table can not be created here, we create it in upsert_tmp_test.
-- restriction test
CREATE SCHEMA upsert_test_etc;
SET CURRENT_SCHEMA TO upsert_test_etc;
create table up_neg_01 (c1 int, c2 int, c3 int) with (ORIENTATION = COLUMN);
create table up_neg_02 (c1 int, c2 int, c3 int) with (ORIENTATION = COLUMN);
create table up_neg_03 (c1 int, c2 int UNIQUE DEFERRABLE, c3 int);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_03_c2_key" for table "up_neg_03"
create table up_neg_04 (c1 int, c2 int UNIQUE, c3 int);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_04_c2_key" for table "up_neg_04"
create table up_neg_05(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3));
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "up_neg_05_pkey" for table "up_neg_05"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_05_c4_key" for table "up_neg_05"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_05_c2_c3_key" for table "up_neg_05"
create table up_neg_0(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3)) ;
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "up_neg_0_pkey" for table "up_neg_0"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_0_c4_key" for table "up_neg_0"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_0_c2_c3_key" for table "up_neg_0"
create table up_neg_06(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_06_c3_key" for table "up_neg_06"
create table up_neg_07(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_07_c3_key" for table "up_neg_07"
create table up_neg_08(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_08_c3_key" for table "up_neg_08"
create table up_neg_09(c1 int, c2 int, c3 int, unique(c2,c3)) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_09_c2_c3_key" for table "up_neg_09"
create table up_neg_10(c1 int, c2 int, c3 int, unique(c2,c3)) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_neg_10_c2_c3_key" for table "up_neg_10"
create view up_view as select *from up_neg_01;
create materialized view mat_view as select *from up_neg_01;
create table pkt (a int primary key, b int, c int);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pkt_pkey" for table "pkt"
create table fkt (a int primary key, b int references pkt, c int);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "fkt_pkey" for table "fkt"
-- procedure test
CREATE SCHEMA upsert_test_procedure;
-- explain test
CREATE SCHEMA upsert_test_explain;
SET CURRENT_SCHEMA TO upsert_test_explain;
create table up_expl_hash(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_hash_c3_key" for table "up_expl_hash"
create table up_expl_repl(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_repl_c3_key" for table "up_expl_repl"
create table up_expl_part(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_part_c3_key" for table "up_expl_part"
create unlogged table up_expl_unlog(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_unlog_c3_key" for table "up_expl_unlog"
create table up_expl_node(c1 int, c2 int, c3 int unique) ;
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_node_c3_key" for table "up_expl_node"
create table up_expl_repl(c1 int, c2 int, c3 int unique) ;
ERROR: relation "up_expl_repl" already exists
create table up_expl_repl2(c1 int, c2 int, c3 int, c4 int unique, c5 int primary key, unique(c2,c3));
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "up_expl_repl2_pkey" for table "up_expl_repl2"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_repl2_c4_key" for table "up_expl_repl2"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "up_expl_repl2_c2_c3_key" for table "up_expl_repl2"
-- create temp table up_expl_temp(c1 int, c2 int, c3 int unique);

View File

@ -0,0 +1,4 @@
CREATE DATABASE upsert_etc;
\c upsert_etc;
CREATE SCHEMA upsert_test_etc;
SET CURRENT_SCHEMA TO upsert_test_etc;

View File

@ -0,0 +1,229 @@
/*
* upsert negative test cases
*
table cases
col table
ORC table
VIEW
MAT view
index
DEFERABLE index
insert stmt
returning
with
with recur
update stmt
VALUES with expr
update distribute key
update primary key
update unique key
update partition key
where clause
with clause
sub query
*/
--------------------------------------------------------------------------------------------
\c upsert
SET CURRENT_SCHEMA TO upsert_test_etc;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
insert into up_neg_04 select a,a,a from generate_series(1,20) as a;
insert into up_neg_05 select a,a,a,a,a from generate_series(1,20) as a;
insert into up_neg_06 select a,a,a from generate_series(1,20) as a;
insert into up_neg_07 select a,a,a from generate_series(1,20) as a;
insert into up_neg_08 select a,a,a from generate_series(1,20) as a;
insert into up_neg_09 select a,a,a from generate_series(1,20) as a;
insert into up_neg_10 select a,a,a from generate_series(1,20) as a;
-- table cases
---- col table
insert into up_neg_01 values(1,2,3) on duplicate key update c3 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table.
---- ORC table
insert into up_neg_02 values(1,2,3) on duplicate key update c3 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table.
---- VIEW
insert into up_view values(1) on duplicate key update c3 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE is not supported on VIEW.
-- matview
refresh materialized view mat_view;
insert into mat_view values(1) on duplicate key update c3 = 1;
ERROR: cannot change materialized view "mat_view"
-- index
---- DEFERABLE index
insert into up_neg_03 values(1,2,3) on duplicate key update c1 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE does not support deferrable unique constraints/exclusion constraints.
insert into up_neg_03 values(1) on duplicate key update c1 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE does not support deferrable unique constraints/exclusion constraints.
-- insert stmt
----returning
insert into up_neg_04 values(1,1,1) on duplicate key update c1 = 1 returning c1;
ERROR: RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
----with
with sub as (select *from up_neg_04)
insert into up_neg_04 select *from sub on duplicate key update c1 =1;
ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
----with recur
with RECURSIVE sub as (select *from up_neg_04)
insert into up_neg_04 select *from sub on duplicate key update c1 =1;
ERROR: WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
with sub as (select *from up_neg_04)
insert into up_neg_04 select *from sub on duplicate key update c1 =1 returning c1;
ERROR: RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
-- update stmt
---- VALUES with expr
insert into up_neg_05 values(1,1,1,1) on duplicate key update c3 = values(1+100);
ERROR: syntax error at or near "1"
LINE 1: ... values(1,1,1,1) on duplicate key update c3 = values(1+100);
^
insert into up_neg_05 values(1,1,1,1) on duplicate key update c3 = values(100);
ERROR: syntax error at or near "100"
LINE 1: ...05 values(1,1,1,1) on duplicate key update c3 = values(100);
^
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c1 = 1;
---- update primary key
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c5 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
---- update unique key
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c2 = 1, c3=2;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c2 = 1;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c3 = 2;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c1 =1, c2 = 1, c3=2, c4=1,c5=1;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
---- where clause
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1 where c1=1;
ERROR: syntax error at or near "where"
LINE 1: ... values(1,1,1,1,1) on duplicate key update c4 = 1 where c1=1...
^
---- from clause
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update c4 = 1 from up_neg_04 where c1=1;
ERROR: syntax error at or near "from"
LINE 1: ... values(1,1,1,1,1) on duplicate key update c4 = 1 from up_ne...
^
---- sub query
insert into up_neg_05 values(1,1,1,1,1) on duplicate key update (c2) = (select c2 from up_neg_05);
ERROR: Update with subquery is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.
---- update distribute key
insert into up_neg_06 values(101, 1, 1) on duplicate key update c3=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
---- update partition key
insert into up_neg_07 values(101, 1, 300) on duplicate key update c3=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_08 values(101, 1, 300) on duplicate key update c3=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_09 values(101, 1, 1) on duplicate key update c3=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_09 values(101, 1, 1) on duplicate key update c2=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
insert into up_neg_09 values(101, 1, 1) on duplicate key update c2 =1,c3=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--update unique key mul type plans
EXPLAIN (VERBOSE on, COSTS off) insert into up_neg_10 values(101, 1, 300) on duplicate key update c1=101;
QUERY PLAN
-------------------------------------------------
Insert on upsert_test_etc.up_neg_10
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: up_neg_10_c2_c3_key
-> Result
Output: 101, 1, 300
(5 rows)
insert into up_neg_10 values(101, 1, 300) on duplicate key update c3=101;
ERROR: INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key.
--trigger
create table upsert_base
(c1 int primary key,
c2 varchar(100), --record the type
cold varchar(1024),
cnew varchar(1024) );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "upsert_base_pkey" for table "upsert_base"
create table upsert_tri
(c1 int,
c2 varchar(100),
c3 int,
unique(c1,c3)
);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "upsert_tri_c1_c3_key" for table "upsert_tri"
--S1 after trigger
CREATE OR REPLACE FUNCTION trig_after() RETURNS TRIGGER AS $emp_audit$
BEGIN
IF (TG_OP = 'INSERT') THEN
insert into upsert_base(c2,cnew) values('after insert',new.c2);
RETURN NEW;
ELSIF (TG_OP = 'UPDATE') THEN
insert into upsert_base(c2,cold,cnew) values('after update',old.c2,new.c2);
RETURN NEW;
END IF;
RETURN NULL;
END;
$emp_audit$ LANGUAGE plpgsql;
CREATE TRIGGER tri_after
AFTER UPDATE OR INSERT ON upsert_tri
FOR EACH ROW EXECUTE PROCEDURE trig_after();
--S2 before trigger
CREATE OR REPLACE FUNCTION trig_before() RETURNS TRIGGER AS $emp_audit$
BEGIN
IF (TG_OP = 'INSERT') THEN
insert into upsert_base(c2,cnew) values('before insert',new.c2);
RETURN NEW;
ELSIF (TG_OP = 'UPDATE') THEN
insert into upsert_base(c2,cold,cnew) values('before update',old.c2,new.c2);
RETURN NEW;
END IF;
RETURN NULL;
END;
$emp_audit$ LANGUAGE plpgsql;
CREATE TRIGGER tri_before
BEFORE UPDATE OR INSERT ON upsert_tri
FOR EACH ROW EXECUTE PROCEDURE trig_before();
---- upsert-insert
insert into upsert_tri(c1,c2,c3) values(1000,'abc',1) on duplicate key update nothing;
ERROR: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, before insert, null, abc).
CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)"
PL/pgSQL function trig_before() line 4 at SQL statement
insert into upsert_tri(c1,c2,c3) values(2000,'abc',1) on duplicate key update c2='bcd';
ERROR: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, before insert, null, abc).
CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)"
PL/pgSQL function trig_before() line 4 at SQL statement
---- upsert-nothing
insert into upsert_tri(c1,c2,c3) values(1000,'abc',1) on duplicate key update nothing;
ERROR: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, before insert, null, abc).
CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)"
PL/pgSQL function trig_before() line 4 at SQL statement
---- upsert-update
insert into upsert_tri(c1,c2,c3) values(2000,'abc',1) on duplicate key update c2='bcd';
ERROR: null value in column "c1" violates not-null constraint
DETAIL: Failing row contains (null, before insert, null, abc).
CONTEXT: SQL statement "insert into upsert_base(c2,cnew) values('before insert',new.c2)"
PL/pgSQL function trig_before() line 4 at SQL statement
---- check results
SELECT * from upsert_tri;
c1 | c2 | c3
----+----+----
(0 rows)
SELECT * from upsert_base;
c1 | c2 | cold | cnew
----+----+------+------
(0 rows)
-- foreign key
insert into pkt values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5);
insert into fkt values(1,1,1),(2,2,2),(3,3,3);
insert into fkt values(1,1,1),(2,2,2),(3,3,3) on duplicate key update b=excluded.b+1, c=0;
insert into fkt values(1,1,1),(2,2,2),(3,3,3) on duplicate key update b=excluded.b+3, c=-1;
ERROR: insert or update on table "fkt" violates foreign key constraint "fkt_b_fkey"
DETAIL: Key (b)=(6) is not present in table "pkt".

View File

@ -0,0 +1,173 @@
\c upsert
CREATE SCHEMA upsert_test_tmp;
SET CURRENT_SCHEMA TO upsert_test_tmp;
CREATE TYPE atype AS(a int, b int);
CREATE TYPE btype AS(a int, b atype, c varchar[3]);
CREATE TEMP TABLE t_hash_tmp_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype);
CREATE TEMP TABLE t_hash_tmp_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_hash_tmp_1_pkey" for table "t_hash_tmp_1"
CREATE TEMP TABLE t_rep_tmp_0 (c1 INT, c2 INT, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ;
CREATE TEMP TABLE t_rep_tmp_1 (c1 INT, c2 INT PRIMARY KEY, c3 VARCHAR, c4 INT[3], c5 INT[5], c6 atype, c7 btype) ;
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_rep_tmp_1_pkey" for table "t_rep_tmp_1"
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
-- hash table
-- tmp table without primary key
INSERT INTO t_hash_tmp_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_hash_tmp_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c2 = 100;
-- tmp table with primary key
-- multi insert
INSERT INTO t_hash_tmp_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_hash_tmp_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_hash_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+----+---------+------------------+---------+-----------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
(4 rows)
INSERT INTO t_hash_tmp_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_hash_tmp_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_hash_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(4 rows)
-- insert and update same tuple, and update twice for another same tuple
INSERT INTO t_hash_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
SELECT * FROM t_hash_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
(5 rows)
INSERT INTO t_hash_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_hash_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(5 rows)
-- replication table
-- tmp table without primary key
INSERT INTO t_rep_tmp_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_rep_tmp_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c2 = 100;
-- tmp table with primary key
-- multi insert
INSERT INTO t_rep_tmp_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_rep_tmp_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_rep_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+----+---------+------------------+---------+-----------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
(4 rows)
INSERT INTO t_rep_tmp_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_rep_tmp_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_rep_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(4 rows)
-- insert and update same tuple, and update twice for another same tuple
INSERT INTO t_rep_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
SELECT * FROM t_rep_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
(5 rows)
INSERT INTO t_rep_tmp_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_rep_tmp_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(5 rows)
DROP SCHEMA upsert_test_tmp CASCADE;
NOTICE: drop cascades to 10 other objects
DETAIL: drop cascades to type atype
drop cascades to table t_hash_tmp_0 column c6
drop cascades to table t_hash_tmp_1 column c6
drop cascades to table t_rep_tmp_0 column c6
drop cascades to table t_rep_tmp_1 column c6
drop cascades to type btype
drop cascades to table t_hash_tmp_0 column c7
drop cascades to table t_hash_tmp_1 column c7
drop cascades to table t_rep_tmp_0 column c7
drop cascades to table t_rep_tmp_1 column c7

View File

@ -0,0 +1,148 @@
\c upsert
SET CURRENT_SCHEMA TO upsert_test;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
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.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 t_trigger
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): %', old.*::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 t_trigger
FOR EACH ROW EXECUTE procedure upsert_after_func();
INSERT INTO t_trigger values(1, 'black') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (1,black)
WARNING: after insert (new): (1,black)
INSERT INTO t_trigger values(2, 'red') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (2,red)
WARNING: before insert (new, modified): (2,"red trig modified")
WARNING: after insert (new): (2,"red trig modified")
INSERT INTO t_trigger values(3, 'orange') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (3,orange)
WARNING: after insert (new): (3,orange)
INSERT INTO t_trigger values(4, 'green') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (4,green)
WARNING: before insert (new, modified): (4,"green trig modified")
WARNING: after insert (new): (4,"green trig modified")
INSERT INTO t_trigger values(5, 'purple') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (5,purple)
WARNING: after insert (new): (5,purple)
INSERT INTO t_trigger values(6, 'white') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (6,white)
WARNING: before insert (new, modified): (6,"white trig modified")
WARNING: after insert (new): (6,"white trig modified")
INSERT INTO t_trigger values(7, 'pink') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (7,pink)
WARNING: after insert (new): (7,pink)
INSERT INTO t_trigger values(8, 'yellow') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (8,yellow)
WARNING: before insert (new, modified): (8,"yellow trig modified")
WARNING: after insert (new): (8,"yellow trig modified")
SELECT * FROM t_trigger ORDER BY key;
key | color
-----+----------------------
1 | black
2 | red trig modified
3 | orange
4 | green trig modified
5 | purple
6 | white trig modified
7 | pink
8 | yellow trig modified
(8 rows)
INSERT INTO t_trigger values(2, 'black') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (2,black)
WARNING: before insert (new, modified): (2,"black trig modified")
WARNING: before update (old): (2,"red trig modified")
WARNING: before update (new): (2,"updated red trig modified")
WARNING: after update (old): (2,"red trig modified")
WARNING: after update (new): (2,"updated red trig modified")
INSERT INTO t_trigger values(3, 'red') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (3,red)
WARNING: before update (old): (3,orange)
WARNING: before update (new): (3,"updated orange")
WARNING: after update (old): (3,orange)
WARNING: after update (new): (3,"updated orange")
INSERT INTO t_trigger values(4, 'orange') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (4,orange)
WARNING: before insert (new, modified): (4,"orange trig modified")
WARNING: before update (old): (4,"green trig modified")
WARNING: before update (new): (4,"updated green trig modified")
WARNING: after update (old): (4,"green trig modified")
WARNING: after update (new): (4,"updated green trig modified")
INSERT INTO t_trigger values(5, 'green') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (5,green)
WARNING: before update (old): (5,purple)
WARNING: before update (new): (5,"updated purple")
WARNING: after update (old): (5,purple)
WARNING: after update (new): (5,"updated purple")
INSERT INTO t_trigger values(6, 'purple') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (6,purple)
WARNING: before insert (new, modified): (6,"purple trig modified")
WARNING: before update (old): (6,"white trig modified")
WARNING: before update (new): (6,"updated white trig modified")
WARNING: after update (old): (6,"white trig modified")
WARNING: after update (new): (6,"updated white trig modified")
INSERT INTO t_trigger values(7, 'white') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (7,white)
WARNING: before update (old): (7,pink)
WARNING: before update (new): (7,"updated pink")
WARNING: after update (old): (7,pink)
WARNING: after update (new): (7,"updated pink")
INSERT INTO t_trigger values(8, 'pink') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (8,pink)
WARNING: before insert (new, modified): (8,"pink trig modified")
WARNING: before update (old): (8,"yellow trig modified")
WARNING: before update (new): (8,"updated yellow trig modified")
WARNING: after update (old): (8,"yellow trig modified")
WARNING: after update (new): (8,"updated yellow trig modified")
INSERT INTO t_trigger values(9, 'yellow') ON DUPLICATE KEY UPDATE color = 'updated ' || t_trigger.color;
WARNING: before insert (new): (9,yellow)
WARNING: after insert (new): (9,yellow)
SELECT * FROM t_trigger ORDER BY key;
key | color
-----+------------------------------
1 | black
2 | updated red trig modified
3 | updated orange
4 | updated green trig modified
5 | updated purple
6 | updated white trig modified
7 | updated pink
8 | updated yellow trig modified
9 | yellow
(9 rows)

View File

@ -0,0 +1,152 @@
\c upsert
SET CURRENT_SCHEMA TO upsert_test_unlog;
-- enable_upsert_to_merge must be off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
SHOW enable_upsert_to_merge;
enable_upsert_to_merge
------------------------
off
(1 row)
-- hash table
-- unlogged table without primary key
INSERT INTO t_hash_unlog_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_hash_unlog_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c2 = 100;
-- unlogged table with primary key
-- multi insert
INSERT INTO t_hash_unlog_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_hash_unlog_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_hash_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+----+---------+------------------+---------+-----------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
(4 rows)
INSERT INTO t_hash_unlog_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_hash_unlog_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_hash_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(4 rows)
-- insert and update same tuple, and update twice for another same tuple
INSERT INTO t_hash_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
SELECT * FROM t_hash_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
(5 rows)
INSERT INTO t_hash_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_hash_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(5 rows)
-- replication table
-- unlogged table without primary key
INSERT INTO t_rep_unlog_0 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_rep_unlog_0 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c2 = 100;
-- unlogged table with primary key
-- multi insert
INSERT INTO t_rep_unlog_1 VALUES(1, 1, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(1, 2, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_rep_unlog_1 VALUES(2, 3, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}')),
(2, 4, 'C3', '{1,2,3}', '{10,20,30,40,50}', ROW(10,20), ROW(100, ROW(10,20), '{100,200}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_rep_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+----+---------+------------------+---------+-----------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 3 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
2 | 4 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
(4 rows)
INSERT INTO t_rep_unlog_1 VALUES(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 2, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
INSERT INTO t_rep_unlog_1 VALUES(20, 3, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(21, 4, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_rep_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(4 rows)
-- insert and update same tuple, and update twice for another same tuple
INSERT INTO t_rep_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE NOTHING;
SELECT * FROM t_rep_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
1 | 1 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
0 | 5 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
(5 rows)
INSERT INTO t_rep_unlog_1 VALUES(0, 5, 'C30', '{10,20,30}', '{10,20,30,40,50}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(1, 5, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}')),
(10, 1, 'C30', '{10,20,30}', '{100,200,300,400,500}', ROW(100,200), ROW(1000, ROW(100,200), '{1000,2000}')),
(11, 1, 'C31', '{11,21,31}', '{101,201,301,401,501}', ROW(101,201), ROW(1001, ROW(101,201), '{1001,2001}'))
ON DUPLICATE KEY UPDATE c1 = EXCLUDED.c1, c3 = EXCLUDED.c3, c4 = EXCLUDED.c4, c5 = EXCLUDED.c5, c6 = EXCLUDED.c6, c7 = EXCLUDED.c7;
SELECT * FROM t_rep_unlog_1 ORDER BY c2;
c1 | c2 | c3 | c4 | c5 | c6 | c7
----+----+-----+------------+-----------------------+-----------+----------------------------------
11 | 1 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 2 | C3 | {1,2,3} | {10,20,30,40,50} | (10,20) | (100,"(10,20)","{100,200}")
20 | 3 | C30 | {10,20,30} | {10,20,30,40,50} | (100,200) | (1000,"(100,200)","{1000,2000}")
21 | 4 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
1 | 5 | C31 | {11,21,31} | {101,201,301,401,501} | (101,201) | (1001,"(101,201)","{1001,2001}")
(5 rows)

View File

@ -227,6 +227,7 @@ select name,vartype,unit,min_val,max_val from pg_settings where name <> 'qunit_c
enable_tsdb | bool | | |
enable_twophase_commit | bool | | |
enable_upgrade_merge_lock_mode | bool | | |
enable_upsert_to_merge | bool | | |
enable_user_metric_persistent | bool | | |
enable_valuepartition_pruning | bool | | |
enable_vector_engine | bool | | |

View File

@ -44,12 +44,18 @@ test: hw_smp
# test MERGE INTO
# test INSERT UPDATE
# test INSERT UPDATE UPSERT
test: insert_update_001 insert_update_002 insert_update_003 insert_update_008 insert_update_009 insert_update_010
test: delete update namespace case select_having select_implicit
test: hw_test_operate_user
test: hw_createtbl_llt gsqlerr
test: hw_sql_llt sqlLLT
test: upsert_prepare
test: upsert_001 upsert_002 upsert_003 upsert_008 upsert_009 upsert_010
test: upsert_grammer_test_01 upsert_unlog_test upsert_tmp_test
test: upsert_grammer_test_02 upsert_restriction upsert_composite
test: upsert_trigger_test upsert_explain
test: upsert_clean
# all pass
# run tablespace by itself, and first, because it forces a checkpoint;

View File

@ -17,3 +17,11 @@ test: delete update namespace case select_having select_implicit
test: hw_test_operate_user
test: hw_createtbl_llt gsqlerr
test: hw_sql_llt sqlLLT
# test UPSERT
test: upsert_prepare
test: upsert_001 upsert_002 upsert_003 upsert_008 upsert_009 upsert_010
test: upsert_grammer_test_01 upsert_unlog_test upsert_tmp_test
test: upsert_grammer_test_02 upsert_restriction upsert_composite
test: upsert_trigger_test upsert_explain
test: upsert_clean

View File

@ -2,6 +2,10 @@ DROP SCHEMA test_insert_update_001 CASCADE;
CREATE SCHEMA test_insert_update_001;
SET CURRENT_SCHEMA TO test_insert_update_001;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test description
\h INSERT

View File

@ -2,6 +2,10 @@ DROP SCHEMA test_insert_update_002 CASCADE;
CREATE SCHEMA test_insert_update_002;
SET CURRENT_SCHEMA TO test_insert_update_002;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,

View File

@ -2,6 +2,10 @@ DROP SCHEMA test_insert_update_003 CASCADE;
CREATE SCHEMA test_insert_update_003;
SET CURRENT_SCHEMA TO test_insert_update_003;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,

View File

@ -6,6 +6,10 @@
CREATE SCHEMA test_insert_update_008;
SET current_schema = test_insert_update_008;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
CREATE TABLE products_base
(
product_id INTEGER DEFAULT 0,
@ -66,9 +70,9 @@ EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
@ -79,18 +83,18 @@ INSERT INTO products_row
FROM newproducts_row, products_row
WHERE products_row.total + newproducts_row.total < 1000
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total
FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
-- explain analyze
BEGIN;
@ -98,9 +102,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- explain performance
@ -110,9 +114,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
@ -122,9 +126,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- pretty mode performance
@ -135,9 +139,9 @@ EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
-- explain analyze
BEGIN;
@ -145,9 +149,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- explain analyze
@ -156,9 +160,9 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- explain performance
@ -168,9 +172,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = run;
@ -180,9 +184,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = summary;
@ -192,9 +196,9 @@ EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = __unnamed_subquery_source__.product_name,
category = __unnamed_subquery_source__.category,
total = __unnamed_subquery_source__.total;
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o

View File

@ -2,6 +2,11 @@ DROP SCHEMA test_insert_update_009 CASCADE;
CREATE SCHEMA test_insert_update_009;
SET CURRENT_SCHEMA TO test_insert_update_009;
SET enable_light_proxy=off;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,

View File

@ -1,6 +1,11 @@
DROP SCHEMA test_insert_update_010 CASCADE;
CREATE SCHEMA test_insert_update_010;
SET CURRENT_SCHEMA TO test_insert_update_010;
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,

View File

@ -1,3 +1,6 @@
-- SET enable_upsert_to_merge=ON to test the upsert implemented by merge,
-- real upsert will be tested in specialized case.
SET enable_upsert_to_merge TO ON;
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%' ORDER BY name;

View File

@ -0,0 +1,284 @@
DROP SCHEMA test_upsert_001 CASCADE;
CREATE SCHEMA test_upsert_001;
SET CURRENT_SCHEMA TO test_upsert_001;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test description
\h INSERT
-- test permission
--- test with no sequence column
CREATE TABLE t00 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT);
CREATE USER upsert_tester PASSWORD '123456@cc';
GRANT ALL PRIVILEGES ON SCHEMA test_upsert_001 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
RESET SESSION AUTHORIZATION;
---- error: only have INSERT permission
GRANT INSERT ON test_upsert_001.t00 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
RESET SESSION AUTHORIZATION;
---- success: have INSERT UPDATE permission
GRANT INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
RESET SESSION AUTHORIZATION;
--- have SELECT INSERT UPDATE permission
GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t00 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO test_upsert_001.t00 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
RESET SESSION AUTHORIZATION;
--- test with sequnce column
CREATE TABLE t01 (col1 INT , col2 BIGSERIAL PRIMARY KEY, col3 INT) ;
---- error: don't have UPDATE permission on sequence table.
GRANT SELECT, INSERT, UPDATE ON test_upsert_001.t01 TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
RESET SESSION AUTHORIZATION;
---- have SELECT INSERT UPDATE permission on target relation, and UPDATE permission on sequence table.
GRANT UPDATE ON test_upsert_001.t01_col2_seq TO upsert_tester;
SET SESSION SESSION AUTHORIZATION upsert_tester PASSWORD '123456@cc';
INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
INSERT INTO test_upsert_001.t01 VALUES(1) ON DUPLICATE KEY UPDATE col3 = 5;
RESET SESSION AUTHORIZATION;
-- test ommit INSERT target column
CREATE TABLE t02 (col1 INT DEFAULT 1 PRIMARY KEY, col2 INT, col3 INT);
INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20;
SELECT * FROM t02 ORDER BY 1, 2;
INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col2 = 20;
SELECT * FROM t02 ORDER BY 1, 2;
ALTER TABLE t02 DROP COLUMN col2;
ALTER TABLE t02 ADD COLUMN col4 INT;
INSERT INTO t02 VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE col4 = 40;
SELECT * FROM t02 ORDER BY 1, 2;
INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40;
SELECT * FROM t02 ORDER BY 1, 2;
INSERT INTO t02 VALUES(2, 3, 4) ON DUPLICATE KEY UPDATE col4 = 40;
SELECT * FROM t02 ORDER BY 1, 2;
-- test restriction
--- test replication table
CREATE TABLE t03 (col1 int PRIMARY KEY, col2 INT, col3 smallserial) ;
--- error: not allowed volatile function as default value
INSERT INTO t03(col2) VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100;
ALTER TABLE t03 DROP COLUMN col3;
--- error: primary key are not allowed to update
INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col1 = 1;
--- error: clause other than VALUSES are not allowed to use
INSERT INTO t03 SELECT * FROM t03 ON DUPLICATE KEY UPDATE col2 = 1;
--- success: expression index are supported
CREATE UNIQUE INDEX u_expr_index ON t03 USING btree (abs(col1));
INSERT INTO t03 VALUES(-10, 10) ON DUPLICATE KEY UPDATE col2 = 20;
DROP INDEX u_expr_index;
-- test with stream operator on
INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100;
SELECT * FROM t03;
INSERT INTO t03 VALUES(1) ON DUPLICATE KEY UPDATE col2 = 100;
SELECT * FROM t03;
SELECT * FROM t03;
--- test PBE
PREPARE p1 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100;
EXECUTE p1(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
EXECUTE p1(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
DELETE t03 WHERE col1 = 5;
---- test with primary key
INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200;
SELECT * FROM t03;
INSERT INTO t03 VALUES(2) ON DUPLICATE KEY UPDATE col2 = 200;
SELECT * FROM t03;
SELECT * FROM t03;
---- test with unique key without NOT NULL constraint
ALTER TABLE t03 DROP CONSTRAINT t03_pkey;
ALTER TABLE t03 ADD COLUMN col3 INT;
CREATE UNIQUE INDEX ON t03 (col1, col3);
---- unique constraints might contain NULL, depends on the plan
----- for cn light and fqs, it can be done
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
----- for stream or pgxc it can not be done
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
---- test with unique key with NOT NULL constraint, should success
CREATE UNIQUE INDEX ON t03 (col1);
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
SELECT * FROM t03;
INSERT INTO t03 VALUES(3) ON DUPLICATE KEY UPDATE col2 = 300;
SELECT * FROM t03;
SELECT * FROM t03;
---- test PBE
PREPARE p2 AS INSERT INTO t03 VALUES($1, $2) ON DUPLICATE KEY UPDATE col2 = $1*100;
EXECUTE p2(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
EXECUTE p2(5, 50);
SELECT * FROM t03 WHERE col1 = 5;
--- error: test with clause
WITH tmp(col1, col2) AS (SELECT * FROM t01)
INSERT INTO t01 SELECT * FROM tmp ON DUPLICATE KEY UPDATE col1 = 1;
WITH RECURSIVE rq AS
(
SELECT col1, col2 FROM t00 WHERE col1 = 1
UNION ALL
SELECT origin.col1, rq.col2
FROM rq JOIN t00 AS origin ON origin.col1 = rq.col1
)
INSERT INTO t03 SELECT * FROM rq ON DUPLICATE KEY UPDATE col1 = rq.col1;
--- error: test returning clause
INSERT INTO t01 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 1 RETURNING NOT(1::bool);
--- error: distribute key are not allowed to UPDATE
CREATE TABLE t04 (col1 INT, col2 INT) ;
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 5;
--- error: unique index referenced column are not allowed to UPDATE
CREATE UNIQUE INDEX t04_u_index ON t04(col1, col2);
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
DROP INDEX t04_u_index;
--- error: primary key referenced column are not allowed to UPDATE
ALTER TABLE t04 ADD PRIMARY KEY (col1, col2);
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
--- error: invalid column
INSERT INTO t04 (col2, col3) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5;
--- error: duplicate column
INSERT INTO t04 (col2, col2) VALUES (2, 3) ON DUPLICATE KEY UPDATE col2 = 5;
-- error: target column more than insert target
INSERT INTO t04 (col1, col2) VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 (col1, col2) VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 (col1, col2) SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 (col1, col2) SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 VALUES (1) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 SELECT col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t04 SELECT *, col1 FROM t04 ON DUPLICATE KEY UPDATE col2 = 5;
-- test DEFAULT VALUES
TRUNCATE t00;
TRUNCATE t01;
--- without sequence
----should insert
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
---- should update
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
--- test drop column
TRUNCATE t00;
ALTER TABLE t00 DROP COLUMN col2;
ALTER TABLE t00 ADD COLUMN col3 INT DEFAULT 100;
----should insert
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
---- should update
INSERT INTO t00 DEFAULT VALUES ON DUPLICATE KEY UPDATE col3 = col1;
SELECT * FROM t00 ORDER BY 1, 2;
--- with sequence
----should insert
INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2;
INSERT INTO t01 (col2) SELECT col2 + 1 FROM t01 LIMIT 1;
SELECT * FROM t01 ORDER BY 1, 2, 3;
---- should update
INSERT INTO t01 DEFAULT VALUES ON DUPLICATE KEY UPDATE col1 = col2;
SELECT * FROM t01 ORDER BY 1, 2, 3;
-- test VALUES(DEFAULT)
CREATE TABLE t05 (col1 INT , col2 INT DEFAULT 1 PRIMARY KEY, col3 INT DEFAULT 100) ;
--- should insert
INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000;
SELECT * FROM t05 ORDER BY 1, 2, 3;
--- should update
INSERT INTO t05 VALUES(DEFAULT) ON DUPLICATE KEY UPDATE col3 = 1000;
SELECT * FROM t05 ORDER BY 1, 2, 3;
-- test UPDATE DEFAULT
INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
INSERT INTO t05 (col1, col2, col3) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col3 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
-- test VALUES (DEFAULT, ...)
TRUNCATE t05;
--- should insert
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM t05 ORDER BY 1, 2, 3;
--- should update
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM t05 ORDER BY 1, 2, 3;
--- test drop coulmn
BEGIN;
ALTER TABLE t05 ADD COLUMN col4 INT;
ALTER TABLE t05 ADD COLUMN col5 INT DEFAULT 500;
ALTER TABLE t05 DROP COLUMN col3;
TRUNCATE t05;
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
INSERT INTO t05 VALUES(DEFAULT, DEFAULT, DEFAULT, 600) ON DUPLICATE KEY UPDATE col5 = DEFAULT;
SELECT * FROM t05 ORDER BY 1, 2, 3;
ROLLBACK;
-- test schema
SET current_schema = public;
TRUNCATE test_upsert_001.t05;
--- should insert
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3;
--- should update
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE col3 = DEFAULT, col1 = col3;
SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3;
--- test using schema on update
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1;
SELECT * FROM test_upsert_001.t05 ORDER BY 1, 2, 3;
--- error: should not append schema
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE test_upsert_001.t05.col3 = DEFAULT, t05.col1 = t05.col3 + 1;
INSERT INTO test_upsert_001.t05 VALUES(DEFAULT, DEFAULT, 200), (DEFAULT, 200, DEFAULT)
ON DUPLICATE KEY UPDATE t05.col3 = DEFAULT, t05.col1 = test_upsert_001.t05.col3 + 1;
SET CURRENT_SCHEMA TO test_upsert_001;
DROP USER upsert_tester CASCADE;
DROP SCHEMA test_upsert_001 CASCADE;

View File

@ -0,0 +1,279 @@
DROP SCHEMA test_upsert_002 CASCADE;
CREATE SCHEMA test_upsert_002;
SET CURRENT_SCHEMA TO test_upsert_002;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3;
--- should always insert
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3;
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4;
--- appoint column list in insert clause, should always insert
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6;
--- multiple rows, should always insert
INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7;
SELECT * FROM t1 ORDER BY col5;
--- test union, should insert
INSERT INTO t1 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3) * 10;
SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2;
--- test subquery, should insert
INSERT INTO t1
(SELECT col1 || col2 || '00' FROM t1 ORDER BY col5)
ON DUPLICATE KEY UPDATE col3 = col1 * 100;
SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1;
-- test t2 with one primary key
CREATE TABLE t2 (
col1 INT,
col2 INT PRIMARY KEY,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
--- primary key or unique key are not allowed to update
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3;
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col2 = 100,
col3 = 100,
col4 = '2019-08-09'::TIMESTAMP,
col5 = 100;
--- should insert
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30;
INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40;
INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2;
INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1;
INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
--- should update
INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1;
INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5;
INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
-- primary key are not allowed to be null
INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10;
--- appoint column list in insert clause
---- should insert
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5;
---- should update
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5, col2;
--- test subquery
---- should insert
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
---- should update
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
--- test union, some insert some update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1 ORDER BY 1, 2;
INSERT INTO t2 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
reset behavior_compat_options;
-- test INTERSECT, should update
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1;
INSERT INTO t2 (col1, col2)
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2;
-- test EXCEPT, should update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
-- test unique index with not default value
ALTER TABLE t2 DROP CONSTRAINT t2_pkey;
CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5);
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
INSERT INTO t2
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = col3 + 1;
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
-- test t3 with one primary index with two columns
CREATE TABLE t3 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3)
) ;
--- column referenced by primary key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
--- should insert when not contains primary key and not all primary key referred columns have default value.
--- but will fail cause primary key should not be null
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
--- should insert
--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence)
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
--- test subquery
---- should insert
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
-- test t3 with one unique index with two columns
TRUNCATE t3;
ALTER TABLE t3 DROP CONSTRAINT t3_pkey;
ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL;
CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3);
--- column referenced by unique key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
--- should insert cause not contains unique key and not all unique key referred columns have default value.
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
SELECT * FROM t3 order by col5;
--- should insert
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20;
SELECT * FROM t3 ORDER BY col5;
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 ORDER BY col5;
--- test subquery
---- should insert
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
DROP SCHEMA test_upsert_002 CASCADE;

View File

@ -0,0 +1,144 @@
DROP SCHEMA test_insert_update_003 CASCADE;
CREATE SCHEMA test_insert_update_003;
SET CURRENT_SCHEMA TO test_insert_update_003;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,
col2 INT DEFAULT 0,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3, col5)
) ;
--- should insert
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
--- should update
INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
--- error: duplicate key update on (x, x, 20)
--- this is because current version is not inplace update but merge,
--- so when the subquery contains multiple same values, it will cause duplicate insert failure.
SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2;
INSERT INTO t4 (col1, col5)
(SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3)
ON DUPLICATE KEY UPDATE col1 = 3;
-- test t5 with sequence or default column with volatile function in constaint index
CREATE TABLE t5 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM() + 1
) ;
-- test t5 with sequence column in constaint index
CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3);
--- should insert
INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
--- should update
INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10;
SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3;
--- should some insert some update
INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
--- should INSERT and sequence starting from 7
INSERT INTO t5 VALUES (2), (2);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
-- test with volatile function as default column in constraint index
TRUNCATE t5;
DROP INDEX u_t5_index1;
CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2;
--- should insert
INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
--- should update
INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
--- test subquery
---- should insert
INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
---- should update
INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000;
SELECT * FROM t5 ORDER BY col3;
-- test t6 with one more index
CREATE TABLE t6 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM(),
col6 INT,
col7 TEXT
) ;
ALTER TABLE t6 ADD PRIMARY KEY (col1, col3);
CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6);
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5);
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
--- should not insert
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2);
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
--- should update because primary key matches
INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12);
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
--- should update for those col6 is not null bacause they will match the unique index,
--- and insert for those col6 is null because the unique index containing null never matches,
--- also primary key will not match
--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows,
--- because those sequence have to be generated during the unique index join stage.
INSERT INTO t6 (col1, col5, col6)
(SELECT col1, col5, col6 FROM t6 WHERE col1 = 6)
ON DUPLICATE KEY UPDATE
col7 = col2 + 1;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
--- should update because unique index and primary key both match
INSERT INTO t6 (col1, col3, col5, col6)
(SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL)
ON DUPLICATE KEY UPDATE
col7 = col7 * 10;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
--- insert when col3 = 17 because constaints does not match,
--- but update when col3 = 18 because 18 has been inserted and will cause a match
INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100);
SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7;
INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE
col7 = col3 * 100;
SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7;
DROP SCHEMA test_insert_update_003 CASCADE;

View File

@ -0,0 +1,359 @@
--
-- INSERT UPDATE, test explain command, comes from merge_explain and merge_explain_pretty
--
-- initial
CREATE SCHEMA test_insert_update_008;
SET current_schema = test_insert_update_008;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
CREATE TABLE products_base
(
product_id INTEGER DEFAULT 0,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
INSERT INTO products_base VALUES (1501, 'vivitar 35mm', 'electrncs', 100);
INSERT INTO products_base VALUES (1502, 'olympus is50', 'electrncs', 100);
INSERT INTO products_base VALUES (1600, 'play gym', 'toys', 100);
INSERT INTO products_base VALUES (1601, 'lamaze', 'toys', 100);
INSERT INTO products_base VALUES (1666, 'harry potter', 'dvd', 100);
CREATE TABLE newproducts_base
(
product_id INTEGER DEFAULT 0,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
INSERT INTO newproducts_base VALUES (1502, 'olympus camera', 'electrncs', 200);
INSERT INTO newproducts_base VALUES (1601, 'lamaze', 'toys', 200);
INSERT INTO newproducts_base VALUES (1666, 'harry potter', 'toys', 200);
INSERT INTO newproducts_base VALUES (1700, 'wait interface', 'books', 200);
ANALYZE products_base;
ANALYZE newproducts_base;
--
-- row table
--
CREATE TABLE products_row
(
product_id INTEGER DEFAULT 0 PRIMARY KEY,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
CREATE TABLE newproducts_row
(
product_id INTEGER DEFAULT 0 PRIMARY KEY,
product_name VARCHAR(60) DEFAULT 'null',
category VARCHAR(60) DEFAULT 'unknown',
total INTEGER DEFAULT '0'
);
INSERT INTO products_row SELECT * FROM products_base;
INSERT INTO newproducts_row SELECT * FROM newproducts_base;
ANALYZE products_row;
ANALYZE newproducts_row;
SET explain_perf_mode = normal;
-- explain verbose
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT newproducts_row.product_id,
newproducts_row.product_name,
newproducts_row.category,
newproducts_row.total
FROM newproducts_row, products_row
WHERE products_row.total + newproducts_row.total < 1000
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total
FROM newproducts_row WHERE product_id IS NOT NULL AND product_name IS NOT NULL
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- explain performance
\o insert_update_explain.txt
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- pretty mode performance
SET explain_perf_mode = pretty;
-- explain verbose
EXPLAIN (VERBOSE on, COSTS off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- explain analyze
BEGIN;
EXPLAIN (ANALYZE on, COSTS off, TIMING off)
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
-- explain performance
\o insert_update_explain_pretty.txt
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = run;
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
SET explain_perf_mode = summary;
BEGIN;
EXPLAIN PERFORMANCE
INSERT INTO products_row
SELECT product_id, product_name, category, total FROM newproducts_row
ON DUPLICATE KEY UPDATE
product_name = excluded.product_name,
category = excluded.category,
total = excluded.total;
ROLLBACK;
\o
CREATE TABLE item
(
a INT DEFAULT 3,
item_id NUMERIC(18,10),
item_name VARCHAR(100),
item_level NUMERIC(39,0),
item_desc VARCHAR(250),
item_subclass_cd VARCHAR(50),
item_type_cd VARCHAR(50),
inventory_ind CHAR(300),
vendor_party_id SMALLINT,
commodity_cd VARCHAR(50),
brand_cd VARCHAR(50),
item_available CHAR(100),
CONSTRAINT u_item_index UNIQUE (item_subclass_cd, vendor_party_id)
)
PARTITION BY RANGE (vendor_party_id)
(
PARTITION item_1 VALUES LESS THAN (0),
PARTITION item_2 VALUES LESS THAN (1),
PARTITION item_3 VALUES LESS THAN (2),
PARTITION item_4 VALUES LESS THAN (3),
PARTITION item_5 VALUES LESS THAN (6),
PARTITION item_6 VALUES LESS THAN (8),
PARTITION item_7 VALUES LESS THAN (10),
PARTITION item_8 VALUES LESS THAN (15),
PARTITION item_9 VALUES LESS THAN (MAXVALUE)
) ENABLE ROW MOVEMENT;
CREATE TABLE region
(
a INT DEFAULT 8,
region_cd VARCHAR(50),
region_name VARCHAR(100),
division_cd VARCHAR(50),
region_mgr_associate_id number(18,9)
);
CREATE TABLE associate_benefit_expense
(
a INT DEFAULT 44,
period_end_dt DATE,
associate_expns_type_cd VARCHAR(50),
associate_party_id INTEGER,
benefit_hours_qty decimal(38,11),
benefit_cost_amt number(38,4)
)
PARTITION BY RANGE (associate_expns_type_cd)
(
PARTITION associate_benefit_expense_1 VALUES LESS THAN ('B'),
PARTITION associate_benefit_expense_2 VALUES LESS THAN ('E'),
PARTITION associate_benefit_expense_3 VALUES LESS THAN ('G'),
PARTITION associate_benefit_expense_4 VALUES LESS THAN ('I'),
PARTITION associate_benefit_expense_5 VALUES LESS THAN ('L'),
PARTITION associate_benefit_expense_6 VALUES LESS THAN ('N'),
PARTITION associate_benefit_expense_7 VALUES LESS THAN ('P'),
PARTITION associate_benefit_expense_8 VALUES LESS THAN ('Q'),
PARTITION associate_benefit_expense_9 VALUES LESS THAN ('R'),
PARTITION associate_benefit_expense_10 VALUES LESS THAN ('T'),
PARTITION associate_benefit_expense_11 VALUES LESS THAN ('U'),
PARTITION associate_benefit_expense_12 VALUES LESS THAN ('V'),
PARTITION associate_benefit_expense_13 VALUES LESS THAN (MAXVALUE)
) ENABLE ROW MOVEMENT;
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.12, ' ' , 'A' , NULL, 'TGK' , 'A' , 2, 'A' , 'A' , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (1.3, 'B' , NULL, 'B' , 'B' , NULL, 1, 'B' , NULL , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (2.23, 'C' , 'C' , NULL, 'C' , 'C' , 2, 'C' , 'C' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (3.33, 'D' , 'D' , 'PT' , NULL, 'D' , 3, 'D' , 'D' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (4.98, ' ' , NULL, 'E' , 'E' , 'E' , 4, 'E' , 'E' , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (5.01, NULL, 'F' , ' ' , 'F' , 'F' , 5, 'F' , 'F' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (6, 'G' , 'G' , 'G' , '_D' , 'G' , 6, 'G' , NULL ,'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.7, NULL, NULL, NULL, 'H' , 'H' , 7, NULL, 'G' , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (0.08, 'I' , ' ' , ' T ' , NULL, 'I' , 8, 'I' , '' , 'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (9.12, ' ' , 'J' , ' PP' , 'J' , 'J' , 9, 'J' , NULL , 'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (10.10, NULL, ' ' , 'A' , 'A' , 'A' , 2, NULL, 'A','Y');
--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (11.11, 'B' , 'B' , 'B' , 'BCDAA' , NULL, 1, 'B' , 'B','N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (12.02, 'D' , NULL, NULL, 'C' , 'C' , 2, 'C' , 'C','N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (13.99, NULL, ' ' , 'D' , 'D' , 'D' , 3, 'D' , 'D','Y');
--INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (14, 'G' , 'E' , 'E' , NULL, 'E' , 4, 'E' , 'E','N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (15, 'F' , ' ' , 'C' , 'CLEANING' , 'F' , 5, 'F' , 'F','Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (16, '' , 'Z' , NULL, 'G' , 'G' , 6, 'G' , NULL,'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (17, NULL, '' , ' PAPER' , 'H' , '' , 7, NULL, NULL,'Y');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (19, ' ' , 'B' , '' , '' , 'I' , 8, 'I' , NULL,'N');
INSERT INTO item (ITEM_ID, ITEM_NAME, ITEM_DESC, ITEM_SUBCLASS_CD, ITEM_TYPE_CD, INVENTORY_IND, VENDOR_PARTY_ID, COMMODITY_CD, BRAND_CD,ITEM_AVAILABLE) VALUES (20 , 'A' , 'J' , 'J' , 'J' , NULL, 9, 'J' , 'G','Y');
/*--REGION--*/
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('A', 'A ', 'A', 0.123433);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('B', 'B', 'B', NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('C', 'C', 'C', 2.232008908);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('D', ' DD', 'D', 3.878789);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('E', 'A', 'E', 4.89060603);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('F', 'F', 'F', 5.82703827);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('G', 'G', 'TTT', NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('H', 'H', 'G', 7.3829083);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('I', 'C', 'M', 8.983989);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('J', 'J', 'G', NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('K', ' ', 'C', 2.232008908);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('L', 'D', 'X', 3.878789);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('M', 'TTTTTT ', 'D' , 4.89060603);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('N', 'G' , 'B' , NULL);
INSERT INTO REGION (REGION_CD, REGION_NAME, DIVISION_CD, REGION_MGR_ASSOCIATE_ID) VALUES ('O' , 'G', 'F', 6.6703972);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1970-01-01', 'A', 5, 0.5 , 0.5);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1973-01-01', 'B', 1, NULL, 1.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1976-01-01', 'C', 2, 2.0 , NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1979-01-01', 'D', 3, 3.0 , 3.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1982-01-01', 'E', 4, 4.0 , 4.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-01-01', 'F', 5, 5.0 , 5.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1988-01-01', 'F', 6, NULL, 6.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1991-01-01', 'G', 6, NULL, NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1994-01-01', 'G', 15, 8.0 , 8.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-01-01', 'G', 16, 9.0 , 9.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1983-01-03', 'I', 14, 4.0 , 4.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1984-01-01', 'GO', 15, 5.0 , NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1985-05-01', 'I', 16, 6.0 , 6.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1990-01-01', 'TTT', 16, NULL, 7.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1992-02-01', 'A', 15, 8.0 , 8.0);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-02-01', 'G', 17, 9.0 , NULL);
INSERT INTO ASSOCIATE_BENEFIT_EXPENSE (PERIOD_END_DT, ASSOCIATE_EXPNS_TYPE_CD, ASSOCIATE_PARTY_ID, BENEFIT_HOURS_QTY, BENEFIT_COST_AMT) VALUES (DATE '1997-05-01', 'G' , 17, 9.0 , NULL);
ANALYZE item;
ANALYZE region;
ANALYZE associate_benefit_expense;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO item (item_level, item_subclass_cd, item_desc, vendor_party_id)
SELECT Table_001.REGION_MGR_ASSOCIATE_ID Column_003,
Table_002.associate_expns_type_cd Column_004,
CAST(Table_001.region_name AS VARCHAR) Column_005,
10 Column_006
-- 'o' Column_007,
-- 'F' Column_008,
-- pg_client_encoding() Column_009
FROM region Table_001, associate_benefit_expense Table_002
ON DUPLICATE KEY UPDATE item_level = -1000;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
SET enable_light_proxy=off;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
EXPLAIN (VERBOSE ON, COSTS OFF)
INSERT INTO products_row VALUES(100)
ON DUPLICATE KEY UPDATE total=100;
RESET enable_light_proxy;
DROP SCHEMA test_insert_update_008 CASCADE;

View File

@ -0,0 +1,281 @@
DROP SCHEMA test_insert_update_009 CASCADE;
CREATE SCHEMA test_insert_update_009;
SET CURRENT_SCHEMA TO test_insert_update_009;
SET enable_light_proxy=off;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t1 with no index
CREATE TABLE t1 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
--- distribute key are not allowed to update
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col1 = 3;
--- should always insert
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE col2 = 3;
INSERT INTO t1 VALUES (1, 2) ON DUPLICATE KEY UPDATE t1.col2 = 4;
--- appoint column list in insert clause, should always insert
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE col2 = 5;
INSERT INTO t1(col1, col3) VALUES (1, 3) ON DUPLICATE KEY UPDATE t1.col2 = 6;
--- multiple rows, should always insert
INSERT INTO t1 VALUES (2, 1), (2, 1) ON DUPLICATE KEY UPDATE col2 = 7, col3 = 7;
SELECT * FROM t1 ORDER BY col5;
--- test union, should insert
INSERT INTO t1 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3 FROM t1 WHERE col5 > 6 ORDER BY col1, col2;
--- test subquery, should insert
INSERT INTO t1
(SELECT col1 || col2 || '00' FROM t1 ORDER BY col5)
ON DUPLICATE KEY UPDATE col3 = col1 * 100;
SELECT col1, col2, col3 FROM t1 WHERE col1 >= 100 ORDER BY col1;
-- test t2 with one primary key
CREATE TABLE t2 (
col1 INT,
col2 INT PRIMARY KEY,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL
) ;
--- distribute key are not allowed to update
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col2 = 3;
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE t2.col2 = 3;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col2 = 100,
col3 = 100,
col4 = '2019-08-09'::TIMESTAMP,
col5 = 100;
--- should insert
INSERT INTO t2 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 30;
INSERT INTO t2 VALUES (2, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40;
INSERT INTO t2 VALUES (3, 3) ON DUPLICATE KEY UPDATE col1 = col1 * 2;
INSERT INTO t2 VALUES (4, 4) ON DUPLICATE KEY UPDATE t2.col1 = t2.col1 * 2 ;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = col2 + 1;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = t2.col2 + 1;
INSERT INTO t2 VALUES (7, 7) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (8, 8) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
--- should update
INSERT INTO t2 VALUES (3, 1) ON DUPLICATE KEY UPDATE col1 = 30, col3 = col5 + 1;
INSERT INTO t2 VALUES (4, 2) ON DUPLICATE KEY UPDATE t2.col1 = 40, t2.col3 = t2.col5;
INSERT INTO t2 VALUES (3, 3), (4, 4) ON DUPLICATE KEY UPDATE col3 = t2.col5 + 1;
INSERT INTO t2 VALUES (5, 5) ON DUPLICATE KEY UPDATE col1 = extract(dow from col4) + 10;
INSERT INTO t2 VALUES (6, 6) ON DUPLICATE KEY UPDATE t2.col1 = extract(century from col4) * 100 + extract(isodow from col4);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5;
-- primary key are not allowed to be null
INSERT INTO t2 (col1) VALUES (10) ON DUPLICATE KEY UPDATE col1 = 10;
--- appoint column list in insert clause
---- should insert
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5;
---- should update
INSERT INTO t2 (col2, col3) VALUES (9, 9) ON DUPLICATE KEY UPDATE col1 = 90;
INSERT INTO t2 (col2, col3, col4, col5)
VALUES (10, 10, CURRENT_TIMESTAMP(0), 10),
(20, 20, CURRENT_TIMESTAMP(1), 20),
(30, 30, CURRENT_TIMESTAMP(2), 30)
ON DUPLICATE KEY UPDATE
col1 = 100,
col3 = 100,
col4 = '2019-08-20'::TIMESTAMP,
col5 = 100;
SELECT * FROM t2 ORDER BY col5, col2;
--- test subquery
---- should insert
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
---- should update
INSERT INTO t2
(SELECT col1 * 1000, col2 * 1000 + 1 FROM t2 ORDER BY col5 LIMIT 2)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
INSERT INTO t2 (col2, col3)
(SELECT col1 * 1000 + 2, col2 * 1000 FROM t2 ORDER BY col5 LIMIT 2 OFFSET 1)
ON DUPLICATE KEY UPDATE col3 = col2 + 1;
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
--- test union, some insert some update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1 ORDER BY 1, 2;
INSERT INTO t2 (col1, col2)
SELECT * FROM
(SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1) AS union_table
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 ORDER BY col5, col2;
reset behavior_compat_options;
-- test INTERSECT, should update
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1;
INSERT INTO t2 (col1, col2)
(SELECT col1, col1 + col2 FROM t1 WHERE col2 IS NOT NULL
UNION
SELECT 1, 2)
INTERSECT
SELECT col1, col3 FROM t1
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 3 ORDER BY col5, col2;
-- test EXCEPT, should update
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
INSERT INTO t2 (col1, col2)
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
-- test unique index with not default value
ALTER TABLE t2 DROP CONSTRAINT t2_pkey;
CREATE UNIQUE INDEX t2_u_index ON t2(col2, col5);
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL);
INSERT INTO t2
SELECT col1, col2 FROM t1 WHERE col2 IS NOT NULL
EXCEPT
(SELECT col1, col3 FROM t1
UNION
SELECT NULL, NULL)
ON DUPLICATE KEY UPDATE col3 = (col1 + col2 + col3);
SELECT col1, col2, col3, col5 FROM t2 WHERE col2 = 2 ORDER BY col5, col2;
-- test t3 with one primary index with two columns
CREATE TABLE t3 (
col1 INT,
col2 INT,
col3 INT DEFAULT 1,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3)
) ;
--- column referenced by primary key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
--- should insert when not contains primary key and not all primary key referred columns have default value.
--- but will fail cause primary key should not be null
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
--- should insert
--- (SEQUENCE BUG: the serial column will starts from 2 since the above statement has applied for a sequence)
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 order by col5;
--- test subquery
---- should insert
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 order by col5;
-- test t3 with one unique index with two columns
TRUNCATE t3;
ALTER TABLE t3 DROP CONSTRAINT t3_pkey;
ALTER TABLE t3 ALTER COLUMN col2 DROP NOT NULL;
CREATE UNIQUE INDEX t3_ukey ON t3 (col2, col3);
--- column referenced by unique key are not allowed to update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col3 = 3;
--- should insert cause not contains unique key and not all unique key referred columns have default value.
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
INSERT INTO t3 (col1) VALUES (1) ON DUPLICATE KEY UPDATE col1 = 2;
SELECT * FROM t3 order by col5;
--- should insert
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE t3.col5 = 20;
SELECT * FROM t3 ORDER BY col5;
--- should update
INSERT INTO t3 VALUES (1, 1) ON DUPLICATE KEY UPDATE t3.col1 = 10;
INSERT INTO t3 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col5 = 20;
SELECT * FROM t3 ORDER BY col5;
--- test subquery
---- should insert
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (SELECT 100, NULL, max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
INSERT INTO t3 (col2, col3) (SELECT max(col2) + 1, max(col3) + 1 FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
---- should update
INSERT INTO t3 (col2, col3) (SELECT max(col2), max(col3) FROM t3) ON DUPLICATE KEY UPDATE col1 = col2 + col3;
SELECT * FROM t3 ORDER BY col5;
RESET enable_light_proxy;
DROP SCHEMA test_insert_update_009 CASCADE;

View File

@ -0,0 +1,144 @@
DROP SCHEMA test_insert_update_010 CASCADE;
CREATE SCHEMA test_insert_update_010;
SET CURRENT_SCHEMA TO test_insert_update_010;
-- enable_upsert_to_merge must is off, or upsert will be translated to merge.
SET enable_upsert_to_merge TO OFF;
-- test t4 with one primary key with three columns
CREATE TABLE t4 (
col1 INT,
col2 INT DEFAULT 0,
col3 INT DEFAULT 1,
col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 BIGSERIAL,
PRIMARY KEY (col2, col3, col5)
) ;
--- should insert
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (1) ON DUPLICATE KEY UPDATE col1 = 10;
INSERT INTO t4 VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
--- should update
INSERT INTO t4 VALUES (2, 2, 2, CURRENT_TIMESTAMP, 3) ON DUPLICATE KEY UPDATE col1 = 200;
INSERT INTO t4 VALUES (100, 100, 100, CURRENT_TIMESTAMP, 100) ON DUPLICATE KEY UPDATE col1 = 1000;
SELECT col1, col2, col3, col5 FROM t4 ORDER BY col5;
--- error: duplicate key update on (x, x, 20)
--- this is because current version is not inplace update but merge,
--- so when the subquery contains multiple same values, it will cause duplicate insert failure.
SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3 ORDER BY 1, 2;
INSERT INTO t4 (col1, col5)
(SELECT col3, sum(col3) * 10 FROM t4 GROUP BY col3)
ON DUPLICATE KEY UPDATE col1 = 3;
-- test t5 with sequence or default column with volatile function in constaint index
CREATE TABLE t5 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM() + 1
) ;
-- test t5 with sequence column in constaint index
CREATE UNIQUE INDEX u_t5_index1 ON t5(col1, col3);
--- should insert
INSERT INTO t5 VALUES (1), (1), (1) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 DEFAULT VALUES ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
--- should update
INSERT INTO t5 (col1, col3) VALUES (1, 1), (1, 2), (1, 3) ON DUPLICATE KEY UPDATE col5 = col2, col2 = col3 * 10;
SELECT * FROM t5 WHERE col1 = 1 ORDER BY col3;
--- should some insert some update
INSERT INTO t5 (col1, col3) VALUES (2, 5), (2, 6);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
INSERT INTO t5 (col1) VALUES (2), (2), (2) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
--- should INSERT and sequence starting from 7
INSERT INTO t5 VALUES (2), (2);
SELECT col1, col2, col3 FROM t5 ORDER BY col3;
-- test with volatile function as default column in constraint index
TRUNCATE t5;
DROP INDEX u_t5_index1;
CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2;
--- should insert
INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3;
INSERT INTO t5 (col1) VALUES (4), (4), (4) ON DUPLICATE KEY UPDATE col2 = col3;
SELECT * FROM t5 ORDER BY col3;
--- should update
INSERT INTO t5 (col1, col5) SELECT col1, col5 FROM t5 where col1 = 3 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
--- test subquery
---- should insert
INSERT INTO t5 (col1, col2) SELECT col1, col2 FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 100;
SELECT * FROM t5 ORDER BY col3;
---- should update
INSERT INTO t5 SELECT * FROM t5 ON DUPLICATE KEY UPDATE col2 = col5 * 1000;
SELECT * FROM t5 ORDER BY col3;
-- test t6 with one more index
CREATE TABLE t6 (
col1 INT,
col2 INT DEFAULT 1,
col3 BIGSERIAL,
-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
col5 INTEGER(10, 5) DEFAULT RANDOM(),
col6 INT,
col7 TEXT
) ;
ALTER TABLE t6 ADD PRIMARY KEY (col1, col3);
CREATE UNIQUE INDEX u_t6_index1 ON t6(col1, col5, col6);
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5);
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
--- should insert
INSERT INTO t6 (col1) VALUES (1), (2), (3), (4), (5) ON DUPLICATE KEY UPDATE col6 = power(col1, col2);
SELECT col1, col2, col3, col6, col7 FROM t6 ORDER BY col3;
--- should update because primary key matches
INSERT INTO t6 (col1, col3) VALUES (6, 11), (6, 12);
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
INSERT INTO t6 (col1) VALUES (6), (6), (6) ON DUPLICATE KEY UPDATE col2 = col1 + col3, col6 = col2 * 10;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col3;
--- should update for those col6 is not null bacause they will match the unique index,
--- and insert for those col6 is null because the unique index containing null never matches,
--- also primary key will not match
--- be ware the sequence column of the inserted row will jump n step, where n is the count of the not null rows,
--- because those sequence have to be generated during the unique index join stage.
INSERT INTO t6 (col1, col5, col6)
(SELECT col1, col5, col6 FROM t6 WHERE col1 = 6)
ON DUPLICATE KEY UPDATE
col7 = col2 + 1;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
--- should update because unique index and primary key both match
INSERT INTO t6 (col1, col3, col5, col6)
(SELECT col1, col3, col5, col6 FROM t6 WHERE col1 = 6 AND col6 IS NOT NULL)
ON DUPLICATE KEY UPDATE
col7 = col7 * 10;
SELECT col1, col2, col3, col6, col7 FROM t6 WHERE col1 = 6 ORDER BY col1, col3, col6, col7;
--- insert when col3 = 17 because constaints does not match,
--- but update when col3 = 18 because 18 has been inserted and will cause a match
INSERT INTO t6 (col1, col3, col5, col6) VALUES (7, 18, 100, 100);
SELECT * FROM t6 WHERE col3 > 16 ORDER BY col1, col3, col6, col7;
INSERT INTO t6 (col1, col5, col6) VALUES (7, 10, 10), (7, 100, 100) ON DUPLICATE KEY UPDATE
col7 = col3 * 100;
SELECT * FROM t6 WHERE col1 = 7 ORDER BY col1, col3, col6, col7;
DROP SCHEMA test_insert_update_010 CASCADE;

Some files were not shown because too many files have changed in this diff Show More