UPSERT code

This commit is contained in:
gentle_hu 2020-08-22 14:01:44 +08:00 committed by gentle_hu
parent ca5f135273
commit e936f40df7
60 changed files with 1836 additions and 328 deletions

View File

@ -154,6 +154,7 @@ enable_copy_server_files|bool|0,0|NULL|NULL|
enable_sonic_hashjoin|bool|0,0|NULL|NULL| enable_sonic_hashjoin|bool|0,0|NULL|NULL|
enable_sonic_hashagg|bool|0,0|NULL|NULL| enable_sonic_hashagg|bool|0,0|NULL|NULL|
enable_sonic_optspill|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|bool|0,0|NULL|NULL|
enable_codegen_print|bool|0,0|NULL|Enable dump for llvm function| enable_codegen_print|bool|0,0|NULL|Enable dump for llvm function|
enable_delta_store|bool|0,0|NULL|NULL| enable_delta_store|bool|0,0|NULL|NULL|

View File

@ -1815,6 +1815,12 @@ IndexInfo* BuildIndexInfo(Relation index)
if (indexStruct->indisexclusion) { if (indexStruct->indisexclusion) {
RelationGetExclusionInfo(index, &ii->ii_ExclusionOps, &ii->ii_ExclusionProcs, &ii->ii_ExclusionStrats); 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; return ii;
} }
@ -1865,6 +1871,42 @@ IndexInfo* BuildDummyIndexInfo(Relation index)
return ii; 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 * FormIndexDatum
* Construct values[] and isnull[] arrays for a new index tuple. * Construct values[] and isnull[] arrays for a new index tuple.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1304,6 +1304,9 @@ static Query* _readQuery(void)
IF_EXIST(upsertQuery) { IF_EXIST(upsertQuery) {
READ_NODE_FIELD(upsertQuery); READ_NODE_FIELD(upsertQuery);
} }
IF_EXIST(upsertClause) {
READ_NODE_FIELD(upsertClause);
}
IF_EXIST(isRowTriggerShippable) { IF_EXIST(isRowTriggerShippable) {
READ_BOOL_FIELD(isRowTriggerShippable); READ_BOOL_FIELD(isRowTriggerShippable);
} }
@ -2659,6 +2662,10 @@ static RangeTblEntry* _readRangeTblEntry(void)
READ_NODE_FIELD(buckets); READ_NODE_FIELD(buckets);
} }
IF_EXIST(isexcluded) {
READ_BOOL_FIELD(isexcluded);
}
READ_DONE(); READ_DONE();
} }
@ -3392,6 +3399,44 @@ static ModifyTable* _readModifyTable(ModifyTable* local_node)
READ_NODE_FIELD(mergeActionList); 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(); READ_DONE();
} }
@ -5197,6 +5242,10 @@ Node* parseNodeString(void)
return_value = _readAddPartitionState(); return_value = _readAddPartitionState();
} else if (MATCH("ROWNUM", 6)) { } else if (MATCH("ROWNUM", 6)) {
return_value = _readRownum(); return_value = _readRownum();
} else if (MATCH("UPSERTEXPR", 10)) {
return_value = _readUpsertExpr();
} else if (MATCH("UPSERTCLAUSE", 12)) {
return_value = _readUpsertClause();
} else { } else {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),

View File

@ -82,6 +82,8 @@ THR_LOCAL post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
static Query* transformDeleteStmt(ParseState* pstate, DeleteStmt* stmt); static Query* transformDeleteStmt(ParseState* pstate, DeleteStmt* stmt);
static Query* transformInsertStmt(ParseState* pstate, InsertStmt* 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 int count_rowexpr_columns(ParseState* pstate, Node* expr);
static Query* transformSelectStmt( static Query* transformSelectStmt(
ParseState* pstate, SelectStmt* stmt, bool isFirstNode = true, bool isCreateView = false); 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 Node* transformSetOperationTree(ParseState* pstate, SelectStmt* stmt, bool isTopLevel, List** targetlist);
static void determineRecursiveColTypes(ParseState* pstate, Node* larg, List* nrtargetlist); static void determineRecursiveColTypes(ParseState* pstate, Node* larg, List* nrtargetlist);
static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt); 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 List* transformReturningList(ParseState* pstate, List* returningList);
static Query* transformDeclareCursorStmt(ParseState* pstate, DeclareCursorStmt* stmt); static Query* transformDeclareCursorStmt(ParseState* pstate, DeclareCursorStmt* stmt);
static Query* transformExplainStmt(ParseState* pstate, ExplainStmt* stmt); static Query* transformExplainStmt(ParseState* pstate, ExplainStmt* stmt);
@ -1002,6 +1005,7 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
ListCell* icols = NULL; ListCell* icols = NULL;
ListCell* attnos = NULL; ListCell* attnos = NULL;
ListCell* lc = NULL; ListCell* lc = NULL;
AclMode targetPerms = ACL_INSERT;
/* There can't be any outer WITH to worry about */ /* There can't be any outer WITH to worry about */
AssertEreport(pstate->p_ctenamespace == NIL, MOD_OPT, "para should be NIL"); 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 * mentioned in the SELECT part. Note that the target table is not added
* to the joinlist or namespace. * 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 && if (pstate->p_target_relation != NULL &&
((unsigned int)RelationGetInternalMask(pstate->p_target_relation) & INTERNAL_MASK_DINSERT)) { ((unsigned int)RelationGetInternalMask(pstate->p_target_relation) & INTERNAL_MASK_DINSERT)) {
ereport(ERROR, ereport(ERROR,
@ -1076,6 +1083,23 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
errmsg("Un-support feature"), errmsg("Un-support feature"),
errdetail("internal relation doesn't allow INSERT"))); 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. /* data redistribution for DFS table.
* check if the target relation is being redistributed(insert mode). * check if the target relation is being redistributed(insert mode).
@ -1439,6 +1463,11 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
attnos = lnext(attnos); 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 * 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 * the query namespace before processing it, so that Var references in
@ -1480,6 +1509,153 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
return qry; 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. * 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) static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
{ {
Query* qry = makeNode(Query); Query* qry = makeNode(Query);
RangeTblEntry* target_rte = NULL;
Node* qual = NULL; Node* qual = NULL;
ListCell* origTargetList = NULL;
ListCell* tl = NULL;
qry->commandType = CMD_UPDATE; 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 */ /* set io state for backend status for the thread, we will use it to check user space */
pgstat_set_io_state(IOSTATE_READ); pgstat_set_io_state(IOSTATE_READ);
@ -2714,7 +2887,6 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
transformFromClause(pstate, stmt->fromClause); transformFromClause(pstate, stmt->fromClause);
qry->targetList = transformTargetList(pstate, stmt->targetList); qry->targetList = transformTargetList(pstate, stmt->targetList);
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
qry->returningList = transformReturningList(pstate, stmt->returningList); 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 * Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the UPDATE target columns. * 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 */ /* Prepare to assign non-conflicting resnos to resjunk attributes */
if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) { 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 */ /* Prepare non-junk columns for assignment to target table */
target_rte = pstate->p_target_rangetblentry; 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); TargetEntry* tle = (TargetEntry*)lfirst(tl);
ResTarget* origTarget = NULL; ResTarget* origTarget = NULL;
int attrno; int attrno;
@ -2778,14 +2968,16 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
tle->resname = NULL; tle->resname = NULL;
continue; continue;
} }
if (origTargetList == NULL) {
if (orig_tl == NULL) {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("UPDATE target count mismatch --- internal 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"); AssertEreport(IsA(origTarget, ResTarget), MOD_OPT, "Node type inconsistant here");
if (stmtrel != NULL) {
fixResTargetNameWithTableNameRef(pstate->p_target_relation, stmt->relation, origTarget); fixResTargetNameWithTableNameRef(pstate->p_target_relation, stmtrel, origTarget);
}
attrno = attnameAttNum(pstate->p_target_relation, origTarget->name, true); attrno = attnameAttNum(pstate->p_target_relation, origTarget->name, true);
if (attrno == InvalidAttrNumber) { if (attrno == InvalidAttrNumber) {
@ -2812,16 +3004,14 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
/* Mark the target column as requiring update permissions */ /* Mark the target column as requiring update permissions */
target_rte->updatedCols = bms_add_member(target_rte->updatedCols, attrno - FirstLowInvalidHeapAttributeNumber); 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( ereport(
ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("UPDATE target count mismatch --- internal error"))); ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("UPDATE target count mismatch --- internal error")));
} }
assign_query_collations(pstate, qry); return tlist;
return qry;
} }
/* /*

View File

@ -239,6 +239,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
/* PGXC_END */ /* PGXC_END */
ForeignPartState *foreignpartby; ForeignPartState *foreignpartby;
MergeWhenClause *mergewhen; MergeWhenClause *mergewhen;
UpsertClause *upsert;
} }
%type <node> stmt schema_stmt %type <node> stmt schema_stmt
@ -447,7 +448,7 @@ static void ParseUpdateMultiSet(List *set_target_list, SelectStmt *stmt, core_yy
/* INSERT */ /* INSERT */
%type <istmt> insert_rest %type <istmt> insert_rest
%type <node> duplicate_update_clause %type <node> upsert_clause
%type <mergewhen> merge_insert merge_update %type <mergewhen> merge_insert merge_update
@ -13067,33 +13068,29 @@ InsertStmt: opt_with_clause INSERT INTO qualified_name insert_rest returning_cla
$5->withClause = $1; $5->withClause = $1;
$$ = (Node *) $5; $$ = (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, ereport(ERROR,
(errmodule(MOD_PARSER), (errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement."))); errmsg("RETURNING clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.")));
} }
if ($1 != NULL) if ($1 != NULL) {
{
ereport(ERROR, ereport(ERROR,
(errmodule(MOD_PARSER), (errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement."))); errmsg("WITH clause is not yet supported whithin INSERT ON DUPLICATE KEY UPDATE statement.")));
} }
if ($5 != NULL && $5->cols != NIL) if (unlikely(u_sess->attr.attr_sql.enable_upsert_to_merge)) {
{
if ($5 != NULL && $5->cols != NIL) {
ListCell *c = NULL; ListCell *c = NULL;
List *cols = $5->cols; List *cols = $5->cols;
foreach (c, cols) foreach (c, cols) {
{
ResTarget *rt = (ResTarget *)lfirst(c); ResTarget *rt = (ResTarget *)lfirst(c);
if (rt->indirection != NIL) if (rt->indirection != NIL) {
{
ereport(ERROR, ereport(ERROR,
(errmodule(MOD_PARSER), (errmodule(MOD_PARSER),
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -13120,7 +13117,7 @@ InsertStmt: opt_with_clause INSERT INTO qualified_name insert_rest returning_cla
Alias *a1 = makeAlias(($4->relname), NIL); Alias *a1 = makeAlias(($4->relname), NIL);
$4->alias = a1; $4->alias = a1;
Alias *a2 = makeAlias("__unnamed_subquery_source__", NIL); Alias *a2 = makeAlias("excluded", NIL);
RangeSubselect *r = makeNode(RangeSubselect); RangeSubselect *r = makeNode(RangeSubselect);
r->alias = a2; r->alias = a2;
r->subquery = (Node *) ($5->selectStmt); r->subquery = (Node *) ($5->selectStmt);
@ -13132,15 +13129,18 @@ InsertStmt: opt_with_clause INSERT INTO qualified_name insert_rest returning_cla
n->cols = $5->cols; n->cols = $5->cols;
n->values = NULL; n->values = NULL;
m->mergeWhenClauses = list_make2(($6), (Node *) n); m->mergeWhenClauses = list_make1((Node *) n);
if ($6 != NULL)
m->mergeWhenClauses = list_concat(list_make1($6), m->mergeWhenClauses);
/* for UPSERT, keep the INSERT statement as well */ $$ = (Node *)m;
} else {
$5->relation = $4; $5->relation = $4;
$5->returningList = $7; $5->returningList = $7;
$5->withClause = $1; $5->withClause = $1;
m->insert_stmt = (Node *) copyObject($5); $5->upsertClause = (UpsertClause *)$6;
$$ = (Node *) $5;
$$ = (Node *)m; }
} }
; ;
@ -13188,14 +13188,45 @@ returning_clause:
| /* EMPTY */ { $$ = NIL; } | /* EMPTY */ { $$ = NIL; }
; ;
duplicate_update_clause: upsert_clause:
ON DUPLICATE KEY UPDATE set_clause_list ON DUPLICATE KEY UPDATE set_clause_list
{ {
if (unlikely(u_sess->attr.attr_sql.enable_upsert_to_merge)) {
MergeWhenClause *n = makeNode(MergeWhenClause); MergeWhenClause *n = makeNode(MergeWhenClause);
n->matched = true; n->matched = true;
n->commandType = CMD_UPDATE; n->commandType = CMD_UPDATE;
n->targetList = $5; n->targetList = $5;
$$ = (Node *) n; $$ = (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;
}
} }
; ;
@ -13313,6 +13344,30 @@ single_set_clause:
$$ = $1; $$ = $1;
$$->val = (Node *) $3; $$->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: multiple_set_clause:

View File

@ -459,8 +459,8 @@ static bool assign_collations_walker(Node* node, assign_collations_context* cont
case T_FromExpr: case T_FromExpr:
case T_SortGroupClause: case T_SortGroupClause:
case T_MergeAction: case T_MergeAction:
case T_UpsertExpr:
(void)expression_tree_walker(node, (bool (*)())assign_collations_walker, (void*)&loccontext); (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 * 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 * anything with join nodes except recurse through them to process

View File

@ -56,7 +56,8 @@ static void check_system_column_node(Node* node, bool is_insert_update);
static void check_system_column_reference(List* joinVarList, List* mergeActionList, bool is_insert_update); static void check_system_column_reference(List* joinVarList, List* mergeActionList, bool is_insert_update);
static void checkTargetTableSystemCatalog(Relation targetRel); static void checkTargetTableSystemCatalog(Relation targetRel);
static void check_insert_action_targetlist(List* merge_action_list, List* source_targetlist); 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 find_valid_unique_constraint(Relation relation, List* colnames, List*& index_list);
static bool var_in_list(Var* var, List* list); static bool var_in_list(Var* var, List* list);
static Bitmapset* get_relation_attno_bitmap_by_names(Relation relation, List* colnames); 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(stmt->insert_stmt != NULL);
Assert(IsA(stmt->insert_stmt, InsertStmt)); 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; Query* insert_query = NULL;
ParseState* insert_pstate = make_parsestate(NULL); ParseState* insert_pstate = make_parsestate(NULL);
insert_pstate->p_resolve_unknowns = pstate->p_resolve_unknowns; 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 * @out index_list: unique index list
* @return: true if there is any primary or unique index * @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 */ /* There are no indexes */
if (index_list == NIL) { if (index_list == NIL) {
@ -1790,10 +1801,48 @@ static bool check_unique_constraint(List*& index_list)
return true; 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 */ /* 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) void check_insert_action_targetlist(List* merge_action_list, List* source_targetlist)
{ {
ListCell* var_cell = NULL;
ListCell* action_cell = NULL; ListCell* action_cell = NULL;
List* insert_action_targetlist = NIL; List* insert_action_targetlist = NIL;
@ -1806,25 +1855,10 @@ void check_insert_action_targetlist(List* merge_action_list, List* source_target
break; break;
} }
} }
if (!check_action_targetlist_condition(insert_action_targetlist, source_targetlist, true)) {
/* 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, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("Invalid column reference in the INSERT VALUES Clause"), errmsg("Invalid column reference in the INSERT VALUES Clause"),
errhint("You may have referenced target table's column"))); errhint("You may have referenced target table's column")));
} }
} }
}

View File

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

View File

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

View File

@ -222,6 +222,8 @@ static void get_with_clause(Query* query, deparse_context* context);
static void get_select_query_def(Query* query, deparse_context* context, TupleDesc resultDesc); static void get_select_query_def(Query* query, deparse_context* context, TupleDesc resultDesc);
static void get_insert_query_def(Query* query, deparse_context* context); 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_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_delete_query_def(Query* query, deparse_context* context);
static void get_utility_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); 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 * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
* use OUTER because that could someday conflict with the normal meaning.) * use OUTER because that could someday conflict with the normal meaning.)
* Likewise, for a CteScan, pretend the subquery's plan is INNER referent. * 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)) if (IsA(ps, SubqueryScanState))
dpns->inner_planstate = ((SubqueryScanState*)ps)->subplan; dpns->inner_planstate = ((SubqueryScanState*)ps)->subplan;
@ -3749,6 +3755,7 @@ static void set_deparse_planstate(deparse_namespace* dpns, PlanState* ps)
ModifyTableState* mps = (ModifyTableState*)ps; ModifyTableState* mps = (ModifyTableState*)ps;
dpns->outer_planstate = mps->mt_plans[0]; 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 * 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) * reference sourceTargetList, which comes from outer plan of the join (source table)
@ -3762,7 +3769,9 @@ static void set_deparse_planstate(deparse_namespace* dpns, PlanState* ps)
} else } else
dpns->inner_planstate = innerPlanState(ps); dpns->inner_planstate = innerPlanState(ps);
if (dpns->inner_planstate != NULL) if (IsA(ps, ModifyTableState))
dpns->inner_tlist = ((ModifyTableState*)ps)->mt_upsert->us_excludedtlist;
else if (dpns->inner_planstate != NULL)
dpns->inner_tlist = dpns->inner_planstate->plan->targetlist; dpns->inner_tlist = dpns->inner_planstate->plan->targetlist;
else else
dpns->inner_tlist = NIL; dpns->inner_tlist = NIL;
@ -5564,18 +5573,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 * select_rte are not required by INSERT queries in XC
* Both these should stay null for INSERT queries to work corretly * it should stay null for INSERT queries to work corretly
* Consider an example * 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
* insert into tt select * from tt * insert into tt select * from tt
* This query uses select_rte, but again that is not required in XC * This query uses select_rte, but again that is not required in XC
* Again here the query gets broken down into two queries * Again here the query gets broken down into two queries
@ -5600,7 +5600,35 @@ static void get_insert_query_def(Query* query, deparse_context* context)
} }
select_rte = rte; 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 (rte->rtekind == RTE_VALUES) {
if (values_rte != NULL) { if (values_rte != NULL) {
ereport(ERROR, (errcode(ERRCODE_RESTRICT_VIOLATION), errmsg("too many values RTEs in INSERT"))); ereport(ERROR, (errcode(ERRCODE_RESTRICT_VIOLATION), errmsg("too many values RTEs in INSERT")));
@ -5611,6 +5639,7 @@ static void get_insert_query_def(Query* query, deparse_context* context)
#ifdef PGXC #ifdef PGXC
} }
#endif #endif
if ((select_rte != NULL) && (values_rte != NULL)) { if ((select_rte != NULL) && (values_rte != NULL)) {
ereport(ERROR, (errcode(ERRCODE_RESTRICT_VIOLATION), errmsg("both subquery and values RTEs in INSERT"))); ereport(ERROR, (errcode(ERRCODE_RESTRICT_VIOLATION), errmsg("both subquery and values RTEs in INSERT")));
} }
@ -5758,21 +5787,37 @@ static void get_insert_query_def(Query* query, deparse_context* context)
appendStringInfo(buf, "DEFAULT VALUES"); 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) { if (query->mergeActionList) {
appendStringInfo(buf, " ON DUPLICATE KEY UPDATE "); appendStringInfo(buf, " ON DUPLICATE KEY UPDATE ");
ListCell* l = NULL; ListCell* l = NULL;
bool update = false;
foreach (l, query->mergeActionList) { foreach (l, query->mergeActionList) {
MergeAction* mc = (MergeAction*)lfirst(l); MergeAction* mc = (MergeAction*)lfirst(l);
/* only deparse the update clause */ /* only deparse the update clause */
if (mc->commandType == CMD_UPDATE) { if (mc->commandType == CMD_UPDATE) {
update = true;
get_set_target_list(mc->targetList, rte, context); get_set_target_list(mc->targetList, rte, context);
} else { } else {
continue; 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 */ /* Add RETURNING if present */
@ -5789,9 +5834,7 @@ 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_def(Query* query, deparse_context* context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
char* sep = NULL;
RangeTblEntry* rte = NULL; RangeTblEntry* rte = NULL;
ListCell* l = NULL;
/* Insert the WITH clause if given */ /* Insert the WITH clause if given */
get_with_clause(query, context); get_with_clause(query, context);
@ -5810,10 +5853,38 @@ static void get_update_query_def(Query* query, deparse_context* context)
appendStringInfo(buf, " %s", quote_identifier(rte->alias->aliasname)); appendStringInfo(buf, " %s", quote_identifier(rte->alias->aliasname));
} }
appendStringInfoString(buf, " SET "); 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' */ /* Add the comma separated list of 'attname = value' */
sep = ""; sep = "";
foreach (l, query->targetList) { foreach (l, targetList) {
TargetEntry* tle = (TargetEntry*)lfirst(l); TargetEntry* tle = (TargetEntry*)lfirst(l);
Node* expr = NULL; Node* expr = NULL;
@ -5890,21 +5961,6 @@ static void get_update_query_def(Query* query, deparse_context* context)
get_rule_expr((Node*)tle->expr, context, false); get_rule_expr((Node*)tle->expr, context, false);
} }
} }
/* Add the FROM clause if needed */
get_from_clause(query, " FROM ", context);
/* Add a WHERE clause if given */
if (query->jointree->quals != NULL) {
append_context_keyword(context, " WHERE ", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_rule_expr(query->jointree->quals, context, false);
}
/* Add RETURNING if present */
if (query->returningList) {
append_context_keyword(context, " RETURNING", -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
get_target_list(query, query->returningList, context, NULL);
}
} }
/* ---------- /* ----------

View File

@ -1389,6 +1389,20 @@ static void init_configure_names_bool()
NULL, NULL,
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 #ifdef ENABLE_MULTIPLE_NODES
{ {
{ {

View File

@ -520,6 +520,14 @@ bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
if (!HeapTupleHeaderXminCommitted(tuple)) { if (!HeapTupleHeaderXminCommitted(tuple)) {
if (HeapTupleHeaderXminInvalid(tuple)) if (HeapTupleHeaderXminInvalid(tuple))
return false; 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. */ /* 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 * the case where the tuple is share-locked by a MultiXact, even if the
* MultiXact includes the current transaction. Callers that want to * MultiXact includes the current transaction. Callers that want to
* distinguish that case must test for it themselves.) * 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; bool needSync = false;
HeapTupleHeader tuple = htup->t_data; HeapTupleHeader tuple = htup->t_data;
@ -580,8 +591,10 @@ restart:
return HeapTupleInvisible; return HeapTupleInvisible;
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(page, tuple))) { if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(page, tuple))) {
if (HeapTupleHeaderGetCmin(tuple, page) >= curcid) if (HeapTupleHeaderGetCmin(tuple, page) > curcid)
return HeapTupleInvisible; /* inserted after scan started */ 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 */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HeapTupleMayBeUpdated; return HeapTupleMayBeUpdated;

View File

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

View File

@ -187,6 +187,7 @@ static void ExplainProperty(const char* qlabel, const char* value, bool numeric,
static void ExplainOpenGroup(const char* objtype, const char* labelname, bool labeled, ExplainState* es); static void 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 ExplainCloseGroup(const char* objtype, const char* labelname, bool labeled, ExplainState* es);
static void ExplainDummyGroup(const char* objtype, const char* labelname, 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 #ifdef PGXC
static void ExplainExecNodes(const ExecNodes* en, ExplainState* es); static void ExplainExecNodes(const ExecNodes* en, ExplainState* es);
static void ExplainRemoteQuery(RemoteQuery* plan, PlanState* planstate, List* ancestors, ExplainState* es); static void ExplainRemoteQuery(RemoteQuery* plan, PlanState* planstate, List* ancestors, ExplainState* es);
@ -2326,6 +2327,12 @@ static void ExplainNode(
} }
} }
} else { } 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 */ /* non-merge cases */
foreach (elt, mt->remote_plans) { foreach (elt, mt->remote_plans) {
if (lfirst(elt)) { 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 #ifndef PGXC
/* /*
* Show extra information for a ModifyTable node * Show extra information for a ModifyTable node

View File

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

View File

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

View File

@ -8298,11 +8298,11 @@ static void deparallelize_modifytable(List* subplans)
#ifdef STREAMPLAN #ifdef STREAMPLAN
Plan* make_modifytable(PlannerInfo* root, CmdType operation, bool canSetTag, List* resultRelations, List* subplans, Plan* make_modifytable(PlannerInfo* root, CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation, 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 #else
ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRelations, List* subplans, ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, Index mergeTargetRelation, 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 #endif
{ {
ModifyTable* node = makeNode(ModifyTable); ModifyTable* node = makeNode(ModifyTable);
@ -8377,6 +8377,16 @@ ModifyTable* make_modifytable(CmdType operation, bool canSetTag, List* resultRel
node->mergeTargetRelation = mergeTargetRelation; node->mergeTargetRelation = mergeTargetRelation;
node->mergeSourceTargetList = mergeSourceTargetList; node->mergeSourceTargetList = mergeSourceTargetList;
node->mergeActionList = mergeActionList; 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 #ifdef STREAMPLAN
node->plan.exec_nodes = exec_nodes; 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, 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* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList) List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause)
#else #else
ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRelations, List* subplans, ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRelations, List* subplans,
List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore, Index mergeTargetRelation, List* returningLists, List* rowMarks, int epqParam, bool partKeyUpdated, bool isDfsStore, Index mergeTargetRelation,
List* mergeSourceTargetList, List* mergeActionList) List* mergeSourceTargetList, List* mergeActionList, UpsertExpr* upsertClause)
#endif #endif
{ {
if (isDfsStore) { if (isDfsStore) {
@ -8542,6 +8552,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
0, 0,
NULL, NULL,
NULL, NULL,
upsertClause,
isDfsStore); isDfsStore);
/* /*
* We must adjust the plan tree. Because the make_modifytable function would make a * 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, 0,
NULL, NULL,
NULL, NULL,
upsertClause,
isDfsStore); isDfsStore);
appendSubPlans = lappend(appendSubPlans, (void*)mtplan); appendSubPlans = lappend(appendSubPlans, (void*)mtplan);
#endif #endif
@ -8627,6 +8639,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
mergeTargetRelation, mergeTargetRelation,
mergeSourceTargetList, mergeSourceTargetList,
mergeActionList, mergeActionList,
upsertClause,
isDfsStore); isDfsStore);
if (IS_STREAM_PLAN) if (IS_STREAM_PLAN)
return mt_stream_plan; return mt_stream_plan;
@ -8644,6 +8657,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
mergeTargetRelation, mergeTargetRelation,
mergeSourceTargetList, mergeSourceTargetList,
mergeActionList, mergeActionList,
upsertClause
isDfsStore); isDfsStore);
return pgxc_make_modifytable(root, (Plan*)mtplan); return pgxc_make_modifytable(root, (Plan*)mtplan);
#endif #endif
@ -8659,6 +8673,7 @@ ModifyTable* make_modifytables(CmdType operation, bool canSetTag, List* resultRe
mergeTargetRelation, mergeTargetRelation,
mergeSourceTargetList, mergeSourceTargetList,
mergeActionList, mergeActionList,
upsertClause,
isDfsStore); isDfsStore);
#endif #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 * We are about to change the local_tlist, check if we have already
* copied original local_tlist, if not take a copy * 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); orig_local_tlist = (List*)copyObject(*local_tlist);
/* /*

View File

@ -1332,6 +1332,10 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
parse->mergeSourceTargetList = parse->mergeSourceTargetList =
(List*)preprocess_expression(root, (Node*)parse->mergeSourceTargetList, EXPRKIND_TARGET); (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); 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 */ /* 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->mergeTarget_relation,
parse->mergeSourceTargetList, parse->mergeSourceTargetList,
parse->mergeActionList, parse->mergeActionList,
parse->upsertClause,
isDfsStore); isDfsStore);
#else #else
plan = (Plan*)make_modifytable(parse->commandType, plan = (Plan*)make_modifytable(parse->commandType,
@ -1534,6 +1539,7 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
parse->mergeTarget_relation, parse->mergeTarget_relation,
parse->mergeSourceTargetList, parse->mergeSourceTargetList,
parse->mergeActionList, parse->mergeActionList,
parse->upsertClause,
isDfsStore); isDfsStore);
#endif #endif
#ifdef PGXC #ifdef PGXC
@ -2014,6 +2020,7 @@ static Plan* inheritance_planner(PlannerInfo* root)
isDfsStore, isDfsStore,
0, 0,
NULL, NULL,
NULL,
NULL); NULL);
#else #else
return make_modifytables(parse->commandType, return make_modifytables(parse->commandType,
@ -2027,6 +2034,7 @@ static Plan* inheritance_planner(PlannerInfo* root)
isDfsStore, isDfsStore,
0, 0,
NULL, NULL,
NULL,
NULL); NULL);
#endif #endif
} }
@ -2445,6 +2453,11 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
/* Preprocess targetlist */ /* Preprocess targetlist */
tlist = preprocess_targetlist(root, tlist); 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 * 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 * anywhere else, since expressions used in ORDER BY will be in there

View File

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

View File

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

View File

@ -1056,6 +1056,10 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
*/ */
parse->targetList = (List*)pullup_replace_vars((Node*)parse->targetList, &rvcontext); parse->targetList = (List*)pullup_replace_vars((Node*)parse->targetList, &rvcontext);
parse->returningList = (List*)pullup_replace_vars((Node*)parse->returningList, &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); replace_vars_in_jointree((Node*)parse->jointree, &rvcontext, lowest_outer_join);

View File

@ -555,5 +555,10 @@ static List* add_distribute_column(List* tlist, Index result_relation, List* ran
return tlist; 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 #endif

View File

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

View File

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

View File

@ -64,6 +64,8 @@ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo);
static bool index_recheck_constraint( static bool index_recheck_constraint(
Relation index, Oid* constr_procs, Datum* existing_values, const bool* existing_isnull, Datum* new_values); Relation index, Oid* constr_procs, Datum* existing_values, const bool* existing_isnull, Datum* new_values);
static void ShutdownExprContext(ExprContext* econtext, bool isCommit); 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 * Executor state and memory management functions
@ -1100,7 +1102,7 @@ Partition ExecOpenScanParitition(EState* estate, Relation parent, PartitionIdent
* resultRelInfo->ri_RelationDesc. * resultRelInfo->ri_RelationDesc.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void ExecOpenIndices(ResultRelInfo* resultRelInfo) void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
{ {
Relation resultRelation = resultRelInfo->ri_RelationDesc; Relation resultRelation = resultRelInfo->ri_RelationDesc;
List* indexoidlist = NIL; List* indexoidlist = NIL;
@ -1156,6 +1158,13 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo)
/* extract index key information from the index's pg_index info */ /* extract index key information from the index's pg_index info */
ii = BuildIndexInfo(indexDesc); 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; relationDescs[i] = indexDesc;
indexInfoArray[i] = ii; indexInfoArray[i] = ii;
i++; 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 * ExecInsertIndexTuples
* *
@ -1215,8 +1382,8 @@ void ExecCloseIndices(ResultRelInfo* resultRelInfo)
* Should we change the API to make it safer? * Should we change the API to make it safer?
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
List* ExecInsertIndexTuples( List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* estate,
TupleTableSlot* slot, ItemPointer tupleid, EState* estate, Relation targetPartRel, Partition p, int2 bucketId) Relation targetPartRel, Partition p, int2 bucketId, bool* conflict)
{ {
List* result = NIL; List* result = NIL;
ResultRelInfo* resultRelInfo = NULL; ResultRelInfo* resultRelInfo = NULL;
@ -1362,6 +1529,8 @@ List* ExecInsertIndexTuples(
*/ */
if (!indexRelation->rd_index->indisunique) { if (!indexRelation->rd_index->indisunique) {
checkUnique = UNIQUE_CHECK_NO; checkUnique = UNIQUE_CHECK_NO;
} else if (conflict != NULL) {
checkUnique = UNIQUE_CHECK_PARTIAL;
} else if (indexRelation->rd_index->indimmediate) { } else if (indexRelation->rd_index->indimmediate) {
checkUnique = UNIQUE_CHECK_YES; checkUnique = UNIQUE_CHECK_YES;
} else { } else {
@ -1397,9 +1566,13 @@ List* ExecInsertIndexTuples(
/* /*
* The tuple potentially violates the uniqueness or exclusion * The tuple potentially violates the uniqueness or exclusion
* constraint, so make a note of the index so that we can re-check * 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)); 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, bool check_exclusion_constraint(Relation heap, Relation index, IndexInfo* indexInfo, ItemPointer tupleid, Datum* values,
const bool* isnull, EState* estate, bool newIndex, bool errorOK) 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; Oid* constr_procs = indexInfo->ii_ExclusionProcs;
uint16* constr_strats = indexInfo->ii_ExclusionStrats; 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 * Search the tuples that are in the index for any violations, including
* tuples that aren't visible yet. * tuples that aren't visible yet.
@ -1503,7 +1690,7 @@ retry:
/* /*
* Ignore the entry for the tuple we're trying to check. * 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 */ if (found_self) /* should not happen */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED), (errcode(ERRCODE_FETCH_DATA_FAILED),
@ -1527,33 +1714,44 @@ retry:
} }
/* /*
* At this point we have either a conflict or a potential conflict. If * At this point we have either a conflict or a potential conflict.
* we're not supposed to raise error, just return the fact of the
* potential conflict without waiting to see if it's real.
*/
if (errorOK) {
conflict = true;
break;
}
/*
* If an in-progress transaction is affecting the visibility of this * If an in-progress transaction is affecting the visibility of this
* tuple, we need to wait for it to complete and then recheck. For * tuple, we need to wait for it to complete and then recheck (unless
* simplicity we do rechecking by just restarting the whole scan --- * the caller requested not to). For simplicity we do rechecking by
* this case probably doesn't happen often enough to be worth trying * just restarting the whole scan --- this case probably doesn't
* harder, and anyway we don't want to hold any index internal locks * happen often enough to be worth trying harder, and anyway we don't
* while waiting. * want to hold any index internal locks while waiting.
*/ */
xwait = TransactionIdIsValid(DirtySnapshot.xmin) ? DirtySnapshot.xmin : DirtySnapshot.xmax; xwait = TransactionIdIsValid(DirtySnapshot.xmin) ? DirtySnapshot.xmin : DirtySnapshot.xmax;
if (TransactionIdIsValid(xwait)) { if (TransactionIdIsValid(xwait) && waitMode == CHECK_WAIT) {
index_endscan(index_scan); 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); XactLockTableWait(xwait);
goto retry; 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_new = BuildIndexValueDescription(index, values, isnull);
error_existing = BuildIndexValueDescription(index, existing_values, existing_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 * 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 * However, it is possible to define exclusion constraints for which that
* wouldn't be true --- for instance, if the operator is <>. So we no * wouldn't be true --- for instance, if the operator is <>. So we no
* longer complain if found_self is still false. * longer complain if found_self is still false.

View File

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

View File

@ -248,6 +248,277 @@ static TupleTableSlot* ExecProcessReturning(
return ExecProject(projectReturning, NULL); 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 * ExecInsert
* *
@ -307,9 +578,12 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
if (result_relation_desc->rd_rel->relhasoids) if (result_relation_desc->rd_rel->relhasoids)
HeapTupleSetOid(tuple, InvalidOid); HeapTupleSetOid(tuple, InvalidOid);
/* BEFORE ROW INSERT Triggers */ /* BEFORE ROW INSERT Triggers
if (state->operation != CMD_MERGE && result_rel_info->ri_TrigDesc && * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an except
result_rel_info->ri_TrigDesc->trig_insert_before_row) { * 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); slot = ExecBRInsertTriggers(estate, result_rel_info, slot);
if (slot == NULL) /* "do nothing" */ if (slot == NULL) /* "do nothing" */
return NULL; return NULL;
@ -318,9 +592,12 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
tuple = ExecMaterializeSlot(slot); tuple = ExecMaterializeSlot(slot);
} }
/* INSTEAD OF ROW INSERT Triggers */ /* INSTEAD OF ROW INSERT Triggers
if (state->operation != CMD_MERGE && result_rel_info->ri_TrigDesc && * Note: We fire INSREAD OF ROW TRIGGERS for every attempted insertion except
result_rel_info->ri_TrigDesc->trig_insert_instead_row) { * 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); slot = ExecIRInsertTriggers(estate, result_rel_info, slot);
if (slot == NULL) /* "do nothing" */ if (slot == NULL) /* "do nothing" */
return NULL; return NULL;
@ -449,6 +726,14 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
} }
ExecDropSingleTupleTableSlot(tmp_slot); 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 { } else {
/* /*
* insert the tuple * insert the tuple
@ -524,7 +809,7 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple
estate, estate,
RELATION_IS_PARTITIONED(result_relation_desc) ? heap_rel : NULL, RELATION_IS_PARTITIONED(result_relation_desc) ? heap_rel : NULL,
RELATION_IS_PARTITIONED(result_relation_desc) ? partition : 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)); 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) if (state->operation != CMD_MERGE && !useHeapMultiInsert)
ExecARInsertTriggers(estate, result_rel_info, partition_id, bucket_id, tuple, recheck_indexes); ExecARInsertTriggers(estate, result_rel_info, partition_id, bucket_id, tuple, recheck_indexes);
@ -938,6 +1226,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
#ifdef PGXC #ifdef PGXC
RemoteQueryState* result_remote_rel = NULL; RemoteQueryState* result_remote_rel = NULL;
#endif #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 * abort the operation if not running transactions
@ -1049,7 +1339,6 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
return NULL; return NULL;
} }
/* FDW might have changed tuple */ /* FDW might have changed tuple */
tuple = ExecMaterializeSlot(slot); tuple = ExecMaterializeSlot(slot);
} else { } else {
@ -1102,7 +1391,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
&update_xmax, &update_xmax,
estate->es_output_cid, estate->es_output_cid,
estate->es_crosscheck_snapshot, estate->es_crosscheck_snapshot,
true /* wait for commit */); true /* wait for commit */,
allow_update_self);
switch (result) { switch (result) {
case HeapTupleSelfUpdated: case HeapTupleSelfUpdated:
/* can not update one row more than once for merge into */ /* 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 it's a HOT update, we mustn't insert new index entries.
*/ */
if (result_rel_info->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple)) 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 { } else {
/* for partitioned table */ /* for partitioned table */
bool row_movement = false; bool row_movement = false;
@ -1300,7 +1591,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
&update_xmax, &update_xmax,
estate->es_output_cid, estate->es_output_cid,
estate->es_crosscheck_snapshot, estate->es_crosscheck_snapshot,
true /* wait for commit */); true /* wait for commit */,
allow_update_self);
switch (result) { switch (result) {
case HeapTupleSelfUpdated: case HeapTupleSelfUpdated:
/* can not update one row more than once for merge into */ /* can not update one row more than once for merge into */
@ -1403,7 +1695,7 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
* delete index entries for tuple * delete index entries for tuple
*/ */
recheck_indexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate, recheck_indexes = ExecInsertIndexTuples(slot, &(tuple->t_self), estate,
fake_part_rel, partition, bucketid); fake_part_rel, partition, bucketid, NULL);
} }
} else { } else {
/* row movement */ /* row movement */
@ -1432,7 +1724,8 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
&update_xmax, &update_xmax,
estate->es_output_cid, estate->es_output_cid,
estate->es_crosscheck_snapshot, estate->es_crosscheck_snapshot,
true /* wait for commit */); true /* wait for commit */,
allow_update_self);
switch (result) { switch (result) {
case HeapTupleSelfUpdated: case HeapTupleSelfUpdated:
/* can not update one row more than once for merge into */ /* 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) { if (result_rel_info->ri_NumIndices > 0) {
recheck_indexes = ExecInsertIndexTuples( 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) { switch (node->operation) {
case CMD_INSERT: case CMD_INSERT:
ExecBSInsertTriggers(node->ps.state, node->resultRelInfo); ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
if (node->mt_upsert->us_action == UPSERT_UPDATE) {
ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
}
break; break;
case CMD_UPDATE: case CMD_UPDATE:
ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo); ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
@ -1629,6 +1925,9 @@ static void fireASTriggers(ModifyTableState* node)
switch (node->operation) { switch (node->operation) {
case CMD_INSERT: case CMD_INSERT:
ExecASInsertTriggers(node->ps.state, node->resultRelInfo); ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
if (node->mt_upsert->us_action == UPSERT_UPDATE) {
ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
}
break; break;
case CMD_UPDATE: case CMD_UPDATE:
ExecASUpdateTriggers(node->ps.state, node->resultRelInfo); ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
@ -1755,7 +2054,7 @@ TupleTableSlot* ExecModifyTable(ModifyTableState* node)
#endif #endif
if (operation == CMD_INSERT) { 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 != NULL && (result_rel_info->ri_TrigDesc->trig_insert_before_row ||
result_rel_info->ri_TrigDesc->trig_insert_instead_row))) result_rel_info->ri_TrigDesc->trig_insert_instead_row)))
ExecInsert = ExecInsertT<false>; ExecInsert = ExecInsertT<false>;
@ -2048,6 +2347,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
ResultRelInfo* result_rel_info = NULL; ResultRelInfo* result_rel_info = NULL;
TupleDesc tup_desc = NULL; TupleDesc tup_desc = NULL;
Plan* sub_plan = NULL; Plan* sub_plan = NULL;
UpsertState* upsertState = NULL;
ListCell* l = NULL; ListCell* l = NULL;
int i; int i;
#ifdef PGXC #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_arowmarks = (List**)palloc0(sizeof(List*) * nplans);
mt_state->mt_nplans = 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 */ /* set up epqstate with dummy sub_plan data for the moment */
EvalPlanQualInit(&mt_state->mt_epqstate, estate, NULL, NIL, node->epqParam); EvalPlanQualInit(&mt_state->mt_epqstate, estate, NULL, NIL, node->epqParam);
mt_state->fireBSTriggers = true; mt_state->fireBSTriggers = true;
@ -2160,7 +2467,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl
result_rel_info->ri_IndexRelationDescs == NULL) { result_rel_info->ri_IndexRelationDescs == NULL) {
if (result_rel_info->ri_FdwRoutine == NULL || result_rel_info->ri_FdwRoutine->GetFdwType == NULL || if (result_rel_info->ri_FdwRoutine == NULL || result_rel_info->ri_FdwRoutine->GetFdwType == NULL ||
result_rel_info->ri_FdwRoutine->GetFdwType() != MOT_ORC) 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); init_gtt_storage(operation, result_rel_info);
/* Now init the plan for this result rel */ /* 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; 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 * 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 * be treated like non-locked relations in SELECT FOR UPDATE, ie, the

View File

@ -1075,7 +1075,7 @@ bool InsertFusion::execute(long max_rows, char* completionTag)
m_estate->es_result_relation_info = result_rel_info; m_estate->es_result_relation_info = result_rel_info;
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) { if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info); ExecOpenIndices(result_rel_info, false);
} }
CommandId mycid = GetCurrentCommandId(true); CommandId mycid = GetCurrentCommandId(true);
@ -1109,7 +1109,7 @@ bool InsertFusion::execute(long max_rows, char* completionTag)
/* insert index entries for tuple */ /* insert index entries for tuple */
List* recheck_indexes = NIL; List* recheck_indexes = NIL;
if (result_rel_info->ri_NumIndices > 0) { 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); list_free_ext(recheck_indexes);
@ -1388,7 +1388,7 @@ bool UpdateFusion::execute(long max_rows, char* completionTag)
m_estate->es_output_cid = GetCurrentCommandId(true); m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) { if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info); ExecOpenIndices(result_rel_info, false);
} }
/********************************* /*********************************
@ -1443,7 +1443,8 @@ bool UpdateFusion::execute(long max_rows, char* completionTag)
/* done successfully */ /* done successfully */
nprocessed++; nprocessed++;
if (result_rel_info->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tup)) { 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); list_free_ext(recheck_indexes);
} }
break; break;
@ -1574,7 +1575,7 @@ bool DeleteFusion::execute(long max_rows, char* completionTag)
m_estate->es_output_cid = GetCurrentCommandId(true); m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) { if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info); ExecOpenIndices(result_rel_info, false);
} }
/******************************** /********************************
@ -1781,7 +1782,7 @@ bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
m_estate->es_output_cid = GetCurrentCommandId(true); m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) { if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info); ExecOpenIndices(result_rel_info, false);
} }
/************************************** /**************************************
@ -1850,6 +1851,10 @@ bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
} }
switch (result) { switch (result) {
case HeapTupleSelfCreated:
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("attempted to lock invisible tuple")));
break;
case HeapTupleSelfUpdated: case HeapTupleSelfUpdated:
/* already deleted by self; nothing to do */ /* already deleted by self; nothing to do */
break; break;

View File

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

View File

@ -2547,7 +2547,7 @@ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, Bu
} }
xlrec.offnum = ItemPointerGetOffsetNumber(&heaptup->t_self); xlrec.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)); 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). * image. (XXX We could alternatively store a pointer into the FPW).
*/ */
if (RelationIsLogicallyLogged(relation)) { if (RelationIsLogicallyLogged(relation)) {
xlrec.flags |= XLOG_HEAP_CONTAINS_NEW_TUPLE; xlrec.flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
bufflags |= REGBUF_KEEP_DATA; bufflags |= REGBUF_KEEP_DATA;
} }
@ -2605,6 +2605,7 @@ Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, Bu
*/ */
CacheInvalidateHeapTuple(relation, heaptup, NULL); CacheInvalidateHeapTuple(relation, heaptup, NULL);
/* Note: speculative insertions are counted too, even if aborted later */
pgstat_count_heap_insert(relation, 1); 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); 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. * @Description: Find minimum and maximum short transaction ids which occurs in the page.
* @in: page, heap page * @in: page, heap page
* @in: multi, Whether multixact * @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 */ /* the rest of the scratch space is used for tuple data */
tuple_data = scratchptr; 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; xlrec->ntuples = nthispage;
/* xlog: write the dictionary between header and tuples */ /* 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); Assert((scratchptr - scratch) < BLCKSZ);
if (need_tuple_data) { 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. * decoding so it knows when to cleanup temporary data.
*/ */
if (ndone + nthispage == ntuples) { 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.) * (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, 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; HTSU_Result result;
TransactionId xid = GetCurrentTransactionId(); TransactionId xid = GetCurrentTransactionId();
@ -3694,11 +3833,16 @@ HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, Tr
HeapTupleCopyBaseFromPage(&tp, page); HeapTupleCopyBaseFromPage(&tp, page);
l1: l1:
result = HeapTupleSatisfiesUpdate(&tp, cid, buffer); result = HeapTupleSatisfiesUpdate(&tp, cid, buffer, allow_delete_self);
if (result == HeapTupleInvisible) { if (result == HeapTupleInvisible) {
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to delete invisible tuple"))); 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) { } else if (result == HeapTupleBeingUpdated && wait) {
TransactionId xwait; TransactionId xwait;
uint16 infomask; uint16 infomask;
@ -3866,7 +4010,7 @@ l1:
(void)log_heap_new_cid(relation, &tp); (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); xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
if (old_key_tuple != NULL) { if (old_key_tuple != NULL) {
@ -3890,9 +4034,9 @@ l1:
} }
if (relreplident == REPLICA_IDENTITY_FULL) { if (relreplident == REPLICA_IDENTITY_FULL) {
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_TUPLE; xlrec.flags |= XLH_DELETE_CONTAINS_OLD_TUPLE;
} else { } 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 */ /* toast table entries should never be recursively toasted */
Assert(!HeapTupleHasExternal(&tp)); Assert(!HeapTupleHasExternal(&tp));
} else if (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 * 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 * on the relation associated with the tuple). Any failure is reported
* via ereport(). * via ereport().
*/ */
void simple_heap_delete(Relation relation, ItemPointer tid) void simple_heap_delete(Relation relation, ItemPointer tid, int options)
{ {
HTSU_Result result; HTSU_Result result;
ItemPointerData update_ctid; ItemPointerData update_ctid;
TransactionId update_xmax; TransactionId update_xmax;
bool allow_delete_self = (options & HEAP_INSERT_SPECULATIVE) ? true : false;
result = heap_delete(relation, result = heap_delete(relation,
tid, tid,
@ -3988,7 +4133,8 @@ void simple_heap_delete(Relation relation, ItemPointer tid)
&update_xmax, &update_xmax,
GetCurrentCommandId(true), GetCurrentCommandId(true),
InvalidSnapshot, InvalidSnapshot,
true /* wait for commit */); true /* wait for commit */,
allow_delete_self);
switch (result) { switch (result) {
case HeapTupleSelfUpdated: case HeapTupleSelfUpdated:
/* Tuple was already updated in current command? */ /* 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. * tuple was updated, and t_ctid is the location of the replacement tuple.
* (t_xmax is needed to verify that the replacement tuple matches.) * (t_xmax is needed to verify that the replacement tuple matches.)
*/ */
HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid, HeapTuple newtup, HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid,
ItemPointer ctid, TransactionId* update_xmax, CommandId cid, Snapshot crosscheck, bool wait) HeapTuple newtup, ItemPointer ctid, TransactionId* update_xmax, CommandId cid,
Snapshot crosscheck, bool wait, bool allow_update_self)
{ {
HTSU_Result result; HTSU_Result result;
TransactionId xid = GetCurrentTransactionId(); TransactionId xid = GetCurrentTransactionId();
@ -4144,10 +4291,15 @@ HTSU_Result heap_update(Relation relation, Relation parentRelation, ItemPointer
l2: l2:
HeapTupleCopyBaseFromPage(&oldtup, BufferGetPage(buffer)); HeapTupleCopyBaseFromPage(&oldtup, BufferGetPage(buffer));
result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer); result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer, allow_update_self);
if (result == HeapTupleInvisible) { if (result == HeapTupleInvisible) {
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to update invisible tuple"))); 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) { } else if (result == HeapTupleBeingUpdated && wait) {
TransactionId xwait; TransactionId xwait;
uint16 infomask; uint16 infomask;
@ -4386,7 +4538,8 @@ l2:
*/ */
if (need_toast) { if (need_toast) {
/* Note we always use WAL and FSM during updates */ /* 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); new_tup_size = MAXALIGN(heaptup->t_len);
} else { } else {
heaptup = newtup; 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. * conflict for a tuple, we don't incur any extra overhead.
*/ */
HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer, ItemPointer ctid, 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; HTSU_Result result;
ItemPointer tid = &(tuple->t_self); ItemPointer tid = &(tuple->t_self);
@ -5122,7 +5275,7 @@ HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer,
l3: l3:
HeapTupleCopyBaseFromPage(tuple, page); HeapTupleCopyBaseFromPage(tuple, page);
result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer); result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer, allow_lock_self);
ereport(DEBUG1, ereport(DEBUG1,
(errmsg("heap lock tuple ctid (%u,%d) cur_xid %lu xmin " (errmsg("heap lock tuple ctid (%u,%d) cur_xid %lu xmin "
"%lu xmax %lu infomask %hu result %d", "%lu xmax %lu infomask %hu result %d",
@ -5137,6 +5290,25 @@ l3:
if (result == HeapTupleInvisible) { if (result == HeapTupleInvisible) {
UnlockReleaseBuffer(*buffer); UnlockReleaseBuffer(*buffer);
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("attempted to lock invisible tuple"))); 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) { } else if (result == HeapTupleBeingUpdated) {
TransactionId xwait; TransactionId xwait;
uint16 infomask; 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.new_offnum = ItemPointerGetOffsetNumber(&newtup->t_self);
xlrec.flags = 0; xlrec.flags = 0;
if (all_visible_cleared) { if (all_visible_cleared) {
xlrec.flags |= XLOG_HEAP_ALL_VISIBLE_CLEARED; xlrec.flags |= XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED;
} }
if (new_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) { if (need_tuple_data) {
xlrec.flags |= XLOG_HEAP_CONTAINS_NEW_TUPLE; xlrec.flags |= XLH_UPDATE_CONTAINS_NEW_TUPLE;
if (old_key_tuple) { if (old_key_tuple) {
if (reln->rd_rel->relreplident == REPLICA_IDENTITY_FULL) if (reln->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_TUPLE; xlrec.flags |= XLH_UPDATE_CONTAINS_OLD_TUPLE;
else else
xlrec.flags |= XLOG_HEAP_CONTAINS_OLD_KEY; xlrec.flags |= XLH_UPDATE_CONTAINS_OLD_KEY;
} }
} }
if (need_tuple_data) { 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 * The visibility map may need to be fixed even if the heap page is
* already up-to-date. * already up-to-date.
*/ */
if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED) { if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED) {
RelFileNode target_node; RelFileNode target_node;
BlockNumber blkno; 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 * The visibility map may need to be fixed even if the heap page is
* already up-to-date. * 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); 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 * The visibility map may need to be fixed even if the heap page is
* already up-to-date. * 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); heap_xlog_allvisiblecleared(rnode, blkno);
} }
@ -6902,12 +7074,11 @@ static void heap_xlog_update(XLogReaderState* record, bool hot_update)
oldblk = newblk; oldblk = newblk;
} }
/* /*
* The visibility map may need to be fixed even if the heap page is * The visibility map may need to be fixed even if the heap page is
* already up-to-date. * 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); 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 * The visibility map may need to be fixed even if the heap page is
* already up-to-date. * 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); heap_xlog_allvisiblecleared(rnode, newblk);
} }

View File

@ -45,7 +45,7 @@
#undef TOAST_DEBUG #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 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 bool toastid_valueid_exists(Oid toastrelid, Oid valueid, int2 bucketid);
static struct varlena* toast_fetch_datum(struct varlena* attr); 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 * 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; TupleDesc tuple_desc;
Form_pg_attribute* att = NULL; Form_pg_attribute* att = NULL;
@ -381,7 +381,7 @@ void toast_delete(Relation rel, HeapTuple oldtup)
if (toast_isnull[i]) if (toast_isnull[i])
continue; continue;
else if (VARATT_IS_EXTERNAL_ONDISK_B(PointerGetDatum(value))) 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))) else if (VARATT_IS_EXTERNAL_INDIRECT(PointerGetDatum(value)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("attempt to delete tuple containing indirect datums"))); (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) { if (need_delold) {
for (i = 0; i < num_attrs; i++) { for (i = 0; i < num_attrs; i++) {
if (toast_delold[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. * 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 varlena* attr = (struct varlena*)DatumGetPointer(value);
struct varatt_external toast_pointer; struct varatt_external toast_pointer;
@ -1501,7 +1501,7 @@ static void toast_delete_datum(Relation rel, Datum value)
/* /*
* Have a chunk, delete it * 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) if (u_sess->attr.attr_storage.enable_debug_vacuum)
elogVacuumInfo(toastrel, toasttup, "toast_delete_datum", u_sess->cmd_cxt.OldestXmin); elogVacuumInfo(toastrel, toasttup, "toast_delete_datum", u_sess->cmd_cxt.OldestXmin);

View File

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

View File

@ -1214,7 +1214,7 @@ static void TrackVMPageModification(XLogReaderState* record)
recData += sizeof(TransactionId); recData += sizeof(TransactionId);
xl_heap_insert* xlrec = (xl_heap_insert*)recData; 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); (void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo1);
break; break;
@ -1222,7 +1222,7 @@ static void TrackVMPageModification(XLogReaderState* record)
case XLOG_HEAP_DELETE: { case XLOG_HEAP_DELETE: {
xl_heap_delete* xlrec = (xl_heap_delete*)recData; 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); (void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo1);
break; break;
@ -1232,10 +1232,10 @@ static void TrackVMPageModification(XLogReaderState* record)
recData += sizeof(TransactionId); recData += sizeof(TransactionId);
xl_heap_update* xlrec = (xl_heap_update*)recData; 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); (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); (void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo2);
break; break;
@ -1249,7 +1249,7 @@ static void TrackVMPageModification(XLogReaderState* record)
recData += sizeof(TransactionId); recData += sizeof(TransactionId);
xl_heap_multi_insert* xlrec = (xl_heap_multi_insert*)recData; 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); (void)XLogRecGetBlockTag(record, 0, &rNode, NULL, &heapBlkNo1);
} }
} else } else

View File

@ -675,7 +675,7 @@ static void DecodeInsert(LogicalDecodingContext* ctx, XLogRecordBuffer* buf)
rc = memcpy_s(&change->data.tp.relnode, sizeof(RelFileNode), &target_node, sizeof(RelFileNode)); rc = memcpy_s(&change->data.tp.relnode, sizeof(RelFileNode), &target_node, sizeof(RelFileNode));
securec_check(rc, "\0", "\0"); 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); change->data.tp.newtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
DecodeXLogTuple(tupledata, tuplelen, change->data.tp.newtuple); DecodeXLogTuple(tupledata, tuplelen, change->data.tp.newtuple);
@ -741,11 +741,11 @@ static void DecodeUpdate(LogicalDecodingContext* ctx, XLogRecordBuffer* buf)
change->origin_id = XLogRecGetOrigin(r); change->origin_id = XLogRecGetOrigin(r);
rc = memcpy_s(&change->data.tp.relnode, sizeof(RelFileNode), &target_node, sizeof(RelFileNode)); rc = memcpy_s(&change->data.tp.relnode, sizeof(RelFileNode), &target_node, sizeof(RelFileNode));
securec_check(rc, "", ""); 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); change->data.tp.newtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen_new);
DecodeXLogTuple(data_new, datalen_new, change->data.tp.newtuple); 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); change->data.tp.oldtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen_old);
DecodeXLogTuple(data_old, datalen_old, change->data.tp.oldtuple); DecodeXLogTuple(data_old, datalen_old, change->data.tp.oldtuple);
@ -790,7 +790,7 @@ static void DecodeDelete(LogicalDecodingContext* ctx, XLogRecordBuffer* buf)
securec_check(rc, "", ""); securec_check(rc, "", "");
/* old primary key stored */ /* old primary key stored */
if (xlrec->flags & XLOG_HEAP_CONTAINS_OLD) { if (xlrec->flags & XLH_DELETE_CONTAINS_OLD) {
Assert(XLogRecGetDataLen(r) > (SizeOfHeapDelete + SizeOfHeapHeader)); Assert(XLogRecGetDataLen(r) > (SizeOfHeapDelete + SizeOfHeapHeader));
change->data.tp.oldtuple = ReorderBufferGetTupleBuf(ctx->reorder, datalen); 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, * 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. * 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; HeapTupleHeader header;
xlhdr = (xl_multi_insert_tuple*)data; xlhdr = (xl_multi_insert_tuple*)data;
data = ((char*)xlhdr) + SizeOfMultiInsertTuple; 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() * xl_multi_insert_tuple record emitted by one heap_multi_insert()
* call. * 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; change->data.tp.clear_toast_afterwards = true;
} else { } else {
change->data.tp.clear_toast_afterwards = false; change->data.tp.clear_toast_afterwards = false;

View File

@ -119,6 +119,7 @@ extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState); extern void FreeBulkInsertState(BulkInsertState);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate); 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( extern bool heap_page_prepare_for_xid(
Relation relation, Buffer buffer, TransactionId xid, bool multi, bool pageReplication = false); Relation relation, Buffer buffer, TransactionId xid, bool multi, bool pageReplication = false);
extern bool heap_change_xidbase_after_freeze(Relation relation, Buffer buffer); 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, extern int heap_multi_insert(Relation relation, Relation parent, HeapTuple* tuples, int ntuples, CommandId cid, int options,
BulkInsertState bistate, HeapMultiInsertExtraArgs* args); BulkInsertState bistate, HeapMultiInsertExtraArgs* args);
extern HTSU_Result heap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, TransactionId* update_xmax, 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, 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, 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 void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTuple tuple, TransactionId cutoff_xid); extern bool heap_freeze_tuple(HeapTuple tuple, TransactionId cutoff_xid);
extern bool heap_tuple_needs_freeze(HeapTuple tuple, TransactionId cutoff_xid, Buffer buf); extern bool heap_tuple_needs_freeze(HeapTuple tuple, TransactionId cutoff_xid, Buffer buf);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup); 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 simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup);
extern void heap_markpos(HeapScanDesc scan); extern void heap_markpos(HeapScanDesc scan);

View File

@ -691,23 +691,47 @@ static inline TransactionId HeapTupleGetRawXmax(HeapTuple tup)
#define XLOG_HEAP3_NEW_CID 0x00 #define XLOG_HEAP3_NEW_CID 0x00
#define XLOG_HEAP3_REWRITE 0x10 #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 */ /* 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 */ /* PD_ALL_VISIBLE was cleared in the 2nd page */
#define XLOG_HEAP_NEW_ALL_VISIBLE_CLEARED (1 << 1) #define XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED (1<<1)
#define XLOG_HEAP_CONTAINS_OLD_TUPLE (1 << 2) #define XLH_UPDATE_CONTAINS_OLD_TUPLE (1<<2)
#define XLOG_HEAP_CONTAINS_OLD_KEY (1 << 3) #define XLH_UPDATE_CONTAINS_OLD_KEY (1<<3)
#define XLOG_HEAP_CONTAINS_NEW_TUPLE (1 << 4) #define XLH_UPDATE_CONTAINS_NEW_TUPLE (1<<4)
#define XLOG_HEAP_PREFIX_FROM_OLD (1 << 5) #define XLH_UPDATE_PREFIX_FROM_OLD (1<<5)
#define XLOG_HEAP_SUFFIX_FROM_OLD (1 << 6) #define XLH_UPDATE_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)
/* convenience macro for checking whether any form of old tuple was logged */ /* 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 */ /* This is what we need to know about delete */
typedef struct xl_heap_delete { typedef struct xl_heap_delete {

View File

@ -165,7 +165,7 @@ extern HeapTuple toast_insert_or_update(
* Called by heap_delete(). * 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() - * heap_tuple_fetch_attr() -

View File

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

View File

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

View File

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

View File

@ -61,6 +61,9 @@ typedef struct UtilityDesc {
* ExclusionOps Per-column exclusion operators, or NULL if none * ExclusionOps Per-column exclusion operators, or NULL if none
* ExclusionProcs Underlying function OIDs for ExclusionOps * ExclusionProcs Underlying function OIDs for ExclusionOps
* ExclusionStrats Opclass strategy numbers 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? * Unique is it a unique index?
* ReadyForInserts is it valid for inserts? * ReadyForInserts is it valid for inserts?
* Concurrent are we doing a concurrent index build? * 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_ExclusionOps; /* array with one entry per column */
Oid* ii_ExclusionProcs; /* array with one entry per column */ Oid* ii_ExclusionProcs; /* array with one entry per column */
uint16* ii_ExclusionStrats; /* 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_Unique;
bool ii_ReadyForInserts; bool ii_ReadyForInserts;
bool ii_Concurrent; bool ii_Concurrent;
@ -396,6 +402,7 @@ typedef struct MergeState {
* ConstraintExprs array of constraint-checking expr states * ConstraintExprs array of constraint-checking expr states
* junkFilter for removing junk attributes from tuples * junkFilter for removing junk attributes from tuples
* projectReturning for computing a RETURNING list * projectReturning for computing a RETURNING list
* updateProj for computing a UPSERT update list
* ---------------- * ----------------
*/ */
typedef struct ResultRelInfo { typedef struct ResultRelInfo {
@ -434,6 +441,7 @@ typedef struct ResultRelInfo {
* ri_RangeTableIndex elsewhere. * ri_RangeTableIndex elsewhere.
*/ */
Index ri_mergeTargetRTI; Index ri_mergeTargetRTI;
ProjectionInfo* ri_updateProj;
} ResultRelInfo; } ResultRelInfo;
/* bloom filter controller */ /* bloom filter controller */
@ -1292,6 +1300,18 @@ typedef struct MergeActionState {
VectorBatch* scanBatch; /* scan batch for UPDATE */ VectorBatch* scanBatch; /* scan batch for UPDATE */
} MergeActionState; } 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 * 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_insert_constr_slot; /* slot to store target tuple in for checking constraints */
TupleTableSlot* mt_mergeproj; /* MERGE action projection target */ TupleTableSlot* mt_mergeproj; /* MERGE action projection target */
uint32 mt_merge_subcommands; /* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */ 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 */ instr_time first_tuple_modified; /* record the end time for the first tuple inserted, deleted, or updated */
} ModifyTableState; } ModifyTableState;

View File

@ -210,6 +210,7 @@ typedef enum NodeTag {
T_RangeTblRef, T_RangeTblRef,
T_JoinExpr, T_JoinExpr,
T_FromExpr, T_FromExpr,
T_UpsertExpr,
T_IntoClause, T_IntoClause,
T_IndexVar, T_IndexVar,
#ifdef PGXC #ifdef PGXC
@ -304,7 +305,7 @@ typedef enum NodeTag {
#endif /* PGXC */ #endif /* PGXC */
T_StreamPath, T_StreamPath,
T_MergeAction, T_MergeAction,
T_UpsertState,
/* /*
* TAGS FOR MEMORY NODES (memnodes.h) * TAGS FOR MEMORY NODES (memnodes.h)
*/ */
@ -499,7 +500,7 @@ typedef enum NodeTag {
T_PruningResult, T_PruningResult,
T_Position, T_Position,
T_MergeWhenClause, T_MergeWhenClause,
T_UpsertClause,
/* /*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h) * 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))) != \ (1 << JOIN_RIGHT_ANTI) | (1 << JOIN_LEFT_ANTI_FULL) | (1 << JOIN_RIGHT_ANTI_FULL))) != \
0) 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 */ #endif /* NODES_H */

View File

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

View File

@ -412,6 +412,12 @@ typedef struct ModifyTable {
Index mergeTargetRelation; /* RT index of the merge target */ Index mergeTargetRelation; /* RT index of the merge target */
List* mergeSourceTargetList; List* mergeSourceTargetList;
List* mergeActionList; /* actions for MERGE */ List* mergeActionList; /* actions for MERGE */
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 */ OpMemInfo mem_info; /* Memory info for modify node */
} ModifyTable; } ModifyTable;

View File

@ -1359,4 +1359,14 @@ typedef struct {
bool indexpath; bool indexpath;
} IndexVar; } 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 */ #endif /* PRIMNODES_H */

View File

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

View File

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

View File

@ -58,7 +58,7 @@ extern Expr* canonicalize_qual(Expr* qual);
* prototypes for preptlist.c * prototypes for preptlist.c
*/ */
extern List* preprocess_targetlist(PlannerInfo* root, List* tlist); 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); extern PlanRowMark* get_plan_rowmark(List* rowmarks, Index rtindex);
/* /*

View File

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

View File

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

View File

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

View File

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

View File

@ -70,7 +70,7 @@ extern bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer bu
extern bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer); extern bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer);
extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer); extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer);
/* Special "satisfies" routines with different APIs */ /* 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 HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin); extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);

View File

@ -295,3 +295,4 @@ EXPLAIN (ANALYZE on, COSTS off, TIMING off) insert into up_expl_node values(1,1
-> Result (actual rows=1 loops=1) -> Result (actual rows=1 loops=1)
--? Total runtime: .* ms --? Total runtime: .* ms
(5 rows) (5 rows)