forked from openGauss-Ecosystem/openGauss-server
!1387 Improve concurrency of foreign key locking
Merge pull request !1387 from chenxiaobin/enhancetuplelock
This commit is contained in:
commit
17a872b1fd
|
@ -654,10 +654,18 @@ static ForeignScan *postgresGetForeignPlan(PlannerInfo *root, RelOptInfo *basere
|
|||
* complete information about, and (b) it wouldn't work anyway on
|
||||
* older remote servers. Likewise, we don't worry about NOWAIT.
|
||||
*/
|
||||
if (rc->forUpdate) {
|
||||
appendStringInfoString(&sql, " FOR UPDATE");
|
||||
} else {
|
||||
appendStringInfoString(&sql, " FOR SHARE");
|
||||
switch (rc->strength) {
|
||||
case LCS_FORKEYSHARE:
|
||||
case LCS_FORSHARE:
|
||||
appendStringInfoString(&sql, " FOR SHARE");
|
||||
break;
|
||||
case LCS_FORNOKEYUPDATE:
|
||||
case LCS_FORUPDATE:
|
||||
appendStringInfoString(&sql, " FOR UPDATE");
|
||||
break;
|
||||
default:
|
||||
ereport(ERROR, (errmsg("unknown lock type: %d", rc->strength)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ SELECT [/*+ plan_hint */] [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
|
|||
[ LIMIT { [offset,] count | ALL } ]
|
||||
[ OFFSET start [ ROW | ROWS ] ]
|
||||
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
|
||||
[ {FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ]} [...] ];
|
||||
[ {FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT ]} [...] ];
|
||||
TABLE { ONLY {(table_name)| table_name} | table_name [ * ]};
|
||||
|
||||
where from_item can be:
|
||||
|
|
|
@ -1819,7 +1819,7 @@ extractPageInfo(XLogReaderState *record, XLogReaderData *reader_data,
|
|||
* source system.
|
||||
*/
|
||||
}
|
||||
else if (info & XLR_SPECIAL_REL_UPDATE)
|
||||
else if (rmid != RM_HEAP_ID && rmid != RM_HEAP2_ID && (info & XLR_SPECIAL_REL_UPDATE))
|
||||
{
|
||||
/*
|
||||
* This record type modifies a relation file in some special way, but
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "access/xlog.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/gs_obsscaninfo.h"
|
||||
|
@ -166,6 +167,8 @@ static bool CheckNestedGenerated(ParseState *pstate, Node *node);
|
|||
extern void getErrorTableFilePath(char* buf, int len, Oid databaseid, Oid reid);
|
||||
extern void make_tmptable_cache_key(Oid relNode);
|
||||
|
||||
#define RELKIND_IN_RTM (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE || relkind == RELKIND_MATVIEW)
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* XXX UGLY HARD CODED BADNESS FOLLOWS XXX
|
||||
*
|
||||
|
@ -1085,7 +1088,7 @@ void InsertPgClassTuple(
|
|||
else
|
||||
nulls[Anum_pg_class_reloptions - 1] = true;
|
||||
|
||||
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE || relkind == RELKIND_MATVIEW)
|
||||
if (RELKIND_IN_RTM)
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = u_sess->utils_cxt.RecentXmin;
|
||||
else
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = InvalidTransactionId;
|
||||
|
@ -1103,6 +1106,14 @@ void InsertPgClassTuple(
|
|||
} else {
|
||||
nulls[Anum_pg_class_relbucket - 1] = true;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (RELKIND_IN_RTM && !is_cstore_option(relkind, reloptions)) {
|
||||
values[Anum_pg_class_relminmxid - 1] = GetOldestMultiXactId();
|
||||
} else {
|
||||
values[Anum_pg_class_relminmxid - 1] = InvalidMultiXactId;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bucketcol != NULL)
|
||||
values[Anum_pg_class_relbucketkey - 1] = PointerGetDatum(bucketcol);
|
||||
|
@ -1170,7 +1181,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Relation new_rel_desc, O
|
|||
}
|
||||
|
||||
/* Initialize relfrozenxid */
|
||||
if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE || relkind == RELKIND_MATVIEW) {
|
||||
if (RELKIND_IN_RTM) {
|
||||
/*
|
||||
* Initialize to the minimum XID that could put tuples in the table.
|
||||
* We know that no xacts older than RecentXmin are still running, so
|
||||
|
@ -2904,7 +2915,7 @@ Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltable
|
|||
register_on_commit_action(relid, oncommit);
|
||||
|
||||
if (relpersistence == RELPERSISTENCE_UNLOGGED) {
|
||||
Assert(relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE || relkind == RELKIND_MATVIEW);
|
||||
Assert(RELKIND_IN_RTM);
|
||||
heap_create_init_fork(new_rel_desc);
|
||||
}
|
||||
|
||||
|
@ -6239,6 +6250,9 @@ static void addNewPartitionTupleForValuePartitionedTable(Relation pg_partition_r
|
|||
nulls[Anum_pg_partition_reloptions - 1] = true;
|
||||
}
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = TransactionIdGetDatum(InvalidTransactionId);
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
values[Anum_pg_partition_relminmxid - 1] = TransactionIdGetDatum(InvalidMultiXactId);
|
||||
#endif
|
||||
/*form a tuple using values and null array, and insert it*/
|
||||
tup = heap_form_tuple(RelationGetDescr(pg_partition_rel), values, nulls);
|
||||
HeapTupleSetOid(tup, InvalidOid);
|
||||
|
@ -6459,6 +6473,7 @@ void heap_truncate_one_part(Relation rel, Oid partOid)
|
|||
List* partIndexlist = NULL;
|
||||
Relation parentIndex = NULL;
|
||||
mySubid = GetCurrentSubTransactionId();
|
||||
MultiXactId multiXid = GetOldestMultiXactId();
|
||||
|
||||
partIndexlist = searchPartitionIndexesByblid(partOid);
|
||||
|
||||
|
@ -6477,12 +6492,13 @@ void heap_truncate_one_part(Relation rel, Oid partOid)
|
|||
|
||||
CheckTableForSerializableConflictIn(rel);
|
||||
|
||||
PartitionSetNewRelfilenode(rel, p, u_sess->utils_cxt.RecentXmin);
|
||||
PartitionSetNewRelfilenode(rel, p, u_sess->utils_cxt.RecentXmin,
|
||||
RelationIsColStore(rel) ? InvalidMultiXactId : multiXid);
|
||||
|
||||
/* truncate the toast table */
|
||||
if (toastOid != InvalidOid) {
|
||||
Relation toastRel = heap_open(toastOid, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(toastRel, u_sess->utils_cxt.RecentXmin);
|
||||
RelationSetNewRelfilenode(toastRel, u_sess->utils_cxt.RecentXmin, multiXid);
|
||||
heap_close(toastRel, NoLock);
|
||||
}
|
||||
|
||||
|
|
|
@ -2755,7 +2755,7 @@ void index_build(Relation heapRelation, Partition heapPartition, Relation indexR
|
|||
Oid psortRelId = targetIndexRelation->rd_rel->relcudescrelid;
|
||||
Relation psortRel = relation_open(psortRelId, AccessExclusiveLock);
|
||||
|
||||
RelationSetNewRelfilenode(psortRel, u_sess->utils_cxt.RecentXmin);
|
||||
RelationSetNewRelfilenode(psortRel, u_sess->utils_cxt.RecentXmin, InvalidMultiXactId);
|
||||
relation_close(psortRel, NoLock);
|
||||
}
|
||||
|
||||
|
@ -3192,7 +3192,7 @@ double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexIn
|
|||
* unless it's our own deletion or a system catalog.
|
||||
*/
|
||||
Assert(!(heapTuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
|
||||
xwait = HeapTupleGetRawXmax(heapTuple);
|
||||
xwait = HeapTupleGetUpdateXid(heapTuple);
|
||||
if (!TransactionIdIsCurrentTransactionId(xwait)) {
|
||||
if (!is_system_catalog)
|
||||
ereport(WARNING,
|
||||
|
@ -4220,7 +4220,7 @@ void reindex_indexpart_internal(Relation heapRelation, Relation iRel, IndexInfo*
|
|||
heapPart = partitionOpen(heapRelation, heapPartId, ShareLock);
|
||||
indexpart = partitionOpen(iRel, indexPartId, AccessExclusiveLock);
|
||||
|
||||
PartitionSetNewRelfilenode(iRel, indexpart, InvalidTransactionId);
|
||||
PartitionSetNewRelfilenode(iRel, indexpart, InvalidTransactionId, InvalidMultiXactId);
|
||||
|
||||
index_build(heapRelation, heapPart, iRel, indexpart, indexInfo, false, true, INDEX_CREATE_LOCAL_PARTITION, true);
|
||||
|
||||
|
@ -4243,7 +4243,7 @@ void ReindexGlobalIndexInternal(Relation heapRelation, Relation iRel, IndexInfo*
|
|||
partitionList = relationGetPartitionList(heapRelation, ShareLock);
|
||||
|
||||
/* We'll build a new physical relation for the index */
|
||||
RelationSetNewRelfilenode(iRel, InvalidTransactionId);
|
||||
RelationSetNewRelfilenode(iRel, InvalidTransactionId, InvalidMultiXactId);
|
||||
|
||||
/* Initialize the index and rebuild */
|
||||
/* Note: we do not need to re-establish pkey setting */
|
||||
|
@ -4404,7 +4404,7 @@ void reindex_index(Oid indexId, Oid indexPartId, bool skip_constraint_checks,
|
|||
TrRelationSetNewRelfilenode(iRel, InvalidTransactionId, baseDesc);
|
||||
} else {
|
||||
/* We'll build a new physical relation for the index */
|
||||
RelationSetNewRelfilenode(iRel, InvalidTransactionId);
|
||||
RelationSetNewRelfilenode(iRel, InvalidTransactionId, InvalidMultiXactId);
|
||||
}
|
||||
|
||||
/* Initialize the index and rebuild */
|
||||
|
@ -4859,7 +4859,7 @@ void reindex_partIndex(Relation heapRel, Partition heapPart, Relation indexRel,
|
|||
*/
|
||||
|
||||
// change the storage of part index
|
||||
PartitionSetNewRelfilenode(indexRel, indexPart, InvalidTransactionId);
|
||||
PartitionSetNewRelfilenode(indexRel, indexPart, InvalidTransactionId, InvalidMultiXactId);
|
||||
|
||||
// build the part index
|
||||
indexInfo = BuildIndexInfo(indexRel);
|
||||
|
@ -5139,7 +5139,7 @@ static void reindexPartIndex(Oid indexId, Oid partOid, bool skip_constraint_chec
|
|||
// REINDEX INDEX
|
||||
CheckPartitionNotInUse(indexpart, "REINDEX INDEX index_partition");
|
||||
|
||||
PartitionSetNewRelfilenode(iRel, indexpart, InvalidTransactionId);
|
||||
PartitionSetNewRelfilenode(iRel, indexpart, InvalidTransactionId, InvalidMultiXactId);
|
||||
index_build(heapRelation, heapPart, iRel, indexpart, indexInfo, false, true, INDEX_CREATE_LOCAL_PARTITION);
|
||||
|
||||
/*
|
||||
|
@ -5219,7 +5219,7 @@ static void reindexPartIndex(Oid indexId, Oid partOid, bool skip_constraint_chec
|
|||
}
|
||||
|
||||
/* Update reltuples and relpages in pg_class for partitioned index. */
|
||||
vac_update_pgclass_partitioned_table(iRel, false, InvalidTransactionId);
|
||||
vac_update_pgclass_partitioned_table(iRel, false, InvalidTransactionId, InvalidMultiXactId);
|
||||
|
||||
/* Close rels, but keep locks */
|
||||
index_close(iRel, NoLock);
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "utils/inval.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "access/genam.h"
|
||||
#include "access/multixact.h"
|
||||
#include "access/reloptions.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "access/heapam.h"
|
||||
#include "utils/snapmgr.h"
|
||||
|
@ -129,8 +131,19 @@ void insertPartitionEntry(Relation pg_partition_desc, Partition new_part_desc, O
|
|||
|
||||
if (parttype == PART_OBJ_TYPE_TABLE_PARTITION) {
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = u_sess->utils_cxt.RecentXmin;
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (!is_cstore_option(RELKIND_RELATION, reloptions)) {
|
||||
values[Anum_pg_partition_relminmxid - 1] = GetOldestMultiXactId();
|
||||
} else {
|
||||
values[Anum_pg_partition_relminmxid - 1] = InvalidMultiXactId;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = InvalidTransactionId;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
values[Anum_pg_partition_relminmxid - 1] = InvalidMultiXactId;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* form a tuple using values and null array, and insert it */
|
||||
|
|
|
@ -3493,6 +3493,9 @@ static RowMarkClause* _copyRowMarkClause(const RowMarkClause* from)
|
|||
COPY_SCALAR_FIELD(forUpdate);
|
||||
COPY_SCALAR_FIELD(noWait);
|
||||
COPY_SCALAR_FIELD(pushedDown);
|
||||
if (t_thrd.proc->workingVersionNum >= ENHANCED_TUPLE_LOCK_VERSION_NUM) {
|
||||
COPY_SCALAR_FIELD(strength);
|
||||
}
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
@ -3967,6 +3970,9 @@ static LockingClause* _copyLockingClause(const LockingClause* from)
|
|||
COPY_NODE_FIELD(lockedRels);
|
||||
COPY_SCALAR_FIELD(forUpdate);
|
||||
COPY_SCALAR_FIELD(noWait);
|
||||
if (t_thrd.proc->workingVersionNum >= ENHANCED_TUPLE_LOCK_VERSION_NUM) {
|
||||
COPY_SCALAR_FIELD(strength);
|
||||
}
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
|
|
@ -2542,6 +2542,9 @@ static bool _equalLockingClause(const LockingClause* a, const LockingClause* b)
|
|||
COMPARE_NODE_FIELD(lockedRels);
|
||||
COMPARE_SCALAR_FIELD(forUpdate);
|
||||
COMPARE_SCALAR_FIELD(noWait);
|
||||
if (t_thrd.proc->workingVersionNum >= ENHANCED_TUPLE_LOCK_VERSION_NUM) {
|
||||
COMPARE_SCALAR_FIELD(strength);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2680,6 +2683,9 @@ static bool _equalRowMarkClause(const RowMarkClause* a, const RowMarkClause* b)
|
|||
COMPARE_SCALAR_FIELD(forUpdate);
|
||||
COMPARE_SCALAR_FIELD(noWait);
|
||||
COMPARE_SCALAR_FIELD(pushedDown);
|
||||
if (t_thrd.proc->workingVersionNum >= ENHANCED_TUPLE_LOCK_VERSION_NUM) {
|
||||
COMPARE_SCALAR_FIELD(strength);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3634,6 +3634,9 @@ static void _outLockingClause(StringInfo str, LockingClause* node)
|
|||
WRITE_NODE_FIELD(lockedRels);
|
||||
WRITE_BOOL_FIELD(forUpdate);
|
||||
WRITE_BOOL_FIELD(noWait);
|
||||
if (t_thrd.proc->workingVersionNum >= ENHANCED_TUPLE_LOCK_VERSION_NUM) {
|
||||
WRITE_ENUM_FIELD(strength, LockClauseStrength);
|
||||
}
|
||||
}
|
||||
|
||||
static void _outXmlSerialize(StringInfo str, XmlSerialize* node)
|
||||
|
@ -4269,6 +4272,9 @@ static void _outRowMarkClause(StringInfo str, RowMarkClause* node)
|
|||
WRITE_BOOL_FIELD(forUpdate);
|
||||
WRITE_BOOL_FIELD(noWait);
|
||||
WRITE_BOOL_FIELD(pushedDown);
|
||||
if (t_thrd.proc->workingVersionNum >= ENHANCED_TUPLE_LOCK_VERSION_NUM) {
|
||||
WRITE_ENUM_FIELD(strength, LockClauseStrength);
|
||||
}
|
||||
}
|
||||
|
||||
static void _outWithClause(StringInfo str, WithClause* node)
|
||||
|
|
|
@ -1608,6 +1608,9 @@ static RowMarkClause* _readRowMarkClause(void)
|
|||
READ_BOOL_FIELD(forUpdate);
|
||||
READ_BOOL_FIELD(noWait);
|
||||
READ_BOOL_FIELD(pushedDown);
|
||||
IF_EXIST(strength) {
|
||||
READ_ENUM_FIELD(strength, LockClauseStrength);
|
||||
}
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
|
|
@ -4124,7 +4124,7 @@ static bool is_rel_child_of_rel(RangeTblEntry* child_rte, RangeTblEntry* parent_
|
|||
#endif
|
||||
|
||||
/*
|
||||
* Check for features that are not supported together with FOR UPDATE/SHARE.
|
||||
* Check for features that are not supported together with FOR [KEY] UPDATE/SHARE.
|
||||
*
|
||||
* exported so planner can check again after rewriting, query pullup, etc
|
||||
*/
|
||||
|
@ -4133,42 +4133,44 @@ void CheckSelectLocking(Query* qry)
|
|||
if (qry->setOperations) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed "
|
||||
"with UNION/INTERSECT/EXCEPT")));
|
||||
}
|
||||
if (qry->distinctClause != NIL) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed with DISTINCT clause")));
|
||||
}
|
||||
if (qry->groupClause != NIL) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed with GROUP BY clause")));
|
||||
}
|
||||
if (qry->havingQual != NULL) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed with HAVING clause")));
|
||||
}
|
||||
if (qry->hasAggs) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed with aggregate functions")));
|
||||
}
|
||||
if (qry->hasWindowFuncs) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with window functions")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed with window functions")));
|
||||
}
|
||||
if (expression_returns_set((Node*)qry->targetList)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with set-returning functions in the target list")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed with set-returning functions "
|
||||
"in the target list")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a FOR UPDATE/SHARE clause
|
||||
* Transform a FOR [KEY] UPDATE/SHARE clause
|
||||
*
|
||||
* This basically involves replacing names by integer relids.
|
||||
*
|
||||
|
@ -4189,7 +4191,7 @@ static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause
|
|||
/* make a clause we can pass down to subqueries to select all rels */
|
||||
allrels = makeNode(LockingClause);
|
||||
allrels->lockedRels = NIL; /* indicates all rels */
|
||||
allrels->forUpdate = lc->forUpdate;
|
||||
allrels->strength = lc->strength;
|
||||
allrels->noWait = lc->noWait;
|
||||
|
||||
if (lockedRels == NIL) {
|
||||
|
@ -4206,20 +4208,20 @@ static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause
|
|||
heap_close(rel, AccessShareLock);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be used with column table \"%s\"",
|
||||
rte->eref->aliasname)));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be used with "
|
||||
"column table \"%s\"", rte->eref->aliasname)));
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
applyLockingClause(qry, i, lc->forUpdate, lc->noWait, pushedDown);
|
||||
applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown);
|
||||
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
||||
break;
|
||||
case RTE_SUBQUERY:
|
||||
applyLockingClause(qry, i, lc->forUpdate, lc->noWait, pushedDown);
|
||||
applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown);
|
||||
|
||||
/*
|
||||
* FOR UPDATE/SHARE of subquery is propagated to all of
|
||||
* FOR [KEY] UPDATE/SHARE of subquery is propagated to all of
|
||||
* subquery's rels, too. We could do this later (based on
|
||||
* the marking of the subquery RTE) but it is convenient
|
||||
* to have local knowledge in each query level about which
|
||||
|
@ -4246,7 +4248,8 @@ static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause
|
|||
if (thisrel->catalogname || thisrel->schemaname) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("SELECT FOR UPDATE/SHARE must specify unqualified relation names"),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE must specify unqualified "
|
||||
"relation names"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
}
|
||||
|
||||
|
@ -4263,42 +4266,46 @@ static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause
|
|||
heap_close(rel, AccessShareLock);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be used with column table \"%s\"",
|
||||
rte->eref->aliasname),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be used with "
|
||||
"column table \"%s\"", rte->eref->aliasname),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
applyLockingClause(qry, i, lc->forUpdate, lc->noWait, pushedDown);
|
||||
applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown);
|
||||
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
||||
break;
|
||||
case RTE_SUBQUERY:
|
||||
applyLockingClause(qry, i, lc->forUpdate, lc->noWait, pushedDown);
|
||||
applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown);
|
||||
/* see comment above */
|
||||
transformLockingClause(pstate, rte->subquery, allrels, true);
|
||||
break;
|
||||
case RTE_JOIN:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be applied "
|
||||
"to a join"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be applied "
|
||||
"to a function"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be applied "
|
||||
"to VALUES"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
break;
|
||||
case RTE_CTE:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a WITH query"),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be applied "
|
||||
"to a WITH query"),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
break;
|
||||
default:
|
||||
|
@ -4313,7 +4320,8 @@ static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause
|
|||
if (rt == NULL) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_TABLE),
|
||||
errmsg("relation \"%s\" in FOR UPDATE/SHARE clause not found in FROM clause", thisrel->relname),
|
||||
errmsg("relation \"%s\" in FOR UPDATE/SHARE//NO KEY UPDATE/KEY SHARE clause not found "
|
||||
"in FROM clause", thisrel->relname),
|
||||
parser_errposition(pstate, thisrel->location)));
|
||||
}
|
||||
}
|
||||
|
@ -4323,7 +4331,7 @@ static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause
|
|||
/*
|
||||
* Record locking info for a single rangetable item
|
||||
*/
|
||||
void applyLockingClause(Query* qry, Index rtindex, bool forUpdate, bool noWait, bool pushedDown)
|
||||
void applyLockingClause(Query* qry, Index rtindex, LockClauseStrength strength, bool noWait, bool pushedDown)
|
||||
{
|
||||
RowMarkClause* rc = NULL;
|
||||
|
||||
|
@ -4335,10 +4343,10 @@ void applyLockingClause(Query* qry, Index rtindex, bool forUpdate, bool noWait,
|
|||
/* Check for pre-existing entry for same rtindex */
|
||||
if ((rc = get_parse_rowmark(qry, rtindex)) != NULL) {
|
||||
/*
|
||||
* If the same RTE is specified both FOR UPDATE and FOR SHARE, treat
|
||||
* it as FOR UPDATE. (Reasonable, since you can't take both a shared
|
||||
* and exclusive lock at the same time; it'll end up being exclusive
|
||||
* anyway.)
|
||||
* If the same RTE is specified for more than one locking strength,
|
||||
* treat is as the strongest. (Reasonable, since you can't take both a
|
||||
* shared and exclusive lock at the same time; it'll end up being
|
||||
* exclusive anyway.)
|
||||
*
|
||||
* We also consider that NOWAIT wins if it's specified both ways. This
|
||||
* is a bit more debatable but raising an error doesn't seem helpful.
|
||||
|
@ -4347,7 +4355,7 @@ void applyLockingClause(Query* qry, Index rtindex, bool forUpdate, bool noWait,
|
|||
*
|
||||
* And of course pushedDown becomes false if any clause is explicit.
|
||||
*/
|
||||
rc->forUpdate = rc->forUpdate || forUpdate;
|
||||
rc->strength = Max(rc->strength, strength);
|
||||
rc->noWait = rc->noWait || noWait;
|
||||
rc->pushedDown = rc->pushedDown && pushedDown;
|
||||
return;
|
||||
|
@ -4356,7 +4364,7 @@ void applyLockingClause(Query* qry, Index rtindex, bool forUpdate, bool noWait,
|
|||
/* Make a new RowMarkClause */
|
||||
rc = makeNode(RowMarkClause);
|
||||
rc->rti = rtindex;
|
||||
rc->forUpdate = forUpdate;
|
||||
rc->strength = strength;
|
||||
rc->noWait = noWait;
|
||||
rc->pushedDown = pushedDown;
|
||||
qry->rowMarks = lappend(qry->rowMarks, rc);
|
||||
|
|
|
@ -287,6 +287,7 @@ static Node *make_node_from_scanbuf(int start_pos, int end_pos, core_yyscan_t yy
|
|||
MergeWhenClause *mergewhen;
|
||||
UpsertClause *upsert;
|
||||
EncryptionType algtype;
|
||||
LockClauseStrength lockstrength;
|
||||
}
|
||||
|
||||
%type <node> stmt schema_stmt
|
||||
|
@ -479,6 +480,7 @@ static Node *make_node_from_scanbuf(int start_pos, int end_pos, core_yyscan_t yy
|
|||
%type <ival> OptTemp OptKind
|
||||
%type <oncommit> OnCommitOption
|
||||
|
||||
%type <lockstrength> for_locking_strength
|
||||
%type <node> for_locking_item
|
||||
%type <list> for_locking_clause opt_for_locking_clause for_locking_items
|
||||
%type <list> locked_rels_list
|
||||
|
@ -16901,9 +16903,9 @@ select_with_parens:
|
|||
* The duplicative productions are annoying, but hard to get rid of without
|
||||
* creating shift/reduce conflicts.
|
||||
*
|
||||
* FOR UPDATE/SHARE may be before or after LIMIT/OFFSET.
|
||||
* The locking clause (FOR UPDATE etc) may be before or after LIMIT/OFFSET.
|
||||
* In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE
|
||||
* We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE/SHARE
|
||||
* We now support both orderings, but prefer LIMIT/OFFSET before the locking clause.
|
||||
* 2002-08-28 bjm
|
||||
*/
|
||||
select_no_parens:
|
||||
|
@ -17452,21 +17454,38 @@ for_locking_items:
|
|||
;
|
||||
|
||||
for_locking_item:
|
||||
FOR UPDATE locked_rels_list opt_nowait
|
||||
for_locking_strength locked_rels_list opt_nowait
|
||||
{
|
||||
LockingClause *n = makeNode(LockingClause);
|
||||
n->lockedRels = $3;
|
||||
n->forUpdate = TRUE;
|
||||
n->noWait = $4;
|
||||
n->lockedRels = $2;
|
||||
n->strength = $1;
|
||||
n->noWait = $3;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| FOR SHARE locked_rels_list opt_nowait
|
||||
;
|
||||
|
||||
for_locking_strength:
|
||||
FOR UPDATE { $$ = LCS_FORUPDATE; }
|
||||
| FOR NO KEY UPDATE
|
||||
{
|
||||
LockingClause *n = makeNode(LockingClause);
|
||||
n->lockedRels = $3;
|
||||
n->forUpdate = FALSE;
|
||||
n->noWait = $4;
|
||||
$$ = (Node *) n;
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR NO KEY UPDATE is not yet supported.")));
|
||||
#else
|
||||
$$ = LCS_FORNOKEYUPDATE;
|
||||
#endif
|
||||
}
|
||||
| FOR SHARE { $$ = LCS_FORSHARE; }
|
||||
| FOR KEY SHARE
|
||||
{
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR KEY SHARE is not yet supported.")));
|
||||
#else
|
||||
$$ = LCS_FORKEYSHARE;
|
||||
#endif
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "access/relscan.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/pg_namespace.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
|
@ -9457,7 +9458,8 @@ static void ReceivePageAndTuple(Oid relid, TupleTableSlot* slot, VacuumStmt* stm
|
|||
rel = relation_open(relid, ShareUpdateExclusiveLock);
|
||||
classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
|
||||
vac_update_relstats(rel, classRel, relpages, reltuples, relallvisible, hasindex, BootstrapTransactionId);
|
||||
vac_update_relstats(rel, classRel, relpages, reltuples, relallvisible,
|
||||
hasindex, BootstrapTransactionId, InvalidMultiXactId);
|
||||
|
||||
/* Save the flag identify is there dirty data in the relation. */
|
||||
if (stmt != NULL) {
|
||||
|
@ -9871,7 +9873,9 @@ static void ReceivePartitionPageAndTuple(Oid relid, TupleTableSlot* slot)
|
|||
}
|
||||
partrel = partitionOpen(rel, partitionid, NoLock);
|
||||
|
||||
vac_update_partstats(partrel, (BlockNumber)relpages, reltuples, relallvisible, BootstrapTransactionId);
|
||||
vac_update_partstats(partrel, (BlockNumber)relpages, reltuples, relallvisible,
|
||||
BootstrapTransactionId, RelationIsColStore(rel) ? InvalidMultiXactId : FirstMultiXactId);
|
||||
|
||||
/*
|
||||
* we does not fetch dead tuples info from remote DN/CN, just set deadtuples to 0. it does
|
||||
* not matter because we should fetch all deadtuples info from all datanodes to calculate a
|
||||
|
|
|
@ -285,7 +285,7 @@ static Datum RI_FKey_check(PG_FUNCTION_ARGS)
|
|||
* Get the relation descriptors of the FK and PK tables.
|
||||
*
|
||||
* pk_rel is opened in RowShareLock mode since that's what our eventual
|
||||
* SELECT FOR SHARE will get on it.
|
||||
* SELECT FOR KEY SHARE will get on it.
|
||||
*/
|
||||
fk_rel = trigdata->tg_relation;
|
||||
pk_rel = heap_open(riinfo.pk_relid, RowShareLock);
|
||||
|
@ -320,8 +320,13 @@ static Datum RI_FKey_check(PG_FUNCTION_ARGS)
|
|||
* ----------
|
||||
*/
|
||||
quoteRelationName(pkrelname, pk_rel);
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
rc = snprintf_s(
|
||||
querystr, sizeof(querystr), sizeof(querystr) - 1, "SELECT 1 FROM ONLY %s x FOR SHARE OF x", pkrelname);
|
||||
#else
|
||||
rc = snprintf_s(querystr, sizeof(querystr), sizeof(querystr) - 1,
|
||||
"SELECT 1 FROM ONLY %s x FOR KEY SHARE OF x", pkrelname);
|
||||
#endif
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
|
||||
/* Prepare and save the plan */
|
||||
|
@ -436,7 +441,8 @@ static Datum RI_FKey_check(PG_FUNCTION_ARGS)
|
|||
|
||||
/* ----------
|
||||
* The query string built is
|
||||
* SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
|
||||
* SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
|
||||
* FOR KEY SHARE OF x
|
||||
* The type id's for the $ parameters are those of the
|
||||
* corresponding FK attributes.
|
||||
* ----------
|
||||
|
@ -456,7 +462,11 @@ static Datum RI_FKey_check(PG_FUNCTION_ARGS)
|
|||
querysep = "AND";
|
||||
queryoids[i] = fk_type;
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
appendStringInfo(&querybuf, " FOR SHARE OF x");
|
||||
#else
|
||||
appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
|
||||
#endif
|
||||
|
||||
/* Prepare and save the plan */
|
||||
qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids, &qkey, fk_rel, pk_rel, true);
|
||||
|
@ -581,7 +591,8 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, HeapTuple old_ro
|
|||
|
||||
/* ----------
|
||||
* The query string built is
|
||||
* SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
|
||||
* SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
|
||||
* FOR KEY SHARE OF x
|
||||
* The type id's for the $ parameters are those of the
|
||||
* PK attributes themselves.
|
||||
* ----------
|
||||
|
@ -601,7 +612,12 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, HeapTuple old_ro
|
|||
querysep = "AND";
|
||||
queryoids[i] = pk_type;
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
appendStringInfo(&querybuf, " FOR SHARE OF x");
|
||||
#else
|
||||
appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
|
||||
#endif
|
||||
|
||||
|
||||
/* Prepare and save the plan */
|
||||
qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, &qkey, fk_rel, pk_rel, true);
|
||||
|
@ -664,7 +680,7 @@ Datum RI_FKey_noaction(PG_FUNCTION_ARGS)
|
|||
* (the new and old tuple for update)
|
||||
*
|
||||
* fk_rel is opened in RowShareLock mode since that's what our eventual
|
||||
* SELECT FOR SHARE will get on it.
|
||||
* SELECT FOR KEY SHARE will get on it.
|
||||
*/
|
||||
fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
|
||||
pk_rel = trigdata->tg_relation;
|
||||
|
@ -749,7 +765,8 @@ Datum RI_FKey_noaction(PG_FUNCTION_ARGS)
|
|||
|
||||
/* ----------
|
||||
* The query string built is
|
||||
* SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
|
||||
* SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
|
||||
* FOR KEY SHARE OF x
|
||||
* The type id's for the $ parameters are those of the
|
||||
* corresponding PK attributes.
|
||||
* ----------
|
||||
|
@ -769,7 +786,11 @@ Datum RI_FKey_noaction(PG_FUNCTION_ARGS)
|
|||
querysep = "AND";
|
||||
queryoids[i] = pk_type;
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
appendStringInfo(&querybuf, " FOR SHARE OF x");
|
||||
#else
|
||||
appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
|
||||
#endif
|
||||
|
||||
/* Prepare and save the plan */
|
||||
qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids, &qkey, fk_rel, pk_rel, true);
|
||||
|
@ -929,7 +950,7 @@ Datum RI_FKey_cascade_del(PG_FUNCTION_ARGS)
|
|||
|
||||
/* ----------
|
||||
* The query string built is
|
||||
* DELETE FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
|
||||
* DELETE FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
|
||||
* The type id's for the $ parameters are those of the
|
||||
* corresponding PK attributes.
|
||||
* ----------
|
||||
|
@ -1277,7 +1298,8 @@ Datum RI_FKey_restrict(PG_FUNCTION_ARGS)
|
|||
|
||||
/* ----------
|
||||
* The query string built is
|
||||
* SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
|
||||
* SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
|
||||
* FOR KEY SHARE OF x
|
||||
* The type id's for the $ parameters are those of the
|
||||
* corresponding PK attributes.
|
||||
* ----------
|
||||
|
@ -1297,7 +1319,11 @@ Datum RI_FKey_restrict(PG_FUNCTION_ARGS)
|
|||
querysep = "AND";
|
||||
queryoids[i] = pk_type;
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
appendStringInfo(&querybuf, " FOR SHARE OF x");
|
||||
#else
|
||||
appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
|
||||
#endif
|
||||
|
||||
/* Prepare and save the plan */
|
||||
qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids, &qkey, fk_rel, pk_rel, true);
|
||||
|
|
|
@ -5425,7 +5425,7 @@ static void get_select_query_def(Query* query, deparse_context* context, TupleDe
|
|||
get_rule_expr(query->limitCount, context, false);
|
||||
}
|
||||
|
||||
/* Add FOR UPDATE/SHARE clauses if present */
|
||||
/* Add FOR [KEY] UPDATE/SHARE clauses if present */
|
||||
if (query->hasForUpdate) {
|
||||
foreach (l, query->rowMarks) {
|
||||
RowMarkClause* rc = (RowMarkClause*)lfirst(l);
|
||||
|
@ -5435,10 +5435,30 @@ static void get_select_query_def(Query* query, deparse_context* context, TupleDe
|
|||
if (rc->pushedDown)
|
||||
continue;
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
switch (rc->strength) {
|
||||
case LCS_FORKEYSHARE:
|
||||
appendContextKeyword(context, " FOR KEY SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||
break;
|
||||
case LCS_FORSHARE:
|
||||
appendContextKeyword(context, " FOR SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||
break;
|
||||
case LCS_FORNOKEYUPDATE:
|
||||
appendContextKeyword(context, " FOR NO KEY UPDATE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||
break;
|
||||
case LCS_FORUPDATE:
|
||||
appendContextKeyword(context, " FOR UPDATE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||
break;
|
||||
default:
|
||||
ereport(ERROR, (errmsg("unknown lock type: %d", rc->strength)));
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (rc->forUpdate)
|
||||
appendContextKeyword(context, " FOR UPDATE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||
else
|
||||
appendContextKeyword(context, " FOR SHARE", -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
|
||||
#endif
|
||||
appendStringInfo(buf, " OF %s", quote_identifier(rte->eref->aliasname));
|
||||
if (rc->noWait)
|
||||
appendStringInfo(buf, " NOWAIT");
|
||||
|
|
|
@ -1448,7 +1448,7 @@ static void PartitionReloadIndexInfo(Partition part)
|
|||
* Output :
|
||||
* Notes :
|
||||
*/
|
||||
void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId freezeXid)
|
||||
void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId freezeXid, MultiXactId freezeMultiXid)
|
||||
{
|
||||
Oid newrelfilenode;
|
||||
RelFileNodeBackend newrnode;
|
||||
|
@ -1557,6 +1557,11 @@ void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId f
|
|||
replaces[Anum_pg_partition_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = TransactionIdGetDatum(freezeXid);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
replaces[Anum_pg_partition_relminmxid - 1] = true;
|
||||
values[Anum_pg_partition_relminmxid - 1] = TransactionIdGetDatum(freezeMultiXid);
|
||||
#endif
|
||||
|
||||
ntup = heap_modify_tuple(tuple, RelationGetDescr(pg_partition), values, nulls, replaces);
|
||||
|
||||
simple_heap_update(pg_partition, &ntup->t_self, ntup);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "access/xlog.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/catversion.h"
|
||||
|
@ -3444,6 +3445,7 @@ static void RelationDestroyRelation(Relation relation, bool remember_tupdesc)
|
|||
}
|
||||
list_free_ext(relation->rd_indexlist);
|
||||
bms_free_ext(relation->rd_indexattr);
|
||||
bms_free_ext(relation->rd_keyattr);
|
||||
bms_free_ext(relation->rd_idattr);
|
||||
FreeTriggerDesc(relation->trigdesc);
|
||||
if (relation->rd_rlsdesc) {
|
||||
|
@ -4452,7 +4454,7 @@ extern void heap_create_init_fork(Relation rel);
|
|||
void DeltaTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partition)
|
||||
{
|
||||
Relation deltaRel = heap_open(relid, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(deltaRel, freezeXid);
|
||||
RelationSetNewRelfilenode(deltaRel, freezeXid, InvalidMultiXactId);
|
||||
// skip partition because one partition CANNOT be unlogged.
|
||||
if (!partition && RELPERSISTENCE_UNLOGGED == deltaRel->rd_rel->relpersistence) {
|
||||
heap_create_init_fork(deltaRel);
|
||||
|
@ -4467,7 +4469,7 @@ void DescTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partiti
|
|||
// Because indexRelation has locked as AccessExclusiveLock, so it is safe
|
||||
//
|
||||
Relation cudescRel = heap_open(relid, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(cudescRel, freezeXid);
|
||||
RelationSetNewRelfilenode(cudescRel, freezeXid, InvalidMultiXactId);
|
||||
|
||||
// skip partition because one partition CANNOT be unlogged.
|
||||
if (!partition && RELPERSISTENCE_UNLOGGED == cudescRel->rd_rel->relpersistence) {
|
||||
|
@ -4480,7 +4482,7 @@ void DescTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partiti
|
|||
foreach (indlist, RelationGetIndexList(cudescRel)) {
|
||||
Oid indexId = lfirst_oid(indlist);
|
||||
Relation currentIndex = index_open(indexId, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(currentIndex, InvalidTransactionId);
|
||||
RelationSetNewRelfilenode(currentIndex, InvalidTransactionId, InvalidMultiXactId);
|
||||
// keep the same logic with row index relation, and
|
||||
// skip checking RELPERSISTENCE_UNLOGGED persistence
|
||||
|
||||
|
@ -4512,7 +4514,7 @@ void DescTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partiti
|
|||
* must be passed for indexes and sequences). This should be a lower bound on
|
||||
* the XIDs that will be put into the new relation contents.
|
||||
*/
|
||||
void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool isDfsTruncate)
|
||||
void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, MultiXactId minmulti, bool isDfsTruncate)
|
||||
{
|
||||
Oid newrelfilenode;
|
||||
RelFileNodeBackend newrnode;
|
||||
|
@ -4649,6 +4651,11 @@ void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool
|
|||
replaces[Anum_pg_class_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(freezeXid);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
replaces[Anum_pg_class_relminmxid - 1] = true;
|
||||
values[Anum_pg_class_relminmxid - 1] = TransactionIdGetDatum(minmulti);
|
||||
#endif
|
||||
|
||||
nctup = heap_modify_tuple(tuple, RelationGetDescr(pg_class), values, nulls, replaces);
|
||||
|
||||
simple_heap_update(pg_class, &nctup->t_self, nctup);
|
||||
|
@ -6147,6 +6154,7 @@ static void ClusterConstraintFetch(__inout Relation relation)
|
|||
Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
|
||||
{
|
||||
Bitmapset* indexattrs = NULL;
|
||||
Bitmapset* uindexattrs = NULL;
|
||||
List* indexoidlist = NULL;
|
||||
ListCell* l = NULL;
|
||||
Bitmapset* idindexattrs = NULL; /* columns in the the replica identity */
|
||||
|
@ -6160,7 +6168,7 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
|
|||
case INDEX_ATTR_BITMAP_ALL:
|
||||
return bms_copy(relation->rd_indexattr);
|
||||
case INDEX_ATTR_BITMAP_KEY:
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unknown attrKind %u", attrKind)));
|
||||
return bms_copy(relation->rd_keyattr);
|
||||
case INDEX_ATTR_BITMAP_IDENTITY_KEY:
|
||||
return bms_copy(relation->rd_idattr);
|
||||
default:
|
||||
|
@ -6201,12 +6209,14 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
|
|||
* won't be returned at all by RelationGetIndexList.
|
||||
*/
|
||||
indexattrs = NULL;
|
||||
uindexattrs = NULL;
|
||||
idindexattrs = NULL;
|
||||
foreach (l, indexoidlist) {
|
||||
Oid indexOid = lfirst_oid(l);
|
||||
Relation indexDesc;
|
||||
IndexInfo* indexInfo = NULL;
|
||||
int i;
|
||||
bool isKey = false; /* candidate key */
|
||||
bool isIDKey = false; /* replica identity index */
|
||||
|
||||
indexDesc = index_open(indexOid, AccessShareLock);
|
||||
|
@ -6214,6 +6224,9 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
|
|||
/* Extract index key information from the index's pg_index row */
|
||||
indexInfo = BuildIndexInfo(indexDesc);
|
||||
|
||||
/* Can this index be referenced by a foreign key? */
|
||||
isKey = indexInfo->ii_Unique && indexInfo->ii_Expressions == NIL && indexInfo->ii_Predicate == NIL;
|
||||
|
||||
/* Is this index the configured (or default) replica identity? */
|
||||
isIDKey = (indexOid == relreplindex);
|
||||
|
||||
|
@ -6231,6 +6244,9 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
|
|||
*/
|
||||
if (attrnum != 0) {
|
||||
indexattrs = bms_add_member(indexattrs, attrnum - FirstLowInvalidHeapAttributeNumber);
|
||||
if (isKey && i < indexInfo->ii_NumIndexKeyAttrs) {
|
||||
uindexattrs = bms_add_member(uindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber);
|
||||
}
|
||||
if (isIDKey && i < indexInfo->ii_NumIndexKeyAttrs)
|
||||
idindexattrs = bms_add_member(idindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber);
|
||||
}
|
||||
|
@ -6255,6 +6271,7 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
|
|||
* empty.
|
||||
*/
|
||||
oldcxt = MemoryContextSwitchTo(u_sess->cache_mem_cxt);
|
||||
relation->rd_keyattr = bms_copy(uindexattrs);
|
||||
relation->rd_idattr = bms_copy(idindexattrs);
|
||||
relation->rd_indexattr = bms_copy(indexattrs);
|
||||
(void)MemoryContextSwitchTo(oldcxt);
|
||||
|
@ -6264,7 +6281,7 @@ Bitmapset* RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind att
|
|||
case INDEX_ATTR_BITMAP_ALL:
|
||||
return indexattrs;
|
||||
case INDEX_ATTR_BITMAP_KEY:
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unknown attrKind %u", attrKind)));
|
||||
return uindexattrs;
|
||||
case INDEX_ATTR_BITMAP_IDENTITY_KEY:
|
||||
return idindexattrs;
|
||||
default:
|
||||
|
@ -6822,6 +6839,7 @@ static bool load_relcache_init_file(bool shared)
|
|||
rel->rd_indexlist = NIL;
|
||||
rel->rd_oidindex = InvalidOid;
|
||||
rel->rd_indexattr = NULL;
|
||||
rel->rd_keyattr = NULL;
|
||||
rel->rd_idattr = NULL;
|
||||
rel->rd_createSubid = InvalidSubTransactionId;
|
||||
rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
|
||||
|
|
|
@ -59,7 +59,7 @@ bool open_join_children = true;
|
|||
bool will_shutdown = false;
|
||||
|
||||
/* hard-wired binary version number */
|
||||
const uint32 GRAND_VERSION_NUM = 92422;
|
||||
const uint32 GRAND_VERSION_NUM = 92423;
|
||||
|
||||
const uint32 HINT_ENHANCEMENT_VERSION_NUM = 92359;
|
||||
const uint32 MATVIEW_VERSION_NUM = 92213;
|
||||
|
@ -97,6 +97,8 @@ const uint32 V5R2C00_BACKEND_VERSION_NUM = 92412;
|
|||
const uint32 ANALYZER_HOOK_VERSION_NUM = 92420;
|
||||
const uint32 SUPPORT_HASH_XLOG_VERSION_NUM = 92420;
|
||||
|
||||
const uint32 ENHANCED_TUPLE_LOCK_VERSION_NUM = 92423;
|
||||
|
||||
/* This variable indicates wheather the instance is in progress of upgrade as a whole */
|
||||
uint32 volatile WorkingGrandVersionNum = GRAND_VERSION_NUM;
|
||||
|
||||
|
|
|
@ -115,9 +115,8 @@ CommandId HeapTupleGetCmax(HeapTuple tup)
|
|||
HeapTupleHeader htup = tup->t_data;
|
||||
CommandId cid = HeapTupleHeaderGetRawCommandId(htup);
|
||||
|
||||
/* We do not store cmax when locking a tuple */
|
||||
Assert(!(htup->t_infomask & (HEAP_IS_LOCKED)));
|
||||
Assert(TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(tup)));
|
||||
Assert(!(htup->t_infomask & HEAP_MOVED));
|
||||
Assert(TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXid(tup)));
|
||||
|
||||
if (htup->t_infomask & HEAP_COMBOCID)
|
||||
return GetRealCmax(cid);
|
||||
|
@ -129,9 +128,8 @@ CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup, Page page)
|
|||
{
|
||||
CommandId cid = HeapTupleHeaderGetRawCommandId(tup);
|
||||
|
||||
/* We do not store cmax when locking a tuple */
|
||||
Assert(!(tup->t_infomask & (HEAP_MOVED | HEAP_IS_LOCKED)));
|
||||
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tup)));
|
||||
Assert(!(tup->t_infomask & HEAP_MOVED));
|
||||
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(page, tup)));
|
||||
|
||||
if (tup->t_infomask & HEAP_COMBOCID)
|
||||
return GetRealCmax(cid);
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
#include "utils/timestamp.h"
|
||||
#include "tcop/utility.h"
|
||||
#include "tcop/dest.h"
|
||||
#include "access/multixact.h"
|
||||
#ifdef PGXC
|
||||
#include "pgxc/pgxc.h"
|
||||
#endif
|
||||
|
@ -2884,7 +2885,7 @@ retry:
|
|||
.* pre-image but not the post-image. We also get sane
|
||||
.* results if the concurrent transaction never commits.
|
||||
*/
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(targpage, targtuple.t_data)))
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXid(&targtuple)))
|
||||
deadrows += 1;
|
||||
else {
|
||||
sample_it = true;
|
||||
|
@ -6775,7 +6776,8 @@ static void update_pages_and_tuples_pgclass(Relation onerel, VacuumStmt* vacstmt
|
|||
}
|
||||
|
||||
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
vac_update_relstats(onerel, classRel, updrelpages, totalrows, mapCont, hasindex, InvalidTransactionId);
|
||||
vac_update_relstats(onerel, classRel, updrelpages, totalrows, mapCont,
|
||||
hasindex, InvalidTransactionId, InvalidMultiXactId);
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
|
@ -6815,7 +6817,8 @@ static void update_pages_and_tuples_pgclass(Relation onerel, VacuumStmt* vacstmt
|
|||
(0 != vacstmt->pstGlobalStatEx[vacstmt->tableidx].totalRowCnts)) {
|
||||
nblocks = estimate_index_blocks(Irel[ind], totalindexrows, table_factor);
|
||||
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
vac_update_relstats(Irel[ind], classRel, nblocks, totalindexrows, 0, false, BootstrapTransactionId);
|
||||
vac_update_relstats(Irel[ind], classRel, nblocks, totalindexrows, 0,
|
||||
false, BootstrapTransactionId, InvalidMultiXactId);
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
continue;
|
||||
}
|
||||
|
@ -6827,7 +6830,8 @@ static void update_pages_and_tuples_pgclass(Relation onerel, VacuumStmt* vacstmt
|
|||
nblocks = GetOneRelNBlocks(onerel, Irel[ind], vacstmt, totalindexrows);
|
||||
|
||||
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
vac_update_relstats(Irel[ind], classRel, nblocks, totalindexrows, 0, false, InvalidTransactionId);
|
||||
vac_update_relstats(Irel[ind], classRel, nblocks, totalindexrows, 0,
|
||||
false, InvalidTransactionId, InvalidMultiXactId);
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "access/xact.h"
|
||||
#include "access/xlog.h"
|
||||
#include "access/sysattr.h"
|
||||
#include "access/reloptions.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/heap.h"
|
||||
|
@ -75,6 +76,7 @@
|
|||
#include "gstrace/gstrace_infra.h"
|
||||
#include "gstrace/commands_gstrace.h"
|
||||
#include "parser/parse_utilcmd.h"
|
||||
#include "access/multixact.h"
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
#include "tsdb/storage/part_merge.h"
|
||||
#include "tsdb/utils/ts_relcache.h"
|
||||
|
@ -125,7 +127,8 @@ extern DfsSrvOptions* GetDfsSrvOptions(Oid spcNode);
|
|||
static void swap_relation_names(Oid r1, Oid r2);
|
||||
|
||||
static void swapCascadeHeapTables(
|
||||
Oid relId1, Oid relId2, Oid tempTableOid, bool swapByContent, TransactionId frozenXid, Oid* mappedTables);
|
||||
Oid relId1, Oid relId2, Oid tempTableOid, bool swapByContent, TransactionId frozenXid,
|
||||
MultiXactId multiXid, Oid* mappedTables);
|
||||
|
||||
static void SwapCStoreTables(Oid relId1, Oid relId2, Oid parentOid, Oid tempTableOid);
|
||||
|
||||
|
@ -139,7 +142,7 @@ static void rebuildPartition(Relation partTableRel, Oid partitionOid, Oid indexO
|
|||
|
||||
static void copyPartitionHeapData(Relation newHeap, Relation oldHeap, Oid indexOid, PlannerInfo* root,
|
||||
RelOptInfo* relOptInfo, int freezeMinAge, int freezeTableAge, bool verbose, bool* pSwapToastByContent,
|
||||
TransactionId* pFreezeXid, AdaptMem* mem_info, double* ptrDeleteTupleNum = NULL);
|
||||
TransactionId* pFreezeXid, MultiXactId* pFreezeMulti, AdaptMem* mem_info, double* ptrDeleteTupleNum = NULL);
|
||||
static void CopyCStoreData(Relation oldRel, Relation newRel, int freeze_min_age, int freeze_table_age, bool verbose,
|
||||
bool* pSwapToastByContent, TransactionId* pFreezeXid, AdaptMem* mem_info);
|
||||
static void DoCopyPaxFormatData(Relation oldRel, Relation newRel);
|
||||
|
@ -148,7 +151,8 @@ static void DoCopyCUFormatData(Relation oldRel, Relation newRel, TupleDesc oldTu
|
|||
static List* FindMergedDescs(Relation oldRel, Relation newRel);
|
||||
extern ValuePartitionMap* buildValuePartitionMap(Relation relation, Relation pg_partition, HeapTuple partitioned_tuple);
|
||||
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int freeze_min_age, int freeze_table_age,
|
||||
bool verbose, bool* pSwapToastByContent, TransactionId* pFreezeXid, double* ptrDeleteTupleNum, AdaptMem* mem_info);
|
||||
bool verbose, bool* pSwapToastByContent, TransactionId* pFreezeXid, MultiXactId *pFreezeMulti,
|
||||
double* ptrDeleteTupleNum, AdaptMem* mem_info);
|
||||
static List* get_tables_to_cluster(MemoryContext cluster_context);
|
||||
static void reform_and_rewrite_tuple(HeapTuple tuple, TupleDesc oldTupDesc, TupleDesc newTupDesc, Datum* values,
|
||||
bool* isnull, bool newRelHasOids, RewriteState rwstate);
|
||||
|
@ -163,8 +167,8 @@ static void RebuildCStoreRelation(
|
|||
extern void Start_Prefetch(TableScanDesc scan, SeqScanAccessor* pAccessor, ScanDirection dir);
|
||||
extern void SeqScan_Init(TableScanDesc Scan, SeqScanAccessor* pAccessor, Relation relation);
|
||||
|
||||
static void swap_partition_relfilenode(
|
||||
Oid partitionOid1, Oid partitionOid2, bool swapToastByContent, TransactionId frozenXid, Oid* mappedTables);
|
||||
static void swap_partition_relfilenode( Oid partitionOid1, Oid partitionOid2, bool swapToastByContent,
|
||||
TransactionId frozenXid, MultiXactId multiXid, Oid* mappedTables);
|
||||
static void partition_relfilenode_swap(Oid OIDOldHeap, Oid OIDNewHeap, uint8 needSwitch);
|
||||
static void relfilenode_swap(Oid OIDOldHeap, Oid OIDNewHeap, uint8 needSwitch);
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
|
@ -775,6 +779,7 @@ static void rebuild_relation(
|
|||
TransactionId frozenXid = InvalidTransactionId;
|
||||
double deleteTupleNum = 0;
|
||||
bool is_shared = OldHeap->rd_rel->relisshared;
|
||||
MultiXactId multiXid;
|
||||
|
||||
/* Mark the correct index as clustered */
|
||||
if (OidIsValid(indexOid))
|
||||
|
@ -798,6 +803,7 @@ static void rebuild_relation(
|
|||
verbose,
|
||||
&swap_toast_by_content,
|
||||
&frozenXid,
|
||||
&multiXid,
|
||||
&deleteTupleNum,
|
||||
memUsage);
|
||||
|
||||
|
@ -814,7 +820,8 @@ static void rebuild_relation(
|
|||
* Swap the physical files of the target and transient tables, then
|
||||
* rebuild the target's indexes and throw away the transient table.
|
||||
*/
|
||||
finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog, swap_toast_by_content, false, frozenXid, memUsage);
|
||||
finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog, swap_toast_by_content,
|
||||
false, frozenXid, multiXid, memUsage);
|
||||
|
||||
/* report vacuum full stat to PgStatCollector */
|
||||
pgstat_report_vacuum(tableOid, InvalidOid, is_shared, deleteTupleNum);
|
||||
|
@ -822,10 +829,8 @@ static void rebuild_relation(
|
|||
clearAttrInitDefVal(tableOid);
|
||||
}
|
||||
|
||||
TransactionId getPartitionRelfrozenxid(Relation ordTableRel)
|
||||
void getPartitionRelxids(Relation ordTableRel, TransactionId* frozenXid, MultiXactId* multiXid)
|
||||
{
|
||||
bool relfrozenxid_isNull = true;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
Relation rel = heap_open(PartitionRelationId, AccessShareLock);
|
||||
HeapTuple tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(RelationGetRelid(ordTableRel)));
|
||||
if (!HeapTupleIsValid(tuple)) {
|
||||
|
@ -833,28 +838,35 @@ TransactionId getPartitionRelfrozenxid(Relation ordTableRel)
|
|||
(errcode(ERRCODE_UNDEFINED_TABLE),
|
||||
errmsg("cache lookup failed for relation %u", RelationGetRelid(ordTableRel))));
|
||||
}
|
||||
bool isNull = true;
|
||||
Datum xid64datum =
|
||||
tableam_tops_tuple_getattr(tuple, Anum_pg_partition_relfrozenxid64, RelationGetDescr(rel), &relfrozenxid_isNull);
|
||||
heap_close(rel, AccessShareLock);
|
||||
heap_freetuple(tuple);
|
||||
tableam_tops_tuple_getattr(tuple, Anum_pg_partition_relfrozenxid64, RelationGetDescr(rel), &isNull);
|
||||
|
||||
if (relfrozenxid_isNull) {
|
||||
relfrozenxid = ordTableRel->rd_rel->relfrozenxid;
|
||||
if (isNull) {
|
||||
*frozenXid = ordTableRel->rd_rel->relfrozenxid;
|
||||
|
||||
if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, relfrozenxid) ||
|
||||
!TransactionIdIsNormal(relfrozenxid))
|
||||
relfrozenxid = FirstNormalTransactionId;
|
||||
if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, *frozenXid) ||
|
||||
!TransactionIdIsNormal(*frozenXid))
|
||||
*frozenXid = FirstNormalTransactionId;
|
||||
} else {
|
||||
relfrozenxid = DatumGetTransactionId(xid64datum);
|
||||
*frozenXid = DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
|
||||
return relfrozenxid;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (multiXid != NULL) {
|
||||
xid64datum =
|
||||
tableam_tops_tuple_getattr(tuple, Anum_pg_partition_relminmxid, RelationGetDescr(rel), &isNull);
|
||||
*multiXid = isNull ? InvalidMultiXactId : DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
#endif
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
|
||||
TransactionId getRelationRelfrozenxid(Relation ordTableRel)
|
||||
void getRelationRelxids(Relation ordTableRel, TransactionId* frozenXid, MultiXactId* multiXid)
|
||||
{
|
||||
bool relfrozenxid_isNull = true;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
bool isNull = true;
|
||||
Relation rel = heap_open(RelationRelationId, AccessShareLock);
|
||||
HeapTuple tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(ordTableRel)));
|
||||
if (!HeapTupleIsValid(tuple)) {
|
||||
|
@ -862,21 +874,27 @@ TransactionId getRelationRelfrozenxid(Relation ordTableRel)
|
|||
(errcode(ERRCODE_UNDEFINED_TABLE),
|
||||
errmsg("cache lookup failed for relation %u", RelationGetRelid(ordTableRel))));
|
||||
}
|
||||
Datum xid64datum = tableam_tops_tuple_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &relfrozenxid_isNull);
|
||||
heap_close(rel, AccessShareLock);
|
||||
heap_freetuple(tuple);
|
||||
Datum xid64datum = tableam_tops_tuple_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &isNull);
|
||||
|
||||
if (relfrozenxid_isNull) {
|
||||
relfrozenxid = ordTableRel->rd_rel->relfrozenxid;
|
||||
if (isNull) {
|
||||
*frozenXid = ordTableRel->rd_rel->relfrozenxid;
|
||||
|
||||
if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, relfrozenxid) ||
|
||||
!TransactionIdIsNormal(relfrozenxid))
|
||||
relfrozenxid = FirstNormalTransactionId;
|
||||
if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, *frozenXid) ||
|
||||
!TransactionIdIsNormal(*frozenXid))
|
||||
*frozenXid = FirstNormalTransactionId;
|
||||
} else {
|
||||
relfrozenxid = DatumGetTransactionId(xid64datum);
|
||||
*frozenXid = DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
|
||||
return relfrozenxid;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (multiXid != NULL) {
|
||||
xid64datum = tableam_tops_tuple_getattr(tuple, Anum_pg_class_relminmxid, RelationGetDescr(rel), &isNull);
|
||||
*multiXid = isNull ? InvalidMultiXactId : DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
#endif
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -895,6 +913,7 @@ static void rebuildPartitionedTable(
|
|||
Oid OIDNewHeap = InvalidOid;
|
||||
bool swapToastByContent = false;
|
||||
TransactionId* frozenXid = NULL;
|
||||
MultiXactId* multiXid = NULL;
|
||||
|
||||
TupleDesc partTabHeapDesc;
|
||||
HeapTuple tuple = NULL;
|
||||
|
@ -922,6 +941,7 @@ static void rebuildPartitionedTable(
|
|||
int OIDNewHeapArrayLen = 0;
|
||||
int pos = 0;
|
||||
int loc = 0;
|
||||
int temp = 0;
|
||||
|
||||
/* Mark the correct index as clustered */
|
||||
if (OidIsValid(indexOid)) {
|
||||
|
@ -976,6 +996,7 @@ static void rebuildPartitionedTable(
|
|||
OIDNewHeapArrayLen = list_length(partitions);
|
||||
OIDNewHeapArray = (Oid*)palloc(sizeof(Oid) * OIDNewHeapArrayLen);
|
||||
frozenXid = (TransactionId*)palloc(sizeof(TransactionId) * OIDNewHeapArrayLen);
|
||||
multiXid = (MultiXactId*)palloc(sizeof(MultiXactId) * OIDNewHeapArrayLen);
|
||||
foreach (cell, partitions) {
|
||||
partition = (Partition)lfirst(cell);
|
||||
partRel = partitionGetRelation(partTableRel, partition);
|
||||
|
@ -1022,7 +1043,8 @@ static void rebuildPartitionedTable(
|
|||
&swapToastByContent,
|
||||
&frozenXid[loc++],
|
||||
memUsage);
|
||||
else
|
||||
else {
|
||||
temp = loc++;
|
||||
copyPartitionHeapData(newHeap,
|
||||
partRel,
|
||||
indexOid,
|
||||
|
@ -1032,9 +1054,11 @@ static void rebuildPartitionedTable(
|
|||
freezeTableAge,
|
||||
verbose,
|
||||
&swapToastByContent,
|
||||
&frozenXid[loc++],
|
||||
&frozenXid[temp],
|
||||
&multiXid[temp],
|
||||
memUsage,
|
||||
&deleteTuplesNum);
|
||||
}
|
||||
|
||||
heap_close(newHeap, NoLock);
|
||||
releaseDummyRelation(&partRel);
|
||||
|
@ -1060,8 +1084,10 @@ static void rebuildPartitionedTable(
|
|||
partRel = partitionGetRelation(partTableRel, partition);
|
||||
OIDNewHeap = OIDNewHeapArray[pos++];
|
||||
|
||||
temp = loc++;
|
||||
/* swap the temp table and partition */
|
||||
finishPartitionHeapSwap(partRel->rd_id, OIDNewHeap, swapToastByContent, frozenXid[loc++]);
|
||||
finishPartitionHeapSwap(partRel->rd_id, OIDNewHeap, swapToastByContent, frozenXid[temp],
|
||||
isCStore ? InvalidMultiXactId : multiXid[temp]);
|
||||
|
||||
/* release this partition relation. */
|
||||
releaseDummyRelation(&partRel);
|
||||
|
@ -1106,6 +1132,7 @@ static void rebuildPartition(Relation partTableRel, Oid partitionOid, Oid indexO
|
|||
Oid OIDNewHeap = InvalidOid;
|
||||
bool swapToastByContent = false;
|
||||
TransactionId frozenXid = InvalidTransactionId;
|
||||
MultiXactId multiXid = InvalidMultiXactId;
|
||||
bool isCStore = RelationIsColStore(partTableRel);
|
||||
|
||||
TupleDesc partTabHeapDesc;
|
||||
|
@ -1234,6 +1261,7 @@ static void rebuildPartition(Relation partTableRel, Oid partitionOid, Oid indexO
|
|||
verbose,
|
||||
&swapToastByContent,
|
||||
&frozenXid,
|
||||
&multiXid,
|
||||
memUsage,
|
||||
&deleteTuplesNum);
|
||||
}
|
||||
|
@ -1266,7 +1294,7 @@ static void rebuildPartition(Relation partTableRel, Oid partitionOid, Oid indexO
|
|||
*/
|
||||
TransferPredicateLocksToHeapRelation(partRel);
|
||||
/* swap the temp table and partition */
|
||||
finishPartitionHeapSwap(partRel->rd_id, OIDNewHeap, swapToastByContent, frozenXid);
|
||||
finishPartitionHeapSwap(partRel->rd_id, OIDNewHeap, swapToastByContent, frozenXid, multiXid);
|
||||
/* rebuild index of partition table */
|
||||
reindexFlags = REINDEX_REL_SUPPRESS_INDEX_USE;
|
||||
(void)reindexPartition(RelationGetRelid(partTableRel), partitionOid, reindexFlags, REINDEX_ALL_INDEX);
|
||||
|
@ -2049,7 +2077,7 @@ double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relation New
|
|||
* case we had better copy it.
|
||||
*/
|
||||
if (!is_system_catalog &&
|
||||
!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(page, tuple->t_data)))
|
||||
!TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXid(tuple)))
|
||||
ereport(messageLevel, (errcode(ERRCODE_OBJECT_IN_USE),
|
||||
errmsg("concurrent insert in progress within table \"%s\"",
|
||||
RelationGetRelationName(OldHeap))));
|
||||
|
@ -2180,11 +2208,13 @@ double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relation New
|
|||
* *pFreezeXid receives the TransactionId used as freeze cutoff point.
|
||||
*/
|
||||
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int freeze_min_age, int freeze_table_age,
|
||||
bool verbose, bool* pSwapToastByContent, TransactionId* pFreezeXid, double* ptrDeleteTupleNum, AdaptMem* memUsage)
|
||||
bool verbose, bool* pSwapToastByContent, TransactionId* pFreezeXid, MultiXactId *pFreezeMulti,
|
||||
double* ptrDeleteTupleNum, AdaptMem* memUsage)
|
||||
{
|
||||
Relation NewHeap, OldHeap, OldIndex;
|
||||
TransactionId OldestXmin;
|
||||
TransactionId FreezeXid;
|
||||
MultiXactId MultiXactFrzLimit;
|
||||
bool use_sort = false;
|
||||
double tups_vacuumed = 0;
|
||||
bool isGtt = false;
|
||||
|
@ -2259,7 +2289,8 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int
|
|||
* freeze_min_age to avoid having CLUSTER freeze tuples earlier than a
|
||||
* plain VACUUM would.
|
||||
*/
|
||||
vacuum_set_xid_limits(OldHeap, 0, freeze_table_age, &OldestXmin, &FreezeXid, NULL);
|
||||
|
||||
vacuum_set_xid_limits(OldHeap, 0, freeze_table_age, &OldestXmin, &FreezeXid, NULL, &MultiXactFrzLimit);
|
||||
|
||||
/*
|
||||
* FreezeXid will become the table's new relfrozenxid, and that mustn't go
|
||||
|
@ -2272,6 +2303,7 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int
|
|||
} else {
|
||||
bool isNull = false;
|
||||
TransactionId relfrozenxid;
|
||||
MultiXactId relminmxid;
|
||||
Relation rel = heap_open(RelationRelationId, AccessShareLock);
|
||||
HeapTuple tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
|
||||
if (!HeapTupleIsValid(tuple)) {
|
||||
|
@ -2280,8 +2312,6 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int
|
|||
errmsg("cache lookup failed for relation %u", RelationGetRelid(OldHeap))));
|
||||
}
|
||||
Datum xid64datum = tableam_tops_tuple_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &isNull);
|
||||
heap_close(rel, AccessShareLock);
|
||||
heap_freetuple(tuple);
|
||||
|
||||
if (isNull) {
|
||||
relfrozenxid = OldHeap->rd_rel->relfrozenxid;
|
||||
|
@ -2300,6 +2330,16 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int
|
|||
if (TransactionIdPrecedes(FreezeXid, relfrozenxid)) {
|
||||
FreezeXid = relfrozenxid;
|
||||
}
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum minmxidDatum = tableam_tops_tuple_getattr(tuple, Anum_pg_class_relminmxid, RelationGetDescr(rel), &isNull);
|
||||
relminmxid = isNull ? InvalidMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
|
||||
if (MultiXactIdPrecedes(MultiXactFrzLimit, relminmxid)) {
|
||||
MultiXactFrzLimit = relminmxid;
|
||||
}
|
||||
#endif
|
||||
heap_close(rel, AccessShareLock);
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
} else {
|
||||
/* We will eventually freeze all tuples of ustore tables here.
|
||||
|
@ -2310,6 +2350,7 @@ static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, int
|
|||
|
||||
/* return selected value to caller */
|
||||
*pFreezeXid = FreezeXid;
|
||||
*pFreezeMulti = MultiXactFrzLimit;
|
||||
|
||||
/*
|
||||
* Decide whether to use an indexscan or seqscan-and-optional-sort to scan
|
||||
|
@ -2414,15 +2455,17 @@ static Relation GetPartitionIndexRel(
|
|||
*/
|
||||
static void copyPartitionHeapData(Relation newHeap, Relation oldHeap, Oid indexOid, PlannerInfo* root,
|
||||
RelOptInfo* relOptInfo, int freezeMinAge, int freezeTableAge, bool verbose, bool* pSwapToastByContent,
|
||||
TransactionId* pFreezeXid, AdaptMem* memUsage, double* ptrDeleteTupleNum)
|
||||
TransactionId* pFreezeXid, MultiXactId* pFreezeMulti, AdaptMem* memUsage, double* ptrDeleteTupleNum)
|
||||
{
|
||||
Relation oldIndex = NULL;
|
||||
TransactionId oldestXmin = 0;
|
||||
TransactionId freezeXid = 0;
|
||||
MultiXactId freezeMulti = 0;
|
||||
bool useSort = false;
|
||||
Relation partTabIndexRel = NULL;
|
||||
Partition partIndexRel = NULL;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
MultiXactId relfrozenmxid = InvalidMultiXactId;
|
||||
double tups_vacuumed = 0;
|
||||
*pSwapToastByContent = false;
|
||||
|
||||
|
@ -2448,25 +2491,30 @@ static void copyPartitionHeapData(Relation newHeap, Relation oldHeap, Oid indexO
|
|||
/*
|
||||
* compute xids used to freeze and weed out dead tuples.
|
||||
*/
|
||||
vacuum_set_xid_limits(oldHeap, 0, freezeTableAge, &oldestXmin, &freezeXid, NULL);
|
||||
vacuum_set_xid_limits(oldHeap, 0, freezeTableAge, &oldestXmin, &freezeXid, NULL, &freezeMulti);
|
||||
|
||||
/*
|
||||
* FreezeXid will become the table's new relfrozenxid, and that mustn't go
|
||||
* backwards, so take the max.
|
||||
*/
|
||||
relfrozenxid = getPartitionRelfrozenxid(oldHeap);
|
||||
getPartitionRelxids(oldHeap, &relfrozenxid, &relfrozenmxid);
|
||||
|
||||
if (TransactionIdPrecedes(freezeXid, relfrozenxid))
|
||||
freezeXid = relfrozenxid;
|
||||
|
||||
if (MultiXactIdPrecedes(freezeMulti, relfrozenmxid))
|
||||
freezeMulti = relfrozenmxid;
|
||||
} else {
|
||||
/* We will eventually freeze all tuples of ustore tables here.
|
||||
* Hence freeze xid should be CurrentTransactionId
|
||||
*/
|
||||
freezeXid = GetCurrentTransactionId();
|
||||
freezeMulti = GetOldestMultiXactId();
|
||||
}
|
||||
|
||||
/* return selected value to caller */
|
||||
*pFreezeXid = freezeXid;
|
||||
*pFreezeMulti = freezeMulti;
|
||||
|
||||
/*
|
||||
* Decide whether to use an indexscan or seqscan-and-optional-sort to scan
|
||||
|
@ -2546,7 +2594,8 @@ static void copyPartitionHeapData(Relation newHeap, Relation oldHeap, Oid indexO
|
|||
* having to look the information up again later in finish_heap_swap.
|
||||
*/
|
||||
static void swap_relation_files(
|
||||
Oid r1, Oid r2, bool target_is_pg_class, bool swap_toast_by_content, TransactionId frozenXid, Oid* mapped_tables)
|
||||
Oid r1, Oid r2, bool target_is_pg_class, bool swap_toast_by_content, TransactionId frozenXid,
|
||||
MultiXactId frozenMulti, Oid* mapped_tables)
|
||||
{
|
||||
Relation relRelation;
|
||||
HeapTuple reltup1, reltup2;
|
||||
|
@ -2675,7 +2724,7 @@ static void swap_relation_files(
|
|||
* mapped catalog, because it's possible that we'll commit the map change
|
||||
* and then fail to commit the pg_class update.
|
||||
*
|
||||
* set rel1's frozen Xid
|
||||
* set rel1's frozen Xid and minimum MultiXid
|
||||
*/
|
||||
nctup = NULL;
|
||||
if (relform1->relkind != RELKIND_INDEX && relform1->relkind != RELKIND_GLOBAL_INDEX) {
|
||||
|
@ -2697,6 +2746,11 @@ static void swap_relation_files(
|
|||
replaces[Anum_pg_class_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenXid);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
replaces[Anum_pg_class_relminmxid - 1] = true;
|
||||
values[Anum_pg_class_relminmxid - 1] = TransactionIdGetDatum(frozenMulti);
|
||||
#endif
|
||||
|
||||
nctup = (HeapTuple) tableam_tops_modify_tuple(reltup1, RelationGetDescr(relRelation), values, nulls, replaces);
|
||||
|
||||
relform1 = (Form_pg_class)GETSTRUCT(nctup);
|
||||
|
@ -2761,6 +2815,7 @@ static void swap_relation_files(
|
|||
target_is_pg_class,
|
||||
swap_toast_by_content,
|
||||
frozenXid,
|
||||
frozenMulti,
|
||||
mapped_tables);
|
||||
} else {
|
||||
/* caller messed up */
|
||||
|
@ -2851,6 +2906,7 @@ static void swap_relation_files(
|
|||
target_is_pg_class,
|
||||
swap_toast_by_content,
|
||||
InvalidTransactionId,
|
||||
InvalidMultiXactId,
|
||||
mapped_tables);
|
||||
/* Clean up. */
|
||||
if (nctup)
|
||||
|
@ -2929,7 +2985,8 @@ static void swap_relation_names(Oid r1, Oid r2)
|
|||
* Output : NA
|
||||
*/
|
||||
static void swapPartitionfiles(
|
||||
Oid partitionOid, Oid tempTableOid, bool swapToastByContent, TransactionId frozenXid, Oid* mappedTables)
|
||||
Oid partitionOid, Oid tempTableOid, bool swapToastByContent, TransactionId frozenXid,
|
||||
MultiXactId multiXid, Oid* mappedTables)
|
||||
{
|
||||
Relation relRelation1 = NULL;
|
||||
Relation relRelation2 = NULL;
|
||||
|
@ -3010,6 +3067,11 @@ static void swapPartitionfiles(
|
|||
replaces[Anum_pg_partition_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenXid);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
replaces[Anum_pg_partition_relminmxid - 1] = true;
|
||||
values[Anum_pg_partition_relminmxid - 1] = TransactionIdGetDatum(multiXid);
|
||||
#endif
|
||||
|
||||
ntup = (HeapTuple) tableam_tops_modify_tuple(reltup1, RelationGetDescr(relRelation1), values, nulls, replaces);
|
||||
|
||||
relform1 = (Form_pg_partition)GETSTRUCT(ntup);
|
||||
|
@ -3057,7 +3119,8 @@ static void swapPartitionfiles(
|
|||
* deal with them too.
|
||||
*/
|
||||
swapCascadeHeapTables(
|
||||
relform1->reltoastrelid, relform2->reltoastrelid, tempTableOid, swapToastByContent, frozenXid, mappedTables);
|
||||
relform1->reltoastrelid, relform2->reltoastrelid, tempTableOid, swapToastByContent, frozenXid,
|
||||
multiXid, mappedTables);
|
||||
SwapCStoreTables(relform1->relcudescrelid, relform2->relcudescrelid, InvalidOid, tempTableOid);
|
||||
SwapCStoreTables(relform1->reldeltarelid, relform2->reldeltarelid, InvalidOid, tempTableOid);
|
||||
|
||||
|
@ -3071,6 +3134,7 @@ static void swapPartitionfiles(
|
|||
false,
|
||||
swapToastByContent,
|
||||
InvalidTransactionId,
|
||||
InvalidMultiXactId,
|
||||
mappedTables);
|
||||
|
||||
/* Clean up. */
|
||||
|
@ -3090,13 +3154,14 @@ static void swapPartitionfiles(
|
|||
}
|
||||
|
||||
static void swapCascadeHeapTables(
|
||||
Oid relId1, Oid relId2, Oid tempTableOid, bool swapByContent, TransactionId frozenXid, Oid* mappedTables)
|
||||
Oid relId1, Oid relId2, Oid tempTableOid, bool swapByContent, TransactionId frozenXid,
|
||||
MultiXactId multiXid, Oid* mappedTables)
|
||||
{
|
||||
if (relId1 || relId2) {
|
||||
if (swapByContent) {
|
||||
if (relId1 && relId2) {
|
||||
/* Recursively swap the contents of the toast tables */
|
||||
swap_relation_files(relId1, relId2, false, swapByContent, frozenXid, mappedTables);
|
||||
swap_relation_files(relId1, relId2, false, swapByContent, frozenXid, multiXid, mappedTables);
|
||||
} else {
|
||||
/* caller messed up */
|
||||
ereport(ERROR,
|
||||
|
@ -3219,7 +3284,7 @@ static void SwapCStoreTables(Oid relId1, Oid relId2, Oid parentOid, Oid tempTabl
|
|||
* cleaning up (including rebuilding all indexes on the old heap).
|
||||
*/
|
||||
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content,
|
||||
bool checkConstraints, TransactionId frozenXid, AdaptMem* memInfo)
|
||||
bool checkConstraints, TransactionId frozenXid, MultiXactId frozenMulti, AdaptMem* memInfo)
|
||||
{
|
||||
ObjectAddress object;
|
||||
Oid mapped_tables[4];
|
||||
|
@ -3240,7 +3305,7 @@ void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bo
|
|||
GttSwapRelationFiles(OIDOldHeap, OIDNewHeap);
|
||||
} else {
|
||||
swap_relation_files(OIDOldHeap, OIDNewHeap, (OIDOldHeap == RelationRelationId),
|
||||
swap_toast_by_content, frozenXid, mapped_tables);
|
||||
swap_toast_by_content, frozenXid, frozenMulti, mapped_tables);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3351,7 +3416,8 @@ void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bo
|
|||
* Output : NA
|
||||
*/
|
||||
void finishPartitionHeapSwap(
|
||||
Oid partitionOid, Oid tempTableOid, bool swapToastByContent, TransactionId frozenXid, bool tempTableIsPartition)
|
||||
Oid partitionOid, Oid tempTableOid, bool swapToastByContent, TransactionId frozenXid,
|
||||
MultiXactId multiXid, bool tempTableIsPartition)
|
||||
{
|
||||
Oid mapped_tables[4];
|
||||
int i = 0;
|
||||
|
@ -3367,10 +3433,10 @@ void finishPartitionHeapSwap(
|
|||
*/
|
||||
if (tempTableIsPartition) {
|
||||
/* For redistribution, exchange meta info between two partitions */
|
||||
swap_partition_relfilenode(partitionOid, tempTableOid, swapToastByContent, frozenXid, mapped_tables);
|
||||
swap_partition_relfilenode(partitionOid, tempTableOid, swapToastByContent, frozenXid, multiXid, mapped_tables);
|
||||
} else {
|
||||
/* For alter table exchange, between partition and a normal table */
|
||||
swapPartitionfiles(partitionOid, tempTableOid, swapToastByContent, frozenXid, mapped_tables);
|
||||
swapPartitionfiles(partitionOid, tempTableOid, swapToastByContent, frozenXid, multiXid, mapped_tables);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3782,6 +3848,7 @@ static void rebuildPartVacFull(Relation oldHeap, Oid partOid, int freezeMinAge,
|
|||
Oid OIDNewHeap = InvalidOid;
|
||||
bool swapToastByContent = false;
|
||||
TransactionId frozenXid = InvalidTransactionId;
|
||||
MultiXactId multiXid = InvalidMultiXactId;
|
||||
TupleDesc partTabHeapDesc;
|
||||
HeapTuple tuple = NULL;
|
||||
Datum partTabRelOptions = 0;
|
||||
|
@ -3860,6 +3927,7 @@ static void rebuildPartVacFull(Relation oldHeap, Oid partOid, int freezeMinAge,
|
|||
verbose,
|
||||
&swapToastByContent,
|
||||
&frozenXid,
|
||||
&multiXid,
|
||||
&vacstmt->memUsage,
|
||||
&deleteTupleNum);
|
||||
}
|
||||
|
@ -3872,7 +3940,7 @@ static void rebuildPartVacFull(Relation oldHeap, Oid partOid, int freezeMinAge,
|
|||
* Swap the physical files of the target and transient tables, then
|
||||
* rebuild the target's indexes and throw away the transient table.
|
||||
*/
|
||||
finishPartitionHeapSwap(partRel->rd_id, OIDNewHeap, swapToastByContent, frozenXid);
|
||||
finishPartitionHeapSwap(partRel->rd_id, OIDNewHeap, swapToastByContent, frozenXid, multiXid);
|
||||
|
||||
/* Close relcache entry, but keep lock until transaction commit */
|
||||
releaseDummyRelation(&partRel);
|
||||
|
@ -3906,7 +3974,7 @@ static void rebuildPartVacFull(Relation oldHeap, Oid partOid, int freezeMinAge,
|
|||
*/
|
||||
partTable = try_relation_open(tableOid, AccessShareLock);
|
||||
/* Update reltuples and relpages in pg_class for partitioned table. */
|
||||
vac_update_pgclass_partitioned_table(partTable, partTable->rd_rel->relhasindex, frozenXid);
|
||||
vac_update_pgclass_partitioned_table(partTable, partTable->rd_rel->relhasindex, frozenXid, multiXid);
|
||||
/*
|
||||
* report vacuum full stat to PgStatCollector.
|
||||
* For CStore table, we delete all invisible tuple, so dead tuple should be 0; and
|
||||
|
@ -4000,7 +4068,7 @@ static void CopyCStoreData(Relation oldRel, Relation newRel, int freeze_min_age,
|
|||
* freeze_min_age to avoid having CLUSTER freeze tuples earlier than a
|
||||
* plain VACUUM would.
|
||||
**/
|
||||
vacuum_set_xid_limits(oldRel, freeze_min_age, freeze_table_age, &OldestXmin, &FreezeXid, NULL);
|
||||
vacuum_set_xid_limits(oldRel, freeze_min_age, freeze_table_age, &OldestXmin, &FreezeXid, NULL, NULL);
|
||||
bool isNull = false;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
Relation rel;
|
||||
|
@ -4406,7 +4474,7 @@ static void RebuildCStoreRelation(
|
|||
LockRelationOid(tableOid, AccessExclusiveLock);
|
||||
|
||||
/* swap relation files */
|
||||
finish_heap_swap(tableOid, oidNewHeap, false, swapToastByContent, false, frozenXid, mem_info);
|
||||
finish_heap_swap(tableOid, oidNewHeap, false, swapToastByContent, false, frozenXid, InvalidMultiXactId, mem_info);
|
||||
|
||||
/*
|
||||
* Report vacuum full stat to PgStatCollector.
|
||||
|
@ -5293,7 +5361,8 @@ static Datum pgxc_parallel_execution(const char* query, ExecNodes* exec_nodes)
|
|||
* @return : None
|
||||
*/
|
||||
static void swap_partition_relfilenode(
|
||||
Oid partitionOid1, Oid partitionOid2, bool swapToastByContent, TransactionId frozenXid, Oid* mappedTables)
|
||||
Oid partitionOid1, Oid partitionOid2, bool swapToastByContent, TransactionId frozenXid,
|
||||
MultiXactId multiXid, Oid* mappedTables)
|
||||
{
|
||||
Relation relRelation = NULL;
|
||||
HeapTuple reltup1 = NULL;
|
||||
|
@ -5373,6 +5442,11 @@ static void swap_partition_relfilenode(
|
|||
replaces[Anum_pg_partition_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenXid);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
replaces[Anum_pg_partition_relminmxid - 1] = true;
|
||||
values[Anum_pg_partition_relminmxid - 1] = TransactionIdGetDatum(multiXid);
|
||||
#endif
|
||||
|
||||
ntup = (HeapTuple) tableam_tops_modify_tuple(reltup1, RelationGetDescr(relRelation), values, nulls, replaces);
|
||||
|
||||
relform1 = (Form_pg_partition)GETSTRUCT(ntup);
|
||||
|
@ -5433,6 +5507,7 @@ static void swap_partition_relfilenode(
|
|||
false,
|
||||
swapToastByContent,
|
||||
InvalidTransactionId,
|
||||
InvalidMultiXactId,
|
||||
mappedTables);
|
||||
|
||||
/* Clean up. */
|
||||
|
@ -5641,11 +5716,13 @@ static void PartitionRelfilenodeSwap(
|
|||
}
|
||||
Relation old_partRel = partitionGetRelation(oldHeap, old_partition);
|
||||
Relation new_partRel = partitionGetRelation(newHeap, new_partition);
|
||||
TransactionId relfrozenxid = getPartitionRelfrozenxid(old_partRel);
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
MultiXactId relminmxid = InvalidMultiXactId;
|
||||
getPartitionRelxids(old_partRel, &relfrozenxid, &relminmxid);
|
||||
|
||||
/* Exchange two partition's meta information */
|
||||
if (RelationIsIndex(oldHeap)) {
|
||||
finishPartitionHeapSwap(old_partRel->rd_id, new_partRel->rd_id, false, relfrozenxid, true);
|
||||
finishPartitionHeapSwap(old_partRel->rd_id, new_partRel->rd_id, false, relfrozenxid, relminmxid, true);
|
||||
} else {
|
||||
List* old_part_idx_list = PartitionGetPartIndexList(old_partition, true);
|
||||
List* new_part_idx_list = PartitionGetPartIndexList(new_partition, true);
|
||||
|
@ -5771,6 +5848,7 @@ void relfilenode_swap(Oid OIDOldHeap, Oid OIDNewHeap, uint8 needSwitch)
|
|||
(OIDOldHeap == RelationRelationId),
|
||||
false,
|
||||
u_sess->utils_cxt.RecentGlobalXmin,
|
||||
GetOldestMultiXactId(),
|
||||
mapped_tables);
|
||||
/*
|
||||
* Now we must remove any relation mapping entries that we set up for the
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "access/xact.h"
|
||||
#include "access/xloginsert.h"
|
||||
#include "access/xlogutils.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/indexing.h"
|
||||
|
@ -100,8 +101,8 @@ static void createdb_failure_callback(int code, Datum arg);
|
|||
static void movedb(const char* dbname, const char* tblspcname);
|
||||
static void movedb_failure_callback(int code, Datum arg);
|
||||
static bool get_db_info(const char* name, LOCKMODE lockmode, Oid* dbIdP, Oid* ownerIdP, int* encodingP,
|
||||
bool* dbIsTemplateP, bool* dbAllowConnP, Oid* dbLastSysOidP, TransactionId* dbFrozenXidP, Oid* dbTablespace,
|
||||
char** dbCollate, char** dbCtype, char** src_compatibility = NULL);
|
||||
bool* dbIsTemplateP, bool* dbAllowConnP, Oid* dbLastSysOidP, TransactionId* dbFrozenXidP, MultiXactId *dbMinMultiP,
|
||||
Oid* dbTablespace, char** dbCollate, char** dbCtype, char** src_compatibility = NULL);
|
||||
static void remove_dbtablespaces(Oid db_id);
|
||||
static bool check_db_file_conflict(Oid db_id);
|
||||
static void createdb_xact_callback(bool isCommit, const void* arg);
|
||||
|
@ -152,6 +153,7 @@ void createdb(const CreatedbStmt* stmt)
|
|||
bool src_allowconn = false;
|
||||
Oid src_lastsysoid;
|
||||
TransactionId src_frozenxid;
|
||||
MultiXactId src_minmxid;
|
||||
Oid src_deftablespace;
|
||||
volatile Oid dst_deftablespace;
|
||||
Relation pg_database_rel;
|
||||
|
@ -324,6 +326,7 @@ void createdb(const CreatedbStmt* stmt)
|
|||
&src_allowconn,
|
||||
&src_lastsysoid,
|
||||
&src_frozenxid,
|
||||
&src_minmxid,
|
||||
&src_deftablespace,
|
||||
&src_collate,
|
||||
&src_ctype,
|
||||
|
@ -529,6 +532,9 @@ void createdb(const CreatedbStmt* stmt)
|
|||
*/
|
||||
new_record_nulls[Anum_pg_database_datacl - 1] = true;
|
||||
new_record[Anum_pg_database_datfrozenxid64 - 1] = TransactionIdGetDatum(src_frozenxid);
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
new_record[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(src_minmxid);
|
||||
#endif
|
||||
tuple = heap_form_tuple(RelationGetDescr(pg_database_rel), new_record, new_record_nulls);
|
||||
|
||||
HeapTupleSetOid(tuple, dboid);
|
||||
|
@ -1001,7 +1007,8 @@ void dropdb(const char* dbname, bool missing_ok)
|
|||
pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(
|
||||
dbname, AccessExclusiveLock, &db_id, NULL, NULL, &db_istemplate, NULL, NULL, NULL, NULL, NULL, NULL)) {
|
||||
dbname, AccessExclusiveLock, &db_id, NULL, NULL, &db_istemplate,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
|
||||
if (!missing_ok) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname)));
|
||||
} else {
|
||||
|
@ -1211,7 +1218,7 @@ void RenameDatabase(const char* oldname, const char* newname)
|
|||
*/
|
||||
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", oldname)));
|
||||
|
||||
/* Permission check. */
|
||||
|
@ -1356,7 +1363,7 @@ static void movedb(const char* dbname, const char* tblspcname)
|
|||
pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
|
||||
if (!get_db_info(
|
||||
dbname, AccessExclusiveLock, &db_id, NULL, NULL, NULL, NULL, NULL, NULL, &src_tblspcoid, NULL, NULL))
|
||||
dbname, AccessExclusiveLock, &db_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &src_tblspcoid, NULL, NULL))
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname)));
|
||||
|
||||
/*
|
||||
|
@ -1925,8 +1932,8 @@ void AlterDatabaseOwner(const char* dbname, Oid newOwnerId)
|
|||
* return FALSE.
|
||||
*/
|
||||
static bool get_db_info(const char* name, LOCKMODE lockmode, Oid* dbIdP, Oid* ownerIdP, int* encodingP,
|
||||
bool* dbIsTemplateP, bool* dbAllowConnP, Oid* dbLastSysOidP, TransactionId* dbFrozenXidP, Oid* dbTablespace,
|
||||
char** dbCollate, char** dbCtype, char** dbcompatibility)
|
||||
bool* dbIsTemplateP, bool* dbAllowConnP, Oid* dbLastSysOidP, TransactionId* dbFrozenXidP, MultiXactId *dbMinMultiP,
|
||||
Oid* dbTablespace, char** dbCollate, char** dbCtype, char** dbcompatibility)
|
||||
{
|
||||
bool result = false;
|
||||
Relation relation;
|
||||
|
@ -2019,6 +2026,15 @@ static bool get_db_info(const char* name, LOCKMODE lockmode, Oid* dbIdP, Oid* ow
|
|||
|
||||
*dbFrozenXidP = datfrozenxid;
|
||||
}
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
/* limit of frozen Multixacts */
|
||||
if (dbMinMultiP != NULL) {
|
||||
bool isNull = false;
|
||||
Datum minmxidDatum =
|
||||
heap_getattr(tuple, Anum_pg_database_datminmxid, RelationGetDescr(relation), &isNull);
|
||||
*dbMinMultiP = isNull ? FirstMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
}
|
||||
#endif
|
||||
/* default tablespace for this database */
|
||||
if (dbTablespace != NULL)
|
||||
*dbTablespace = dbform->dattablespace;
|
||||
|
|
|
@ -1068,7 +1068,7 @@ void ExecRefreshCtasMatViewAll(RefreshMatViewStmt *stmt, const char *queryString
|
|||
* Swap the physical files of the target and transient tables, then
|
||||
* rebuild the target's indexes and throw away the transient table.
|
||||
*/
|
||||
finish_heap_swap(matviewOid, OIDNewHeap, false, false, true, u_sess->utils_cxt.RecentXmin);
|
||||
finish_heap_swap(matviewOid, OIDNewHeap, false, false, true, u_sess->utils_cxt.RecentXmin, GetOldestMultiXactId());
|
||||
|
||||
RelationCacheInvalidateEntry(matviewOid);
|
||||
}
|
||||
|
|
|
@ -1059,7 +1059,7 @@ void ResetSequence(Oid seq_relid)
|
|||
* Create a new storage file for the sequence. We want to keep the
|
||||
* sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs.
|
||||
*/
|
||||
RelationSetNewRelfilenode(seq_rel, InvalidTransactionId);
|
||||
RelationSetNewRelfilenode(seq_rel, InvalidTransactionId, InvalidMultiXactId);
|
||||
|
||||
/*
|
||||
* Insert the modified tuple into the new storage file.
|
||||
|
@ -1274,7 +1274,7 @@ void AlterSequence(AlterSeqStmt* stmt)
|
|||
* changes transactional. We want to keep the sequence's relfrozenxid
|
||||
* at 0, since it won't contain any unfrozen XIDs.
|
||||
*/
|
||||
RelationSetNewRelfilenode(seqrel, InvalidTransactionId);
|
||||
RelationSetNewRelfilenode(seqrel, InvalidTransactionId, InvalidMultiXactId);
|
||||
/*
|
||||
* Insert the modified tuple into the new storage file.
|
||||
*/
|
||||
|
@ -1904,6 +1904,7 @@ static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel, Buffer* buf,
|
|||
* bit update, ie, don't bother to WAL-log it, since we can certainly do
|
||||
* this again if the update gets lost.
|
||||
*/
|
||||
Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
|
||||
if (HeapTupleGetRawXmax(seqtuple) != InvalidTransactionId) {
|
||||
HeapTupleSetXmax(seqtuple, InvalidTransactionId);
|
||||
seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "access/tableam.h"
|
||||
#include "access/ustore/knl_uheap.h"
|
||||
#include "access/ustore/knl_uscan.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/dfsstore_ctlg.h"
|
||||
|
@ -197,7 +198,7 @@
|
|||
#include "pgxc/redistrib.h"
|
||||
|
||||
extern void vacuum_set_xid_limits(Relation rel, int64 freeze_min_age, int64 freeze_table_age, TransactionId* oldestXmin,
|
||||
TransactionId* freezeLimit, TransactionId* freezeTableLimit);
|
||||
TransactionId* freezeLimit, TransactionId* freezeTableLimit, MultiXactId* multiXactFrzLimit);
|
||||
|
||||
/*
|
||||
* ON COMMIT action list
|
||||
|
@ -679,7 +680,7 @@ static void AlterPartitionedSetWaitCleanGPI(bool alterGPI, Relation partTableRel
|
|||
static Oid AddTemporaryRangePartitionForAlterPartitions(const AlterTableCmd* cmd, Relation partTableRel,
|
||||
int sequence, bool* renameTargetPart);
|
||||
static void ExchangePartitionWithGPI(const AlterTableCmd* cmd, Relation partTableRel, Oid srcPartOid,
|
||||
TransactionId frozenXid);
|
||||
TransactionId frozenXid, MultiXactId multiXid);
|
||||
static void fastAddPartition(Relation partTableRel, List* destPartDefList, List** newPartOidList);
|
||||
static void readTuplesAndInsert(Relation tempTableRel, Relation partTableRel);
|
||||
static Oid createTempTableForPartition(Relation partTableRel, Partition part);
|
||||
|
@ -3988,6 +3989,7 @@ void ExecuteTruncate(TruncateStmt* stmt)
|
|||
Oid heap_relid;
|
||||
Oid toast_relid;
|
||||
bool is_shared = rel->rd_rel->relisshared;
|
||||
MultiXactId minmulti;
|
||||
/*
|
||||
* This effectively deletes all rows in the table, and may be done
|
||||
* in a serializable transaction. In that case we must record a
|
||||
|
@ -4000,6 +4002,8 @@ void ExecuteTruncate(TruncateStmt* stmt)
|
|||
continue;
|
||||
}
|
||||
|
||||
minmulti = GetOldestMultiXactId();
|
||||
|
||||
#ifdef ENABLE_MOT
|
||||
if (RelationIsForeignTable(rel) && isMOTFromTblOid(RelationGetRelid(rel))) {
|
||||
FdwRoutine* fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(rel));
|
||||
|
@ -4038,7 +4042,9 @@ void ExecuteTruncate(TruncateStmt* stmt)
|
|||
}
|
||||
}
|
||||
|
||||
RelationSetNewRelfilenode(rel, u_sess->utils_cxt.RecentXmin, isDfsTruncate);
|
||||
RelationSetNewRelfilenode(rel, u_sess->utils_cxt.RecentXmin,
|
||||
RelationIsColStore(rel) ? InvalidMultiXactId : minmulti,
|
||||
isDfsTruncate);
|
||||
|
||||
if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
|
||||
heap_create_init_fork(rel);
|
||||
|
@ -4051,7 +4057,7 @@ void ExecuteTruncate(TruncateStmt* stmt)
|
|||
*/
|
||||
if (OidIsValid(toast_relid)) {
|
||||
rel = relation_open(toast_relid, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(rel, u_sess->utils_cxt.RecentXmin);
|
||||
RelationSetNewRelfilenode(rel, u_sess->utils_cxt.RecentXmin, minmulti);
|
||||
if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
|
||||
heap_create_init_fork(rel);
|
||||
heap_close(rel, NoLock);
|
||||
|
@ -4082,13 +4088,14 @@ void ExecuteTruncate(TruncateStmt* stmt)
|
|||
|
||||
Oid partOid = HeapTupleGetOid(tup);
|
||||
Partition p = partitionOpen(rel, partOid, AccessExclusiveLock);
|
||||
PartitionSetNewRelfilenode(rel, p, u_sess->utils_cxt.RecentXmin);
|
||||
PartitionSetNewRelfilenode(rel, p, u_sess->utils_cxt.RecentXmin,
|
||||
RelationIsColStore(rel) ? InvalidMultiXactId : minmulti);
|
||||
|
||||
/* process the toast table */
|
||||
if (OidIsValid(toastOid)) {
|
||||
Assert(rel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED);
|
||||
toastRel = heap_open(toastOid, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(toastRel, u_sess->utils_cxt.RecentXmin);
|
||||
RelationSetNewRelfilenode(toastRel, u_sess->utils_cxt.RecentXmin, minmulti);
|
||||
heap_close(toastRel, NoLock);
|
||||
}
|
||||
partitionClose(rel, p, NoLock);
|
||||
|
@ -4097,7 +4104,9 @@ void ExecuteTruncate(TruncateStmt* stmt)
|
|||
pgstat_report_truncate(partOid, heap_relid, is_shared);
|
||||
}
|
||||
|
||||
RelationSetNewRelfilenode(rel, u_sess->utils_cxt.RecentXmin, isDfsTruncate);
|
||||
RelationSetNewRelfilenode(rel, u_sess->utils_cxt.RecentXmin,
|
||||
RelationIsColStore(rel) ? InvalidMultiXactId : minmulti,
|
||||
isDfsTruncate);
|
||||
freePartList(partTupleList);
|
||||
pgstat_report_truncate(
|
||||
heap_relid, InvalidOid, is_shared); /* report truncate partitioned table to PgStatCollector */
|
||||
|
@ -19943,7 +19952,8 @@ static void destroyMergeingIndexes(Relation srcIndexRelation, List* merging_btre
|
|||
list_free_ext(merging_part_list);
|
||||
}
|
||||
|
||||
static void mergePartitionIndexSwap(List* indexRel, List* indexDestPartOid, List* indexDestOid, TransactionId FreezeXid)
|
||||
static void mergePartitionIndexSwap(List* indexRel, List* indexDestPartOid, List* indexDestOid, TransactionId FreezeXid,
|
||||
MultiXactId FreezeMultiXid)
|
||||
{
|
||||
ListCell* cell1 = NULL;
|
||||
ListCell* cell2 = NULL;
|
||||
|
@ -19966,12 +19976,13 @@ static void mergePartitionIndexSwap(List* indexRel, List* indexDestPartOid, List
|
|||
getPartitionName(dstPartOid, false))));
|
||||
}
|
||||
/* swap relfilenode between temp index relation and dest index partition */
|
||||
finishPartitionHeapSwap(dstPartOid, clonedIndexRelationId, false, FreezeXid);
|
||||
finishPartitionHeapSwap(dstPartOid, clonedIndexRelationId, false, FreezeXid, FreezeMultiXid);
|
||||
partitionClose(currentIndex, dstPart, NoLock);
|
||||
}
|
||||
}
|
||||
|
||||
static void mergePartitionHeapSwap(Relation partTableRel, Oid destPartOid, Oid tempTableOid, TransactionId FreezeXid)
|
||||
static void mergePartitionHeapSwap(Relation partTableRel, Oid destPartOid, Oid tempTableOid, TransactionId FreezeXid,
|
||||
MultiXactId FreezeMultiXid)
|
||||
{
|
||||
Partition destPart;
|
||||
|
||||
|
@ -19986,7 +19997,7 @@ static void mergePartitionHeapSwap(Relation partTableRel, Oid destPartOid, Oid t
|
|||
getPartitionName(destPartOid, false))));
|
||||
}
|
||||
|
||||
finishPartitionHeapSwap(destPartOid, tempTableOid, false, FreezeXid);
|
||||
finishPartitionHeapSwap(destPartOid, tempTableOid, false, FreezeXid, FreezeMultiXid);
|
||||
partitionClose(partTableRel, destPart, NoLock);
|
||||
}
|
||||
|
||||
|
@ -20048,9 +20059,10 @@ static void mergePartitionBTreeIndexes(List* srcPartOids, List* srcPartMergeOffs
|
|||
}
|
||||
|
||||
static void mergePartitionHeapData(Relation partTableRel, Relation tempTableRel, List* srcPartOids, List* indexRel_list,
|
||||
List* indexDestOid_list, int2 bucketId, TransactionId* freezexid)
|
||||
List* indexDestOid_list, int2 bucketId, TransactionId* freezexid, MultiXactId* freezeMultixid)
|
||||
{
|
||||
TransactionId FreezeXid = InvalidTransactionId;
|
||||
MultiXactId FreezeMultiXid = InvalidMultiXactId;
|
||||
HTAB* chunkIdHashTable = NULL;
|
||||
ListCell* cell1 = NULL;
|
||||
List* mergeToastIndexes = NIL;
|
||||
|
@ -20177,18 +20189,22 @@ static void mergePartitionHeapData(Relation partTableRel, Relation tempTableRel,
|
|||
Relation srcPartRel = NULL;
|
||||
char persistency;
|
||||
BlockNumber srcPartHeapBlocks = 0;
|
||||
TransactionId relfrozenxid;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
MultiXactId relminmxid = InvalidMultiXactId;
|
||||
|
||||
srcPartition = partitionOpen(partTableRel, srcPartOid, ExclusiveLock, bucketId); // already ExclusiveLock
|
||||
// locked
|
||||
srcPartRel = partitionGetRelation(partTableRel, srcPartition);
|
||||
PartitionOpenSmgr(srcPartition);
|
||||
|
||||
relfrozenxid = getPartitionRelfrozenxid(srcPartRel);
|
||||
getPartitionRelxids(srcPartRel, &relfrozenxid, &relminmxid);
|
||||
/* update final fronzenxid, we choose the least one */
|
||||
if (!TransactionIdIsValid(FreezeXid) || TransactionIdPrecedes(relfrozenxid, FreezeXid))
|
||||
FreezeXid = relfrozenxid;
|
||||
|
||||
if (!MultiXactIdIsValid(FreezeMultiXid) || MultiXactIdPrecedes(relminmxid, FreezeMultiXid))
|
||||
FreezeMultiXid = relminmxid;
|
||||
|
||||
/* Retry to open smgr in case it is cloesd when we process SI messages */
|
||||
RelationOpenSmgr(tempTableRel);
|
||||
|
||||
|
@ -20271,6 +20287,9 @@ static void mergePartitionHeapData(Relation partTableRel, Relation tempTableRel,
|
|||
|
||||
if (freezexid != NULL)
|
||||
*freezexid = FreezeXid;
|
||||
|
||||
if (freezeMultixid != NULL)
|
||||
*freezeMultixid = FreezeMultiXid;
|
||||
/*
|
||||
* 3.4 merge toast indexes and destroy chunkId hash table
|
||||
*/
|
||||
|
@ -20368,6 +20387,7 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
Relation tempTableRel = NULL;
|
||||
ObjectAddress object;
|
||||
TransactionId FreezeXid;
|
||||
MultiXactId FreezeMultiXid;
|
||||
LOCKMODE lockMode = NoLock;
|
||||
|
||||
srcPartitions = (List*)cmd->def;
|
||||
|
@ -20587,7 +20607,7 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
/* set new empty filenode for toast index */
|
||||
Relation toastRel = relation_open(tempTableRel->rd_rel->reltoastrelid, AccessExclusiveLock);
|
||||
Relation toastIndexRel = index_open(toastRel->rd_rel->reltoastidxid, AccessExclusiveLock);
|
||||
RelationSetNewRelfilenode(toastIndexRel, InvalidTransactionId);
|
||||
RelationSetNewRelfilenode(toastIndexRel, InvalidTransactionId, InvalidMultiXactId);
|
||||
relation_close(toastRel, NoLock);
|
||||
index_close(toastIndexRel, NoLock);
|
||||
}
|
||||
|
@ -20612,7 +20632,8 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
indexRel_list,
|
||||
clonedIndexRelId_list,
|
||||
bucketlist->values[i],
|
||||
&FreezeXid);
|
||||
&FreezeXid,
|
||||
&FreezeMultiXid);
|
||||
|
||||
/* first bucket already merged into target cross bucket index. */
|
||||
if (i != 0) {
|
||||
|
@ -20622,7 +20643,8 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
}
|
||||
} else {
|
||||
mergePartitionHeapData(
|
||||
partTableRel, tempTableRel, srcPartOids, indexRel_list, clonedIndexRelId_list, InvalidBktId, &FreezeXid);
|
||||
partTableRel, tempTableRel, srcPartOids, indexRel_list, clonedIndexRelId_list, InvalidBktId, &FreezeXid,
|
||||
&FreezeMultiXid);
|
||||
}
|
||||
|
||||
/* close temp relation */
|
||||
|
@ -20630,10 +20652,10 @@ static void ATExecMergePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
heap_close(tempTableRel, NoLock);
|
||||
|
||||
/* swap the index relfilenode*/
|
||||
mergePartitionIndexSwap(indexRel_list, indexDestPartOid_list, clonedIndexRelId_list, FreezeXid);
|
||||
mergePartitionIndexSwap(indexRel_list, indexDestPartOid_list, clonedIndexRelId_list, FreezeXid, FreezeMultiXid);
|
||||
|
||||
/* swap the heap relfilenode */
|
||||
mergePartitionHeapSwap(partTableRel, destPartOid, tempTableOid, FreezeXid);
|
||||
mergePartitionHeapSwap(partTableRel, destPartOid, tempTableOid, FreezeXid, FreezeMultiXid);
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*free index list*/
|
||||
|
@ -20907,6 +20929,7 @@ static void ATExecExchangePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
List* partIndexList = NIL;
|
||||
List* ordIndexList = NIL;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
MultiXactId relminmxid = InvalidMultiXactId;
|
||||
|
||||
ordTableOid = RangeVarGetRelid(cmd->exchange_with_rel, AccessExclusiveLock, false);
|
||||
|
||||
|
@ -20998,12 +21021,12 @@ static void ATExecExchangePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
checkValidationForExchange(partTableRel, ordTableRel, partOid, cmd->exchange_verbose);
|
||||
}
|
||||
if (RelationIsPartition(ordTableRel))
|
||||
relfrozenxid = getPartitionRelfrozenxid(ordTableRel);
|
||||
getPartitionRelxids(ordTableRel, &relfrozenxid, &relminmxid);
|
||||
else
|
||||
relfrozenxid = getRelationRelfrozenxid(ordTableRel);
|
||||
getRelationRelxids(ordTableRel, &relfrozenxid, &relminmxid);
|
||||
|
||||
// Swap relfilenode of table and toast table
|
||||
finishPartitionHeapSwap(partOid, ordTableRel->rd_id, false, relfrozenxid);
|
||||
finishPartitionHeapSwap(partOid, ordTableRel->rd_id, false, relfrozenxid, relminmxid);
|
||||
|
||||
// Swap relfilenode of index
|
||||
Assert(list_length(partIndexList) == list_length(ordIndexList));
|
||||
|
@ -21019,7 +21042,7 @@ static void ATExecExchangePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
// Unusable Global Index
|
||||
ATUnusableGlobalIndex(partTableRel);
|
||||
} else {
|
||||
ExchangePartitionWithGPI(cmd, partTableRel, partOid, relfrozenxid);
|
||||
ExchangePartitionWithGPI(cmd, partTableRel, partOid, relfrozenxid, relminmxid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22272,7 +22295,7 @@ static void finishIndexSwap(List* partIndexList, List* ordIndexList)
|
|||
partOid = (Oid)lfirst_oid(cell1);
|
||||
ordOid = (Oid)lfirst_oid(cell2);
|
||||
|
||||
finishPartitionHeapSwap(partOid, ordOid, true, u_sess->utils_cxt.RecentGlobalXmin);
|
||||
finishPartitionHeapSwap(partOid, ordOid, true, u_sess->utils_cxt.RecentGlobalXmin, GetOldestMultiXactId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22469,7 +22492,7 @@ static void ATExecSplitPartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
|
||||
// creat temp table and swap relfilenode with src partition
|
||||
tempTableOid = createTempTableForPartition(partTableRel, part);
|
||||
finishPartitionHeapSwap(srcPartOid, tempTableOid, false, u_sess->utils_cxt.RecentXmin);
|
||||
finishPartitionHeapSwap(srcPartOid, tempTableOid, false, u_sess->utils_cxt.RecentXmin, GetOldestMultiXactId());
|
||||
|
||||
CommandCounterIncrement();
|
||||
|
||||
|
@ -22940,7 +22963,7 @@ static Oid AddTemporaryHashPartitionForAlterPartitions(const AlterTableCmd* cmd,
|
|||
* @in srcPartOid: current partition oid.
|
||||
*/
|
||||
static void ExchangePartitionWithGPI(const AlterTableCmd* cmd, Relation partTableRel, Oid srcPartOid,
|
||||
TransactionId frozenXid)
|
||||
TransactionId frozenXid, MultiXactId multiXid)
|
||||
{
|
||||
List* indexList = NIL;
|
||||
ListCell* cell = NULL;
|
||||
|
@ -22994,14 +23017,14 @@ static void ExchangePartitionWithGPI(const AlterTableCmd* cmd, Relation partTabl
|
|||
}
|
||||
|
||||
/* swap relfilenode between temp index relation and dest index partition */
|
||||
finishPartitionHeapSwap(indexDestPartOid, indexSrcPartOid, false, frozenXid, true);
|
||||
finishPartitionHeapSwap(indexDestPartOid, indexSrcPartOid, false, frozenXid, multiXid, true);
|
||||
partitionClose(indexRel, dstPart, NoLock);
|
||||
relation_close(indexRel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
// Swap relfilenode of table and toast table
|
||||
CommandCounterIncrement();
|
||||
finishPartitionHeapSwap(srcPartOid, destPartOid, false, frozenXid, true);
|
||||
finishPartitionHeapSwap(srcPartOid, destPartOid, false, frozenXid, multiXid, true);
|
||||
|
||||
CommandCounterIncrement();
|
||||
AlterPartitionedSetWaitCleanGPI(cmd->alterGPI, partTableRel, srcPartOid);
|
||||
|
@ -24121,7 +24144,8 @@ static void ExecRewriteRowTable(AlteredTableInfo* tab, Oid NewTableSpace, LOCKMO
|
|||
* we never try to swap toast tables by content, since we have no
|
||||
* interest in letting this code work on system catalogs.
|
||||
*/
|
||||
finish_heap_swap(tab->relid, OIDNewHeap, false, swapToastByContent, true, u_sess->utils_cxt.RecentXmin);
|
||||
finish_heap_swap(tab->relid, OIDNewHeap, false, swapToastByContent, true, u_sess->utils_cxt.RecentXmin,
|
||||
GetOldestMultiXactId());
|
||||
|
||||
/* clear all attrinitdefval */
|
||||
clearAttrInitDefVal(tab->relid);
|
||||
|
@ -24201,7 +24225,7 @@ static void ExecRewriteRowPartitionedTable(AlteredTableInfo* tab, Oid NewTableSp
|
|||
heap_close(newRel, NoLock);
|
||||
|
||||
/* swap the temp table and partition */
|
||||
finishPartitionHeapSwap(oldRel->rd_id, OIDNewHeap, false, u_sess->utils_cxt.RecentXmin);
|
||||
finishPartitionHeapSwap(oldRel->rd_id, OIDNewHeap, false, u_sess->utils_cxt.RecentXmin, GetOldestMultiXactId());
|
||||
|
||||
/* record the temp table oid for dropping */
|
||||
tempTableOidList = lappend_oid(tempTableOidList, OIDNewHeap);
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
static void ConvertTriggerToFK(CreateTrigStmt* stmt, Oid funcoid);
|
||||
static void SetTriggerFlags(TriggerDesc* trigdesc, const Trigger* trigger);
|
||||
static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRelInfo* relinfo, Oid targetPartitionOid,
|
||||
int2 bucketid, ItemPointer tid, TupleTableSlot** newSlot);
|
||||
int2 bucketid, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot** newSlot);
|
||||
static void ReleaseFakeRelation(Relation relation, Partition part, Relation* fakeRelation);
|
||||
static bool TriggerEnabled(EState* estate, ResultRelInfo* relinfo, Trigger* trigger, TriggerEvent event,
|
||||
const Bitmapset* modifiedCols, HeapTuple oldtup, HeapTuple newtup);
|
||||
|
@ -2125,7 +2125,8 @@ bool ExecBRDeleteTriggers(EState* estate, EPQState* epqstate, ResultRelInfo* rel
|
|||
} else {
|
||||
/* On datanode, do the usual way */
|
||||
#endif
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, deletePartitionOid, bucketid, tupleid, &newSlot);
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, deletePartitionOid,
|
||||
bucketid, tupleid, LockTupleExclusive, &newSlot);
|
||||
#ifdef PGXC
|
||||
}
|
||||
#endif
|
||||
|
@ -2192,7 +2193,8 @@ void ExecARDeleteTriggers(EState* estate, ResultRelInfo* relinfo, Oid deletePart
|
|||
} else {
|
||||
/* Do the usual PG-way for datanode */
|
||||
#endif
|
||||
trigtuple = GetTupleForTrigger(estate, NULL, relinfo, deletePartitionOid, bucketid, tupleid, NULL);
|
||||
trigtuple = GetTupleForTrigger(estate, NULL, relinfo, deletePartitionOid,
|
||||
bucketid, tupleid, LockTupleExclusive, NULL);
|
||||
#ifdef PGXC
|
||||
}
|
||||
#endif
|
||||
|
@ -2346,6 +2348,25 @@ TupleTableSlot* ExecBRUpdateTriggers(EState* estate, EPQState* epqstate, ResultR
|
|||
TupleTableSlot* newSlot = NULL;
|
||||
int i;
|
||||
Bitmapset* updatedCols = NULL;
|
||||
Bitmapset* keyCols = NULL;
|
||||
LockTupleMode lockmode;
|
||||
|
||||
/*
|
||||
* Compute lock mode to use. If columns that are part of the key have not
|
||||
* been modified, then we can use a weaker lock, allowing for better
|
||||
* concurrency.
|
||||
*/
|
||||
updatedCols = GET_ALL_UPDATED_COLUMNS(relinfo, estate);
|
||||
keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc, INDEX_ATTR_BITMAP_KEY);
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (!bms_overlap(keyCols, updatedCols)) {
|
||||
lockmode = LockTupleNoKeyExclusive;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
lockmode = LockTupleExclusive;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PGXC
|
||||
bool exec_all_triggers = false;
|
||||
|
@ -2377,7 +2398,8 @@ TupleTableSlot* ExecBRUpdateTriggers(EState* estate, EPQState* epqstate, ResultR
|
|||
/* On datanode, do the usual way */
|
||||
#endif
|
||||
/* get a copy of the on-disk tuple we are planning to update */
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, oldPartitionOid, bucketid, tupleid, &newSlot);
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, oldPartitionOid,
|
||||
bucketid, tupleid, lockmode, &newSlot);
|
||||
if (trigtuple == NULL)
|
||||
return NULL; /* cancel the update action */
|
||||
|
||||
|
@ -2401,8 +2423,6 @@ TupleTableSlot* ExecBRUpdateTriggers(EState* estate, EPQState* epqstate, ResultR
|
|||
}
|
||||
#endif
|
||||
|
||||
updatedCols = GET_ALL_UPDATED_COLUMNS(relinfo, estate);
|
||||
|
||||
LocTriggerData.type = T_TriggerData;
|
||||
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
|
||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||
|
@ -2476,7 +2496,8 @@ void ExecARUpdateTriggers(EState* estate, ResultRelInfo* relinfo, Oid oldPartiti
|
|||
} else {
|
||||
/* Do the usual PG-way for datanode */
|
||||
#endif
|
||||
trigtuple = GetTupleForTrigger(estate, NULL, relinfo, oldPartitionOid, bucketid, tupleid, NULL);
|
||||
trigtuple = GetTupleForTrigger(estate, NULL, relinfo, oldPartitionOid,
|
||||
bucketid, tupleid, LockTupleExclusive, NULL);
|
||||
#ifdef PGXC
|
||||
}
|
||||
#endif
|
||||
|
@ -2629,7 +2650,7 @@ void ExecASTruncateTriggers(EState* estate, ResultRelInfo* relinfo)
|
|||
}
|
||||
|
||||
static HeapTuple GetTupleForTrigger(EState* estate, EPQState* epqstate, ResultRelInfo* relinfo, Oid targetPartitionOid,
|
||||
int2 bucketid, ItemPointer tid, TupleTableSlot** newSlot)
|
||||
int2 bucketid, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot** newSlot)
|
||||
{
|
||||
Relation relation = relinfo->ri_RelationDesc;
|
||||
HeapTupleData tuple;
|
||||
|
@ -2796,7 +2817,7 @@ ltrmark:;
|
|||
&tuple,
|
||||
&buffer,
|
||||
estate->es_output_cid,
|
||||
LockTupleExclusive,
|
||||
lockmode,
|
||||
false,
|
||||
&tmfd,
|
||||
false, // fake params below are for uheap implementation
|
||||
|
@ -2838,7 +2859,8 @@ ltrmark:;
|
|||
TupleTableSlot* epqslot = NULL;
|
||||
|
||||
epqslot = EvalPlanQual(
|
||||
estate, epqstate, fakeRelation, relinfo->ri_RangeTableIndex, &tmfd.ctid, tmfd.xmax, false);
|
||||
estate, epqstate, fakeRelation, relinfo->ri_RangeTableIndex,
|
||||
lockmode, &tmfd.ctid, tmfd.xmax, false);
|
||||
if (!TupIsNull(epqslot)) {
|
||||
*tid = tmfd.ctid;
|
||||
*newSlot = epqslot;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "access/tableam.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/dfsstore_ctlg.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/gs_matview.h"
|
||||
|
@ -127,7 +128,7 @@ static void DropEmptyPartitionDirectories(Oid relid);
|
|||
static THR_LOCAL BufferAccessStrategy vac_strategy;
|
||||
static THR_LOCAL int elevel = -1;
|
||||
|
||||
static void vac_truncate_clog(TransactionId frozenXID);
|
||||
static void vac_truncate_clog(TransactionId frozenXID, MultiXactId frozenMulti);
|
||||
static bool vacuum_rel(Oid relid, VacuumStmt* vacstmt, bool do_toast);
|
||||
static void GPIVacuumMainPartition(
|
||||
Relation onerel, const VacuumStmt* vacstmt, LOCKMODE lockmode, BufferAccessStrategy bstrategy);
|
||||
|
@ -899,7 +900,7 @@ List* get_rel_oids(Oid relid, VacuumStmt* vacstmt)
|
|||
* vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points
|
||||
*/
|
||||
void vacuum_set_xid_limits(Relation rel, int64 freeze_min_age, int64 freeze_table_age, TransactionId* oldestXmin,
|
||||
TransactionId* freezeLimit, TransactionId* freezeTableLimit)
|
||||
TransactionId* freezeLimit, TransactionId* freezeTableLimit, MultiXactId* multiXactFrzLimit)
|
||||
{
|
||||
int64 freezemin;
|
||||
TransactionId limit;
|
||||
|
@ -990,6 +991,28 @@ void vacuum_set_xid_limits(Relation rel, int64 freeze_min_age, int64 freeze_tabl
|
|||
|
||||
*freezeTableLimit = limit;
|
||||
}
|
||||
|
||||
if (multiXactFrzLimit != NULL) {
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
MultiXactId mxLimit;
|
||||
|
||||
/*
|
||||
* simplistic multixactid freezing: use the same freezing policy as
|
||||
* for Xids
|
||||
*/
|
||||
mxLimit = GetOldestMultiXactId();
|
||||
if (mxLimit > FirstMultiXactId + freezemin)
|
||||
mxLimit -= freezemin;
|
||||
else
|
||||
mxLimit = FirstMultiXactId;
|
||||
|
||||
*multiXactFrzLimit = mxLimit;
|
||||
#else
|
||||
*multiXactFrzLimit = InvalidMultiXactId;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1092,7 +1115,7 @@ static void debug_print_rows_and_pages(Relation relation, Form_pg_class pgcform)
|
|||
* This routine is shared by VACUUM and ANALYZE.
|
||||
*/
|
||||
void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_pages, double num_tuples,
|
||||
BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid)
|
||||
BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti)
|
||||
{
|
||||
Oid relid = RelationGetRelid(relation);
|
||||
HeapTuple ctup;
|
||||
|
@ -1103,6 +1126,7 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p
|
|||
TransactionId relfrozenxid;
|
||||
Datum xid64datum;
|
||||
bool isGtt = false;
|
||||
MultiXactId relminmxid;
|
||||
|
||||
/* global temp table remember relstats to localhash and rel->rd_rel, not catalog */
|
||||
if (RELATION_IS_GLOBAL_TEMP(relation)) {
|
||||
|
@ -1190,11 +1214,20 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p
|
|||
relfrozenxid = DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
|
||||
if (TransactionIdIsNormal(frozenxid) && (TransactionIdPrecedes(relfrozenxid, frozenxid)
|
||||
#ifdef PGXC
|
||||
|| !IsPostmasterEnvironment)
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum minmxidDatum = tableam_tops_tuple_getattr(ctup,
|
||||
Anum_pg_class_relminmxid,
|
||||
RelationGetDescr(classRel),
|
||||
&isNull);
|
||||
relminmxid = isNull ? InvalidMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
#endif
|
||||
) {
|
||||
|
||||
if ((TransactionIdIsNormal(frozenxid) && (TransactionIdPrecedes(relfrozenxid, frozenxid)
|
||||
#ifdef PGXC
|
||||
|| !IsPostmasterEnvironment
|
||||
#endif
|
||||
)) || (MultiXactIdIsValid(minmulti) && (MultiXactIdPrecedes(relminmxid, minmulti) || !IsPostmasterEnvironment)) ||
|
||||
isNull) {
|
||||
|
||||
Datum values[Natts_pg_class];
|
||||
bool nulls[Natts_pg_class];
|
||||
|
@ -1210,8 +1243,17 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p
|
|||
rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
|
||||
securec_check(rc, "", "");
|
||||
|
||||
replaces[Anum_pg_class_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenxid);
|
||||
if (TransactionIdIsNormal(frozenxid) && TransactionIdPrecedes(relfrozenxid, frozenxid)) {
|
||||
replaces[Anum_pg_class_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_class_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenxid);
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if ((MultiXactIdIsValid(minmulti) && MultiXactIdPrecedes(relminmxid, minmulti)) || isNull) {
|
||||
replaces[Anum_pg_class_relminmxid - 1] = true;
|
||||
values[Anum_pg_class_relminmxid - 1] = TransactionIdGetDatum(minmulti);
|
||||
}
|
||||
#endif
|
||||
|
||||
nctup = (HeapTuple) tableam_tops_modify_tuple(ctup, RelationGetDescr(classRel), values, nulls, replaces);
|
||||
ctup = nctup;
|
||||
|
@ -1235,8 +1277,13 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p
|
|||
* vac_update_datfrozenxid() -- update pg_database.datfrozenxid for our DB
|
||||
*
|
||||
* Update pg_database's datfrozenxid entry for our database to be the
|
||||
* minimum of the pg_class.relfrozenxid values. If we are able to
|
||||
* advance pg_database.datfrozenxid, also try to truncate pg_clog.
|
||||
* minimum of the pg_class.relfrozenxid values.
|
||||
*
|
||||
* Similarly, update our datfrozenmulti to be the minimum of the
|
||||
* pg_class.relfrozenmulti values.
|
||||
*
|
||||
* If we are able to advance either pg_database value, also try to
|
||||
* truncate pg_clog and pg_multixact.
|
||||
*
|
||||
* We violate transaction semantics here by overwriting the database's
|
||||
* existing pg_database tuple with the new value. This is reasonably
|
||||
|
@ -1260,6 +1307,12 @@ void vac_update_datfrozenxid(void)
|
|||
TransactionId datfrozenxid;
|
||||
TransactionId relfrozenxid;
|
||||
Datum xid64datum;
|
||||
MultiXactId newFrozenMulti = InvalidMultiXactId;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum minmxidDatum;
|
||||
MultiXactId relminmxid = InvalidMultiXactId;
|
||||
#endif
|
||||
MultiXactId datminmxid = InvalidMultiXactId;
|
||||
|
||||
/* Don't update datfrozenxid when cluser is resizing */
|
||||
if (ClusterResizingInProgress()) {
|
||||
|
@ -1269,10 +1322,18 @@ void vac_update_datfrozenxid(void)
|
|||
* Initialize the "min" calculation with GetOldestXmin, which is a
|
||||
* reasonable approximation to the minimum relfrozenxid for not-yet-
|
||||
* committed pg_class entries for new tables; see AddNewRelationTuple().
|
||||
* Se we cannot produce a wrong minimum by starting with this.
|
||||
* So we cannot produce a wrong minimum by starting with this.
|
||||
*/
|
||||
newFrozenXid = GetOldestXmin(NULL);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
/*
|
||||
* Similarly, initialize the MultiXact "min" with the value that would
|
||||
* be used on pg_class for new tables. See AddNewRelationTuple().
|
||||
*/
|
||||
newFrozenMulti = GetOldestMultiXactId();
|
||||
#endif
|
||||
|
||||
lastSaneFrozenXid = ReadNewTransactionId();
|
||||
|
||||
/*
|
||||
|
@ -1342,6 +1403,15 @@ void vac_update_datfrozenxid(void)
|
|||
|
||||
if (TransactionIdPrecedes(relfrozenxid, newFrozenXid))
|
||||
newFrozenXid = relfrozenxid;
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
minmxidDatum = tableam_tops_tuple_getattr(
|
||||
classTup, Anum_pg_class_relminmxid, RelationGetDescr(relation), &isNull);
|
||||
relminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
|
||||
if (MultiXactIdIsValid(relminmxid) && MultiXactIdPrecedes(relminmxid, newFrozenMulti))
|
||||
newFrozenMulti = relminmxid;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* we're done with pg_class */
|
||||
|
@ -1425,11 +1495,16 @@ void vac_update_datfrozenxid(void)
|
|||
/* Consider frozenxid of objects in recyclebin. */
|
||||
TrAdjustFrozenXid64(u_sess->proc_cxt.MyDatabaseId, &newFrozenXid);
|
||||
|
||||
if ((TransactionIdPrecedes(datfrozenxid, newFrozenXid))
|
||||
#ifdef PGXC
|
||||
|| !IsPostmasterEnvironment)
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
minmxidDatum = tableam_tops_tuple_getattr(tuple, Anum_pg_database_datminmxid, RelationGetDescr(relation), &isNull);
|
||||
datminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
#endif
|
||||
{
|
||||
|
||||
if ((TransactionIdPrecedes(datfrozenxid, newFrozenXid)) || (MultiXactIdPrecedes(datminmxid, newFrozenMulti))
|
||||
#ifdef PGXC
|
||||
|| !IsPostmasterEnvironment
|
||||
#endif
|
||||
) {
|
||||
Datum values[Natts_pg_database];
|
||||
bool nulls[Natts_pg_database];
|
||||
bool replaces[Natts_pg_database];
|
||||
|
@ -1444,9 +1519,16 @@ void vac_update_datfrozenxid(void)
|
|||
rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
|
||||
securec_check(rc, "", "");
|
||||
|
||||
replaces[Anum_pg_database_datfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_database_datfrozenxid64 - 1] = TransactionIdGetDatum(newFrozenXid);
|
||||
|
||||
if (TransactionIdPrecedes(datfrozenxid, newFrozenXid)) {
|
||||
replaces[Anum_pg_database_datfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_database_datfrozenxid64 - 1] = TransactionIdGetDatum(newFrozenXid);
|
||||
}
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (MultiXactIdPrecedes(datminmxid, newFrozenMulti)) {
|
||||
replaces[Anum_pg_database_datminmxid - 1] = true;
|
||||
values[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(newFrozenMulti);
|
||||
}
|
||||
#endif
|
||||
newtuple = (HeapTuple) tableam_tops_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
|
||||
dirty = true;
|
||||
}
|
||||
|
@ -1470,7 +1552,7 @@ void vac_update_datfrozenxid(void)
|
|||
* this action will update that too.
|
||||
*/
|
||||
if (dirty || ForceTransactionIdLimitUpdate()) {
|
||||
vac_truncate_clog(newFrozenXid);
|
||||
vac_truncate_clog(newFrozenXid, newFrozenMulti);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1486,14 +1568,17 @@ void vac_update_datfrozenxid(void)
|
|||
* This routine is only invoked when we've managed to change our
|
||||
* DB's datfrozenxid entry.
|
||||
*/
|
||||
static void vac_truncate_clog(TransactionId frozenXID)
|
||||
static void vac_truncate_clog(TransactionId frozenXID, MultiXactId frozenMulti)
|
||||
{
|
||||
Relation relation;
|
||||
TableScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
Oid oldest_datoid;
|
||||
/* init oldest_datoid to sync with my frozenXID */
|
||||
oldest_datoid = u_sess->proc_cxt.MyDatabaseId;
|
||||
Oid oldestxid_datoid;
|
||||
Oid oldestmulti_datoid;
|
||||
|
||||
/* init oldest datoids to sync with my frozen values */
|
||||
oldestxid_datoid = u_sess->proc_cxt.MyDatabaseId;
|
||||
oldestmulti_datoid = u_sess->proc_cxt.MyDatabaseId;
|
||||
|
||||
/*
|
||||
* Scan pg_database to compute the minimum datfrozenxid
|
||||
|
@ -1518,6 +1603,7 @@ static void vac_truncate_clog(TransactionId frozenXID)
|
|||
scan = tableam_scan_begin(relation, SnapshotNow, 0, NULL);
|
||||
while ((tuple = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
|
||||
volatile FormData_pg_database* dbform = (Form_pg_database)GETSTRUCT(tuple);
|
||||
|
||||
bool isNull = false;
|
||||
TransactionId datfrozenxid;
|
||||
Datum xid64datum = tableam_tops_tuple_getattr(tuple, Anum_pg_database_datfrozenxid64, RelationGetDescr(relation), &isNull);
|
||||
|
@ -1534,8 +1620,20 @@ static void vac_truncate_clog(TransactionId frozenXID)
|
|||
|
||||
if (TransactionIdPrecedes(datfrozenxid, frozenXID)) {
|
||||
frozenXID = datfrozenxid;
|
||||
oldest_datoid = HeapTupleGetOid(tuple);
|
||||
oldestxid_datoid = HeapTupleGetOid(tuple);
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum minmxidDatum = tableam_tops_tuple_getattr(tuple, Anum_pg_database_datminmxid,
|
||||
RelationGetDescr(relation), &isNull);
|
||||
MultiXactId datminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
Assert(MultiXactIdIsValid(datminmxid));
|
||||
|
||||
if (MultiXactIdPrecedes(datminmxid, frozenMulti)) {
|
||||
frozenMulti = datminmxid;
|
||||
oldestmulti_datoid = HeapTupleGetOid(tuple);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
|
@ -1544,20 +1642,26 @@ static void vac_truncate_clog(TransactionId frozenXID)
|
|||
|
||||
/* Truncate CLOG to the oldest frozenxid */
|
||||
TruncateCLOG(frozenXID);
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
TruncateMultiXact(frozenMulti);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Update the wrap limit for GetNewTransactionId. Note: this function
|
||||
* will also signal the postmaster for an(other) autovac cycle if needed.
|
||||
* Update the wrap limit for GetNewTransactionId and creation of new
|
||||
* MultiXactIds. Note: these functions will also signal the postmaster for
|
||||
* an(other) autovac cycle if needed. XXX should we avoid possibly
|
||||
* signalling twice?
|
||||
*/
|
||||
SetTransactionIdLimit(frozenXID, oldest_datoid);
|
||||
SetTransactionIdLimit(frozenXID, oldestxid_datoid);
|
||||
MultiXactAdvanceOldest(frozenMulti, oldestmulti_datoid);
|
||||
|
||||
ereport(LOG,
|
||||
(errmsg("In truncate clog: frozenXID:" XID_FMT ", oldest_datoid: %u, xid:" XID_FMT
|
||||
(errmsg("In truncate clog: frozenXID:" XID_FMT ", oldestxid_datoid: %u, xid:" XID_FMT
|
||||
", pid: %lu, ShmemVariableCache: nextOid:%u, oldestXid:" XID_FMT ","
|
||||
"nextXid:" XID_FMT ", xidVacLimit:%lu, oldestXidDB:%u, RecentXmin:" XID_FMT
|
||||
", RecentGlobalXmin:" XID_FMT ", OldestXmin:" XID_FMT ", FreezeLimit:%lu, useLocalSnapshot:%d.",
|
||||
frozenXID,
|
||||
oldest_datoid,
|
||||
oldestxid_datoid,
|
||||
t_thrd.pgxact->xid,
|
||||
t_thrd.proc->pid,
|
||||
t_thrd.xact_cxt.ShmemVariableCache->nextOid,
|
||||
|
@ -2516,7 +2620,7 @@ void vacuum_delay_point(void)
|
|||
}
|
||||
|
||||
void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages,
|
||||
TransactionId frozenxid)
|
||||
TransactionId frozenxid, MultiXactId minmulti)
|
||||
{
|
||||
Oid partid = PartitionGetPartid(part);
|
||||
Relation rd;
|
||||
|
@ -2527,6 +2631,7 @@ void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tupl
|
|||
bool isNull = false;
|
||||
TransactionId relfrozenxid;
|
||||
Datum xid64datum;
|
||||
MultiXactId relminmxid = MaxMultiXactId;
|
||||
|
||||
rd = heap_open(PartitionRelationId, RowExclusiveLock);
|
||||
|
||||
|
@ -2576,7 +2681,14 @@ void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tupl
|
|||
relfrozenxid = DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
|
||||
if (TransactionIdIsNormal(frozenxid) && TransactionIdPrecedes(relfrozenxid, frozenxid)) {
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum minmxidDatum = tableam_tops_tuple_getattr(parttup, Anum_pg_partition_relminmxid,
|
||||
RelationGetDescr(rd), &isNull);
|
||||
relminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(minmxidDatum);
|
||||
#endif
|
||||
|
||||
if ((TransactionIdIsNormal(frozenxid) && TransactionIdPrecedes(relfrozenxid, frozenxid)) ||
|
||||
(MultiXactIdIsValid(minmulti) && MultiXactIdPrecedes(relminmxid, minmulti))) {
|
||||
Datum values[Natts_pg_partition];
|
||||
bool nulls[Natts_pg_partition];
|
||||
bool replaces[Natts_pg_partition];
|
||||
|
@ -2591,8 +2703,17 @@ void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tupl
|
|||
rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
|
||||
securec_check(rc, "", "");
|
||||
|
||||
replaces[Anum_pg_partition_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenxid);
|
||||
if (TransactionIdIsNormal(frozenxid) && TransactionIdPrecedes(relfrozenxid, frozenxid)) {
|
||||
replaces[Anum_pg_partition_relfrozenxid64 - 1] = true;
|
||||
values[Anum_pg_partition_relfrozenxid64 - 1] = TransactionIdGetDatum(frozenxid);
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (MultiXactIdIsValid(minmulti) && MultiXactIdPrecedes(relminmxid, minmulti)) {
|
||||
replaces[Anum_pg_partition_relminmxid - 1] = true;
|
||||
values[Anum_pg_partition_relminmxid - 1] = TransactionIdGetDatum(minmulti);
|
||||
}
|
||||
#endif
|
||||
|
||||
nparttup = (HeapTuple) tableam_tops_modify_tuple(parttup, RelationGetDescr(rd), values, nulls, replaces);
|
||||
parttup = nparttup;
|
||||
|
@ -2742,16 +2863,18 @@ void vac_close_part_indexes(
|
|||
}
|
||||
|
||||
/* Scan pg_partition to get all the partitions of the partitioned table,
|
||||
* calculate all the pages, tuples, and the min frozenXid
|
||||
* calculate all the pages, tuples, and the min frozenXid, multiXid
|
||||
*/
|
||||
void CalculatePartitionedRelStats(_in_ Relation partitionRel, _in_ Relation partRel, _out_ BlockNumber* totalPages,
|
||||
_out_ BlockNumber* totalVisiblePages, _out_ double* totalTuples, _out_ TransactionId* minFrozenXid)
|
||||
_out_ BlockNumber* totalVisiblePages, _out_ double* totalTuples, _out_ TransactionId* minFrozenXid,
|
||||
_out_ MultiXactId* minMultiXid)
|
||||
{
|
||||
ScanKeyData partKey[2];
|
||||
BlockNumber pages = 0;
|
||||
BlockNumber allVisiblePages = 0;
|
||||
double tuples = 0;
|
||||
TransactionId frozenXid;
|
||||
MultiXactId multiXid = InvalidMultiXactId;
|
||||
Form_pg_partition partForm;
|
||||
|
||||
Assert(partitionRel->rd_rel->parttype == PARTTYPE_PARTITIONED_RELATION);
|
||||
|
@ -2781,7 +2904,7 @@ void CalculatePartitionedRelStats(_in_ Relation partitionRel, _in_ Relation part
|
|||
SysScanDesc partScan = systable_beginscan(partRel, PartitionParentOidIndexId, true, NULL, 2, partKey);
|
||||
|
||||
HeapTuple partTuple = NULL;
|
||||
/* compute all pages, tuples and the minimum frozenXid */
|
||||
/* compute all pages, tuples and the minimum frozenXid, multiXid */
|
||||
partTuple = systable_getnext(partScan);
|
||||
if (partTuple != NULL) {
|
||||
partForm = (Form_pg_partition)GETSTRUCT(partTuple);
|
||||
|
@ -2803,6 +2926,12 @@ void CalculatePartitionedRelStats(_in_ Relation partitionRel, _in_ Relation part
|
|||
|
||||
frozenXid = relfrozenxid;
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
xid64datum = tableam_tops_tuple_getattr(partTuple, Anum_pg_partition_relminmxid,
|
||||
RelationGetDescr(rel), &isNull);
|
||||
multiXid = isNull ? FirstMultiXactId : DatumGetTransactionId(xid64datum);
|
||||
#endif
|
||||
|
||||
do {
|
||||
partForm = (Form_pg_partition)GETSTRUCT(partTuple);
|
||||
|
||||
|
@ -2826,11 +2955,22 @@ void CalculatePartitionedRelStats(_in_ Relation partitionRel, _in_ Relation part
|
|||
if (TransactionIdPrecedes(relfrozenxid, frozenXid)) {
|
||||
frozenXid = relfrozenxid;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
xid64datum = tableam_tops_tuple_getattr(partTuple, Anum_pg_partition_relminmxid,
|
||||
RelationGetDescr(rel), &isNull);
|
||||
MultiXactId relminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(xid64datum);
|
||||
|
||||
if (TransactionIdPrecedes(relminmxid, multiXid)) {
|
||||
multiXid = relminmxid;
|
||||
}
|
||||
#endif
|
||||
} while ((partTuple = systable_getnext(partScan)) != NULL);
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
} else {
|
||||
frozenXid = InvalidTransactionId;
|
||||
multiXid = InvalidMultiXactId;
|
||||
}
|
||||
|
||||
systable_endscan(partScan);
|
||||
|
@ -2843,17 +2983,21 @@ void CalculatePartitionedRelStats(_in_ Relation partitionRel, _in_ Relation part
|
|||
*totalTuples = tuples;
|
||||
if (minFrozenXid != NULL)
|
||||
*minFrozenXid = frozenXid;
|
||||
if (minMultiXid != NULL)
|
||||
*minMultiXid = multiXid;
|
||||
}
|
||||
|
||||
/*
|
||||
* After VACUUM or ANALYZE, update pg_class for the partitioned tables.
|
||||
*/
|
||||
void vac_update_pgclass_partitioned_table(Relation partitionRel, bool hasIndex, TransactionId newFrozenXid)
|
||||
void vac_update_pgclass_partitioned_table(Relation partitionRel, bool hasIndex, TransactionId newFrozenXid,
|
||||
MultiXactId newMultiXid)
|
||||
{
|
||||
BlockNumber pages = 0;
|
||||
BlockNumber allVisiblePages = 0;
|
||||
double tuples = 0;
|
||||
TransactionId frozenXid = newFrozenXid;
|
||||
MultiXactId multiXid = newMultiXid;
|
||||
|
||||
Assert(partitionRel->rd_rel->parttype == PARTTYPE_PARTITIONED_RELATION);
|
||||
|
||||
|
@ -2864,8 +3008,8 @@ void vac_update_pgclass_partitioned_table(Relation partitionRel, bool hasIndex,
|
|||
*/
|
||||
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
Relation partRel = heap_open(PartitionRelationId, ShareUpdateExclusiveLock);
|
||||
CalculatePartitionedRelStats(partitionRel, partRel, &pages, &allVisiblePages, &tuples, &frozenXid);
|
||||
vac_update_relstats(partitionRel, classRel, pages, tuples, allVisiblePages, hasIndex, frozenXid);
|
||||
CalculatePartitionedRelStats(partitionRel, partRel, &pages, &allVisiblePages, &tuples, &frozenXid, &multiXid);
|
||||
vac_update_relstats(partitionRel, classRel, pages, tuples, allVisiblePages, hasIndex, frozenXid, multiXid);
|
||||
heap_close(partRel, ShareUpdateExclusiveLock);
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
}
|
||||
|
@ -2884,7 +3028,7 @@ void CStoreVacUpdatePartitionRelStats(Relation partitionRel, TransactionId newFr
|
|||
*/
|
||||
Relation pgclassRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
Relation pgPartitionRel = heap_open(PartitionRelationId, ShareUpdateExclusiveLock);
|
||||
CalculatePartitionedRelStats(partitionRel, pgPartitionRel, NULL, NULL, NULL, &frozenXid);
|
||||
CalculatePartitionedRelStats(partitionRel, pgPartitionRel, NULL, NULL, NULL, &frozenXid, NULL);
|
||||
CStoreVacUpdateNormalRelStats(RelationGetRelid(partitionRel), frozenXid, pgclassRel);
|
||||
heap_close(pgPartitionRel, ShareUpdateExclusiveLock);
|
||||
heap_close(pgclassRel, RowExclusiveLock);
|
||||
|
@ -3149,7 +3293,8 @@ void merge_cu_relation(void* _info, VacuumStmt* stmt)
|
|||
getTuplesAndInsert(delta_rel, OIDNewHeap);
|
||||
|
||||
/* swap relfile node */
|
||||
finish_heap_swap(deltaOid, OIDNewHeap, false, false, false, u_sess->utils_cxt.RecentGlobalXmin);
|
||||
finish_heap_swap(deltaOid, OIDNewHeap, false, false, false, u_sess->utils_cxt.RecentGlobalXmin,
|
||||
InvalidMultiXactId);
|
||||
|
||||
/* close relation */
|
||||
relation_close(delta_rel, NoLock);
|
||||
|
@ -3191,7 +3336,8 @@ void merge_cu_relation(void* _info, VacuumStmt* stmt)
|
|||
getTuplesAndInsert(delta_rel, OIDNewHeap);
|
||||
|
||||
/* swap relfile node */
|
||||
finish_heap_swap(deltaOid, OIDNewHeap, false, false, false, u_sess->utils_cxt.RecentGlobalXmin);
|
||||
finish_heap_swap(deltaOid, OIDNewHeap, false, false, false, u_sess->utils_cxt.RecentGlobalXmin,
|
||||
InvalidMultiXactId);
|
||||
|
||||
/* close relation */
|
||||
if (delta_rel != NULL)
|
||||
|
@ -3939,7 +4085,8 @@ static void GPIVacuumMainPartition(
|
|||
cbi_set_enable_clean(iRel[i]);
|
||||
}
|
||||
vac_update_relstats(
|
||||
iRel[i], classRel, indstats[i]->num_pages, indstats[i]->num_index_tuples, 0, false, InvalidTransactionId);
|
||||
iRel[i], classRel, indstats[i]->num_pages, indstats[i]->num_index_tuples, 0, false, InvalidTransactionId,
|
||||
InvalidMultiXactId);
|
||||
pfree_ext(indstats[i]);
|
||||
index_close(iRel[i], lockmode);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "access/transam.h"
|
||||
#include "access/visibilitymap.h"
|
||||
#include "access/xlog.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/storage.h"
|
||||
#include "catalog/pg_hashbucket_fn.h"
|
||||
|
@ -181,6 +182,7 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
double new_rel_tuples;
|
||||
BlockNumber new_rel_allvisible;
|
||||
TransactionId new_frozen_xid;
|
||||
MultiXactId new_min_multi;
|
||||
Relation* indexrel = NULL;
|
||||
Partition* indexpart = NULL;
|
||||
uint32 statFlag = onerel->parentId;
|
||||
|
@ -193,7 +195,8 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
vacstmt->freeze_table_age,
|
||||
&u_sess->cmd_cxt.OldestXmin,
|
||||
&u_sess->cmd_cxt.FreezeLimit,
|
||||
&freezeTableLimit);
|
||||
&freezeTableLimit,
|
||||
&u_sess->cmd_cxt.MultiXactFrzLimit);
|
||||
|
||||
new_frozen_xid = u_sess->cmd_cxt.FreezeLimit;
|
||||
|
||||
|
@ -322,7 +325,8 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
vacstmt->freeze_table_age,
|
||||
&u_sess->cmd_cxt.OldestXmin,
|
||||
&u_sess->cmd_cxt.FreezeLimit,
|
||||
&freezeTableLimit);
|
||||
&freezeTableLimit,
|
||||
&u_sess->cmd_cxt.MultiXactFrzLimit);
|
||||
|
||||
bool isNull = false;
|
||||
TransactionId relfrozenxid;
|
||||
|
@ -488,10 +492,16 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
new_frozen_xid = InvalidTransactionId;
|
||||
}
|
||||
|
||||
new_min_multi = u_sess->cmd_cxt.MultiXactFrzLimit;
|
||||
if (vacrelstats->scanned_pages < vacrelstats->rel_pages || vacrelstats->hasKeepInvisbleTuples) {
|
||||
new_min_multi = InvalidMultiXactId;
|
||||
}
|
||||
|
||||
if (RelationIsPartition(onerel)) {
|
||||
Assert(vacstmt->onepart != NULL);
|
||||
|
||||
vac_update_partstats(vacstmt->onepart, new_rel_pages, new_rel_tuples, new_rel_allvisible, new_frozen_xid);
|
||||
vac_update_partstats(vacstmt->onepart, new_rel_pages, new_rel_tuples,
|
||||
new_rel_allvisible, new_frozen_xid, new_min_multi);
|
||||
/*
|
||||
* when vacuum partition, do not change the relhasindex field in pg_class
|
||||
* for partitioned table, as some partition may be altered as "all local
|
||||
|
@ -500,7 +510,7 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
* misdguge as hot update even if update indexes columns.
|
||||
*/
|
||||
vac_update_pgclass_partitioned_table(
|
||||
vacstmt->onepartrel, vacstmt->onepartrel->rd_rel->relhasindex, new_frozen_xid);
|
||||
vacstmt->onepartrel, vacstmt->onepartrel->rd_rel->relhasindex, new_frozen_xid, new_min_multi);
|
||||
|
||||
// update stats of local partition indexes
|
||||
for (int idx = 0; idx < nindexes - nindexesGlobal; idx++) {
|
||||
|
@ -512,9 +522,10 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
vacrelstats->new_idx_pages[idx],
|
||||
vacrelstats->new_idx_tuples[idx],
|
||||
0,
|
||||
InvalidTransactionId);
|
||||
InvalidTransactionId,
|
||||
InvalidMultiXactId);
|
||||
|
||||
vac_update_pgclass_partitioned_table(indexrel[idx], false, InvalidTransactionId);
|
||||
vac_update_pgclass_partitioned_table(indexrel[idx], false, InvalidTransactionId, InvalidMultiXactId);
|
||||
}
|
||||
|
||||
// update stats of global partition indexes
|
||||
|
@ -531,13 +542,15 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
vacrelstats->new_idx_tuples[idx],
|
||||
0,
|
||||
false,
|
||||
InvalidTransactionId);
|
||||
InvalidTransactionId,
|
||||
InvalidMultiXactId);
|
||||
}
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
} else {
|
||||
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
vac_update_relstats(
|
||||
onerel, classRel, new_rel_pages, new_rel_tuples, new_rel_allvisible, vacrelstats->hasindex, new_frozen_xid);
|
||||
onerel, classRel, new_rel_pages, new_rel_tuples, new_rel_allvisible,
|
||||
vacrelstats->hasindex, new_frozen_xid, new_min_multi);
|
||||
|
||||
for (int idx = 0; idx < nindexes; idx++) {
|
||||
/* update index status */
|
||||
|
@ -551,7 +564,8 @@ void lazy_vacuum_rel(Relation onerel, VacuumStmt* vacstmt, BufferAccessStrategy
|
|||
vacrelstats->new_idx_tuples[idx],
|
||||
0,
|
||||
false,
|
||||
InvalidTransactionId);
|
||||
InvalidTransactionId,
|
||||
InvalidMultiXactId);
|
||||
}
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
}
|
||||
|
@ -1032,6 +1046,7 @@ static IndexBulkDeleteResult** lazy_scan_heap(
|
|||
bool all_visible = false;
|
||||
bool has_dead_tuples = false;
|
||||
TransactionId visibility_cutoff_xid = InvalidTransactionId;
|
||||
bool changedMultiXid;
|
||||
|
||||
/* IO collector and IO scheduler for vacuum */
|
||||
if (ENABLE_WORKLOAD_CONTROL)
|
||||
|
@ -1266,6 +1281,7 @@ static IndexBulkDeleteResult** lazy_scan_heap(
|
|||
all_visible = true;
|
||||
has_dead_tuples = false;
|
||||
nfrozen = 0;
|
||||
changedMultiXid = false;
|
||||
hastup = false;
|
||||
prev_dead_count = vacrelstats->num_dead_tuples;
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
|
@ -1417,7 +1433,8 @@ static IndexBulkDeleteResult** lazy_scan_heap(
|
|||
* Each non-removable tuple must be checked to see if it needs
|
||||
* freezing. Note we already have exclusive buffer lock.
|
||||
*/
|
||||
if (heap_freeze_tuple(&tuple, u_sess->cmd_cxt.FreezeLimit))
|
||||
if (heap_freeze_tuple(&tuple, u_sess->cmd_cxt.FreezeLimit, u_sess->cmd_cxt.MultiXactFrzLimit,
|
||||
&changedMultiXid))
|
||||
frozen[nfrozen++] = offnum;
|
||||
}
|
||||
|
||||
|
@ -1435,7 +1452,9 @@ static IndexBulkDeleteResult** lazy_scan_heap(
|
|||
if (RelationNeedsWAL(onerel)) {
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = log_heap_freeze(onerel, buf, u_sess->cmd_cxt.FreezeLimit, frozen, nfrozen);
|
||||
recptr = log_heap_freeze(onerel, buf, u_sess->cmd_cxt.FreezeLimit,
|
||||
changedMultiXid ? u_sess->cmd_cxt.MultiXactFrzLimit : InvalidMultiXactId,
|
||||
frozen, nfrozen);
|
||||
PageSetLSN(page, recptr);
|
||||
}
|
||||
END_CRIT_SECTION();
|
||||
|
@ -1782,7 +1801,7 @@ static bool lazy_check_needs_freeze(Buffer buf)
|
|||
HeapTupleCopyBaseFromPage(&tuple, page);
|
||||
ItemPointerSet(&(tuple.t_self), BufferGetBlockNumber(buf), offnum);
|
||||
|
||||
if (heap_tuple_needs_freeze(&tuple, u_sess->cmd_cxt.FreezeLimit, buf))
|
||||
if (heap_tuple_needs_freeze(&tuple, u_sess->cmd_cxt.FreezeLimit, u_sess->cmd_cxt.MultiXactFrzLimit, buf))
|
||||
return true;
|
||||
} /* scan along page */
|
||||
|
||||
|
|
|
@ -885,11 +885,11 @@ static SpecialJoinInfo* make_outerjoininfo(
|
|||
"unexpected join type.");
|
||||
|
||||
/*
|
||||
* Presently the executor cannot support FOR UPDATE/SHARE marking of rels
|
||||
* Presently the executor cannot support FOR [KEY] UPDATE/SHARE marking of rels
|
||||
* appearing on the nullable side of an outer join. (It's somewhat unclear
|
||||
* what that would mean, anyway: what should we mark when a result row is
|
||||
* generated from no element of the nullable relation?) So, complain if
|
||||
* any nullable rel is FOR UPDATE/SHARE.
|
||||
* any nullable rel is FOR [KEY] UPDATE/SHARE.
|
||||
*
|
||||
* You might be wondering why this test isn't made far upstream in the
|
||||
* parser. It's because the parser hasn't got enough info --- consider
|
||||
|
@ -906,7 +906,8 @@ static SpecialJoinInfo* make_outerjoininfo(
|
|||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT),
|
||||
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of an outer join")));
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be applied to the nullable side "
|
||||
"of an outer join")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1781,7 +1781,7 @@ Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_ro
|
|||
returningLists = NIL;
|
||||
|
||||
/*
|
||||
* If there was a FOR UPDATE/SHARE clause, the LockRows node will
|
||||
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will
|
||||
* have dealt with fetching non-locked marked rows, else we need
|
||||
* to have ModifyTable do that.
|
||||
*/
|
||||
|
@ -2298,7 +2298,7 @@ static Plan* inheritance_planner(PlannerInfo* root)
|
|||
root->simple_rel_array = save_rel_array;
|
||||
root->simple_rte_array = save_rte_array;
|
||||
/*
|
||||
* If there was a FOR UPDATE/SHARE clause, the LockRows node will have
|
||||
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will have
|
||||
* dealt with fetching non-locked marked rows, else we need to have
|
||||
* ModifyTable do that.
|
||||
*/
|
||||
|
@ -2649,13 +2649,14 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
tlist = postprocess_setop_tlist((List*)copyObject(result_plan->targetlist), tlist);
|
||||
|
||||
/*
|
||||
* Can't handle FOR UPDATE/SHARE here (parser should have checked
|
||||
* Can't handle FOR [KEY] UPDATE/SHARE here (parser should have checked
|
||||
* already, but let's make sure).
|
||||
*/
|
||||
if (parse->rowMarks)
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"),
|
||||
errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE is not allowed "
|
||||
"with UNION/INTERSECT/EXCEPT"),
|
||||
errdetail("N/A"),
|
||||
errcause("SQL uses unsupported feature."),
|
||||
erraction("Modify SQL statement according to the manual.")));
|
||||
|
@ -4142,7 +4143,7 @@ static Plan* grouping_planner(PlannerInfo* root, double tuple_fraction)
|
|||
}
|
||||
|
||||
/*
|
||||
* If there is a FOR UPDATE/SHARE clause, add the LockRows node. (Note: we
|
||||
* If there is a FOR [KEY] UPDATE/SHARE clause, add the LockRows node. (Note: we
|
||||
* intentionally test parse->rowMarks not root->rowMarks here. If there
|
||||
* are only non-locking rowmarks, they should be handled by the
|
||||
* ModifyTable node instead.)
|
||||
|
@ -4832,7 +4833,7 @@ static void preprocess_rowmarks(PlannerInfo* root)
|
|||
|
||||
if (parse->rowMarks) {
|
||||
/*
|
||||
* We've got trouble if FOR UPDATE/SHARE appears inside grouping,
|
||||
* We've got trouble if FOR [KEY] UPDATE/SHARE appears inside grouping,
|
||||
* since grouping renders a reference to individual tuple CTIDs
|
||||
* invalid. This is also checked at parse time, but that's
|
||||
* insufficient because of rule substitution, query pullup, etc.
|
||||
|
@ -4840,7 +4841,7 @@ static void preprocess_rowmarks(PlannerInfo* root)
|
|||
CheckSelectLocking(parse);
|
||||
} else {
|
||||
/*
|
||||
* We only need rowmarks for UPDATE, DELETE, MEREG INTO, or FOR UPDATE/SHARE.
|
||||
* We only need rowmarks for UPDATE, DELETE, MEREG INTO, or FOR [KEY] UPDATE/SHARE.
|
||||
*/
|
||||
if (parse->commandType != CMD_UPDATE && parse->commandType != CMD_DELETE &&
|
||||
(parse->commandType != CMD_MERGE || (u_sess->opt_cxt.is_stream == false && IS_SINGLE_NODE == false)))
|
||||
|
@ -4850,7 +4851,7 @@ static void preprocess_rowmarks(PlannerInfo* root)
|
|||
/*
|
||||
* We need to have rowmarks for all base relations except the target. We
|
||||
* make a bitmapset of all base rels and then remove the items we don't
|
||||
* need or have FOR UPDATE/SHARE marks for.
|
||||
* need or have FOR [KEY] UPDATE/SHARE marks for.
|
||||
*/
|
||||
rels = get_base_rel_indexes((Node*)parse->jointree);
|
||||
if (parse->resultRelation)
|
||||
|
@ -4897,10 +4898,23 @@ static void preprocess_rowmarks(PlannerInfo* root)
|
|||
newrc = makeNode(PlanRowMark);
|
||||
newrc->rti = newrc->prti = rc->rti;
|
||||
newrc->rowmarkId = ++(root->glob->lastRowMarkId);
|
||||
if (rc->forUpdate)
|
||||
newrc->markType = ROW_MARK_EXCLUSIVE;
|
||||
else
|
||||
newrc->markType = ROW_MARK_SHARE;
|
||||
switch (rc->strength) {
|
||||
case LCS_FORUPDATE:
|
||||
newrc->markType = ROW_MARK_EXCLUSIVE;
|
||||
break;
|
||||
case LCS_FORNOKEYUPDATE:
|
||||
newrc->markType = ROW_MARK_NOKEYEXCLUSIVE;
|
||||
break;
|
||||
case LCS_FORSHARE:
|
||||
newrc->markType = ROW_MARK_SHARE;
|
||||
break;
|
||||
case LCS_FORKEYSHARE:
|
||||
newrc->markType = ROW_MARK_KEYSHARE;
|
||||
break;
|
||||
default:
|
||||
ereport(ERROR, (errmsg("unknown lock type: %d", rc->strength)));
|
||||
break;
|
||||
}
|
||||
newrc->noWait = rc->noWait;
|
||||
newrc->isParent = false;
|
||||
newrc->bms_nodeids = ng_get_baserel_data_nodeids(rte->relid, rte->relkind);
|
||||
|
|
|
@ -73,7 +73,7 @@ static TargetEntry* process_matched_tle(TargetEntry* src_tle, TargetEntry* prior
|
|||
static Node* get_assignment_input(Node* node);
|
||||
static void rewriteValuesRTE(RangeTblEntry* rte, Relation target_relation, List* attrnos);
|
||||
static void rewriteTargetListUD(Query* parsetree, RangeTblEntry* target_rte, Relation target_relation);
|
||||
static void markQueryForLocking(Query* qry, Node* jtnode, bool forUpdate, bool noWait, bool pushedDown);
|
||||
static void markQueryForLocking(Query* qry, Node* jtnode, LockClauseStrength strength, bool noWait, bool pushedDown);
|
||||
static List* matchLocks(CmdType event, RuleLock* rulelocks, int varno, Query* parsetree);
|
||||
static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePushedDown);
|
||||
|
||||
|
@ -152,7 +152,7 @@ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown)
|
|||
*
|
||||
* If the relation is the query's result relation, then we
|
||||
* need RowExclusiveLock. Otherwise, check to see if the
|
||||
* relation is accessed FOR UPDATE/SHARE or not. We can't
|
||||
* relation is accessed FOR [KEY] UPDATE/SHARE or not. We can't
|
||||
* just grab AccessShareLock because then the executor would
|
||||
* be trying to upgrade the lock, leading to possible
|
||||
* deadlocks.
|
||||
|
@ -1555,7 +1555,7 @@ static Query* ApplyRetrieveRule(Query* parsetree, RewriteRule* rule, int rt_inde
|
|||
}
|
||||
|
||||
/*
|
||||
* If FOR UPDATE/SHARE of view, be sure we get right initial lock on the
|
||||
* If FOR [KEY] UPDATE/SHARE of view, be sure we get right initial lock on the
|
||||
* relations it references.
|
||||
*/
|
||||
rc = get_parse_rowmark(parsetree, rt_index);
|
||||
|
@ -1607,21 +1607,21 @@ static Query* ApplyRetrieveRule(Query* parsetree, RewriteRule* rule, int rt_inde
|
|||
rte->extraUpdatedCols = NULL;
|
||||
|
||||
/*
|
||||
* If FOR UPDATE/SHARE of view, mark all the contained tables as implicit
|
||||
* FOR UPDATE/SHARE, the same as the parser would have done if the view's
|
||||
* If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as implicit
|
||||
* FOR [KEY] UPDATE/SHARE, the same as the parser would have done if the view's
|
||||
* subquery had been written out explicitly.
|
||||
*
|
||||
* Note: we don't consider forUpdatePushedDown here; such marks will be
|
||||
* made by recursing from the upper level in markQueryForLocking.
|
||||
*/
|
||||
if (rc != NULL)
|
||||
markQueryForLocking(rule_action, (Node*)rule_action->jointree, rc->forUpdate, rc->noWait, true);
|
||||
markQueryForLocking(rule_action, (Node*)rule_action->jointree, rc->strength, rc->noWait, true);
|
||||
|
||||
return parsetree;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively mark all relations used by a view as FOR UPDATE/SHARE.
|
||||
* Recursively mark all relations used by a view as FOR [KEY] UPDATE/SHARE.
|
||||
*
|
||||
* This may generate an invalid query, eg if some sub-query uses an
|
||||
* aggregate. We leave it to the planner to detect that.
|
||||
|
@ -1631,7 +1631,7 @@ static Query* ApplyRetrieveRule(Query* parsetree, RewriteRule* rule, int rt_inde
|
|||
* OLD and NEW rels for updating. The best way to handle that seems to be
|
||||
* to scan the jointree to determine which rels are used.
|
||||
*/
|
||||
static void markQueryForLocking(Query* qry, Node* jtnode, bool forUpdate, bool noWait, bool pushedDown)
|
||||
static void markQueryForLocking(Query* qry, Node* jtnode, LockClauseStrength strength, bool noWait, bool pushedDown)
|
||||
{
|
||||
if (jtnode == NULL)
|
||||
return;
|
||||
|
@ -1640,12 +1640,12 @@ static void markQueryForLocking(Query* qry, Node* jtnode, bool forUpdate, bool n
|
|||
RangeTblEntry* rte = rt_fetch(rti, qry->rtable);
|
||||
|
||||
if (rte->rtekind == RTE_RELATION) {
|
||||
applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
|
||||
applyLockingClause(qry, rti, strength, noWait, pushedDown);
|
||||
rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
|
||||
} else if (rte->rtekind == RTE_SUBQUERY) {
|
||||
applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
|
||||
/* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
|
||||
markQueryForLocking(rte->subquery, (Node*)rte->subquery->jointree, forUpdate, noWait, true);
|
||||
applyLockingClause(qry, rti, strength, noWait, pushedDown);
|
||||
/* FOR [KYE] UPDATE/SHARE of subquery is propagated to subquery's rels */
|
||||
markQueryForLocking(rte->subquery, (Node*)rte->subquery->jointree, strength, noWait, true);
|
||||
}
|
||||
/* other RTE types are unaffected by FOR UPDATE */
|
||||
} else if (IsA(jtnode, FromExpr)) {
|
||||
|
@ -1653,12 +1653,12 @@ static void markQueryForLocking(Query* qry, Node* jtnode, bool forUpdate, bool n
|
|||
ListCell* l = NULL;
|
||||
|
||||
foreach (l, f->fromlist)
|
||||
markQueryForLocking(qry, (Node*)lfirst(l), forUpdate, noWait, pushedDown);
|
||||
markQueryForLocking(qry, (Node*)lfirst(l), strength, noWait, pushedDown);
|
||||
} else if (IsA(jtnode, JoinExpr)) {
|
||||
JoinExpr* j = (JoinExpr*)jtnode;
|
||||
|
||||
markQueryForLocking(qry, j->larg, forUpdate, noWait, pushedDown);
|
||||
markQueryForLocking(qry, j->rarg, forUpdate, noWait, pushedDown);
|
||||
markQueryForLocking(qry, j->larg, strength, noWait, pushedDown);
|
||||
markQueryForLocking(qry, j->rarg, strength, noWait, pushedDown);
|
||||
} else
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_OPT_REWRITE),
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
#include "access/twophase.h"
|
||||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_database.h"
|
||||
|
@ -137,6 +138,7 @@ typedef struct avw_dbase {
|
|||
Oid adw_datid;
|
||||
char* adw_name;
|
||||
TransactionId adw_frozenxid;
|
||||
MultiXactId adw_frozenmulti;
|
||||
PgStat_StatDBEntry* adw_entry;
|
||||
} avw_dbase;
|
||||
|
||||
|
@ -863,7 +865,11 @@ static Oid do_start_worker(void)
|
|||
List* dblist = NIL;
|
||||
ListCell* cell = NULL;
|
||||
TransactionId xidForceLimit;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
MultiXactId multiForceLimit;
|
||||
#endif
|
||||
bool for_xid_wrap = false;
|
||||
bool for_multi_wrap = false;
|
||||
avw_dbase* avdb = NULL;
|
||||
TimestampTz current_time;
|
||||
bool skipit = false;
|
||||
|
@ -906,6 +912,16 @@ static Oid do_start_worker(void)
|
|||
else
|
||||
xidForceLimit = FirstNormalTransactionId;
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
/* Also determine the oldest datminmxid we will consider. */
|
||||
t_thrd.autovacuum_cxt.recentMulti = ReadNextMultiXactId();
|
||||
if (t_thrd.autovacuum_cxt.recentMulti >
|
||||
FirstMultiXactId + g_instance.attr.attr_storage.autovacuum_freeze_max_age)
|
||||
multiForceLimit = t_thrd.autovacuum_cxt.recentMulti - g_instance.attr.attr_storage.autovacuum_freeze_max_age;
|
||||
else
|
||||
multiForceLimit = FirstMultiXactId;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Choose a database to connect to. We pick the database that was least
|
||||
* recently auto-vacuumed, or one that needs vacuuming to recycle clog.
|
||||
|
@ -923,6 +939,7 @@ static Oid do_start_worker(void)
|
|||
*/
|
||||
avdb = NULL;
|
||||
for_xid_wrap = false;
|
||||
for_multi_wrap = false;
|
||||
current_time = GetCurrentTimestamp();
|
||||
foreach (cell, dblist) {
|
||||
avw_dbase* tmp = (avw_dbase*)lfirst(cell);
|
||||
|
@ -936,6 +953,15 @@ static Oid do_start_worker(void)
|
|||
continue;
|
||||
} else if (for_xid_wrap)
|
||||
continue; /* ignore not-at-risk DBs */
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
else if (MultiXactIdPrecedes(tmp->adw_frozenmulti, multiForceLimit)) {
|
||||
if (avdb == NULL || MultiXactIdPrecedes(tmp->adw_frozenmulti, avdb->adw_frozenmulti))
|
||||
avdb = tmp;
|
||||
for_multi_wrap = true;
|
||||
continue;
|
||||
} else if (for_multi_wrap)
|
||||
continue; /* ignore not-at-risk DBs */
|
||||
#endif
|
||||
|
||||
/* Find pgstat entry if any */
|
||||
tmp->adw_entry = pgstat_fetch_stat_dbentry(tmp->adw_datid);
|
||||
|
@ -1359,6 +1385,7 @@ NON_EXEC_STATIC void AutoVacWorkerMain()
|
|||
|
||||
/* And do an appropriate amount of work */
|
||||
t_thrd.autovacuum_cxt.recentXid = ReadNewTransactionId();
|
||||
t_thrd.autovacuum_cxt.recentMulti = ReadNextMultiXactId();
|
||||
do_autovacuum();
|
||||
}
|
||||
|
||||
|
@ -1576,7 +1603,11 @@ static List* get_database_list(void)
|
|||
datfrozenxid = FirstNormalTransactionId;
|
||||
} else
|
||||
datfrozenxid = DatumGetTransactionId(xid64datum);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum mxidDatum = heap_getattr(tup, Anum_pg_database_datminmxid, RelationGetDescr(rel), &isNull);
|
||||
MultiXactId datminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(mxidDatum);
|
||||
avdb->adw_frozenmulti = datminmxid;
|
||||
#endif
|
||||
avdb->adw_frozenxid = datfrozenxid;
|
||||
/* this gets set later: */
|
||||
avdb->adw_entry = NULL;
|
||||
|
@ -2873,7 +2904,7 @@ static autovac_table* table_recheck_autovac(
|
|||
*/
|
||||
static void determine_vacuum_params(float4& vac_scale_factor, int& vac_base_thresh, float4& anl_scale_factor,
|
||||
int& anl_base_thresh, int64& freeze_max_age, bool& av_enabled, TransactionId& xidForceLimit,
|
||||
const AutoVacOpts* relopts)
|
||||
MultiXactId& multiForceLimit, const AutoVacOpts* relopts)
|
||||
{
|
||||
/* -1 in autovac setting means use plain vacuum_cost_delay */
|
||||
vac_scale_factor = (relopts && relopts->vacuum_scale_factor >= 0) ? relopts->vacuum_scale_factor
|
||||
|
@ -2899,6 +2930,15 @@ static void determine_vacuum_params(float4& vac_scale_factor, int& vac_base_thre
|
|||
xidForceLimit = t_thrd.autovacuum_cxt.recentXid - freeze_max_age;
|
||||
else
|
||||
xidForceLimit = FirstNormalTransactionId;
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (t_thrd.autovacuum_cxt.recentMulti >
|
||||
FirstMultiXactId + g_instance.attr.attr_storage.autovacuum_freeze_max_age)
|
||||
multiForceLimit = t_thrd.autovacuum_cxt.recentMulti -
|
||||
g_instance.attr.attr_storage.autovacuum_freeze_max_age;
|
||||
else
|
||||
multiForceLimit = FirstMultiXactId;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2968,18 +3008,18 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts* relopts, Form_pg_c
|
|||
/* freeze parameters */
|
||||
int64 freeze_max_age = 0;
|
||||
TransactionId xidForceLimit = InvalidTransactionId;
|
||||
MultiXactId multiForceLimit = InvalidMultiXactId;
|
||||
|
||||
AssertArg(classForm != NULL);
|
||||
AssertArg(OidIsValid(relid));
|
||||
|
||||
determine_vacuum_params(vac_scale_factor, vac_base_thresh, anl_scale_factor, anl_base_thresh, freeze_max_age,
|
||||
av_enabled, xidForceLimit, relopts);
|
||||
av_enabled, xidForceLimit, multiForceLimit, relopts);
|
||||
|
||||
bool isNull = false;
|
||||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
Relation rel = heap_open(RelationRelationId, AccessShareLock);
|
||||
Datum xid64datum = heap_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &isNull);
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
if (isNull) {
|
||||
relfrozenxid = classForm->relfrozenxid;
|
||||
|
@ -2993,6 +3033,14 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts* relopts, Form_pg_c
|
|||
}
|
||||
|
||||
force_vacuum = (TransactionIdIsNormal(relfrozenxid) && TransactionIdPrecedes(relfrozenxid, xidForceLimit));
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (!force_vacuum) {
|
||||
Datum mxidDatum = heap_getattr(tuple, Anum_pg_class_relminmxid, RelationGetDescr(rel), &isNull);
|
||||
MultiXactId relminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(mxidDatum);
|
||||
force_vacuum = (MultiXactIdIsValid(relminmxid) && MultiXactIdPrecedes(relminmxid, multiForceLimit));
|
||||
}
|
||||
#endif
|
||||
heap_close(rel, AccessShareLock);
|
||||
*need_freeze = force_vacuum;
|
||||
AUTOVAC_LOG(DEBUG2, "vac \"%s\": need freeze is %s", NameStr(classForm->relname), force_vacuum ? "true" : "false");
|
||||
|
||||
|
@ -3366,6 +3414,7 @@ static void partition_needs_vacanalyze(Oid partid, AutoVacOpts* relopts, Form_pg
|
|||
/* freeze parameters */
|
||||
int64 freeze_max_age = 0;
|
||||
TransactionId xidForceLimit = InvalidTransactionId;
|
||||
MultiXactId multiForceLimit = InvalidMultiXactId;
|
||||
|
||||
char* relname = NULL;
|
||||
Oid nameSpaceOid = InvalidOid;
|
||||
|
@ -3373,7 +3422,7 @@ static void partition_needs_vacanalyze(Oid partid, AutoVacOpts* relopts, Form_pg
|
|||
|
||||
AssertArg(partForm != NULL && OidIsValid(partid));
|
||||
determine_vacuum_params(vac_scale_factor, vac_base_thresh, anl_scale_factor, anl_base_thresh, freeze_max_age,
|
||||
av_enabled, xidForceLimit, relopts);
|
||||
av_enabled, xidForceLimit, multiForceLimit, relopts);
|
||||
/* Force vacuum if table need freeze the old tuple to recycle clog */
|
||||
if (t_thrd.autovacuum_cxt.recentXid > FirstNormalTransactionId + freeze_max_age)
|
||||
xidForceLimit = t_thrd.autovacuum_cxt.recentXid - freeze_max_age;
|
||||
|
@ -3384,7 +3433,6 @@ static void partition_needs_vacanalyze(Oid partid, AutoVacOpts* relopts, Form_pg
|
|||
TransactionId relfrozenxid = InvalidTransactionId;
|
||||
Relation rel = heap_open(PartitionRelationId, AccessShareLock);
|
||||
Datum xid64datum = heap_getattr(partTuple, Anum_pg_partition_relfrozenxid64, RelationGetDescr(rel), &isNull);
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
if (isNull) {
|
||||
relfrozenxid = partForm->relfrozenxid;
|
||||
|
@ -3397,6 +3445,15 @@ static void partition_needs_vacanalyze(Oid partid, AutoVacOpts* relopts, Form_pg
|
|||
relfrozenxid = DatumGetTransactionId(xid64datum);
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (!force_vacuum) {
|
||||
Datum mxidDatum = heap_getattr(partTuple, Anum_pg_partition_relminmxid, RelationGetDescr(rel), &isNull);
|
||||
MultiXactId relminmxid = isNull ? FirstMultiXactId : DatumGetTransactionId(mxidDatum);
|
||||
force_vacuum = (MultiXactIdIsValid(relminmxid) && MultiXactIdPrecedes(relminmxid, multiForceLimit));
|
||||
}
|
||||
#endif
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
force_vacuum = (TransactionIdIsNormal(relfrozenxid) && TransactionIdPrecedes(relfrozenxid, xidForceLimit));
|
||||
*need_freeze = force_vacuum;
|
||||
|
||||
|
|
|
@ -343,7 +343,7 @@ bool CommandIsReadOnly(Node* parse_tree)
|
|||
switch (stmt->commandType) {
|
||||
case CMD_SELECT:
|
||||
if (stmt->rowMarks != NIL)
|
||||
return false; /* SELECT FOR UPDATE/SHARE */
|
||||
return false; /* SELECT FOR [KEY] UPDATE/SHARE */
|
||||
else if (stmt->hasModifyingCTE)
|
||||
return false; /* data-modifying CTE */
|
||||
else
|
||||
|
@ -8522,10 +8522,23 @@ const char* CreateCommandTag(Node* parse_tree)
|
|||
tag = "DECLARE CURSOR";
|
||||
} else if (stmt->rowMarks != NIL) {
|
||||
/* not 100% but probably close enough */
|
||||
if (((PlanRowMark*)linitial(stmt->rowMarks))->markType == ROW_MARK_EXCLUSIVE)
|
||||
tag = "SELECT FOR UPDATE";
|
||||
else
|
||||
tag = "SELECT FOR SHARE";
|
||||
switch (((PlanRowMark *)linitial(stmt->rowMarks))->markType) {
|
||||
case ROW_MARK_EXCLUSIVE:
|
||||
tag = "SELECT FOR UPDATE";
|
||||
break;
|
||||
case ROW_MARK_NOKEYEXCLUSIVE:
|
||||
tag = "SELECT FOR NO KEY UPDATE";
|
||||
break;
|
||||
case ROW_MARK_SHARE:
|
||||
tag = "SELECT FOR SHARE";
|
||||
break;
|
||||
case ROW_MARK_KEYSHARE:
|
||||
tag = "SELECT FOR KEY SHARE";
|
||||
break;
|
||||
default:
|
||||
tag = "SELECT";
|
||||
break;
|
||||
}
|
||||
} else
|
||||
tag = "SELECT";
|
||||
break;
|
||||
|
@ -8566,10 +8579,23 @@ const char* CreateCommandTag(Node* parse_tree)
|
|||
tag = "DECLARE CURSOR";
|
||||
} else if (stmt->rowMarks != NIL) {
|
||||
/* not 100% but probably close enough */
|
||||
if (((RowMarkClause*)linitial(stmt->rowMarks))->forUpdate)
|
||||
tag = "SELECT FOR UPDATE";
|
||||
else
|
||||
tag = "SELECT FOR SHARE";
|
||||
switch (((RowMarkClause *)linitial(stmt->rowMarks))->strength) {
|
||||
case LCS_FORKEYSHARE:
|
||||
tag = "SELECT FOR KEY SHARE";
|
||||
break;
|
||||
case LCS_FORSHARE:
|
||||
tag = "SELECT FOR SHARE";
|
||||
break;
|
||||
case LCS_FORNOKEYUPDATE:
|
||||
tag = "SELECT FOR NO KEY UPDATE";
|
||||
break;
|
||||
case LCS_FORUPDATE:
|
||||
tag = "SELECT FOR UPDATE";
|
||||
break;
|
||||
default:
|
||||
tag = "?\?\?";
|
||||
break;
|
||||
}
|
||||
} else
|
||||
tag = "SELECT";
|
||||
break;
|
||||
|
|
|
@ -317,7 +317,7 @@ void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
|
|||
switch (queryDesc->operation) {
|
||||
case CMD_SELECT:
|
||||
/*
|
||||
* SELECT FOR UPDATE/SHARE and modifying CTEs need to mark tuples
|
||||
* SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark tuples
|
||||
*/
|
||||
if (queryDesc->plannedstmt->rowMarks != NIL || queryDesc->plannedstmt->hasModifyingCTE) {
|
||||
estate->es_output_cid = GetCurrentCommandId(true);
|
||||
|
@ -1241,7 +1241,7 @@ void InitPlan(QueryDesc *queryDesc, int eflags)
|
|||
}
|
||||
|
||||
/*
|
||||
* Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE
|
||||
* Similarly, we have to lock relations selected FOR [KEY] UPDATE/SHARE
|
||||
* before we initialize the plan tree, else we'd be risking lock upgrades.
|
||||
* While we are at it, build the ExecRowMark list.
|
||||
*/
|
||||
|
@ -1264,7 +1264,9 @@ void InitPlan(QueryDesc *queryDesc, int eflags)
|
|||
*/
|
||||
switch (rc->markType) {
|
||||
case ROW_MARK_EXCLUSIVE:
|
||||
case ROW_MARK_NOKEYEXCLUSIVE:
|
||||
case ROW_MARK_SHARE:
|
||||
case ROW_MARK_KEYSHARE:
|
||||
if (IS_PGXC_COORDINATOR || u_sess->pgxc_cxt.PGXCNodeId < 0 ||
|
||||
bms_is_member(u_sess->pgxc_cxt.PGXCNodeId, rc->bms_nodeids)) {
|
||||
relid = getrelid(rc->rti, rangeTable);
|
||||
|
@ -1979,7 +1981,7 @@ static void ExecEndPlan(PlanState *planstate, EState *estate)
|
|||
}
|
||||
|
||||
/*
|
||||
* close any relations selected FOR UPDATE/FOR SHARE, again keeping locks
|
||||
* close any relations selected FOR [KEY] UPDATE/SHARE, again keeping locks
|
||||
*/
|
||||
foreach (l, estate->es_rowMarks) {
|
||||
ExecRowMark *erm = (ExecRowMark *)lfirst(l);
|
||||
|
@ -2773,6 +2775,7 @@ TupleTableSlot *EvalPlanQualUHeap(EState *estate, EPQState *epqstate, Relation r
|
|||
* epqstate - state for EvalPlanQual rechecking
|
||||
* relation - table containing tuple
|
||||
* rti - rangetable index of table containing tuple
|
||||
* lockmode - requested tuple lock mode
|
||||
* *tid - t_ctid from the outdated tuple (ie, next updated version)
|
||||
* priorXmax - t_xmax from the outdated tuple
|
||||
*
|
||||
|
@ -2781,9 +2784,12 @@ TupleTableSlot *EvalPlanQualUHeap(EState *estate, EPQState *epqstate, Relation r
|
|||
*
|
||||
* Returns a slot containing the new candidate update/delete tuple, or
|
||||
* NULL if we determine we shouldn't process the row.
|
||||
*
|
||||
* Note: properly, lockmode should be declared as enum LockTupleMode,
|
||||
* but we use "int" to avoid having to include heapam.h in executor.h.
|
||||
*/
|
||||
TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, ItemPointer tid,
|
||||
TransactionId priorXmax, bool partRowMoveUpdate)
|
||||
TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode,
|
||||
ItemPointer tid, TransactionId priorXmax, bool partRowMoveUpdate)
|
||||
{
|
||||
TupleTableSlot *slot = NULL;
|
||||
Tuple copyTuple;
|
||||
|
@ -2793,7 +2799,7 @@ TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relati
|
|||
/*
|
||||
* Get and lock the updated version of the row; if fail, return NULL.
|
||||
*/
|
||||
copyTuple = tableam_tuple_lock_updated(estate->es_output_cid, relation, LockTupleExclusive, tid, priorXmax,
|
||||
copyTuple = tableam_tuple_lock_updated(estate->es_output_cid, relation, lockmode, tid, priorXmax,
|
||||
estate->es_snapshot);
|
||||
|
||||
if (copyTuple == NULL) {
|
||||
|
@ -3073,7 +3079,7 @@ HeapTuple heap_lock_updated(CommandId cid, Relation relation, int lockmode, Item
|
|||
/* updated, so look at the updated row */
|
||||
tuple.t_self = tuple.t_data->t_ctid;
|
||||
/* updated row should have xmin matching this xmax */
|
||||
priorXmax = HeapTupleGetRawXmax(&tuple);
|
||||
priorXmax = HeapTupleGetUpdateXid(&tuple);
|
||||
ReleaseBuffer(buffer);
|
||||
/* loop back to fetch next in chain */
|
||||
}
|
||||
|
|
|
@ -835,7 +835,8 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate
|
|||
* occured after taking the snapshot. Skip for explain only commands.
|
||||
*/
|
||||
if (isUstoreRel && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) {
|
||||
TransactionId relfrozenxid64 = getPartitionRelfrozenxid(partitiontrel);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getPartitionRelxids(partitiontrel, &relfrozenxid64);
|
||||
if (TransactionIdPrecedes(FirstNormalTransactionId, scanSnap->xmax) &&
|
||||
!TransactionIdIsCurrentTransactionId(relfrozenxid64) &&
|
||||
TransactionIdPrecedes(scanSnap->xmax, relfrozenxid64)) {
|
||||
|
@ -859,7 +860,8 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate
|
|||
* occured after taking the snapshot. Skip for explain only commands.
|
||||
*/
|
||||
if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY)) {
|
||||
TransactionId relfrozenxid64 = getRelationRelfrozenxid(currentRelation);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getRelationRelxids(currentRelation, &relfrozenxid64);
|
||||
if (TransactionIdPrecedes(FirstNormalTransactionId, scanSnap->xmax) &&
|
||||
!TransactionIdIsCurrentTransactionId(relfrozenxid64) &&
|
||||
TransactionIdPrecedes(scanSnap->xmax, relfrozenxid64)) {
|
||||
|
|
|
@ -711,7 +711,8 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i
|
|||
* occured after taking the snapshot.
|
||||
*/
|
||||
if (RelationIsUstoreFormat(indexstate->ss.ss_currentPartition)) {
|
||||
TransactionId relfrozenxid64 = getPartitionRelfrozenxid(indexstate->ss.ss_currentPartition);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getPartitionRelxids(indexstate->ss.ss_currentPartition, &relfrozenxid64);
|
||||
if (TransactionIdPrecedes(FirstNormalTransactionId, scanSnap->xmax) &&
|
||||
!TransactionIdIsCurrentTransactionId(relfrozenxid64) &&
|
||||
TransactionIdPrecedes(scanSnap->xmax, relfrozenxid64)) {
|
||||
|
@ -735,7 +736,8 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i
|
|||
* occured after taking the snapshot.
|
||||
*/
|
||||
if (RelationIsUstoreFormat(currentRelation)) {
|
||||
TransactionId relfrozenxid64 = getRelationRelfrozenxid(currentRelation);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getRelationRelxids(currentRelation, &relfrozenxid64);
|
||||
if (TransactionIdPrecedes(FirstNormalTransactionId, scanSnap->xmax) &&
|
||||
!TransactionIdIsCurrentTransactionId(relfrozenxid64) &&
|
||||
TransactionIdPrecedes(scanSnap->xmax, relfrozenxid64)) {
|
||||
|
|
|
@ -708,7 +708,8 @@ IndexScanState* ExecInitIndexScan(IndexScan* node, EState* estate, int eflags)
|
|||
* occured after taking the snapshot.
|
||||
*/
|
||||
if (RelationIsUstoreFormat(index_state->ss.ss_currentPartition)) {
|
||||
TransactionId relfrozenxid64 = getPartitionRelfrozenxid(index_state->ss.ss_currentPartition);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getPartitionRelxids(index_state->ss.ss_currentPartition, &relfrozenxid64);
|
||||
if (TransactionIdPrecedes(FirstNormalTransactionId, scanSnap->xmax) &&
|
||||
!TransactionIdIsCurrentTransactionId(relfrozenxid64) &&
|
||||
TransactionIdPrecedes(scanSnap->xmax, relfrozenxid64)) {
|
||||
|
@ -733,7 +734,8 @@ IndexScanState* ExecInitIndexScan(IndexScan* node, EState* estate, int eflags)
|
|||
* occured after taking the snapshot.
|
||||
*/
|
||||
if (RelationIsUstoreFormat(current_relation)) {
|
||||
TransactionId relfrozenxid64 = getRelationRelfrozenxid(current_relation);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getRelationRelxids(current_relation, &relfrozenxid64);
|
||||
if (TransactionIdPrecedes(FirstNormalTransactionId, scanSnap->xmax) &&
|
||||
!TransactionIdIsCurrentTransactionId(relfrozenxid64) &&
|
||||
TransactionIdPrecedes(scanSnap->xmax, relfrozenxid64)) {
|
||||
|
|
|
@ -181,15 +181,34 @@ lnext:
|
|||
searchHBucketFakeRelation(estate->esfRelations, estate->es_query_cxt, target_rel, bucket_id, bucket_rel);
|
||||
}
|
||||
/* okay, try to lock the tuple */
|
||||
if (erm->markType == ROW_MARK_EXCLUSIVE)
|
||||
lock_mode = LockTupleExclusive;
|
||||
else
|
||||
lock_mode = LockTupleShared;
|
||||
switch (erm->markType) {
|
||||
case ROW_MARK_EXCLUSIVE:
|
||||
lock_mode = LockTupleExclusive;
|
||||
break;
|
||||
case ROW_MARK_NOKEYEXCLUSIVE:
|
||||
lock_mode = LockTupleNoKeyExclusive;
|
||||
break;
|
||||
case ROW_MARK_SHARE:
|
||||
lock_mode = LockTupleShared;
|
||||
break;
|
||||
case ROW_MARK_KEYSHARE:
|
||||
lock_mode = LockTupleKeyShare;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unsupported rowmark type");
|
||||
lock_mode = LockTupleNoKeyExclusive; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Need to merge the ustore logic with AM logic */
|
||||
test = tableam_tuple_lock(bucket_rel, &tuple, &buffer,
|
||||
estate->es_output_cid, lock_mode, erm->noWait, &tmfd,
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
false, false, false, estate->es_snapshot, NULL, true);
|
||||
#else
|
||||
false, true, false, estate->es_snapshot, NULL, true);
|
||||
#endif
|
||||
|
||||
ReleaseBuffer(buffer);
|
||||
|
||||
switch (test) {
|
||||
|
@ -262,8 +281,6 @@ lnext:
|
|||
errmsg("could not serialize access due to concurrent update")));
|
||||
|
||||
/* Tuple was deleted, so don't return it */
|
||||
Assert(ItemPointerEquals(&tmfd.ctid, &tuple.t_self));
|
||||
|
||||
if (rowMovement) {
|
||||
/*
|
||||
* the may be a row movement update action which delete tuple from original
|
||||
|
|
|
@ -1315,7 +1315,7 @@ ldelete:
|
|||
errmsg("concurrent update under Stream mode is not yet supported")));
|
||||
}
|
||||
TupleTableSlot *epqslot = EvalPlanQual(estate, epqstate, fake_relation,
|
||||
result_rel_info->ri_RangeTableIndex, &tmfd.ctid, tmfd.xmax, false);
|
||||
result_rel_info->ri_RangeTableIndex, LockTupleExclusive, &tmfd.ctid, tmfd.xmax, false);
|
||||
if (!TupIsNull(epqslot)) {
|
||||
*tupleid = tmfd.ctid;
|
||||
goto ldelete;
|
||||
|
@ -1652,6 +1652,7 @@ TupleTableSlot* ExecUpdate(ItemPointer tupleid,
|
|||
tuple = tableam_tslot_get_tuple_from_slot(result_relation_desc, slot);
|
||||
} else {
|
||||
bool update_indexes = false;
|
||||
LockTupleMode lockmode;
|
||||
|
||||
/*
|
||||
* Compute stored generated columns
|
||||
|
@ -1721,7 +1722,8 @@ lreplace:
|
|||
/* add para 2 for heap_update */
|
||||
result = tableam_tuple_update(fake_relation, parent_relation, tupleid, tuple, estate->es_output_cid,
|
||||
estate->es_crosscheck_snapshot, estate->es_snapshot, true, // wait for commit
|
||||
&oldslot, &tmfd, &update_indexes, &modifiedIdxAttrs, allow_update_self, allowInplaceUpdate);
|
||||
&oldslot, &tmfd, &update_indexes, &modifiedIdxAttrs, allow_update_self,
|
||||
allowInplaceUpdate, &lockmode);
|
||||
switch (result) {
|
||||
case TM_SelfUpdated:
|
||||
case TM_SelfModified:
|
||||
|
@ -1794,7 +1796,7 @@ lreplace:
|
|||
}
|
||||
|
||||
TupleTableSlot *epq_slot = EvalPlanQual(estate, epqstate, fake_relation,
|
||||
result_rel_info->ri_RangeTableIndex, &tmfd.ctid, tmfd.xmax, false);
|
||||
result_rel_info->ri_RangeTableIndex, lockmode, &tmfd.ctid, tmfd.xmax, false);
|
||||
if (!TupIsNull(epq_slot)) {
|
||||
*tupleid = tmfd.ctid;
|
||||
|
||||
|
@ -1977,7 +1979,8 @@ lreplace:
|
|||
&update_indexes,
|
||||
&modifiedIdxAttrs,
|
||||
allow_update_self,
|
||||
allowInplaceUpdate);
|
||||
allowInplaceUpdate,
|
||||
&lockmode);
|
||||
switch (result) {
|
||||
case TM_SelfUpdated:
|
||||
case TM_SelfModified:
|
||||
|
@ -2036,7 +2039,7 @@ lreplace:
|
|||
}
|
||||
|
||||
TupleTableSlot *epq_slot = EvalPlanQual(estate, epqstate, fake_relation,
|
||||
result_rel_info->ri_RangeTableIndex, &tmfd.ctid, tmfd.xmax,
|
||||
result_rel_info->ri_RangeTableIndex, lockmode, &tmfd.ctid, tmfd.xmax,
|
||||
result_relation_desc->rd_rel->relrowmovement);
|
||||
|
||||
if (!TupIsNull(epq_slot)) {
|
||||
|
@ -2220,6 +2223,7 @@ ldelete:
|
|||
epqstate,
|
||||
old_fake_relation,
|
||||
result_rel_info->ri_RangeTableIndex,
|
||||
LockTupleExclusive,
|
||||
&tmfd.ctid,
|
||||
tmfd.xmax,
|
||||
result_relation_desc->rd_rel->relrowmovement);
|
||||
|
|
|
@ -423,7 +423,8 @@ void InitScanRelation(SeqScanState* node, EState* estate, int eflags)
|
|||
|
||||
if (!node->isPartTbl) {
|
||||
/* add qual for redis */
|
||||
TransactionId relfrozenxid64 = getRelationRelfrozenxid(current_relation);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getRelationRelxids(current_relation, &relfrozenxid64);
|
||||
current_scan_desc = BeginScanRelation(node, current_relation, relfrozenxid64, eflags);
|
||||
} else {
|
||||
plan = (SeqScan*)node->ps.plan;
|
||||
|
@ -491,7 +492,8 @@ void InitScanRelation(SeqScanState* node, EState* estate, int eflags)
|
|||
node->ss_currentPartition = current_part_rel;
|
||||
|
||||
/* add qual for redis */
|
||||
TransactionId relfrozenxid64 = getPartitionRelfrozenxid(current_part_rel);
|
||||
TransactionId relfrozenxid64 = InvalidTransactionId;
|
||||
getPartitionRelxids(current_part_rel, &relfrozenxid64);
|
||||
current_scan_desc = BeginScanRelation(node, current_part_rel, relfrozenxid64, eflags);
|
||||
} else {
|
||||
node->ss_currentPartition = NULL;
|
||||
|
|
|
@ -2863,3 +2863,11 @@ bool is_contain_crossbucket(List *defList)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool is_cstore_option(char relkind, Datum reloptions)
|
||||
{
|
||||
StdRdOptions* std_opt = (StdRdOptions*)heap_reloptions(relkind, reloptions, false);
|
||||
bool result = std_opt == NULL && pg_strcasecmp(ORIENTATION_COLUMN,
|
||||
StdRdOptionsGetStringData(std_opt, orientation, ORIENTATION_ROW)) == 0;
|
||||
pfree_ext(std_opt);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
Locking tuples
|
||||
--------------
|
||||
|
||||
Locking tuples is not as easy as locking tables or other database objects.
|
||||
The problem is that transactions might want to lock large numbers of tuples at
|
||||
any one time, so it's not possible to keep the locks objects in shared memory.
|
||||
To work around this limitation, we use a two-level mechanism. The first level
|
||||
is implemented by storing locking information in the tuple header: a tuple is
|
||||
marked as locked by setting the current transaction's XID as its XMAX, and
|
||||
setting additional infomask bits to distinguish this case from the more normal
|
||||
case of having deleted the tuple. When multiple transactions concurrently
|
||||
lock a tuple, a MultiXact is used; see below. This mechanism can accomodate
|
||||
arbitrarily large numbers of tuples being locked simultaneously.
|
||||
|
||||
When it is necessary to wait for a tuple-level lock to be released, the basic
|
||||
delay is provided by XactLockTableWait or MultiXactIdWait on the contents of
|
||||
the tuple's XMAX. However, that mechanism will release all waiters
|
||||
concurrently, so there would be a race condition as to which waiter gets the
|
||||
tuple, potentially leading to indefinite starvation of some waiters. The
|
||||
possibility of share-locking makes the problem much worse --- a steady stream
|
||||
of share-lockers can easily block an exclusive locker forever. To provide
|
||||
more reliable semantics about who gets a tuple-level lock first, we use the
|
||||
standard lock manager, which implements the second level mentioned above. The
|
||||
protocol for waiting for a tuple-level lock is really
|
||||
|
||||
LockTuple()
|
||||
XactLockTableWait()
|
||||
mark tuple as locked by me
|
||||
UnlockTuple()
|
||||
|
||||
When there are multiple waiters, arbitration of who is to get the lock next
|
||||
is provided by LockTuple(). However, at most one tuple-level lock will
|
||||
be held or awaited per backend at any time, so we don't risk overflow
|
||||
of the lock table. Note that incoming share-lockers are required to
|
||||
do LockTuple as well, if there is any conflict, to ensure that they don't
|
||||
starve out waiting exclusive-lockers. However, if there is not any active
|
||||
conflict for a tuple, we don't incur any extra overhead.
|
||||
|
||||
We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
|
||||
super-exclusive locking (used to delete tuples and more generally to update
|
||||
tuples modifying the values of the columns that make up the key of the tuple);
|
||||
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
|
||||
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
|
||||
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
|
||||
UPDATE. This last mode implements a mode just strong enough to implement RI
|
||||
checks, i.e. it ensures that tuples do not go away from under a check, without
|
||||
blocking when some other transaction that want to update the tuple without
|
||||
changing its key.
|
||||
|
||||
The conflict table is:
|
||||
|
||||
KEY UPDATE UPDATE SHARE KEY SHARE
|
||||
KEY UPDATE conflict conflict conflict conflict
|
||||
UPDATE conflict conflict conflict
|
||||
SHARE conflict conflict
|
||||
KEY SHARE conflict
|
||||
|
||||
When there is a single locker in a tuple, we can just store the locking info
|
||||
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
|
||||
setting infomask bits specifying the locking strength. There is one exception
|
||||
here: since infomask space is limited, we do not provide a separate bit
|
||||
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
|
||||
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
|
||||
presumably more commonly used due to being the standards-mandated locking
|
||||
mechanism, or heavily used by the RI code, so we want to provide fast paths
|
||||
for those.)
|
||||
|
||||
MultiXacts
|
||||
----------
|
||||
|
||||
A tuple header provides very limited space for storing information about tuple
|
||||
locking and updates: there is room only for a single Xid and a small number of
|
||||
infomask bits. Whenever we need to store more than one lock, we replace the
|
||||
first locker's Xid with a new MultiXactId. Each MultiXact provides extended
|
||||
locking data; it comprises an array of Xids plus some flags bits for each one.
|
||||
The flags are currently used to store the locking strength of each member
|
||||
transaction. (The flags also distinguish a pure locker from an updater.)
|
||||
|
||||
In earlier releases, a MultiXact always meant that the tuple was
|
||||
locked in shared mode by multiple transactions. This is no longer the case; a
|
||||
MultiXact may contain an update or delete Xid. (Keep in mind that tuple locks
|
||||
in a transaction do not conflict with other tuple locks in the same
|
||||
transaction, so it's possible to have otherwise conflicting locks in a
|
||||
MultiXact if they belong to the same transaction).
|
||||
|
||||
Note that each lock is attributed to the subtransaction that acquires it.
|
||||
This means that a subtransaction that aborts is seen as though it releases the
|
||||
locks it acquired; concurrent transactions can then proceed without having to
|
||||
wait for the main transaction to finish. It also means that a subtransaction
|
||||
can upgrade to a stronger lock level than an earlier transaction had, and if
|
||||
the subxact aborts, the earlier, weaker lock is kept.
|
||||
|
||||
The possibility of having an update within a MultiXact means that they must
|
||||
persist across crashes and restarts: a future reader of the tuple needs to
|
||||
figure out whether the update committed or aborted. So we have a requirement
|
||||
that pg_multixact needs to retain pages of its data until we're certain that
|
||||
the MultiXacts in them are no longer of interest.
|
||||
|
||||
Infomask Bits
|
||||
-------------
|
||||
|
||||
The following infomask bits are applicable:
|
||||
|
||||
- HEAP_XMAX_INVALID
|
||||
Any tuple with this bit set does not have a valid value stored in XMAX.
|
||||
|
||||
- HEAP_XMAX_IS_MULTI
|
||||
This bit is set if the tuple's Xmax is a MultiXactId (as opposed to a
|
||||
regular TransactionId).
|
||||
|
||||
- HEAP_XMAX_LOCK_ONLY
|
||||
This bit lives in t_infomask2. This bit is set when the XMAX is a locker only;
|
||||
that is, if it's a multixact, it does not contain an update among its members.
|
||||
It's set when the XMAX is a plain Xid that locked the tuple, as well.
|
||||
|
||||
- HEAP_XMAX_KEYSHR_LOCK
|
||||
- HEAP_XMAX_EXCL_LOCK
|
||||
These bits indicate the strength of the lock acquired; they are useful when
|
||||
the XMAX is not a MultiXactId. If it's a multi, the info is to be found in
|
||||
the member flags. If HEAP_XMAX_IS_MULTI is not set and HEAP_XMAX_LOCK_ONLY
|
||||
is set, then one of these *must* be set as well.
|
||||
Note there is no infomask bit for a SELECT FOR SHARE lock. Also there is no
|
||||
separate bit for a SELECT FOR KEY UPDATE lock; this is implemented by the
|
||||
HEAP_KEYS_UPDATED bit.
|
||||
|
||||
- HEAP_KEYS_UPDATED
|
||||
This bit lives in t_infomask2. If set, indicates that the XMAX updated
|
||||
this tuple and changed the key values, or it deleted the tuple.
|
||||
It's set regardless of whether the XMAX is a TransactionId or a MultiXactId.
|
||||
|
||||
We currently never set the HEAP_XMAX_COMMITTED when the HEAP_XMAX_IS_MULTI bit
|
||||
is set.
|
File diff suppressed because it is too large
Load Diff
|
@ -211,10 +211,20 @@ static bool DealCurrentTansactionNotCommited(HeapTupleHeader tuple, Page page, B
|
|||
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
||||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) /* not deleter */
|
||||
return true;
|
||||
|
||||
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
/* updating subtransaction must have aborted */
|
||||
if (!TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
/* deleting subtransaction must have aborted */
|
||||
|
@ -297,19 +307,31 @@ bool HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
|
|||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_COMMITTED) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
return false; /* updated by other */
|
||||
}
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
/* MultiXacts are currently only allowed to lock tuples */
|
||||
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
if (TransactionIdIsCurrentTransactionId(xmax))
|
||||
return false;
|
||||
if (TransactionIdIsInProgress(xmax)) {
|
||||
return true;
|
||||
}
|
||||
if (TransactionIdDidCommit(xmax))
|
||||
return false;
|
||||
/* it must have aborted or crashed */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -329,7 +351,7 @@ bool HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
|
|||
|
||||
/* xmax transaction committed */
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
return true;
|
||||
}
|
||||
|
@ -408,10 +430,22 @@ restart:
|
|||
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
||||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) /* not deleter */
|
||||
return true;
|
||||
|
||||
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
if (!TransactionIdIsValid(xmax)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* updating subtransaction must have aborted */
|
||||
if (!TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
/* deleting subtransaction must have aborted */
|
||||
|
@ -451,19 +485,37 @@ restart:
|
|||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_COMMITTED) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
/* MultiXacts are currently only allowed to lock tuples */
|
||||
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
if (!TransactionIdIsValid(xmax)) {
|
||||
return true;
|
||||
}
|
||||
if (TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
if (HeapTupleHeaderGetCmax(tuple, page) >= GetCurrentCommandId(false)) {
|
||||
return true; /* deleted after scan started */
|
||||
} else {
|
||||
return false; /* deleted before scan started */
|
||||
}
|
||||
}
|
||||
if (TransactionIdIsInProgress(xmax)) {
|
||||
return true;
|
||||
}
|
||||
if (TransactionIdDidCommit(xmax)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
if (HeapTupleHeaderGetCmax(tuple, page) >= GetCurrentCommandId(false))
|
||||
return true; /* deleted after scan started */
|
||||
|
@ -494,7 +546,7 @@ restart:
|
|||
|
||||
/* xmax transaction committed */
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
return true;
|
||||
}
|
||||
|
@ -612,10 +664,51 @@ restart:
|
|||
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
||||
return TM_Ok;
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
||||
return TM_Ok;
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
TransactionId xmax = HeapTupleHeaderGetRawXmax(page, tuple);
|
||||
|
||||
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
||||
/*
|
||||
* Careful here: even though this tuple was created by our own
|
||||
* transaction, it might be locked by other transactions, if
|
||||
* the original version was key-share locked when we updated
|
||||
* it.
|
||||
*/
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
if (MultiXactIdIsRunning(xmax))
|
||||
return TM_BeingModified;
|
||||
else
|
||||
return TM_Ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the locker is gone, then there is nothing of interest
|
||||
* left in this Xmax; otherwise, report the tuple as
|
||||
* locked/updated.
|
||||
*/
|
||||
if (!TransactionIdIsInProgress(xmax))
|
||||
return TM_Ok;
|
||||
|
||||
return TM_BeingModified;
|
||||
}
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
TransactionId xmax= HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
|
||||
/* deleting subtransaction must have aborted */
|
||||
if (!TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(page, tuple)))
|
||||
return TM_BeingModified;
|
||||
return TM_Ok;
|
||||
} else {
|
||||
if (HeapTupleHeaderGetCmax(tuple, page) >= curcid)
|
||||
return TM_SelfModified; /* updated after scan started */
|
||||
else
|
||||
return TM_Invisible; /* updated before scan started */
|
||||
}
|
||||
}
|
||||
|
||||
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
/* deleting subtransaction must have aborted */
|
||||
|
@ -654,7 +747,7 @@ restart:
|
|||
return TM_Ok;
|
||||
}
|
||||
if (tuple->t_infomask & HEAP_XMAX_COMMITTED) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
return TM_Ok;
|
||||
}
|
||||
if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid)) {
|
||||
|
@ -665,19 +758,51 @@ restart:
|
|||
}
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
/* MultiXacts are currently only allowed to lock tuples */
|
||||
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
/*
|
||||
* If it's only locked but HEAP_LOCK_MASK is not set,
|
||||
* it cannot possibly be running. Otherwise need to check.
|
||||
*/
|
||||
if ((tuple->t_infomask & HEAP_LOCK_MASK) &&
|
||||
MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(page, tuple)))
|
||||
return TM_BeingModified;
|
||||
|
||||
if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
return TM_BeingModified;
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
return TM_Ok;
|
||||
}
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
|
||||
if (TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
if (HeapTupleHeaderGetCmax(tuple, page) >= curcid)
|
||||
return TM_SelfModified; /* updated after scan started */
|
||||
else
|
||||
return TM_Invisible; /* updated before scan started */
|
||||
}
|
||||
|
||||
if (TransactionIdIsInProgress(xmax))
|
||||
return TM_BeingModified;
|
||||
|
||||
if (TransactionIdDidCommit(xmax)) {
|
||||
if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid)) {
|
||||
return TM_Updated;
|
||||
} else {
|
||||
return TM_Deleted;
|
||||
}
|
||||
}
|
||||
|
||||
/* no member, even just a locker, alive anymore */
|
||||
if (!MultiXactIdIsRunning(HeapTupleHeaderGetXmax(page, tuple)))
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
/* it must have aborted or crashed */
|
||||
return TM_Ok;
|
||||
}
|
||||
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
return TM_Ok;
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
return TM_BeingModified;
|
||||
}
|
||||
if (HeapTupleHeaderGetCmax(tuple, page) >= curcid) {
|
||||
return TM_SelfModified; /* updated after scan started */
|
||||
|
@ -702,7 +827,7 @@ restart:
|
|||
|
||||
/* xmax transaction committed */
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
return TM_Ok;
|
||||
}
|
||||
|
@ -787,19 +912,33 @@ static bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer bu
|
|||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_COMMITTED) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
return false; /* updated by other */
|
||||
}
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
/* MultiXacts are currently only allowed to lock tuples */
|
||||
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
return true;
|
||||
}
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
if (TransactionIdIsCurrentTransactionId(xmax))
|
||||
return false;
|
||||
if (TransactionIdIsInProgress(xmax)) {
|
||||
snapshot->xmax = xmax;
|
||||
return true;
|
||||
}
|
||||
if (TransactionIdDidCommit(xmax)) {
|
||||
return false;
|
||||
}
|
||||
/* it must have aborted or crashed */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -821,7 +960,7 @@ static bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer bu
|
|||
|
||||
/* xmax transaction committed */
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
return true;
|
||||
}
|
||||
|
@ -896,10 +1035,22 @@ static bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buf
|
|||
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
||||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) /* not deleter */
|
||||
return true;
|
||||
|
||||
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
|
||||
/* updating subtransaction must have aborted */
|
||||
if (!TransactionIdIsCurrentTransactionId(xmax))
|
||||
return true;
|
||||
else if (HeapTupleHeaderGetCmax(tuple, page) >= snapshot->curcid)
|
||||
return true; /* updated after scan started */
|
||||
else
|
||||
return false; /* updated before scan started */
|
||||
}
|
||||
|
||||
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(page, tuple))) {
|
||||
/* deleting subtransaction must have aborted */
|
||||
|
@ -952,12 +1103,28 @@ recheck_xmax:
|
|||
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
|
||||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return true;
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
/* MultiXacts are currently only allowed to lock tuples */
|
||||
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
||||
TransactionId xmax = HeapTupleHeaderMultiXactGetUpdateXid(page, tuple);
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
if (TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
if (HeapTupleHeaderGetCmax(tuple, page) >= snapshot->curcid)
|
||||
return true; /* deleted after scan started */
|
||||
else
|
||||
return false; /* deleted before scan started */
|
||||
}
|
||||
if (TransactionIdIsInProgress(xmax))
|
||||
return true;
|
||||
if (TransactionIdDidCommit(xmax)) {
|
||||
/* updating transaction committed, but when? */
|
||||
if (!CommittedXidVisibleInSnapshot(xmax, snapshot, buffer))
|
||||
return true; /* treat as still in progress */
|
||||
return false;
|
||||
}
|
||||
/* it must have aborted or crashed */
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1104,7 +1271,7 @@ HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, B
|
|||
if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(htup))) {
|
||||
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
||||
return HEAPTUPLE_INSERT_IN_PROGRESS;
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2))
|
||||
return HEAPTUPLE_INSERT_IN_PROGRESS;
|
||||
/* inserted and then deleted by same xact */
|
||||
if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup))) {
|
||||
|
@ -1158,7 +1325,7 @@ HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, B
|
|||
if (tuple->t_infomask & HEAP_XMAX_INVALID)
|
||||
return HEAPTUPLE_LIVE;
|
||||
|
||||
if (tuple->t_infomask & HEAP_IS_LOCKED) {
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2)) {
|
||||
/*
|
||||
* "Deleting" xact really only locked it, so the tuple is live in any
|
||||
* case. However, we should make sure that either XMAX_COMMITTED or
|
||||
|
@ -1174,7 +1341,7 @@ HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, B
|
|||
xidstatus = TransactionIdGetStatus(HeapTupleGetRawXmax(htup));
|
||||
if (xidstatus == XID_INPROGRESS && TransactionIdIsInProgress(HeapTupleGetRawXmax(htup))) {
|
||||
return HEAPTUPLE_LIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1192,8 +1359,36 @@ HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, B
|
|||
}
|
||||
|
||||
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) {
|
||||
/* MultiXacts are currently only allowed to lock tuples */
|
||||
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
||||
TransactionId xmax = HeapTupleHeaderGetUpdateXid(page, tuple);
|
||||
|
||||
/* already checked above */
|
||||
Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask, tuple->t_infomask2));
|
||||
|
||||
/* not LOCKED_ONLY, so it has to have an xmax */
|
||||
Assert(TransactionIdIsValid(xmax));
|
||||
|
||||
if (TransactionIdIsInProgress(xmax)) {
|
||||
return HEAPTUPLE_DELETE_IN_PROGRESS;
|
||||
} else if (TransactionIdDidCommit(xmax)) {
|
||||
/*
|
||||
* The multixact might still be running due to lockers. If the
|
||||
* updater is below the xid horizon, we have to return DEAD
|
||||
* regardless -- otherwise we could end up with a tuple where the
|
||||
* updater has to be removed due to the horizon, but is not pruned
|
||||
* away. It's not a problem to prune that tuple, because any
|
||||
* remaining lockers will also be present in newer tuple versions.
|
||||
*/
|
||||
if (!TransactionIdPrecedes(xmax, OldestXmin))
|
||||
return HEAPTUPLE_RECENTLY_DEAD;
|
||||
return HEAPTUPLE_DEAD;
|
||||
} else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(page, tuple))) {
|
||||
/*
|
||||
* Not in Progress, Not Committed, so either Aborted or crashed.
|
||||
* Mark the Xmax as invalid.
|
||||
*/
|
||||
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
|
||||
}
|
||||
/* it must have aborted or crashed */
|
||||
return HEAPTUPLE_LIVE;
|
||||
}
|
||||
|
||||
|
@ -1263,10 +1458,22 @@ bool HeapTupleIsSurelyDead(HeapTuple tuple, TransactionId OldestXmin)
|
|||
|
||||
/*
|
||||
* If the inserting transaction committed, but any deleting transaction
|
||||
* aborted, the tuple is still alive. Likewise, if XMAX is a lock rather
|
||||
* than a delete, the tuple is still alive.
|
||||
* aborted, the tuple is still alive.
|
||||
*/
|
||||
if (tup->t_infomask & (HEAP_XMAX_INVALID | HEAP_IS_LOCKED | HEAP_XMAX_IS_MULTI))
|
||||
if (tup->t_infomask & HEAP_XMAX_INVALID)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the XMAX is just a lock, the tuple is still alive.
|
||||
*/
|
||||
if (HEAP_XMAX_IS_LOCKED_ONLY(tup->t_infomask, tup->t_infomask2))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the Xmax is a MultiXact, it might be dead or alive, but we cannot
|
||||
* know without checking pg_multixact.
|
||||
*/
|
||||
if (tup->t_infomask & HEAP_XMAX_IS_MULTI)
|
||||
return false;
|
||||
|
||||
/* If deleter isn't known to have committed, assume it's still running. */
|
||||
|
@ -1277,6 +1484,64 @@ bool HeapTupleIsSurelyDead(HeapTuple tuple, TransactionId OldestXmin)
|
|||
return TransactionIdPrecedes(HeapTupleGetRawXmax(tuple), OldestXmin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the tuple really only locked? That is, is it not updated?
|
||||
*
|
||||
* It's easy to check just infomask bits if the locker is not a multi; but
|
||||
* otherwise we need to verify that the updating transaction has not aborted.
|
||||
*
|
||||
* This function is here because it follows the same time qualification rules
|
||||
* laid out at the top of this file.
|
||||
*/
|
||||
bool HeapTupleIsOnlyLocked(HeapTuple tuple)
|
||||
{
|
||||
TransactionId xmax;
|
||||
|
||||
/* if there's no valid Xmax, then there's obviously no update either */
|
||||
if (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tuple->t_data->t_infomask2 & HEAP_XMAX_LOCK_ONLY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* invalid xmax means no update */
|
||||
if (!TransactionIdIsValid(HeapTupleGetRawXmax(tuple))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this
|
||||
* must necessarily have been updated
|
||||
*/
|
||||
if (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ... but if it's a multi, then perhaps the updating Xid aborted. */
|
||||
xmax = HeapTupleMultiXactGetUpdateXid(tuple);
|
||||
if (!TransactionIdIsValid(xmax)) { /* shouldn't happen .. */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TransactionIdIsCurrentTransactionId(xmax)) {
|
||||
return false;
|
||||
}
|
||||
if (TransactionIdIsInProgress(xmax)) {
|
||||
return false;
|
||||
}
|
||||
if (TransactionIdDidCommit(xmax)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* not current, not in progress, not committed -- must have aborted or
|
||||
* crashed
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether the transaciont id 'xid' in in the pre-sorted array 'xip'.
|
||||
*/
|
||||
|
|
|
@ -497,7 +497,7 @@ static int heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rooto
|
|||
* This tuple may soon become DEAD. Update the hint field so
|
||||
* that the page is reconsidered for pruning in future.
|
||||
*/
|
||||
heap_prune_record_prunable(prstate, HeapTupleGetRawXmax(&tup));
|
||||
heap_prune_record_prunable(prstate, HeapTupleGetUpdateXid(&tup));
|
||||
break;
|
||||
|
||||
case HEAPTUPLE_DELETE_IN_PROGRESS:
|
||||
|
@ -506,7 +506,7 @@ static int heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rooto
|
|||
* This tuple may soon become DEAD. Update the hint field so
|
||||
* that the page is reconsidered for pruning in future.
|
||||
*/
|
||||
heap_prune_record_prunable(prstate, HeapTupleGetRawXmax(&tup));
|
||||
heap_prune_record_prunable(prstate, HeapTupleGetUpdateXid(&tup));
|
||||
break;
|
||||
|
||||
case HEAPTUPLE_LIVE:
|
||||
|
@ -554,7 +554,7 @@ static int heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rooto
|
|||
*/
|
||||
Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == BufferGetBlockNumber(buffer));
|
||||
offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
|
||||
prior_xmax = HeapTupleGetRawXmax(&tup);
|
||||
prior_xmax = HeapTupleGetUpdateXid(&tup);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -714,7 +714,7 @@ void heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
|
|||
|
||||
/* Set up to scan the HOT-chain */
|
||||
nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
|
||||
prior_xmax = HeapTupleGetRawXmax(&tup);
|
||||
prior_xmax = HeapTupleGetUpdateXid(&tup);
|
||||
} else {
|
||||
/* Must be a redirect item. We do not set its root_offsets entry */
|
||||
Assert(ItemIdIsRedirected(lp));
|
||||
|
@ -751,7 +751,7 @@ void heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
|
|||
break;
|
||||
|
||||
nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
|
||||
prior_xmax = HeapTupleGetRawXmax(&tup);
|
||||
prior_xmax = HeapTupleGetUpdateXid(&tup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,6 +160,8 @@ typedef struct RewriteStateData {
|
|||
bool rs_use_wal; /* must we WAL-log inserts? */
|
||||
TransactionId rs_oldest_xmin; /* oldest xmin used by caller to determine tuple visibility */
|
||||
TransactionId rs_freeze_xid; /* Xid that will be used as freeze cutoff point */
|
||||
MultiXactId rs_freeze_multi; /* MultiXactId that will be used as freeze
|
||||
* cutoff point for multixacts */
|
||||
|
||||
MemoryContext rs_cxt; /* for hash tables and entries and tuples in them */
|
||||
HTAB *rs_unresolved_tups; /* unmatched A tuples */
|
||||
|
@ -212,7 +214,7 @@ typedef OldToNewMappingData *OldToNewMapping;
|
|||
static void raw_heap_insert(RewriteState state, HeapTuple tup);
|
||||
static void RawUHeapInsert(RewriteState state, UHeapTuple tup);
|
||||
static void RawHeapCmprAndMultiInsert(RewriteState state, bool is_last);
|
||||
static void copyHeapTupleInfo(HeapTuple dest_tup, HeapTuple src_tup, TransactionId freeze_xid);
|
||||
static void copyHeapTupleInfo(HeapTuple dest_tup, HeapTuple src_tup, TransactionId freeze_xid, MultiXactId freeze_mxid);
|
||||
static void rewrite_page_list_write(RewriteState state);
|
||||
static void rewrite_flush_page(RewriteState state, Page page);
|
||||
static void rewrite_end_flush_page(RewriteState state);
|
||||
|
@ -457,11 +459,11 @@ static bool CanWriteUpdatedTuple(RewriteState state, HeapTuple old_tuple, HeapTu
|
|||
/*
|
||||
* If the tuple has been updated, check the old-to-new mapping hash table.
|
||||
*/
|
||||
if (!(old_tuple->t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_IS_LOCKED)) &&
|
||||
if (!((old_tuple->t_data->t_infomask & HEAP_XMAX_INVALID) || HeapTupleIsOnlyLocked(old_tuple)) &&
|
||||
!(ItemPointerEquals(&(old_tuple->t_self), &(old_tuple->t_data->t_ctid)))) {
|
||||
OldToNewMapping mapping = NULL;
|
||||
|
||||
hashkey.xmin = HeapTupleGetRawXmax(old_tuple);
|
||||
hashkey.xmin = HeapTupleGetUpdateXid(old_tuple);
|
||||
hashkey.tid = old_tuple->t_data->t_ctid;
|
||||
|
||||
mapping = (OldToNewMapping)hash_search(state->rs_old_new_tid_map, &hashkey, HASH_FIND, NULL);
|
||||
|
@ -520,7 +522,7 @@ void rewrite_heap_tuple(RewriteState state, HeapTuple old_tuple, HeapTuple new_t
|
|||
|
||||
old_cxt = MemoryContextSwitchTo(state->rs_cxt);
|
||||
|
||||
copyHeapTupleInfo(new_tuple, old_tuple, state->rs_freeze_xid);
|
||||
copyHeapTupleInfo(new_tuple, old_tuple, state->rs_freeze_xid, state->rs_freeze_multi);
|
||||
|
||||
if (!CanWriteUpdatedTuple<true>(state, old_tuple, new_tuple)) {
|
||||
/*
|
||||
|
@ -660,7 +662,7 @@ MemoryContext get_heap_rewrite_memcxt(RewriteState state)
|
|||
return state->rs_cxt;
|
||||
}
|
||||
|
||||
static void copyHeapTupleInfo(HeapTuple dest_tup, HeapTuple src_tup, TransactionId freeze_xid)
|
||||
static void copyHeapTupleInfo(HeapTuple dest_tup, HeapTuple src_tup, TransactionId freeze_xid, MultiXactId freeze_mxid)
|
||||
{
|
||||
/*
|
||||
* Copy the original tuple's visibility information into new_tuple.
|
||||
|
@ -683,7 +685,7 @@ static void copyHeapTupleInfo(HeapTuple dest_tup, HeapTuple src_tup, Transaction
|
|||
* While we have our hands on the tuple, we may as well freeze any
|
||||
* very-old xmin or xmax, so that future VACUUM effort can be saved.
|
||||
*/
|
||||
(void)heap_freeze_tuple(dest_tup, freeze_xid);
|
||||
(void)heap_freeze_tuple(dest_tup, freeze_xid, freeze_mxid);
|
||||
|
||||
/*
|
||||
* Invalid ctid means that ctid should point to the tuple itself. We'll
|
||||
|
@ -857,7 +859,7 @@ void RewriteAndCompressTup(RewriteState state, HeapTuple old_tuple, HeapTuple ne
|
|||
errno_t rc = EOK;
|
||||
|
||||
Assert(CurrentMemoryContext == state->rs_cxt);
|
||||
copyHeapTupleInfo(new_tuple, old_tuple, state->rs_freeze_xid);
|
||||
copyHeapTupleInfo(new_tuple, old_tuple, state->rs_freeze_xid, state->rs_freeze_multi);
|
||||
|
||||
/*
|
||||
* Step 1: deal with updated tuples chain.
|
||||
|
|
|
@ -120,7 +120,8 @@ void HeapXlogCleanOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *b
|
|||
PageSetLSN(page, buffer->lsn);
|
||||
}
|
||||
|
||||
void HeapXlogFreezeOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *blkdata, Size datalen)
|
||||
void HeapXlogFreezeOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *blkdata, Size datalen,
|
||||
bool isTupleLockUpgrade)
|
||||
{
|
||||
xl_heap_freeze *xlrec = (xl_heap_freeze *)recorddata;
|
||||
Page page = buffer->pageinfo.page;
|
||||
|
@ -141,7 +142,7 @@ void HeapXlogFreezeOperatorPage(RedoBufferInfo *buffer, void *recorddata, void *
|
|||
HeapTupleCopyBaseFromPage(&tuple, page);
|
||||
ItemPointerSet(&(tuple.t_self), buffer->blockinfo.blkno, *offsets);
|
||||
|
||||
(void)heap_freeze_tuple(&tuple, cutoff_xid);
|
||||
(void)heap_freeze_tuple(&tuple, cutoff_xid, isTupleLockUpgrade ? xlrec->cutoff_multi : InvalidMultiXactId);
|
||||
|
||||
offsets++;
|
||||
}
|
||||
|
@ -237,7 +238,8 @@ inline static void HeapXlogVisibleOperatorVmbuffer(RedoBufferInfo *vmbuffer, voi
|
|||
}
|
||||
}
|
||||
|
||||
void HeapXlogDeleteOperatorPage(RedoBufferInfo *buffer, void *recorddata, TransactionId recordxid)
|
||||
void HeapXlogDeleteOperatorPage(RedoBufferInfo *buffer, void *recorddata, TransactionId recordxid,
|
||||
bool isTupleLockUpgrade)
|
||||
{
|
||||
xl_heap_delete *xlrec = (xl_heap_delete *)recorddata;
|
||||
Page page = buffer->pageinfo.page;
|
||||
|
@ -257,10 +259,24 @@ void HeapXlogDeleteOperatorPage(RedoBufferInfo *buffer, void *recorddata, Transa
|
|||
|
||||
htup = (HeapTupleHeader)PageGetItem(page, lp);
|
||||
|
||||
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_XMAX_IS_MULTI | HEAP_IS_LOCKED | HEAP_MOVED);
|
||||
htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
|
||||
htup->t_infomask2 &= ~(HEAP_XMAX_LOCK_ONLY | HEAP_KEYS_UPDATED);
|
||||
HeapTupleHeaderClearHotUpdated(htup);
|
||||
if (!(xlrec->flags & XLH_DELETE_IS_SUPER)) {
|
||||
|
||||
if (isTupleLockUpgrade) {
|
||||
FixInfomaskFromInfobits(xlrec->infobits_set, &htup->t_infomask, &htup->t_infomask2);
|
||||
HeapTupleHeaderSetXmax(page, htup, xlrec->xmax);
|
||||
} else {
|
||||
htup->t_infomask2 |= HEAP_KEYS_UPDATED;
|
||||
HeapTupleHeaderSetXmax(page, htup, recordxid);
|
||||
}
|
||||
|
||||
if (!(xlrec->flags & XLH_DELETE_IS_SUPER)) {
|
||||
if (isTupleLockUpgrade) {
|
||||
HeapTupleHeaderSetXmax(page, htup, xlrec->xmax);
|
||||
} else {
|
||||
HeapTupleHeaderSetXmax(page, htup, recordxid);
|
||||
}
|
||||
} else {
|
||||
HeapTupleHeaderSetXmin(page, htup, FrozenTransactionId);
|
||||
HeapTupleHeaderSetXmax(page, htup, FrozenTransactionId);
|
||||
|
@ -473,7 +489,7 @@ void HeapXlogMultiInsertOperatorPage(RedoBufferInfo *buffer, const void *recored
|
|||
}
|
||||
|
||||
void HeapXlogUpdateOperatorOldpage(RedoBufferInfo *buffer, void *recoreddata, bool hot_update, bool isnewinit,
|
||||
BlockNumber newblk, TransactionId recordxid)
|
||||
BlockNumber newblk, TransactionId recordxid, bool isTupleLockUpgrade)
|
||||
{
|
||||
Page page = buffer->pageinfo.page;
|
||||
Pointer rec_data = (Pointer)recoreddata;
|
||||
|
@ -499,12 +515,19 @@ void HeapXlogUpdateOperatorOldpage(RedoBufferInfo *buffer, void *recoreddata, bo
|
|||
|
||||
htup = (HeapTupleHeader)PageGetItem(page, lp);
|
||||
|
||||
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_XMAX_IS_MULTI | HEAP_IS_LOCKED | HEAP_MOVED);
|
||||
htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
|
||||
htup->t_infomask2 &= ~(HEAP_XMAX_LOCK_ONLY | HEAP_KEYS_UPDATED);
|
||||
if (hot_update)
|
||||
HeapTupleHeaderSetHotUpdated(htup);
|
||||
else
|
||||
HeapTupleHeaderClearHotUpdated(htup);
|
||||
HeapTupleHeaderSetXmax(page, htup, recordxid);
|
||||
if (isTupleLockUpgrade) {
|
||||
FixInfomaskFromInfobits(xlrec->old_infobits_set, &htup->t_infomask, &htup->t_infomask2);
|
||||
HeapTupleHeaderSetXmax(page, htup, xlrec->old_xmax);
|
||||
} else {
|
||||
htup->t_infomask2 |= HEAP_KEYS_UPDATED;
|
||||
HeapTupleHeaderSetXmax(page, htup, recordxid);
|
||||
}
|
||||
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
|
||||
/* Set forward chain link in t_ctid */
|
||||
htup->t_ctid = newtid;
|
||||
|
@ -530,7 +553,7 @@ void HeapXlogUpdateOperatorOldpage(RedoBufferInfo *buffer, void *recoreddata, bo
|
|||
}
|
||||
|
||||
void HeapXlogUpdateOperatorNewpage(RedoBufferInfo *buffer, void *recorddata, bool isinit, void *blkdata, Size datalen,
|
||||
TransactionId recordxid, Size *freespace, bool tde)
|
||||
TransactionId recordxid, Size *freespace, bool isTupleLockUpgrade, bool tde)
|
||||
{
|
||||
Page page = buffer->pageinfo.page;
|
||||
Pointer rec_data = (Pointer)recorddata;
|
||||
|
@ -618,6 +641,9 @@ void HeapXlogUpdateOperatorNewpage(RedoBufferInfo *buffer, void *recorddata, boo
|
|||
|
||||
HeapTupleHeaderSetXmin(page, htup, recordxid);
|
||||
HeapTupleHeaderSetCmin(htup, FirstCommandId);
|
||||
if (isTupleLockUpgrade) {
|
||||
HeapTupleHeaderSetXmax(page, htup, xlrec->new_xmax);
|
||||
}
|
||||
/* Make sure there is no forward chain link in t_ctid */
|
||||
htup->t_ctid = newtid;
|
||||
|
||||
|
@ -640,7 +666,7 @@ void HeapXlogPageUpgradeOperatorPage(RedoBufferInfo *buffer)
|
|||
PageSetLSN(page, buffer->lsn);
|
||||
}
|
||||
|
||||
void HeapXlogLockOperatorPage(RedoBufferInfo *buffer, void *recorddata)
|
||||
void HeapXlogLockOperatorPage(RedoBufferInfo *buffer, void *recorddata, bool isTupleLockUpgrade)
|
||||
{
|
||||
xl_heap_lock *xlrec = (xl_heap_lock *)recorddata;
|
||||
Page page = buffer->pageinfo.page;
|
||||
|
@ -657,13 +683,26 @@ void HeapXlogLockOperatorPage(RedoBufferInfo *buffer, void *recorddata)
|
|||
|
||||
htup = (HeapTupleHeader)PageGetItem(page, lp);
|
||||
|
||||
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_XMAX_IS_MULTI | HEAP_IS_LOCKED | HEAP_MOVED);
|
||||
if (xlrec->xid_is_mxact)
|
||||
htup->t_infomask |= HEAP_XMAX_IS_MULTI;
|
||||
if (xlrec->shared_lock)
|
||||
htup->t_infomask |= HEAP_XMAX_SHARED_LOCK;
|
||||
else
|
||||
htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
|
||||
htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
|
||||
htup->t_infomask2 &= ~(HEAP_XMAX_LOCK_ONLY | HEAP_KEYS_UPDATED);
|
||||
|
||||
if (isTupleLockUpgrade) {
|
||||
FixInfomaskFromInfobits(xlrec->infobits_set, &htup->t_infomask, &htup->t_infomask2);
|
||||
if (xlrec->lock_updated) {
|
||||
HeapTupleHeaderSetXmax(page, htup, xlrec->locking_xid);
|
||||
PageSetLSN(page, buffer->lsn);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (xlrec->xid_is_mxact)
|
||||
htup->t_infomask |= HEAP_XMAX_IS_MULTI;
|
||||
if (xlrec->shared_lock)
|
||||
htup->t_infomask |= HEAP_XMAX_SHARED_LOCK;
|
||||
else {
|
||||
htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
|
||||
htup->t_infomask2 |= HEAP_KEYS_UPDATED;
|
||||
}
|
||||
}
|
||||
HeapTupleHeaderClearHotUpdated(htup);
|
||||
HeapTupleHeaderSetXmax(page, htup, xlrec->locking_xid);
|
||||
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
|
||||
|
@ -1333,8 +1372,9 @@ static void HeapXlogDeleteBlock(XLogBlockHead *blockhead, XLogBlockDataParse *bl
|
|||
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
|
||||
if (action == BLK_NEEDS_REDO) {
|
||||
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
|
||||
bool isTupleLockUpgrad = (XLogBlockHeadGetInfo(blockhead) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
|
||||
|
||||
HeapXlogDeleteOperatorPage(bufferinfo, (void *)maindata, recordxid);
|
||||
HeapXlogDeleteOperatorPage(bufferinfo, (void *)maindata, recordxid, isTupleLockUpgrad);
|
||||
MakeRedoBufferDirty(bufferinfo);
|
||||
}
|
||||
}
|
||||
|
@ -1346,6 +1386,7 @@ static void HeapXlogUpdateBlock(XLogBlockHead *blockhead, XLogBlockDataParse *bl
|
|||
bool tde = ((blockdatarec->blockhead.cur_block_id) & BKID_HAS_TDE_PAGE) != 0;
|
||||
TransactionId recordxid = XLogBlockHeadGetXid(blockhead);
|
||||
XLogBlockDataParse *datadecode = blockdatarec;
|
||||
bool isTupleLockUpgrade = (XLogBlockHeadGetInfo(blockhead) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
|
||||
|
||||
XLogRedoAction action;
|
||||
|
||||
|
@ -1362,7 +1403,7 @@ static void HeapXlogUpdateBlock(XLogBlockHead *blockhead, XLogBlockDataParse *bl
|
|||
|
||||
if (oldblk == bufferinfo->blockinfo.blkno) {
|
||||
HeapXlogUpdateOperatorOldpage(bufferinfo, (void *)maindata, hot_update, isinit, oldblk,
|
||||
recordxid); /* old tuple */
|
||||
recordxid, isTupleLockUpgrade); /* old tuple */
|
||||
}
|
||||
|
||||
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
|
||||
|
@ -1370,12 +1411,13 @@ static void HeapXlogUpdateBlock(XLogBlockHead *blockhead, XLogBlockDataParse *bl
|
|||
|
||||
/* new block */
|
||||
HeapXlogUpdateOperatorNewpage(bufferinfo, (void *)maindata, isinit, (void *)blkdata, blkdatalen, recordxid,
|
||||
NULL, tde);
|
||||
NULL, isTupleLockUpgrade, tde);
|
||||
} else {
|
||||
BlockNumber newblk = XLogBlockDataGetAuxiBlock1(datadecode);
|
||||
|
||||
/* old block */
|
||||
HeapXlogUpdateOperatorOldpage(bufferinfo, (void *)maindata, hot_update, isinit, newblk, recordxid);
|
||||
HeapXlogUpdateOperatorOldpage(bufferinfo, (void *)maindata, hot_update, isinit, newblk, recordxid,
|
||||
isTupleLockUpgrade);
|
||||
}
|
||||
MakeRedoBufferDirty(bufferinfo);
|
||||
}
|
||||
|
@ -1414,8 +1456,9 @@ static void HeapXlogLockBlock(XLogBlockHead *blockhead, XLogBlockDataParse *bloc
|
|||
action = XLogCheckBlockDataRedoAction(datadecode, bufferinfo);
|
||||
if (action == BLK_NEEDS_REDO) {
|
||||
char *maindata = XLogBlockDataGetMainData(datadecode, NULL);
|
||||
bool isTupleLockUpgrade = (XLogBlockHeadGetInfo(blockhead) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
|
||||
|
||||
HeapXlogLockOperatorPage(bufferinfo, (void *)maindata);
|
||||
HeapXlogLockOperatorPage(bufferinfo, (void *)maindata, isTupleLockUpgrade);
|
||||
MakeRedoBufferDirty(bufferinfo);
|
||||
}
|
||||
}
|
||||
|
@ -1500,7 +1543,7 @@ static void HeapXlogFreezeBlock(XLogBlockHead *blockhead, XLogBlockDataParse *bl
|
|||
|
||||
blkdata = XLogBlockDataGetBlockData(datadecode, &blkdatalen);
|
||||
Assert(blkdata != NULL);
|
||||
HeapXlogFreezeOperatorPage(bufferinfo, (void *)maindata, (void *)blkdata, blkdatalen);
|
||||
HeapXlogFreezeOperatorPage(bufferinfo, (void *)maindata, (void *)blkdata, blkdatalen, false);
|
||||
MakeRedoBufferDirty(bufferinfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,25 @@ void heap_add_lock_info(StringInfo buf, xl_heap_lock *xlrec)
|
|||
}
|
||||
|
||||
|
||||
static void OutInfobits(StringInfo buf, uint8 infobits)
|
||||
{
|
||||
if (infobits & XLHL_XMAX_IS_MULTI) {
|
||||
appendStringInfo(buf, "IS_MULTI ");
|
||||
}
|
||||
if (infobits & XLHL_XMAX_LOCK_ONLY) {
|
||||
appendStringInfo(buf, "LOCK_ONLY ");
|
||||
}
|
||||
if (infobits & XLHL_XMAX_EXCL_LOCK) {
|
||||
appendStringInfo(buf, "EXCL_LOCK ");
|
||||
}
|
||||
if (infobits & XLHL_XMAX_KEYSHR_LOCK) {
|
||||
appendStringInfo(buf, "KEYSHR_LOCK ");
|
||||
}
|
||||
if (infobits & XLHL_KEYS_UPDATED) {
|
||||
appendStringInfo(buf, "KEYS_UPDATED ");
|
||||
}
|
||||
}
|
||||
|
||||
void heap3_new_cid(StringInfo buf, int bucket_id, xl_heap_new_cid *xlrec)
|
||||
{
|
||||
if (bucket_id == -1) {
|
||||
|
@ -56,6 +75,7 @@ void heap_desc(StringInfo buf, XLogReaderState *record)
|
|||
{
|
||||
char *rec = XLogRecGetData(record);
|
||||
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
|
||||
bool isTupleLockUPgrade = (XLogRecGetInfo(record) & XLOG_TUPLE_LOCK_UPGRADE_FLAG) != 0;
|
||||
|
||||
info &= XLOG_HEAP_OPMASK;
|
||||
if (info == XLOG_HEAP_INSERT) {
|
||||
|
@ -71,6 +91,10 @@ void heap_desc(StringInfo buf, XLogReaderState *record)
|
|||
|
||||
appendStringInfo(buf, "delete: ");
|
||||
appendStringInfo(buf, "off %u", (uint32)xlrec->offnum);
|
||||
if (isTupleLockUPgrade) {
|
||||
appendStringInfoChar(buf, ' ');
|
||||
OutInfobits(buf, xlrec->infobits_set);
|
||||
}
|
||||
} else if (info == XLOG_HEAP_UPDATE) {
|
||||
xl_heap_update *xlrec = (xl_heap_update *)rec;
|
||||
|
||||
|
@ -79,6 +103,10 @@ void heap_desc(StringInfo buf, XLogReaderState *record)
|
|||
else
|
||||
appendStringInfo(buf, "XLOG_HEAP_UPDATE update: ");
|
||||
appendStringInfo(buf, "off %u new off %u", (uint32)xlrec->old_offnum, (uint32)xlrec->new_offnum);
|
||||
if (isTupleLockUPgrade) {
|
||||
appendStringInfoChar(buf, ' ');
|
||||
OutInfobits(buf, xlrec->old_infobits_set);
|
||||
}
|
||||
} else if (info == XLOG_HEAP_HOT_UPDATE) {
|
||||
xl_heap_update *xlrec = (xl_heap_update *)rec;
|
||||
|
||||
|
@ -87,6 +115,10 @@ void heap_desc(StringInfo buf, XLogReaderState *record)
|
|||
else
|
||||
appendStringInfo(buf, "XLOG_HEAP_HOT_UPDATE hot_update: ");
|
||||
appendStringInfo(buf, "off %u new off %u", (uint32)xlrec->old_offnum, (uint32)xlrec->new_offnum);
|
||||
if (isTupleLockUPgrade) {
|
||||
appendStringInfoChar(buf, ' ');
|
||||
OutInfobits(buf, xlrec->old_infobits_set);
|
||||
}
|
||||
} else if (info == XLOG_HEAP_NEWPAGE) {
|
||||
appendStringInfo(buf, "new page");
|
||||
/* no further information */
|
||||
|
@ -94,6 +126,10 @@ void heap_desc(StringInfo buf, XLogReaderState *record)
|
|||
xl_heap_lock *xlrec = (xl_heap_lock *)rec;
|
||||
|
||||
heap_add_lock_info(buf, xlrec);
|
||||
if (isTupleLockUPgrade) {
|
||||
appendStringInfoChar(buf, ' ');
|
||||
OutInfobits(buf, xlrec->infobits_set);
|
||||
}
|
||||
} else if (info == XLOG_HEAP_INPLACE) {
|
||||
xl_heap_inplace *xlrec = (xl_heap_inplace *)rec;
|
||||
|
||||
|
|
|
@ -20,6 +20,34 @@
|
|||
#include "common/fe_memutils.h"
|
||||
#endif
|
||||
|
||||
static void OutMember(StringInfo buf, TransactionId xidWithStatus)
|
||||
{
|
||||
appendStringInfo(buf, "" XID_FMT " ", GET_MEMBER_XID_FROM_SLRU_XID(xidWithStatus));
|
||||
switch (GET_MEMBER_STATUS_FROM_SLRU_XID(xidWithStatus)) {
|
||||
case MultiXactStatusForKeyShare:
|
||||
appendStringInfoString(buf, "(keysh) ");
|
||||
break;
|
||||
case MultiXactStatusForShare:
|
||||
appendStringInfoString(buf, "(sh) ");
|
||||
break;
|
||||
case MultiXactStatusForNoKeyUpdate:
|
||||
appendStringInfoString(buf, "(fornokeyupd) ");
|
||||
break;
|
||||
case MultiXactStatusForUpdate:
|
||||
appendStringInfoString(buf, "(forupd) ");
|
||||
break;
|
||||
case MultiXactStatusNoKeyUpdate:
|
||||
appendStringInfoString(buf, "(nokeyupd) ");
|
||||
break;
|
||||
case MultiXactStatusUpdate:
|
||||
appendStringInfoString(buf, "(upd) ");
|
||||
break;
|
||||
default:
|
||||
appendStringInfoString(buf, "(unk) ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void multixact_desc(StringInfo buf, XLogReaderState *record)
|
||||
{
|
||||
char *rec = XLogRecGetData(record);
|
||||
|
@ -43,9 +71,9 @@ void multixact_desc(StringInfo buf, XLogReaderState *record)
|
|||
xl_multixact_create *xlrec = (xl_multixact_create *)rec;
|
||||
int i = 0;
|
||||
|
||||
appendStringInfo(buf, "create multixact " XID_FMT " offset %lu:", xlrec->mid, xlrec->moff);
|
||||
appendStringInfo(buf, "create multixact " XID_FMT " offset %lu: ", xlrec->mid, xlrec->moff);
|
||||
for (i = 0; i < xlrec->nxids; i++)
|
||||
appendStringInfo(buf, " " XID_FMT "", xlrec->xids[i]);
|
||||
OutMember(buf, xlrec->xids[i]);
|
||||
} else
|
||||
appendStringInfo(buf, "UNKNOWN");
|
||||
}
|
||||
|
|
|
@ -388,10 +388,10 @@ TM_Result tableam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid
|
|||
TM_Result tableam_tuple_update(Relation relation, Relation parentRelation, ItemPointer otid, Tuple newtup,
|
||||
CommandId cid, Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot **oldslot, TM_FailureData *tmfd,
|
||||
bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self,
|
||||
bool allow_inplace_update)
|
||||
bool allow_inplace_update, LockTupleMode *lockmode)
|
||||
{
|
||||
return g_tableam_routines[relation->rd_tam_type]->tuple_update(relation, parentRelation, otid, newtup, cid,
|
||||
crosscheck, snapshot, wait, oldslot, tmfd, update_indexes, modifiedIdxAttrs, allow_update_self,
|
||||
crosscheck, snapshot, wait, oldslot, tmfd, lockmode, update_indexes, modifiedIdxAttrs, allow_update_self,
|
||||
allow_inplace_update);
|
||||
}
|
||||
|
||||
|
@ -845,10 +845,11 @@ TM_Result HeapamTupleDelete(Relation relation, ItemPointer tid,
|
|||
/* -------------------------------------------------------------------------- */
|
||||
TM_Result HeapamTupleUpdate(Relation relation, Relation parentRelation, ItemPointer otid, Tuple newtup, CommandId cid,
|
||||
Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot **oldslot, TM_FailureData *tmfd,
|
||||
bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self, bool allow_inplace_update)
|
||||
LockTupleMode* lockmode, bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self,
|
||||
bool allow_inplace_update)
|
||||
{
|
||||
TM_Result result = heap_update(relation, parentRelation, otid, (HeapTuple)newtup,
|
||||
cid, crosscheck, wait, tmfd, allow_update_self);
|
||||
cid, crosscheck, wait, tmfd, lockmode, allow_update_self);
|
||||
|
||||
/* make update_indexes optional */
|
||||
if(update_indexes) {
|
||||
|
@ -863,7 +864,8 @@ TM_Result HeapamTupleLock(Relation relation, Tuple tuple, Buffer *buffer,
|
|||
bool allow_lock_self, bool follow_updates, bool eval, Snapshot snapshot,
|
||||
ItemPointer tid, bool isSelectForUpdate, bool isUpsert, TransactionId conflictXid)
|
||||
{
|
||||
return heap_lock_tuple(relation, (HeapTuple)tuple, buffer, cid, mode, nowait, tmfd, allow_lock_self);
|
||||
return heap_lock_tuple(relation, (HeapTuple)tuple, buffer, cid, mode, nowait, follow_updates, tmfd,
|
||||
allow_lock_self);
|
||||
}
|
||||
|
||||
Tuple HeapamTupleLockUpdated(CommandId cid, Relation relation, int lockmode, ItemPointer tid,
|
||||
|
@ -1598,7 +1600,8 @@ TM_Result UHeapamTupleDelete(Relation relation, ItemPointer tid, CommandId cid,
|
|||
|
||||
TM_Result UHeapamTupleUpdate(Relation relation, Relation parentRelation, ItemPointer otid, Tuple newtup, CommandId cid,
|
||||
Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot **oldslot, TM_FailureData *tmfd,
|
||||
bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self, bool allow_inplace_update)
|
||||
LockTupleMode *mode, bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self,
|
||||
bool allow_inplace_update)
|
||||
{
|
||||
TM_Result result = UHeapUpdate(relation, parentRelation, otid, (UHeapTuple)newtup, cid, crosscheck, snapshot, wait,
|
||||
oldslot, tmfd, update_indexes, modifiedIdxAttrs, allow_inplace_update);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7175,6 +7175,7 @@ void BootStrapXLOG(void)
|
|||
t_thrd.xact_cxt.ShmemVariableCache->startupMaxXid = t_thrd.xact_cxt.ShmemVariableCache->nextXid;
|
||||
MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset);
|
||||
SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB);
|
||||
SetMultiXactIdLimit(FirstMultiXactId, TemplateDbOid);
|
||||
|
||||
/* Set up the XLOG page header */
|
||||
page->xlp_magic = XLOG_PAGE_MAGIC;
|
||||
|
@ -9688,6 +9689,7 @@ void StartupXLOG(void)
|
|||
t_thrd.xact_cxt.ShmemVariableCache->oidCount = 0;
|
||||
MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset);
|
||||
SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB);
|
||||
SetMultiXactIdLimit(FirstMultiXactId, TemplateDbOid);
|
||||
t_thrd.shemem_ptr_cxt.XLogCtl->ckptXid = checkPoint.oldestXid;
|
||||
t_thrd.shemem_ptr_cxt.XLogCtl->IsRecoveryDone = false;
|
||||
|
||||
|
|
|
@ -1501,12 +1501,12 @@ static bool UHeapWait(Relation relation, Buffer buffer, UHeapTuple utuple, LockT
|
|||
|
||||
// wait multixid
|
||||
if (nowait) {
|
||||
if (!ConditionalMultiXactIdWait((MultiXactId)xwait)) {
|
||||
if (!ConditionalMultiXactIdWait((MultiXactId)xwait, GetMXactStatusForLock(mode, false), NULL)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
|
||||
errmsg("could not obtain lock on row in relation \"%s\"", RelationGetRelationName(relation))));
|
||||
}
|
||||
} else {
|
||||
MultiXactIdWait(xwait, true);
|
||||
MultiXactIdWait(xwait, GetMXactStatusForLock(mode, false), NULL);
|
||||
}
|
||||
|
||||
// reacquire lock
|
||||
|
@ -1667,7 +1667,7 @@ static void UHeapExecuteLockTuple(Relation relation, Buffer buffer, UHeapTuple u
|
|||
if (SINGLE_LOCKER_XID_IS_SHR_LOCKED(oldinfomask) && TransactionIdIsInProgress(xidOnTup)) {
|
||||
// create a multixid
|
||||
MultiXactIdSetOldestMember();
|
||||
xid = MultiXactIdCreate(xidOnTup, curxid);
|
||||
xid = MultiXactIdCreate(xidOnTup, MultiXactStatusForShare, curxid, MultiXactStatusForShare);
|
||||
multi = true;
|
||||
utuple->disk_tuple->flag |= UHEAP_MULTI_LOCKERS;
|
||||
elog(DEBUG5, "locker %ld + locker %ld = multi %ld", curxid, xidOnTup, xid);
|
||||
|
@ -1677,7 +1677,7 @@ static void UHeapExecuteLockTuple(Relation relation, Buffer buffer, UHeapTuple u
|
|||
* expand multixid to contain the current transaction id.
|
||||
*/
|
||||
MultiXactIdSetOldestMember();
|
||||
xid = MultiXactIdExpand((MultiXactId)xidOnTup, curxid);
|
||||
xid = MultiXactIdExpand((MultiXactId)xidOnTup, curxid, MultiXactStatusForShare);
|
||||
multi = true;
|
||||
utuple->disk_tuple->flag |= UHEAP_MULTI_LOCKERS;
|
||||
elog(DEBUG5, "locker %ld + multi %ld = multi %ld", curxid, xidOnTup, xid);
|
||||
|
|
|
@ -640,7 +640,8 @@ void LazyVacuumUHeapRel(Relation onerel, VacuumStmt *vacstmt, BufferAccessStrate
|
|||
if (RelationIsPartition(onerel)) {
|
||||
Assert(vacstmt->onepart != NULL);
|
||||
|
||||
vac_update_partstats(vacstmt->onepart, newRelPages, newRelTuples, newRelAllvisible, InvalidTransactionId);
|
||||
vac_update_partstats(vacstmt->onepart, newRelPages, newRelTuples, newRelAllvisible, InvalidTransactionId,
|
||||
InvalidMultiXactId);
|
||||
/*
|
||||
* when vacuum partition, do not change the relhasindex field in pg_class
|
||||
* for partitioned table, as some partition may be altered as "all local
|
||||
|
@ -649,11 +650,11 @@ void LazyVacuumUHeapRel(Relation onerel, VacuumStmt *vacstmt, BufferAccessStrate
|
|||
* misdguge as hot update even if update indexes columns.
|
||||
*/
|
||||
vac_update_pgclass_partitioned_table(vacstmt->onepartrel, vacstmt->onepartrel->rd_rel->relhasindex,
|
||||
InvalidTransactionId);
|
||||
InvalidTransactionId, InvalidMultiXactId);
|
||||
} else {
|
||||
Relation classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
vac_update_relstats(onerel, classRel, newRelPages, newRelTuples, newRelAllvisible, nindexes > 0,
|
||||
InvalidTransactionId);
|
||||
InvalidTransactionId, InvalidMultiXactId);
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "catalog/objectaccess.h"
|
||||
#include "access/cstore_insert.h"
|
||||
#include "access/tableam.h"
|
||||
#include "access/multixact.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "catalog/index.h"
|
||||
|
@ -593,8 +594,8 @@ void CStoreRewriter::RewriteColsData()
|
|||
index_close(oldCudescIndex, NoLock);
|
||||
|
||||
// finish rewriting cudesc relation.
|
||||
finish_heap_swap(
|
||||
m_OldHeapRel->rd_rel->relcudescrelid, m_NewCuDescHeap, false, swapToastByContent, false, m_NewCudescFrozenXid);
|
||||
finish_heap_swap(m_OldHeapRel->rd_rel->relcudescrelid, m_NewCuDescHeap, false,
|
||||
swapToastByContent, false, m_NewCudescFrozenXid, FirstMultiXactId);
|
||||
}
|
||||
|
||||
void CStoreRewriter::EndRewriteCols()
|
||||
|
@ -894,7 +895,7 @@ void CStoreRewriter::FetchCudescFrozenXid(Relation oldCudescHeap)
|
|||
//
|
||||
Assert(oldCudescHeap->rd_rel->relisshared == false);
|
||||
TransactionId OldestXmin = InvalidTransactionId;
|
||||
vacuum_set_xid_limits(oldCudescHeap, -1, -1, &OldestXmin, &m_NewCudescFrozenXid, NULL);
|
||||
vacuum_set_xid_limits(oldCudescHeap, -1, -1, &OldestXmin, &m_NewCudescFrozenXid, NULL, NULL);
|
||||
|
||||
// FreezeXid will become the table's new relfrozenxid, and that mustn't go
|
||||
// backwards, so take the max.
|
||||
|
@ -2117,7 +2118,7 @@ void ATExecCStoreMergePartition(Relation partTableRel, AlterTableCmd* cmd)
|
|||
getPartitionName(destPartOid, false))));
|
||||
}
|
||||
|
||||
finishPartitionHeapSwap(destPartOid, tempTableOid, true, u_sess->utils_cxt.RecentXmin);
|
||||
finishPartitionHeapSwap(destPartOid, tempTableOid, true, u_sess->utils_cxt.RecentXmin, InvalidMultiXactId);
|
||||
partitionClose(partTableRel, destPart, NoLock);
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
|
|
|
@ -3456,10 +3456,10 @@ void CheckForSerializableConflictOut(bool visible, Relation relation, void* stup
|
|||
case HEAPTUPLE_RECENTLY_DEAD:
|
||||
if (!visible)
|
||||
return;
|
||||
xid = HeapTupleGetRawXmax(tuple);
|
||||
xid = HeapTupleGetUpdateXid(tuple);
|
||||
break;
|
||||
case HEAPTUPLE_DELETE_IN_PROGRESS:
|
||||
xid = HeapTupleGetRawXmax(tuple);
|
||||
xid = HeapTupleGetUpdateXid(tuple);
|
||||
break;
|
||||
case HEAPTUPLE_INSERT_IN_PROGRESS:
|
||||
xid = HeapTupleGetRawXmin(tuple);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "access/sdir.h"
|
||||
#include "access/skey.h"
|
||||
#include "access/xlogrecord.h"
|
||||
#include "access/multixact.h"
|
||||
#include "executor/tuptable.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "storage/lock/lock.h"
|
||||
|
@ -139,6 +140,62 @@ typedef enum {
|
|||
|
||||
#define MaxLockTupleMode LockTupleExclusive
|
||||
|
||||
static const struct {
|
||||
LOCKMODE hwlock;
|
||||
MultiXactStatus lockstatus;
|
||||
MultiXactStatus updstatus;
|
||||
} TupleLockExtraInfo[MaxLockTupleMode + 1] = {
|
||||
{
|
||||
/* LockTupleKeyShare */
|
||||
AccessShareLock,
|
||||
MultiXactStatusForKeyShare,
|
||||
(MultiXactStatus)-1 /* KeyShare does not allow updating tuples */
|
||||
},
|
||||
{
|
||||
RowShareLock, /* LockTupleShared */
|
||||
MultiXactStatusForShare,
|
||||
(MultiXactStatus)-1
|
||||
},
|
||||
{
|
||||
ExclusiveLock, /* LockTupleNoKeyExclusive */
|
||||
MultiXactStatusForNoKeyUpdate,
|
||||
MultiXactStatusNoKeyUpdate
|
||||
},
|
||||
{
|
||||
AccessExclusiveLock, /* LockTupleExclusive */
|
||||
MultiXactStatusForUpdate,
|
||||
MultiXactStatusUpdate
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
|
||||
* This is more readable than having every caller translate it to lock.h's
|
||||
* LOCKMODE.
|
||||
*/
|
||||
#define LOCK_TUPLE_TUP_LOCK(rel, tup, mode) LockTuple((rel), (tup), TupleLockExtraInfo[mode].hwlock, true)
|
||||
#define UNLOCK_TUPLE_TUP_LOCK(rel, tup, mode) UnlockTuple((rel), (tup), TupleLockExtraInfo[mode].hwlock)
|
||||
#define ConditionalLockTupleTuplock(_rel, _tup, _mode) \
|
||||
ConditionalLockTuple((_rel), (_tup), TupleLockExtraInfo[_mode].hwlock)
|
||||
|
||||
/*
|
||||
* This table maps tuple lock strength values for each particular
|
||||
* MultiXactStatus value.
|
||||
*/
|
||||
static const LockTupleMode MULTIXACT_STATUS_LOCK[MultiXactStatusUpdate + 1] = {
|
||||
LockTupleShared, /* ForShare */
|
||||
LockTupleKeyShare, /* ForKeyShare */
|
||||
LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
|
||||
LockTupleExclusive, /* ForUpdate */
|
||||
LockTupleNoKeyExclusive, /* NoKeyUpdate */
|
||||
LockTupleExclusive /* Update */
|
||||
};
|
||||
|
||||
/* Get the LockTupleMode for a given MultiXactStatus */
|
||||
#define TUPLOCK_FROM_MXSTATUS(status) (MULTIXACT_STATUS_LOCK[(status)])
|
||||
/* Get the LOCKMODE for a given MultiXactStatus */
|
||||
#define LOCKMODE_FROM_MXSTATUS(status) (TupleLockExtraInfo[TUPLOCK_FROM_MXSTATUS((status))].hwlock)
|
||||
|
||||
/* the last arguments info for heap_multi_insert() */
|
||||
typedef struct {
|
||||
/* compression info: dictionary buffer and its size */
|
||||
|
@ -266,17 +323,22 @@ extern int heap_multi_insert(Relation relation, Relation parent, HeapTuple* tupl
|
|||
extern TM_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck,
|
||||
bool wait, TM_FailureData *tmfd, bool allow_delete_self = false);
|
||||
extern TM_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid, HeapTuple newtup,
|
||||
CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool allow_delete_self = false);
|
||||
CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode,
|
||||
bool allow_delete_self = false);
|
||||
extern TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer,
|
||||
CommandId cid, LockTupleMode mode, bool nowait, TM_FailureData *tmfd, bool allow_lock_self = false);
|
||||
CommandId cid, LockTupleMode mode, bool nowait, bool follow_updates, TM_FailureData *tmfd,
|
||||
bool allow_lock_self = false);
|
||||
void FixInfomaskFromInfobits(uint8 infobits, uint16 *infomask, uint16 *infomask2);
|
||||
|
||||
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
|
||||
extern bool heap_freeze_tuple(HeapTuple tuple, TransactionId cutoff_xid);
|
||||
extern bool heap_tuple_needs_freeze(HeapTuple tuple, TransactionId cutoff_xid, Buffer buf);
|
||||
extern bool heap_freeze_tuple(HeapTuple tuple, TransactionId cutoff_xid, TransactionId cutoff_multi,
|
||||
bool *changedMultiXid = NULL);
|
||||
extern bool heap_tuple_needs_freeze(HeapTuple tuple, TransactionId cutoff_xid, MultiXactId cutoff_multi, Buffer buf);
|
||||
|
||||
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
|
||||
extern void simple_heap_delete(Relation relation, ItemPointer tid, int options = 0, bool allow_update_self = false);
|
||||
extern void simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup);
|
||||
extern MultiXactStatus GetMXactStatusForLock(LockTupleMode mode, bool isUpdate);
|
||||
|
||||
extern void heap_markpos(TableScanDesc scan);
|
||||
extern void heap_restrpos(TableScanDesc scan);
|
||||
|
@ -301,7 +363,7 @@ extern XLogRecPtr log_heap_clean(Relation reln, Buffer buffer, OffsetNumber* red
|
|||
OffsetNumber* nowdead, int ndead, OffsetNumber* nowunused, int nunused, TransactionId latestRemovedXid,
|
||||
bool repair_fragmentation);
|
||||
extern XLogRecPtr log_heap_freeze(
|
||||
Relation reln, Buffer buffer, TransactionId cutoff_xid, OffsetNumber* offsets, int offcnt);
|
||||
Relation reln, Buffer buffer, TransactionId cutoff_xid, MultiXactId cutoff_multi, OffsetNumber* offsets, int offcnt);
|
||||
extern XLogRecPtr log_heap_visible(RelFileNode rnode, BlockNumber block, Buffer heap_buffer, Buffer vm_buffer,
|
||||
TransactionId cutoff_xid, bool free_dict);
|
||||
extern XLogRecPtr log_cu_bcm(const RelFileNode* rnode, int col, uint64 block, int status, int count);
|
||||
|
@ -361,6 +423,7 @@ extern TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buff
|
|||
extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer, bool isAnalyzing = false);
|
||||
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
|
||||
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid);
|
||||
extern bool HeapTupleIsOnlyLocked(HeapTuple tuple);
|
||||
|
||||
/*
|
||||
* To avoid leaking to much knowledge about reorderbuffer implementation
|
||||
|
|
|
@ -194,8 +194,9 @@ typedef HeapTupleHeaderData* HeapTupleHeader;
|
|||
#define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */
|
||||
#define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */
|
||||
#define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */
|
||||
/* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
|
||||
#define HEAP_IS_LOCKED (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK)
|
||||
/* xmax is a key-shared locker */
|
||||
#define HEAP_XMAX_KEYSHR_LOCK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK)
|
||||
#define HEAP_LOCK_MASK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK | HEAP_XMAX_KEYSHR_LOCK)
|
||||
#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */
|
||||
#define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */
|
||||
#define HEAP_XMIN_FROZEN (HEAP_XMIN_INVALID | HEAP_XMIN_COMMITTED)
|
||||
|
@ -219,12 +220,13 @@ typedef HeapTupleHeaderData* HeapTupleHeader;
|
|||
* information stored in t_infomask2:
|
||||
*/
|
||||
#define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */
|
||||
/* bits 0x1800 are available */
|
||||
#define HEAP_XMAX_LOCK_ONLY 0x0800 /* xmax, if valid, is only a locker */
|
||||
#define HEAP_KEYS_UPDATED 0x1000 /* tuple was updated and key cols modified, or tuple deleted */
|
||||
#define HEAP_HAS_REDIS_COLUMNS 0x2000 /* tuple has hidden columns added by redis */
|
||||
#define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */
|
||||
#define HEAP_ONLY_TUPLE 0x8000 /* this is heap-only tuple */
|
||||
|
||||
#define HEAP2_XACT_MASK 0xC000 /* visibility-related bits */
|
||||
#define HEAP2_XACT_MASK 0xD800 /* visibility-related bits */
|
||||
|
||||
/*
|
||||
* HEAP_TUPLE_HAS_MATCH is a temporary flag used during hash joins. It is
|
||||
|
@ -234,6 +236,28 @@ typedef HeapTupleHeaderData* HeapTupleHeader;
|
|||
*/
|
||||
#define HEAP_TUPLE_HAS_MATCH HEAP_ONLY_TUPLE /* tuple has a join match */
|
||||
|
||||
/*
|
||||
* A tuple is only locked (i.e. not updated by its Xmax) if it the
|
||||
* HEAP_XMAX_LOCK_ONLY bit is set.
|
||||
*
|
||||
* See also HeapTupleIsOnlyLocked, which also checks for a possible
|
||||
* aborted updater transaction.
|
||||
*/
|
||||
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask, infomask2) \
|
||||
(((infomask2) & HEAP_XMAX_LOCK_ONLY) || \
|
||||
((infomask) & HEAP_XMAX_SHARED_LOCK) || \
|
||||
(((infomask) & (HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)) == HEAP_XMAX_EXCL_LOCK))
|
||||
|
||||
/*
|
||||
* Use these to test whether a particular lock is applied to a tuple
|
||||
*/
|
||||
#define HEAP_XMAX_IS_SHR_LOCKED(infomask) (((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_SHARED_LOCK)
|
||||
#define HEAP_XMAX_IS_EXCL_LOCKED(infomask) (((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_EXCL_LOCK)
|
||||
#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) (((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_KEYSHR_LOCK)
|
||||
|
||||
/* turn these all off when Xmax is to change */
|
||||
#define HEAP_XMAX_BITS (HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)
|
||||
|
||||
/*
|
||||
* HeapTupleHeader accessor macros
|
||||
*
|
||||
|
@ -301,6 +325,27 @@ typedef HeapTupleHeaderData* HeapTupleHeader;
|
|||
((tup)->t_data->t_choice.t_heap.t_xmax) \
|
||||
))
|
||||
|
||||
/*
|
||||
* HeapTupleGetRawXmax gets you the raw Xmax field. To find out the Xid
|
||||
* that updated a tuple, you might need to resolve the MultiXactId if certain
|
||||
* bits are set. HeapTupleGetUpdateXid checks those bits and takes care
|
||||
* to resolve the MultiXactId if necessary. This might involve multixact I/O,
|
||||
* so it should only be used if absolutely necessary.
|
||||
*/
|
||||
#define HeapTupleGetUpdateXid(tup) \
|
||||
((!((tup)->t_data->t_infomask & HEAP_XMAX_INVALID) && \
|
||||
((tup)->t_data->t_infomask & HEAP_XMAX_IS_MULTI) && \
|
||||
!((tup)->t_data->t_infomask2 & HEAP_XMAX_LOCK_ONLY)) ? \
|
||||
HeapTupleMultiXactGetUpdateXid(tup) : \
|
||||
HeapTupleGetRawXmax(tup))
|
||||
|
||||
#define HeapTupleHeaderGetUpdateXid(page, tup) \
|
||||
((!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
|
||||
((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
|
||||
!((tup)->t_infomask2 & HEAP_XMAX_LOCK_ONLY)) ? \
|
||||
HeapTupleHeaderMultiXactGetUpdateXid(page, tup) : \
|
||||
HeapTupleHeaderGetRawXmax(page, tup))
|
||||
|
||||
#define HeapTupleHeaderGetXmax(page, tup) \
|
||||
(ShortTransactionIdToNormal(((tup)->t_infomask & HEAP_XMAX_IS_MULTI) \
|
||||
? (PageIs8BXidHeapVersion(page) ? ((HeapPageHeader)(page))->pd_multi_base : 0) \
|
||||
|
@ -319,10 +364,10 @@ typedef HeapTupleHeaderData* HeapTupleHeader;
|
|||
|
||||
#define HeapTupleSetXmax(tup, xid) \
|
||||
((tup)->t_data->t_choice.t_heap.t_xmax = NormalTransactionIdToShort( \
|
||||
((tup)->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ? tup->t_multi_base : tup->t_xid_base, (xid)))
|
||||
((tup)->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ? (tup)->t_multi_base : (tup)->t_xid_base, (xid)))
|
||||
|
||||
#define HeapTupleSetXmin(tup, xid) \
|
||||
((tup)->t_data->t_choice.t_heap.t_xmin = NormalTransactionIdToShort(tup->t_xid_base, (xid)))
|
||||
((tup)->t_data->t_choice.t_heap.t_xmin = NormalTransactionIdToShort((tup)->t_xid_base, (xid)))
|
||||
|
||||
/*
|
||||
* HeapTupleHeaderGetRawCommandId will give you what's in the header whether
|
||||
|
@ -671,6 +716,10 @@ inline HeapTuple heaptup_alloc(Size size)
|
|||
* or MULTI_INSERT, we can (and we do) restore entire page in redo
|
||||
*/
|
||||
#define XLOG_HEAP_INIT_PAGE 0x80
|
||||
|
||||
/* Upgrade support for enhanced tupl lock mode */
|
||||
#define XLOG_TUPLE_LOCK_UPGRADE_FLAG 0x01
|
||||
|
||||
/*
|
||||
* We ran out of opcodes, so heapam.c now has a second RmgrId. These opcodes
|
||||
* are associated with RM_HEAP2_ID, but are not logically different from
|
||||
|
@ -744,9 +793,12 @@ inline HeapTuple heaptup_alloc(Size size)
|
|||
typedef struct xl_heap_delete {
|
||||
OffsetNumber offnum; /* deleted tuple's offset */
|
||||
uint8 flags;
|
||||
TransactionId xmax; /* xmax of the deleted tuple */
|
||||
uint8 infobits_set; /* infomask bits */
|
||||
} xl_heap_delete;
|
||||
|
||||
#define SizeOfHeapDelete (offsetof(xl_heap_delete, flags) + sizeof(bool))
|
||||
#define SizeOfOldHeapDelete (offsetof(xl_heap_delete, flags) + sizeof(uint8))
|
||||
#define SizeOfHeapDelete (offsetof(xl_heap_delete, infobits_set) + sizeof(uint8))
|
||||
|
||||
/*
|
||||
* We don't store the whole fixed part (HeapTupleHeaderData) of an inserted
|
||||
|
@ -829,9 +881,13 @@ typedef struct xl_heap_update {
|
|||
OffsetNumber old_offnum; /* old tuple's offset */
|
||||
OffsetNumber new_offnum; /* new tuple's offset */
|
||||
uint8 flags; /* NEW TUPLE xl_heap_header AND TUPLE DATA FOLLOWS AT END OF STRUCT */
|
||||
TransactionId old_xmax; /* xmax of the old tuple */
|
||||
TransactionId new_xmax; /* xmax of the new tuple */
|
||||
uint8 old_infobits_set; /* infomask bits to set on old tuple */
|
||||
} xl_heap_update;
|
||||
|
||||
#define SizeOfHeapUpdate (offsetof(xl_heap_update, flags) + sizeof(uint8))
|
||||
#define SizeOfOldHeapUpdate (offsetof(xl_heap_update, flags) + sizeof(uint8))
|
||||
#define SizeOfHeapUpdate (offsetof(xl_heap_update, old_infobits_set) + sizeof(uint8))
|
||||
/*
|
||||
* This is what we need to know about vacuum page cleanup/redirect
|
||||
*
|
||||
|
@ -879,15 +935,25 @@ typedef struct xl_heap_logical_newpage {
|
|||
|
||||
#define SizeOfHeapLogicalNewPage (offsetof(xl_heap_logical_newpage, blockSize) + sizeof(int32))
|
||||
|
||||
/* flags for infobits_set */
|
||||
#define XLHL_XMAX_IS_MULTI 0x01
|
||||
#define XLHL_XMAX_LOCK_ONLY 0x02
|
||||
#define XLHL_XMAX_EXCL_LOCK 0x04
|
||||
#define XLHL_XMAX_KEYSHR_LOCK 0x08
|
||||
#define XLHL_KEYS_UPDATED 0x10
|
||||
|
||||
/* This is what we need to know about lock */
|
||||
typedef struct xl_heap_lock {
|
||||
TransactionId locking_xid; /* might be a MultiXactId not xid */
|
||||
OffsetNumber offnum; /* locked tuple's offset on page */
|
||||
bool xid_is_mxact; /* is it? */
|
||||
bool shared_lock; /* shared or exclusive row lock? */
|
||||
uint8 infobits_set; /* infomask and infomask2 bits to set */
|
||||
bool lock_updated; /* lock an updated version of a row */
|
||||
} xl_heap_lock;
|
||||
|
||||
#define SizeOfHeapLock (offsetof(xl_heap_lock, shared_lock) + sizeof(bool))
|
||||
#define SizeOfOldHeapLock (offsetof(xl_heap_lock, shared_lock) + sizeof(bool))
|
||||
#define SizeOfHeapLock (offsetof(xl_heap_lock, lock_updated) + sizeof(bool))
|
||||
|
||||
/* This is what we need to know about in-place update */
|
||||
typedef struct xl_heap_inplace {
|
||||
|
@ -905,10 +971,12 @@ typedef struct xl_heap_inplace {
|
|||
*/
|
||||
typedef struct xl_heap_freeze {
|
||||
TransactionId cutoff_xid;
|
||||
MultiXactId cutoff_multi;
|
||||
/* TUPLE OFFSET NUMBERS FOLLOW AT THE END */
|
||||
} xl_heap_freeze;
|
||||
|
||||
#define SizeOfHeapFreeze (offsetof(xl_heap_freeze, cutoff_xid) + sizeof(TransactionId))
|
||||
#define SizeOfOldHeapFreeze (offsetof(xl_heap_freeze, cutoff_xid) + sizeof(TransactionId))
|
||||
#define SizeOfHeapFreeze (offsetof(xl_heap_freeze, cutoff_multi) + sizeof(MultiXactId))
|
||||
|
||||
typedef struct xl_heap_freeze_tuple {
|
||||
TransactionId xmax;
|
||||
|
@ -988,6 +1056,8 @@ extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup, Page page);
|
|||
extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup, Page page);
|
||||
extern bool CheckStreamCombocid(HeapTupleHeader tup, CommandId current_cid, Page page);
|
||||
extern void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup, CommandId* cmax, bool* iscombo, Buffer buffer);
|
||||
extern TransactionId HeapTupleMultiXactGetUpdateXid(HeapTuple tuple);
|
||||
extern TransactionId HeapTupleHeaderMultiXactGetUpdateXid(Page page, HeapTupleHeader tuple);
|
||||
|
||||
/* ----------------
|
||||
* fastgetattr && fastgetattr_with_dict
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
* next two are used for update and delete modes.
|
||||
*/
|
||||
typedef enum {
|
||||
MultiXactStatusForKeyShare = 0x00,
|
||||
MultiXactStatusForShare = 0x01,
|
||||
MultiXactStatusForShare = 0x00, /* set FOR_SHARE = 0 here for compatibility */
|
||||
MultiXactStatusForKeyShare = 0x01,
|
||||
MultiXactStatusForNoKeyUpdate = 0x02,
|
||||
MultiXactStatusForUpdate = 0x03,
|
||||
/* an update that doesn't touch "key" columns */
|
||||
|
@ -54,6 +54,17 @@ typedef struct MultiXactMember {
|
|||
MultiXactStatus status;
|
||||
} MultiXactMember;
|
||||
|
||||
/*
|
||||
* In htup.h, we define MaxTransactionId, and first four bits are reserved.
|
||||
* We use low 60 bits to record member xid and high 3 bits to record member status.
|
||||
*/
|
||||
#define MULTIXACT_MEMBER_XID_MASK UINT64CONST((UINT64CONST(1) << 60) - 1)
|
||||
#define GET_MEMBER_XID_FROM_SLRU_XID(xid) ((xid) & MULTIXACT_MEMBER_XID_MASK)
|
||||
#define GET_MEMBER_STATUS_FROM_SLRU_XID(xid) (MultiXactStatus((xid) >> 61))
|
||||
#define GET_SLRU_XID_FROM_MULTIXACT_MEMBER(member) \
|
||||
(((TransactionId)((member)->status) << 61) | (((member)->xid) & MULTIXACT_MEMBER_XID_MASK))
|
||||
|
||||
|
||||
/* ----------------
|
||||
* multixact-related XLOG entries
|
||||
* ----------------
|
||||
|
@ -71,20 +82,22 @@ typedef struct xl_multixact_create {
|
|||
MultiXactId mid; /* new MultiXact's ID */
|
||||
MultiXactOffset moff; /* its starting offset in members file */
|
||||
int32 nxids; /* number of member XIDs */
|
||||
TransactionId xids[FLEXIBLE_ARRAY_MEMBER]; /* VARIABLE LENGTH ARRAY */
|
||||
TransactionId xids[FLEXIBLE_ARRAY_MEMBER]; /* low 60 bits record member xid, high 3 bits record member status */
|
||||
} xl_multixact_create;
|
||||
|
||||
#define MinSizeOfMultiXactCreate offsetof(xl_multixact_create, xids)
|
||||
|
||||
extern MultiXactId MultiXactIdCreate(TransactionId xid1, TransactionId xid2);
|
||||
extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid);
|
||||
MultiXactId MultiXactIdCreate(TransactionId xid1, MultiXactStatus status1,
|
||||
TransactionId xid2, MultiXactStatus status2);
|
||||
extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status);
|
||||
extern bool MultiXactIdIsRunning(MultiXactId multi);
|
||||
extern bool MultiXactIdIsCurrent(MultiXactId multi);
|
||||
extern MultiXactId ReadNextMultiXactId(void);
|
||||
extern void MultiXactIdWait(MultiXactId multi, bool allow_con_update = false);
|
||||
extern bool ConditionalMultiXactIdWait(MultiXactId multi);
|
||||
extern bool DoMultiXactIdWait(MultiXactId multi, MultiXactStatus status, int *remaining, bool nowait);
|
||||
extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, int *remaining);
|
||||
extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, int *remaining);
|
||||
extern void MultiXactIdSetOldestMember(void);
|
||||
extern int GetMultiXactIdMembers(MultiXactId multi, TransactionId** xids);
|
||||
extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember** members);
|
||||
|
||||
extern void AtEOXact_MultiXact(void);
|
||||
extern void AtPrepare_MultiXact(void);
|
||||
|
@ -95,10 +108,14 @@ extern void MultiXactShmemInit(void);
|
|||
extern void BootStrapMultiXact(void);
|
||||
extern void StartupMultiXact(void);
|
||||
extern void ShutdownMultiXact(void);
|
||||
extern void SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid);
|
||||
extern void MultiXactGetCheckptMulti(bool is_shutdown, MultiXactId* nextMulti, MultiXactOffset* nextMultiOffset);
|
||||
extern void CheckPointMultiXact(void);
|
||||
extern MultiXactId GetOldestMultiXactId(void);
|
||||
extern void TruncateMultiXact(MultiXactId cutoff_multi = InvalidMultiXactId);
|
||||
extern void MultiXactSetNextMXact(MultiXactId nextMulti, MultiXactOffset nextMultiOffset);
|
||||
extern void MultiXactAdvanceNextMXact(MultiXactId minMulti, MultiXactOffset minMultiOffset);
|
||||
extern void MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB);
|
||||
|
||||
extern void multixact_twophase_recover(TransactionId xid, uint16 info, void* recdata, uint32 len);
|
||||
extern void multixact_twophase_postcommit(TransactionId xid, uint16 info, void* recdata, uint32 len);
|
||||
|
|
|
@ -260,6 +260,7 @@ extern int8 heaprel_get_compression_from_modes(int16 modes);
|
|||
extern bool get_crossbucket_option(List **options_ptr, bool stmtoptgpi = false, char *accessmethod = NULL,
|
||||
int *crossbucketopt = NULL);
|
||||
extern bool is_contain_crossbucket(List *defList);
|
||||
extern bool is_cstore_option(char relkind, Datum reloptions);
|
||||
|
||||
extern void CheckGetServerIpAndPort(const char* Address, List** AddrList, bool IsCheck, int real_addr_max);
|
||||
extern void CheckFoldernameOrFilenamesOrCfgPtah(const char* OptStr, char* OptType);
|
||||
|
|
|
@ -452,7 +452,8 @@ typedef struct TableAmRoutine {
|
|||
|
||||
TM_Result (*tuple_update)(Relation relation, Relation parentRelation, ItemPointer otid, Tuple newtup, CommandId cid,
|
||||
Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot **oldslot, TM_FailureData *tmfd,
|
||||
bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self, bool allow_inplace_update);
|
||||
LockTupleMode *mode, bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self,
|
||||
bool allow_inplace_update);
|
||||
|
||||
TM_Result (*tuple_lock)(Relation relation, Tuple tuple, Buffer *buffer, CommandId cid, LockTupleMode mode,
|
||||
bool nowait, TM_FailureData *tmfd, bool allow_lock_self, bool follow_updates, bool eval, Snapshot snapshot,
|
||||
|
@ -583,7 +584,7 @@ extern TM_Result tableam_tuple_delete(Relation relation, ItemPointer tid, Comman
|
|||
extern TM_Result tableam_tuple_update(Relation relation, Relation parentRelation, ItemPointer otid, Tuple newtup,
|
||||
CommandId cid, Snapshot crosscheck, Snapshot snapshot, bool wait, TupleTableSlot **oldslot, TM_FailureData *tmfd,
|
||||
bool *update_indexes, Bitmapset **modifiedIdxAttrs, bool allow_update_self = false,
|
||||
bool allow_inplace_update = true);
|
||||
bool allow_inplace_update = true, LockTupleMode *lockmode = NULL);
|
||||
extern TM_Result tableam_tuple_lock(Relation relation, Tuple tuple, Buffer *buffer, CommandId cid,
|
||||
LockTupleMode mode, bool nowait, TM_FailureData *tmfd, bool allow_lock_self, bool follow_updates, bool eval,
|
||||
Snapshot snapshot, ItemPointer tid, bool isSelectForUpdate, bool isUpsert = false,
|
||||
|
|
|
@ -36,9 +36,6 @@
|
|||
#define NUM_BLOCKS_FOR_NON_INPLACE_UPDATES 200
|
||||
#define MIN_SAVING_LEN 3
|
||||
|
||||
#define ConditionalLockTupleTuplock(_rel, _tup, _mode) \
|
||||
ConditionalLockTuple((_rel), (_tup), TupleLockExtraInfo[_mode].hwlock)
|
||||
|
||||
typedef struct UHeapWALInfo {
|
||||
Oid relOid;
|
||||
Oid partitionOid;
|
||||
|
|
|
@ -22,30 +22,6 @@
|
|||
#include "access/multixact.h"
|
||||
#include "access/ustore/knl_utuple.h"
|
||||
|
||||
const struct LockExtraInfo TupleLockExtraInfo[MaxLockTupleMode + 1] = {
|
||||
{
|
||||
/* LockTupleKeyShare */
|
||||
AccessShareLock,
|
||||
MultiXactStatusForKeyShare,
|
||||
-1 /* KeyShare does not allow updating tuples */
|
||||
},
|
||||
{
|
||||
ShareLock, /* LockTupleShared */
|
||||
MultiXactStatusForShare,
|
||||
-1
|
||||
},
|
||||
{
|
||||
ExclusiveLock, /* LockTupleNoKeyExclusive */
|
||||
MultiXactStatusForNoKeyUpdate,
|
||||
MultiXactStatusNoKeyUpdate
|
||||
},
|
||||
{
|
||||
ExclusiveLock, /* LockTupleExclusive */
|
||||
MultiXactStatusForUpdate,
|
||||
MultiXactStatusUpdate
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the heavy-weight lock mode from lock tuple mode.
|
||||
*/
|
||||
|
|
|
@ -901,20 +901,22 @@ extern AbnormalProcFunc g_AbFunList[ABNORMAL_NUM];
|
|||
|
||||
void HeapXlogCleanOperatorPage(
|
||||
RedoBufferInfo* buffer, void* recorddata, void* blkdata, Size datalen, Size* freespace, bool repairFragmentation);
|
||||
void HeapXlogFreezeOperatorPage(RedoBufferInfo* buffer, void* recorddata, void* blkdata, Size datalen);
|
||||
void HeapXlogFreezeOperatorPage(RedoBufferInfo* buffer, void* recorddata, void* blkdata, Size datalen,
|
||||
bool isTupleLockUpgrade);
|
||||
void HeapXlogVisibleOperatorPage(RedoBufferInfo* buffer, void* recorddata);
|
||||
void HeapXlogVisibleOperatorVmpage(RedoBufferInfo* vmbuffer, void* recorddata);
|
||||
void HeapXlogDeleteOperatorPage(RedoBufferInfo* buffer, void* recorddata, TransactionId recordxid);
|
||||
void HeapXlogDeleteOperatorPage(RedoBufferInfo* buffer, void* recorddata, TransactionId recordxid,
|
||||
bool isTupleLockUpgrade);
|
||||
void HeapXlogInsertOperatorPage(RedoBufferInfo* buffer, void* recorddata, bool isinit, void* blkdata, Size datalen,
|
||||
TransactionId recxid, Size* freespace, bool tde = false);
|
||||
void HeapXlogMultiInsertOperatorPage(RedoBufferInfo* buffer, const void* recoreddata, bool isinit, const void* blkdata,
|
||||
Size len, TransactionId recordxid, Size* freespace, bool tde = false);
|
||||
void HeapXlogUpdateOperatorOldpage(RedoBufferInfo* buffer, void* recoreddata, bool hot_update, bool isnewinit,
|
||||
BlockNumber newblk, TransactionId recordxid);
|
||||
BlockNumber newblk, TransactionId recordxid, bool isTupleLockUpgrade);
|
||||
void HeapXlogUpdateOperatorNewpage(RedoBufferInfo* buffer, void* recorddata, bool isinit, void* blkdata,
|
||||
Size datalen, TransactionId recordxid, Size* freespace, bool tde = false);
|
||||
Size datalen, TransactionId recordxid, Size* freespace, bool isTupleLockUpgrade, bool tde = false);
|
||||
void HeapXlogPageUpgradeOperatorPage(RedoBufferInfo* buffer);
|
||||
void HeapXlogLockOperatorPage(RedoBufferInfo* buffer, void* recorddata);
|
||||
void HeapXlogLockOperatorPage(RedoBufferInfo* buffer, void* recorddata, bool isTupleLockUpgrade);
|
||||
void HeapXlogInplaceOperatorPage(RedoBufferInfo* buffer, void* recorddata, void* blkdata, Size newlen);
|
||||
void HeapXlogBaseShiftOperatorPage(RedoBufferInfo* buffer, void* recorddata);
|
||||
|
||||
|
|
|
@ -84,6 +84,10 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
|
|||
TransactionId relfrozenxid64; /* all Xids < this are frozen in this rel */
|
||||
Oid relbucket; /* bucket info in pg_hashbucket */
|
||||
int2vector relbucketkey; /* Column number of hash partition */
|
||||
#ifdef CATALOG_VARLEN
|
||||
TransactionId relminmxid; /* all multixacts in this rel are >= this.
|
||||
* this is really a MultiXactId */
|
||||
#endif
|
||||
}
|
||||
FormData_pg_class;
|
||||
|
||||
|
@ -102,7 +106,7 @@ typedef FormData_pg_class* Form_pg_class;
|
|||
* ----------------
|
||||
*/
|
||||
|
||||
#define Natts_pg_class 39
|
||||
#define Natts_pg_class 40
|
||||
#define Anum_pg_class_relname 1
|
||||
#define Anum_pg_class_relnamespace 2
|
||||
#define Anum_pg_class_reltype 3
|
||||
|
@ -142,6 +146,7 @@ typedef FormData_pg_class* Form_pg_class;
|
|||
#define Anum_pg_class_relfrozenxid64 37
|
||||
#define Anum_pg_class_relbucket 38
|
||||
#define Anum_pg_class_relbucketkey 39
|
||||
#define Anum_pg_class_relminmxid 40
|
||||
|
||||
/* ----------------
|
||||
* initial contents of pg_class
|
||||
|
@ -152,16 +157,19 @@ typedef FormData_pg_class* Form_pg_class;
|
|||
* ----------------
|
||||
*/
|
||||
|
||||
/* Note: "3" in the relfrozenxid and the relfrozenxid64 column stands for FirstNormalTransactionId */
|
||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_));
|
||||
/*
|
||||
* Note: "3" in the relfrozenxid and the relfrozenxid64 column stands for FirstNormalTransactionId;
|
||||
* similarly, "1" in relminmxid stands for FirstMultiXactId.
|
||||
*/
|
||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_ 1));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 24 0 f f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_));
|
||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 24 0 f f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_ 1));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 37 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_));
|
||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 37 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_ 1));
|
||||
DESCR("");
|
||||
DATA(insert OID = 7815 ( gs_package PGNSP 9745 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 7 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_));
|
||||
DATA(insert OID = 7815 ( gs_package PGNSP 9745 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 7 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_ 1));
|
||||
DESCR("");
|
||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 39 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_));
|
||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 0 0 0 0 f f p r 40 0 t f f f f 0 f f n 3 _null_ _null_ n 3 _null_ _null_ 1));
|
||||
DESCR("");
|
||||
|
||||
#define RELKIND_RELATION 'r' /* ordinary table */
|
||||
|
|
|
@ -48,6 +48,7 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION BKI_ROWTYPE_OID(1248) BKI_SCHEMA_M
|
|||
aclitem datacl[1]; /* access permissions */
|
||||
#endif
|
||||
TransactionId datfrozenxid64; /* all Xids < this are frozen in this DB */
|
||||
TransactionId datminmxid; /* all multixacts in the DB are >= this */
|
||||
} FormData_pg_database;
|
||||
|
||||
/* Size of fixed part of pg_database tuples, not counting var-length fields */
|
||||
|
@ -65,7 +66,7 @@ typedef FormData_pg_database *Form_pg_database;
|
|||
* compiler constants for pg_database
|
||||
* ----------------
|
||||
*/
|
||||
#define Natts_pg_database 14
|
||||
#define Natts_pg_database 15
|
||||
#define Anum_pg_database_datname 1
|
||||
#define Anum_pg_database_datdba 2
|
||||
#define Anum_pg_database_encoding 3
|
||||
|
@ -80,8 +81,9 @@ typedef FormData_pg_database *Form_pg_database;
|
|||
#define Anum_pg_database_compatibility 12
|
||||
#define Anum_pg_database_datacl 13
|
||||
#define Anum_pg_database_datfrozenxid64 14
|
||||
#define Anum_pg_database_datminmxid 15
|
||||
|
||||
DATA(insert OID = 1 ( template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1663 "DB_COMPATIBILITY" _null_ 3));
|
||||
DATA(insert OID = 1 ( template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1663 "DB_COMPATIBILITY" _null_ 3 1));
|
||||
SHDESCR("unmodifiable empty database");
|
||||
#define TemplateDbOid 1
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ CATALOG(pg_partition,9016) BKI_ROWTYPE_OID(3790) BKI_SCHEMA_MACRO
|
|||
text reloptions[1]; /* access-method-specific options */
|
||||
#endif
|
||||
TransactionId relfrozenxid64;
|
||||
TransactionId relminmxid; /* all multixacts in this rel are >= this.
|
||||
* this is really a MultiXactId */
|
||||
} FormData_pg_partition;
|
||||
/* Size of fixed part of pg_partition tuples, not counting var-length fields */
|
||||
#define PARTITION_TUPLE_SIZE \
|
||||
|
@ -91,7 +93,7 @@ typedef FormData_pg_partition *Form_pg_partition;
|
|||
#define PART_OBJ_TYPE_TABLE_PARTITION 'p'
|
||||
#define PART_OBJ_TYPE_INDEX_PARTITION 'x'
|
||||
|
||||
#define Natts_pg_partition 28
|
||||
#define Natts_pg_partition 29
|
||||
#define Anum_pg_partition_relname 1
|
||||
#define Anum_pg_partition_parttype 2
|
||||
#define Anum_pg_partition_parentid 3
|
||||
|
@ -120,5 +122,6 @@ typedef FormData_pg_partition *Form_pg_partition;
|
|||
#define Anum_pg_partition_transit 26
|
||||
#define Anum_pg_partition_reloptions 27
|
||||
#define Anum_pg_partition_relfrozenxid64 28
|
||||
#define Anum_pg_partition_relminmxid 29
|
||||
#endif/*PG_PARTITION_H*/
|
||||
|
||||
|
|
|
@ -32,15 +32,15 @@ extern double copy_heap_data_internal(Relation OldHeap, Relation OldIndex, Relat
|
|||
TransactionId FreezeXid, bool verbose, bool use_sort, AdaptMem* memUsage);
|
||||
extern double CopyUHeapDataInternal(Relation oldHeap, Relation oldIndex, Relation newHeap, TransactionId oldestXmin,
|
||||
TransactionId freezeXid, bool verbose, bool useSort, const AdaptMem* memUsage);
|
||||
extern TransactionId getPartitionRelfrozenxid(Relation ordTableRel);
|
||||
extern TransactionId getRelationRelfrozenxid(Relation ordTableRel);
|
||||
extern void getPartitionRelxids(Relation ordTableRel, TransactionId* frozenXid, MultiXactId* multiXid = NULL);
|
||||
extern void getRelationRelxids(Relation ordTableRel, TransactionId* frozenXid, MultiXactId* multiXid = NULL);
|
||||
extern void setRelationRelfrozenxid(Oid relid, TransactionId frozenXid);
|
||||
extern void setPartitionRelfrozenxid(Oid partid, TransactionId frozenXid);
|
||||
extern void finishPartitionHeapSwap(Oid partitionOid, Oid tempTableOid, bool swapToastByContent,
|
||||
TransactionId frozenXid, bool tempTableIsPartition = false);
|
||||
TransactionId frozenXid, MultiXactId multiXid, bool tempTableIsPartition = false);
|
||||
|
||||
extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content,
|
||||
bool check_constraints, TransactionId frozenXid, AdaptMem* memInfo = NULL);
|
||||
bool check_constraints, TransactionId frozenXid, MultiXactId frozenMulti, AdaptMem* memInfo = NULL);
|
||||
|
||||
extern void vacuumFullPart(Oid partOid, VacuumStmt* vacstmt, int freeze_min_age, int freeze_table_age);
|
||||
extern void GpiVacuumFullMainPartiton(Oid parentOid);
|
||||
|
|
|
@ -408,9 +408,10 @@ extern void vac_close_indexes(int nindexes, Relation* Irel, LOCKMODE lockmode);
|
|||
extern double vac_estimate_reltuples(
|
||||
Relation relation, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples);
|
||||
extern void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_pages, double num_tuples,
|
||||
BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid);
|
||||
BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid,
|
||||
MultiXactId minmulti = InvalidMultiXactId);
|
||||
extern void vacuum_set_xid_limits(Relation rel, int64 freeze_min_age, int64 freeze_table_age, TransactionId* oldestXmin,
|
||||
TransactionId* freezeLimit, TransactionId* freezeTableLimit);
|
||||
TransactionId* freezeLimit, TransactionId* freezeTableLimit, MultiXactId* multiXactFrzLimit);
|
||||
extern void vac_update_datfrozenxid(void);
|
||||
extern void vacuum_delay_point(void);
|
||||
|
||||
|
@ -445,19 +446,20 @@ extern void delete_attstats_replication(Oid relid, VacuumStmt* stmt);
|
|||
extern int compute_attr_target(Form_pg_attribute attr);
|
||||
|
||||
extern void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tuples,
|
||||
BlockNumber num_all_visible_pages, TransactionId frozenxid);
|
||||
BlockNumber num_all_visible_pages, TransactionId frozenxid, MultiXactId minmulti = InvalidMultiXactId);
|
||||
extern void vac_open_part_indexes(VacuumStmt* vacstmt, LOCKMODE lockmode, int* nindexes, int* nindexesGlobal,
|
||||
Relation** Irel, Relation** indexrel, Partition** indexpart);
|
||||
extern void vac_close_part_indexes(
|
||||
int nindexes, int nindexesGlobal, Relation* Irel, Relation* indexrel, Partition* indexpart, LOCKMODE lockmode);
|
||||
extern void vac_update_pgclass_partitioned_table(Relation partitionRel, bool hasIndex, TransactionId newFrozenXid);
|
||||
extern void vac_update_pgclass_partitioned_table(Relation partitionRel, bool hasIndex, TransactionId newFrozenXid,
|
||||
MultiXactId newMultiXid);
|
||||
|
||||
extern void CStoreVacUpdateNormalRelStats(Oid relid, TransactionId frozenxid, Relation pgclassRel);
|
||||
extern void CStoreVacUpdatePartitionRelStats(Relation partitionRel, TransactionId newFrozenXid);
|
||||
extern void CStoreVacUpdatePartitionStats(Oid relid, TransactionId frozenxid);
|
||||
extern void CalculatePartitionedRelStats(_in_ Relation partitionRel, _in_ Relation pgPartitionRel,
|
||||
_out_ BlockNumber* totalPages, _out_ BlockNumber* totalVisiblePages, _out_ double* totalTuples,
|
||||
_out_ TransactionId* minFrozenXid);
|
||||
_out_ TransactionId* minFrozenXid, _out_ MultiXactId* minMultiXid);
|
||||
|
||||
extern bool IsToastRelationbyOid(Oid relid);
|
||||
extern Oid pg_toast_get_baseid(Oid relOid, bool* isPartToast);
|
||||
|
|
|
@ -226,7 +226,7 @@ extern void ExecConstraints(ResultRelInfo* resultRelInfo, TupleTableSlot* slot,
|
|||
extern ExecRowMark* ExecFindRowMark(EState* estate, Index rti);
|
||||
extern ExecAuxRowMark* ExecBuildAuxRowMark(ExecRowMark* erm, List* targetlist);
|
||||
extern TupleTableSlot* EvalPlanQual(EState* estate, EPQState* epqstate, Relation relation, Index rti,
|
||||
ItemPointer tid, TransactionId priorXmax, bool partRowMoveUpdate);
|
||||
int lockmode, ItemPointer tid, TransactionId priorXmax, bool partRowMoveUpdate);
|
||||
extern HeapTuple heap_lock_updated(
|
||||
CommandId cid, Relation relation, int lockmode, ItemPointer tid, TransactionId priorXmax);
|
||||
extern TupleTableSlot* EvalPlanQualUHeap(EState* estate, EPQState* epqstate, Relation relation, Index rti, ItemPointer tid, TransactionId priorXmax);
|
||||
|
|
|
@ -728,6 +728,7 @@ typedef struct knl_u_commands_context {
|
|||
List* PendingLibraryDeletes;
|
||||
TransactionId OldestXmin;
|
||||
TransactionId FreezeLimit;
|
||||
MultiXactId MultiXactFrzLimit;
|
||||
struct SeqTableData* seqtab; /* Head of list of SeqTable items */
|
||||
/*
|
||||
* last_used_seq is updated by nextval() to point to the last used
|
||||
|
|
|
@ -1404,8 +1404,9 @@ typedef struct knl_t_autovacuum_context {
|
|||
volatile sig_atomic_t got_SIGUSR2;
|
||||
volatile sig_atomic_t got_SIGTERM;
|
||||
|
||||
/* Comparison point for determining whether freeze_max_age is exceeded */
|
||||
/* Comparison points for determining whether freeze_max_age is exceeded */
|
||||
TransactionId recentXid;
|
||||
MultiXactId recentMulti;
|
||||
|
||||
/* Default freeze ages to use for autovacuum (varies by database) */
|
||||
int64 default_freeze_min_age;
|
||||
|
|
|
@ -72,6 +72,7 @@ extern void register_backend_version(uint32 backend_version);
|
|||
extern bool contain_backend_version(uint32 version_number);
|
||||
extern const uint32 ANALYZER_HOOK_VERSION_NUM;
|
||||
extern const uint32 SUPPORT_HASH_XLOG_VERSION_NUM;
|
||||
extern const uint32 ENHANCED_TUPLE_LOCK_VERSION_NUM;
|
||||
|
||||
#define INPLACE_UPGRADE_PRECOMMIT_VERSION 1
|
||||
|
||||
|
|
|
@ -2375,7 +2375,7 @@ typedef struct SetOpState {
|
|||
/* ----------------
|
||||
* LockRowsState information
|
||||
*
|
||||
* LockRows nodes are used to enforce FOR UPDATE/FOR SHARE locking.
|
||||
* LockRows nodes are used to enforce FOR [KEY] UPDATE/FOR SHARE locking.
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct LockRowsState {
|
||||
|
|
|
@ -87,7 +87,7 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
|
|||
#define ACL_WRITE (1 << 14) /* for pg_directory */
|
||||
#define N_ACL_RIGHTS 15 /* 1 plus the last 1<<x */
|
||||
#define ACL_NO_RIGHTS 0
|
||||
/* Currently, SELECT ... FOR UPDATE/FOR SHARE requires UPDATE privileges */
|
||||
/* Currently, SELECT ... FOR [KEY] UPDATE/FOR SHARE requires UPDATE privileges */
|
||||
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
|
||||
|
||||
/* grantable rights for DDL operations */
|
||||
|
@ -448,22 +448,23 @@ typedef struct WindowClause {
|
|||
|
||||
/*
|
||||
* RowMarkClause -
|
||||
* parser output representation of FOR UPDATE/SHARE clauses
|
||||
* parser output representation of FOR [KEY] UPDATE/SHARE clauses
|
||||
*
|
||||
* Query.rowMarks contains a separate RowMarkClause node for each relation
|
||||
* identified as a FOR UPDATE/SHARE target. If FOR UPDATE/SHARE is applied
|
||||
* to a subquery, we generate RowMarkClauses for all normal and subquery rels
|
||||
* in the subquery, but they are marked pushedDown = true to distinguish them
|
||||
* from clauses that were explicitly written at this query level. Also,
|
||||
* Query.hasForUpdate tells whether there were explicit FOR UPDATE/SHARE
|
||||
* clauses in the current query level.
|
||||
* identified as a FOR [KEY] UPDATE/SHARE target. If one of these clauses
|
||||
* is applied to a subquery, we generate RowMarkClauses for all normal and
|
||||
* subquery rels in the subquery, but they are marked pushedDown = true to
|
||||
* distinguish them from clauses that were explicitly written at this query
|
||||
* level. Also, Query.hasForUpdate tells whether there were explicit FOR
|
||||
* UPDATE/SHARE/KEY SHARE clauses in the current query level
|
||||
*/
|
||||
typedef struct RowMarkClause {
|
||||
NodeTag type;
|
||||
Index rti; /* range table index of target relation */
|
||||
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
|
||||
bool forUpdate; /* for compatibility, we reserve this filed but don't use it */
|
||||
bool noWait; /* NOWAIT option */
|
||||
bool pushedDown; /* pushed down from higher query level? */
|
||||
LockClauseStrength strength;
|
||||
} RowMarkClause;
|
||||
|
||||
/* Convenience macro to get the output tlist of a CTE's query */
|
||||
|
|
|
@ -1448,18 +1448,27 @@ typedef struct GroupingSet {
|
|||
} GroupingSet;
|
||||
|
||||
/*
|
||||
* LockingClause - raw representation of FOR UPDATE/SHARE options
|
||||
* LockingClause - raw representation of FOR [NO KEY] UPDATE/[KEY] SHARE options
|
||||
*
|
||||
* Note: lockedRels == NIL means "all relations in query". Otherwise it
|
||||
* is a list of RangeVar nodes. (We use RangeVar mainly because it carries
|
||||
* a location field --- currently, parse analysis insists on unqualified
|
||||
* names in LockingClause.)
|
||||
*/
|
||||
typedef enum LockClauseStrength {
|
||||
/* order is important -- see applyLockingClause */
|
||||
LCS_FORKEYSHARE,
|
||||
LCS_FORSHARE,
|
||||
LCS_FORNOKEYUPDATE,
|
||||
LCS_FORUPDATE
|
||||
} LockClauseStrength;
|
||||
|
||||
typedef struct LockingClause {
|
||||
NodeTag type;
|
||||
List *lockedRels; /* FOR UPDATE or FOR SHARE relations */
|
||||
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
|
||||
List *lockedRels; /* FOR [KEY] UPDATE/SHARE relations */
|
||||
bool forUpdate; /* for compatibility, we reserve this field but don't use it */
|
||||
bool noWait; /* NOWAIT option */
|
||||
LockClauseStrength strength;
|
||||
} LockingClause;
|
||||
|
||||
/*
|
||||
|
@ -1682,7 +1691,7 @@ typedef struct Query {
|
|||
bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
|
||||
bool hasRecursive; /* WITH RECURSIVE was specified */
|
||||
bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
|
||||
bool hasForUpdate; /* FOR UPDATE or FOR SHARE was specified */
|
||||
bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
|
||||
bool hasRowSecurity; /* rewriter has applied some RLS policy */
|
||||
bool hasSynonyms; /* has synonym mapping in rtable */
|
||||
|
||||
|
|
|
@ -1237,7 +1237,7 @@ typedef struct VecLimit : public Limit {
|
|||
* RowMarkType -
|
||||
* enums for types of row-marking operations
|
||||
*
|
||||
* When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we have to uniquely
|
||||
* When doing UPDATE, DELETE, or SELECT FOR [KEY] UPDATE/SHARE, we have to uniquely
|
||||
* identify all the source rows, not only those from the target relations, so
|
||||
* that we can perform EvalPlanQual rechecking at need. For plain tables we
|
||||
* can just fetch the TID, the same as for a target relation. Otherwise (for
|
||||
|
@ -1246,22 +1246,24 @@ typedef struct VecLimit : public Limit {
|
|||
* performance-critical in practice.
|
||||
*/
|
||||
typedef enum RowMarkType {
|
||||
ROW_MARK_EXCLUSIVE, /* obtain exclusive tuple lock */
|
||||
ROW_MARK_SHARE, /* obtain shared tuple lock */
|
||||
ROW_MARK_REFERENCE, /* just fetch the TID */
|
||||
ROW_MARK_COPY, /* physically copy the row value */
|
||||
ROW_MARK_COPY_DATUM /* physically copy the datum of every row column */
|
||||
ROW_MARK_EXCLUSIVE, /* obtain exclusive tuple lock */
|
||||
ROW_MARK_NOKEYEXCLUSIVE, /* obtain no-key exclusive tuple lock */
|
||||
ROW_MARK_SHARE, /* obtain shared tuple lock */
|
||||
ROW_MARK_KEYSHARE, /* obtain keyshare tuple lock */
|
||||
ROW_MARK_REFERENCE, /* just fetch the TID */
|
||||
ROW_MARK_COPY, /* physically copy the row value */
|
||||
ROW_MARK_COPY_DATUM /* physically copy the datum of every row column */
|
||||
} RowMarkType;
|
||||
|
||||
#define RowMarkRequiresRowShareLock(marktype) ((marktype) <= ROW_MARK_SHARE)
|
||||
#define RowMarkRequiresRowShareLock(marktype) ((marktype) <= ROW_MARK_KEYSHARE)
|
||||
|
||||
/*
|
||||
* PlanRowMark -
|
||||
* plan-time representation of FOR UPDATE/SHARE clauses
|
||||
* plan-time representation of FOR [KEY] UPDATE/SHARE clauses
|
||||
*
|
||||
* When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we create a separate
|
||||
* When doing UPDATE, DELETE, or SELECT FOR [KEY] UPDATE/SHARE, we create a separate
|
||||
* PlanRowMark node for each non-target relation in the query. Relations that
|
||||
* are not specified as FOR UPDATE/SHARE are marked ROW_MARK_REFERENCE (if
|
||||
* are not specified as FOR [KEY] UPDATE/SHARE are marked ROW_MARK_REFERENCE (if
|
||||
* real tables) or ROW_MARK_COPY (if not).
|
||||
*
|
||||
* Initially all PlanRowMarks have rti == prti and isParent == false.
|
||||
|
|
|
@ -42,7 +42,7 @@ extern Query* transformStmt(ParseState* pstate, Node* parseTree, bool isFirstNod
|
|||
extern bool analyze_requires_snapshot(Node* parseTree);
|
||||
|
||||
extern void CheckSelectLocking(Query* qry);
|
||||
extern void applyLockingClause(Query* qry, Index rtindex, bool forUpdate, bool noWait, bool pushedDown);
|
||||
extern void applyLockingClause(Query* qry, Index rtindex, LockClauseStrength strength, bool noWait, bool pushedDown);
|
||||
#ifdef ENABLE_MOT
|
||||
extern void CheckTablesStorageEngine(Query* qry, StorageEngineType* type);
|
||||
extern bool CheckMotIndexedColumnUpdate(Query* qry);
|
||||
|
|
|
@ -131,8 +131,8 @@ typedef struct autovac_table {
|
|||
bool at_doanalyze;
|
||||
bool at_needfreeze;
|
||||
bool at_sharedrel;
|
||||
int at_freeze_min_age;
|
||||
int at_freeze_table_age;
|
||||
int64 at_freeze_min_age;
|
||||
int64 at_freeze_table_age;
|
||||
int at_vacuum_cost_delay;
|
||||
int at_vacuum_cost_limit;
|
||||
char* at_partname;
|
||||
|
|
|
@ -325,12 +325,6 @@ typedef struct LOCKTAG {
|
|||
(locktag).locktag_type = LOCKTAG_CSTORE_FREESPACE, \
|
||||
(locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
|
||||
|
||||
struct LockExtraInfo {
|
||||
LOCKMODE hwlock;
|
||||
int lockstatus;
|
||||
int updstatus;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-locked-object lock information:
|
||||
*
|
||||
|
|
|
@ -82,7 +82,8 @@ extern Relation partitionGetRelation(Relation rel, Partition part);
|
|||
|
||||
void releaseDummyRelation(Relation* relation);
|
||||
|
||||
extern void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId freezeXid);
|
||||
extern void PartitionSetNewRelfilenode(Relation parent, Partition part, TransactionId freezeXid,
|
||||
MultiXactId freezeMultiXid);
|
||||
|
||||
/*
|
||||
* Routines for global partition index open partition
|
||||
|
|
|
@ -157,6 +157,7 @@ typedef struct RelationData {
|
|||
|
||||
/* data managed by RelationGetIndexAttrBitmap: */
|
||||
Bitmapset* rd_indexattr; /* identifies columns used in indexes */
|
||||
Bitmapset* rd_keyattr; /* cols that can be ref'd by foreign keys */
|
||||
Bitmapset* rd_idattr; /* included in replica identity index */
|
||||
|
||||
/*
|
||||
|
|
|
@ -114,7 +114,8 @@ extern Relation RelationBuildLocalRelation(const char* relname, Oid relnamespace
|
|||
*/
|
||||
extern void DescTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partition);
|
||||
extern void DeltaTableSetNewRelfilenode(Oid relid, TransactionId freezeXid, bool partition);
|
||||
extern void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, bool isDfsTruncate = false);
|
||||
extern void RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, MultiXactId minmulti,
|
||||
bool isDfsTruncate = false);
|
||||
extern RelFileNodeBackend CreateNewRelfilenode(Relation relation, TransactionId freezeXid);
|
||||
extern void UpdatePgclass(Relation relation, TransactionId freezeXid, const RelFileNodeBackend *rnode);
|
||||
/*
|
||||
|
|
|
@ -115,7 +115,7 @@ RESET ENABLE_SEQSCAN;
|
|||
RESET ENABLE_HASHAGG;
|
||||
-- LockRows
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM DISABLE_VECTOR_ENGINE.VECTOR_TABLE_01 FOR UPDATE;
|
||||
ERROR: SELECT FOR UPDATE/SHARE cannot be used with column table "vector_table_01"
|
||||
ERROR: SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be used with column table "vector_table_01"
|
||||
-- CteScan
|
||||
-- HashJoin, MergeJoin, NestLoop, Materialize
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM DISABLE_VECTOR_ENGINE.VECTOR_TABLE_01 T1, DISABLE_VECTOR_ENGINE.VECTOR_TABLE_02 T2 WHERE T1.DEPNAME=T2.DEPNAME;
|
||||
|
|
|
@ -360,10 +360,10 @@ explain (verbose on, costs off) select * from distribute_source_replication_01
|
|||
(3 rows)
|
||||
|
||||
explain (verbose on, costs off) select * from pg_class where ctid='(0,1)';
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Tid Scan on pg_catalog.pg_class
|
||||
Output: relname, relnamespace, reltype, reloftype, relowner, relam, relfilenode, reltablespace, relpages, reltuples, relallvisible, reltoastrelid, reltoastidxid, reldeltarelid, reldeltaidx, relcudescrelid, relcudescidx, relhasindex, relisshared, relpersistence, relkind, relnatts, relchecks, relhasoids, relhaspkey, relhasrules, relhastriggers, relhassubclass, relcmprs, relhasclusterkey, relrowmovement, parttype, relfrozenxid, relacl, reloptions, relreplident, relfrozenxid64, relbucket, relbucketkey
|
||||
Output: relname, relnamespace, reltype, reloftype, relowner, relam, relfilenode, reltablespace, relpages, reltuples, relallvisible, reltoastrelid, reltoastidxid, reldeltarelid, reldeltaidx, relcudescrelid, relcudescidx, relhasindex, relisshared, relpersistence, relkind, relnatts, relchecks, relhasoids, relhaspkey, relhasrules, relhastriggers, relhassubclass, relcmprs, relhasclusterkey, relrowmovement, parttype, relfrozenxid, relacl, reloptions, relreplident, relfrozenxid64, relbucket, relbucketkey, relminmxid
|
||||
TID Cond: (pg_class.ctid = '(0,1)'::tid)
|
||||
(3 rows)
|
||||
|
||||
|
|
|
@ -911,7 +911,7 @@ create table sche1.pg_class(id int);
|
|||
set search_path=sche1;
|
||||
insert into pg_class values(1);
|
||||
ERROR: null value in column "relnamespace" violates not-null constraint
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
select * from sche1.pg_class;
|
||||
id
|
||||
----
|
||||
|
@ -922,7 +922,7 @@ set search_path=sche1, pg_catalog;
|
|||
WARNING: It is invalid to set pg_temp or pg_catalog behind other schemas in search path explicitly. The priority order is pg_temp, pg_catalog and other schemas.
|
||||
insert into pg_class values(1);
|
||||
ERROR: null value in column "relnamespace" violates not-null constraint
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
select * from sche1.pg_class;
|
||||
id
|
||||
----
|
||||
|
@ -945,7 +945,7 @@ end;
|
|||
$$ LANGUAGE plpgsql;
|
||||
select sche1.fun_001();
|
||||
ERROR: null value in column "relnamespace" violates not-null constraint
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
CONTEXT: SQL statement "insert into pg_class values(1)"
|
||||
PL/pgSQL function fun_001() line 4 at SQL statement
|
||||
referenced column: fun_001
|
||||
|
@ -958,7 +958,7 @@ set search_path=sche1, pg_catalog;
|
|||
WARNING: It is invalid to set pg_temp or pg_catalog behind other schemas in search path explicitly. The priority order is pg_temp, pg_catalog and other schemas.
|
||||
select sche1.fun_001();
|
||||
ERROR: null value in column "relnamespace" violates not-null constraint
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
DETAIL: Failing row contains (1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null).
|
||||
CONTEXT: SQL statement "insert into pg_class values(1)"
|
||||
PL/pgSQL function fun_001() line 4 at SQL statement
|
||||
referenced column: fun_001
|
||||
|
|
|
@ -12,8 +12,8 @@ select gs_decrypt(gs_encrypt('along','1234@abc','sm4'),'1234@abc','sm4');
|
|||
(1 row)
|
||||
|
||||
select gs_decrypt(gs_encrypt('world','1234@abc','sm4'),'abc@4321','sm4'); --difference key cause error
|
||||
gs_decrypt
|
||||
------------
|
||||
--?
|
||||
--?
|
||||
--?
|
||||
(1 row)
|
||||
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
drop table if exists astore_mult1;
|
||||
NOTICE: table "astore_mult1" does not exist, skipping
|
||||
drop table if exists astore_mult2;
|
||||
NOTICE: table "astore_mult2" does not exist, skipping
|
||||
create table astore_mult1 (a int primary key, b int);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "astore_mult1_pkey" for table "astore_mult1"
|
||||
create table astore_mult2 (c int , d int);
|
||||
alter table astore_mult2 add foreign key (c) references astore_mult1 (a);
|
||||
insert into astore_mult1 values (1, 1);
|
||||
insert into astore_mult2 values (1, 1);
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for no key update;
|
||||
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
\parallel on 2
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 1;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult2 set d = 2 where c = 1;
|
||||
update astore_mult2 set d = 2 where c = 1;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
\parallel on 2
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 1;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
insert into astore_mult1 values (2, 2);
|
||||
\parallel on 2
|
||||
begin
|
||||
perform * from astore_mult1 where a = 2 for key share;
|
||||
perform pg_sleep(2);
|
||||
delete from astore_mult1 where a = 2;
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 2;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
insert into astore_mult1 values (2, 2);
|
||||
\parallel on 2
|
||||
begin
|
||||
perform * from astore_mult1 where a = 2 for key share;
|
||||
perform pg_sleep(2);
|
||||
delete from astore_mult1 where a = 2;
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 2;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
insert into astore_mult1 values (2, 2);
|
||||
\parallel on 2
|
||||
begin
|
||||
perform * from astore_mult1 where a = 2 for key share;
|
||||
perform pg_sleep(2);
|
||||
delete from astore_mult1 where a = 2;
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 2;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
vacuum freeze astore_mult1;
|
||||
vacuum freeze astore_mult2;
|
||||
drop table astore_mult2;
|
||||
drop table astore_mult1;
|
|
@ -1,2 +1,2 @@
|
|||
--run
|
||||
\! @abs_srcdir@/snapshots_test/test.sh -r -p @portstring@ -d regression
|
||||
\! sh @abs_srcdir@/snapshots_test/test.sh -r -p @portstring@ -d regression
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--run
|
||||
\! @abs_srcdir@/snapshots_test/test.sh -r -p @portstring@ -d regression
|
||||
\! sh @abs_srcdir@/snapshots_test/test.sh -r -p @portstring@ -d regression
|
||||
[1;33m## SETTING UP ##[0m harness: SetupTestHarness.sql ..... [1;32mPASS
|
||||
[0m[1;33m# RUNNING TEST #[0m case: 00 CreateSnapshotAPI.sql .... [1;32mPASS
|
||||
[0m[1;33m# RUNNING TEST #[0m case: 01 PrepareSnapshotAPI.sql ... [1;32mPASS
|
||||
|
|
|
@ -835,4 +835,6 @@ test: cast_privileges_test
|
|||
test: hw_cipher_sm4
|
||||
test: hw_cipher_aes128
|
||||
test: sequence_cache_test
|
||||
test: pg_buffercache_pages
|
||||
test: pg_buffercache_pages
|
||||
|
||||
test: test_astore_multixact
|
|
@ -0,0 +1,143 @@
|
|||
drop table if exists astore_mult1;
|
||||
drop table if exists astore_mult2;
|
||||
create table astore_mult1 (a int primary key, b int);
|
||||
create table astore_mult2 (c int , d int);
|
||||
alter table astore_mult2 add foreign key (c) references astore_mult1 (a);
|
||||
insert into astore_mult1 values (1, 1);
|
||||
insert into astore_mult2 values (1, 1);
|
||||
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for no key update;
|
||||
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
\parallel on 2
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for share;
|
||||
perform pg_sleep(2);
|
||||
end;
|
||||
/
|
||||
|
||||
begin
|
||||
perform pg_sleep(0.5);
|
||||
PERFORM * from astore_mult1 where a = 1 for share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
\parallel on 2
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 1;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
|
||||
begin
|
||||
update astore_mult2 set d = 2 where c = 1;
|
||||
update astore_mult2 set d = 2 where c = 1;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
\parallel on 2
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 1;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
|
||||
begin
|
||||
PERFORM * from astore_mult1 where a = 1 for key share;
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
insert into astore_mult1 values (2, 2);
|
||||
\parallel on 2
|
||||
begin
|
||||
perform * from astore_mult1 where a = 2 for key share;
|
||||
perform pg_sleep(2);
|
||||
delete from astore_mult1 where a = 2;
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 2;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
insert into astore_mult1 values (2, 2);
|
||||
\parallel on 2
|
||||
begin
|
||||
perform * from astore_mult1 where a = 2 for key share;
|
||||
perform pg_sleep(2);
|
||||
delete from astore_mult1 where a = 2;
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 2;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
insert into astore_mult1 values (2, 2);
|
||||
\parallel on 2
|
||||
begin
|
||||
perform * from astore_mult1 where a = 2 for key share;
|
||||
perform pg_sleep(2);
|
||||
delete from astore_mult1 where a = 2;
|
||||
end;
|
||||
/
|
||||
begin
|
||||
update astore_mult1 set b = 2 where a = 2;
|
||||
perform pg_sleep(3);
|
||||
end;
|
||||
/
|
||||
\parallel off
|
||||
|
||||
vacuum freeze astore_mult1;
|
||||
vacuum freeze astore_mult2;
|
||||
|
||||
drop table astore_mult2;
|
||||
drop table astore_mult1;
|
Loading…
Reference in New Issue