forked from huawei/openGauss-server
commit
ed27420caa
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() -
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/* ----------------------
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/* ----------------
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -120,7 +120,8 @@ typedef enum {
|
|||
HeapTupleInvisible,
|
||||
HeapTupleSelfUpdated,
|
||||
HeapTupleUpdated,
|
||||
HeapTupleBeingUpdated
|
||||
HeapTupleBeingUpdated,
|
||||
HeapTupleSelfCreated
|
||||
} HTSU_Result;
|
||||
|
||||
#endif /* SNAPSHOT_H */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
drop database upsert_etc;
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
CREATE DATABASE upsert_etc;
|
||||
\c upsert_etc;
|
||||
CREATE SCHEMA upsert_test_etc;
|
||||
SET CURRENT_SCHEMA TO upsert_test_etc;
|
||||
|
|
@ -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".
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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 | | |
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
Loading…
Reference in New Issue