Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit99392cd

Browse files
committed
Rewrite some RI code to avoid using SPI
Modify the subroutines called by RI trigger functions that want to checkif a given referenced value exists in the referenced relation to simplyscan the foreign key constraint's unique index, instead of using SPI toexecute SELECT 1 FROM referenced_relation WHERE ref_key = $1This saves a lot of work, especially when inserting into or updating areferencing relation.This rewrite allows to fix a PK row visibility bug caused by a partitiondescriptor hack which requires ActiveSnapshot to be set to come up withthe correct set of partitions for the RI query running under REPEATABLEREAD isolation. We now set that snapshot indepedently of the snapshotto be used by the PK index scan, so the two no longer interfere. Thebuggy output in src/test/isolation/expected/fk-snapshot.out of therelevant test case added by commit00cb86e has been corrected.(The bug still exists in branch 14, however, but this fix is tooinvasive to backpatch.)Author: Amit Langote <amitlangote09@gmail.com>Reviewed-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com>Reviewed-by: Corey Huinker <corey.huinker@gmail.com>Reviewed-by: Li Japin <japinli@hotmail.com>Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>Reviewed-by: Zhihong Yu <zyu@yugabyte.com>Discussion:https://postgr.es/m/CA+HiwqGkfJfYdeq5vHPh6eqPKjSbfpDDY+j-kXYFePQedtSLeg@mail.gmail.com
1 parentdbe29b0 commit99392cd

File tree

7 files changed

+605
-317
lines changed

7 files changed

+605
-317
lines changed

‎src/backend/executor/execPartition.c

Lines changed: 168 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,9 @@ static void FormPartitionKeyDatum(PartitionDispatch pd,
176176
EState*estate,
177177
Datum*values,
178178
bool*isnull);
179-
staticintget_partition_for_tuple(PartitionDispatchpd,Datum*values,
180-
bool*isnull);
179+
staticintget_partition_for_tuple(PartitionKeykey,
180+
PartitionDescpartdesc,
181+
Datum*values,bool*isnull);
181182
staticchar*ExecBuildSlotPartitionKeyDescription(Relationrel,
182183
Datum*values,
183184
bool*isnull,
@@ -318,7 +319,9 @@ ExecFindPartition(ModifyTableState *mtstate,
318319
* these values, error out.
319320
*/
320321
if (partdesc->nparts==0||
321-
(partidx=get_partition_for_tuple(dispatch,values,isnull))<0)
322+
(partidx=get_partition_for_tuple(dispatch->key,
323+
dispatch->partdesc,
324+
values,isnull))<0)
322325
{
323326
char*val_desc;
324327

@@ -1341,12 +1344,12 @@ FormPartitionKeyDatum(PartitionDispatch pd,
13411344
* found or -1 if none found.
13421345
*/
13431346
staticint
1344-
get_partition_for_tuple(PartitionDispatchpd,Datum*values,bool*isnull)
1347+
get_partition_for_tuple(PartitionKeykey,
1348+
PartitionDescpartdesc,
1349+
Datum*values,bool*isnull)
13451350
{
13461351
intbound_offset;
13471352
intpart_index=-1;
1348-
PartitionKeykey=pd->key;
1349-
PartitionDescpartdesc=pd->partdesc;
13501353
PartitionBoundInfoboundinfo=partdesc->boundinfo;
13511354

13521355
/* Route as appropriate based on partitioning strategy. */
@@ -1438,6 +1441,165 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
14381441
returnpart_index;
14391442
}
14401443

1444+
/*
1445+
* ExecGetLeafPartitionForKey
1446+
*Finds the leaf partition of partitioned table 'root_rel' that would
1447+
*contain the specified key tuple.
1448+
*
1449+
* A subset of the table's columns (including all of the partition key columns)
1450+
* must be specified:
1451+
* - 'key_natts' indicats the number of columns contained in the key
1452+
* - 'key_attnums' indicates their attribute numbers as defined in 'root_rel'
1453+
* - 'key_vals' and 'key_nulls' specify the key tuple
1454+
*
1455+
* Returns the leaf partition, locked with the given lockmode, or NULL if
1456+
* there isn't one. Caller is responsibly for closing it. All intermediate
1457+
* partitions are also locked with the same lockmode. Caller must have locked
1458+
* the root already.
1459+
*
1460+
* In addition, the OID of the index of a unique constraint on the root table
1461+
* must be given as 'root_idxoid'; *leaf_idxoid will be set to the OID of the
1462+
* corresponding index on the returned leaf partition. (This can be used by
1463+
* caller to search for a tuple matching the key in the leaf partition.)
1464+
*
1465+
* This works because the unique key defined on the root relation is required
1466+
* to contain the partition key columns of all of the ancestors that lead up to
1467+
* a given leaf partition.
1468+
*/
1469+
Relation
1470+
ExecGetLeafPartitionForKey(Relationroot_rel,intkey_natts,
1471+
constAttrNumber*key_attnums,
1472+
Datum*key_vals,char*key_nulls,
1473+
Oidroot_idxoid,intlockmode,
1474+
Oid*leaf_idxoid)
1475+
{
1476+
Relationfound_leafpart=NULL;
1477+
Relationrel=root_rel;
1478+
Oidconstr_idxoid=root_idxoid;
1479+
PartitionDirectorypartdir;
1480+
1481+
Assert(root_rel->rd_rel->relkind==RELKIND_PARTITIONED_TABLE);
1482+
1483+
*leaf_idxoid=InvalidOid;
1484+
1485+
partdir=CreatePartitionDirectory(CurrentMemoryContext, true);
1486+
1487+
/*
1488+
* Descend through partitioned parents to find the leaf partition that
1489+
* would accept a row with the provided key values, starting with the root
1490+
* parent.
1491+
*/
1492+
for (;;)
1493+
{
1494+
PartitionKeypartkey=RelationGetPartitionKey(rel);
1495+
PartitionDescpartdesc;
1496+
Datumpartkey_vals[PARTITION_MAX_KEYS];
1497+
boolpartkey_isnull[PARTITION_MAX_KEYS];
1498+
AttrNumber*root_partattrs=partkey->partattrs;
1499+
intfound_att;
1500+
intpartidx;
1501+
Oidpartoid;
1502+
1503+
CHECK_FOR_INTERRUPTS();
1504+
1505+
/*
1506+
* Collect partition key values from the unique key.
1507+
*
1508+
* Because we only have the root table's copy of pk_attnums, must map
1509+
* any non-root table's partition key attribute numbers to the root
1510+
* table's.
1511+
*/
1512+
if (rel!=root_rel)
1513+
{
1514+
/*
1515+
* map->attnums will contain root table attribute numbers for each
1516+
* attribute of the current partitioned relation.
1517+
*/
1518+
AttrMap*map;
1519+
1520+
map=build_attrmap_by_name_if_req(RelationGetDescr(root_rel),
1521+
RelationGetDescr(rel));
1522+
if (map)
1523+
{
1524+
root_partattrs=palloc(partkey->partnatts*
1525+
sizeof(AttrNumber));
1526+
for (intatt=0;att<partkey->partnatts;att++)
1527+
{
1528+
AttrNumberpartattno=partkey->partattrs[att];
1529+
1530+
root_partattrs[att]=map->attnums[partattno-1];
1531+
}
1532+
1533+
free_attrmap(map);
1534+
}
1535+
}
1536+
1537+
/*
1538+
* Map the values/isnulls to match the partition description, as
1539+
* necessary.
1540+
*
1541+
* (Referenced key specification does not allow expressions, so there
1542+
* would not be expressions in the partition keys either.)
1543+
*/
1544+
Assert(partkey->partexprs==NIL);
1545+
found_att=0;
1546+
for (intkeyatt=0;keyatt<key_natts;keyatt++)
1547+
{
1548+
for (intatt=0;att<partkey->partnatts;att++)
1549+
{
1550+
if (root_partattrs[att]==key_attnums[keyatt])
1551+
{
1552+
partkey_vals[found_att]=key_vals[keyatt];
1553+
partkey_isnull[found_att]= (key_nulls[keyatt]=='n');
1554+
found_att++;
1555+
break;
1556+
}
1557+
}
1558+
}
1559+
/* We had better have found values for all partition keys */
1560+
Assert(found_att==partkey->partnatts);
1561+
1562+
if (root_partattrs!=partkey->partattrs)
1563+
pfree(root_partattrs);
1564+
1565+
/* Get the PartitionDesc using the partition directory machinery. */
1566+
partdesc=PartitionDirectoryLookup(partdir,rel);
1567+
if (partdesc->nparts==0)
1568+
break;
1569+
1570+
/* Find the partition for the key. */
1571+
partidx=get_partition_for_tuple(partkey,partdesc,
1572+
partkey_vals,partkey_isnull);
1573+
Assert(partidx<0||partidx<partdesc->nparts);
1574+
1575+
/* close the previous parent if any, but keep lock */
1576+
if (rel!=root_rel)
1577+
table_close(rel,NoLock);
1578+
1579+
/* No partition found. */
1580+
if (partidx<0)
1581+
break;
1582+
1583+
partoid=partdesc->oids[partidx];
1584+
rel=table_open(partoid,lockmode);
1585+
constr_idxoid=index_get_partition(rel,constr_idxoid);
1586+
1587+
/*
1588+
* We're done if the partition is a leaf, else find its partition in
1589+
* the next iteration.
1590+
*/
1591+
if (partdesc->is_leaf[partidx])
1592+
{
1593+
*leaf_idxoid=constr_idxoid;
1594+
found_leafpart=rel;
1595+
break;
1596+
}
1597+
}
1598+
1599+
DestroyPartitionDirectory(partdir);
1600+
returnfound_leafpart;
1601+
}
1602+
14411603
/*
14421604
* ExecBuildSlotPartitionKeyDescription
14431605
*

‎src/backend/executor/nodeLockRows.c

Lines changed: 90 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,7 @@ ExecLockRows(PlanState *pstate)
7979
Datumdatum;
8080
boolisNull;
8181
ItemPointerDatatid;
82-
TM_FailureDatatmfd;
8382
LockTupleModelockmode;
84-
intlockflags=0;
85-
TM_Resulttest;
8683
TupleTableSlot*markSlot;
8784

8885
/* clear any leftover test tuple for this rel */
@@ -179,74 +176,11 @@ ExecLockRows(PlanState *pstate)
179176
break;
180177
}
181178

182-
lockflags=TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS;
183-
if (!IsolationUsesXactSnapshot())
184-
lockflags |=TUPLE_LOCK_FLAG_FIND_LAST_VERSION;
185-
186-
test=table_tuple_lock(erm->relation,&tid,estate->es_snapshot,
187-
markSlot,estate->es_output_cid,
188-
lockmode,erm->waitPolicy,
189-
lockflags,
190-
&tmfd);
191-
192-
switch (test)
193-
{
194-
caseTM_WouldBlock:
195-
/* couldn't lock tuple in SKIP LOCKED mode */
196-
gotolnext;
197-
198-
caseTM_SelfModified:
199-
200-
/*
201-
* The target tuple was already updated or deleted by the
202-
* current command, or by a later command in the current
203-
* transaction. We *must* ignore the tuple in the former
204-
* case, so as to avoid the "Halloween problem" of repeated
205-
* update attempts. In the latter case it might be sensible
206-
* to fetch the updated tuple instead, but doing so would
207-
* require changing heap_update and heap_delete to not
208-
* complain about updating "invisible" tuples, which seems
209-
* pretty scary (table_tuple_lock will not complain, but few
210-
* callers expect TM_Invisible, and we're not one of them). So
211-
* for now, treat the tuple as deleted and do not process.
212-
*/
213-
gotolnext;
214-
215-
caseTM_Ok:
216-
217-
/*
218-
* Got the lock successfully, the locked tuple saved in
219-
* markSlot for, if needed, EvalPlanQual testing below.
220-
*/
221-
if (tmfd.traversed)
222-
epq_needed= true;
223-
break;
224-
225-
caseTM_Updated:
226-
if (IsolationUsesXactSnapshot())
227-
ereport(ERROR,
228-
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
229-
errmsg("could not serialize access due to concurrent update")));
230-
elog(ERROR,"unexpected table_tuple_lock status: %u",
231-
test);
232-
break;
233-
234-
caseTM_Deleted:
235-
if (IsolationUsesXactSnapshot())
236-
ereport(ERROR,
237-
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
238-
errmsg("could not serialize access due to concurrent update")));
239-
/* tuple was deleted so don't return it */
240-
gotolnext;
241-
242-
caseTM_Invisible:
243-
elog(ERROR,"attempted to lock invisible tuple");
244-
break;
245-
246-
default:
247-
elog(ERROR,"unrecognized table_tuple_lock status: %u",
248-
test);
249-
}
179+
/* skip tuple if it couldn't be locked */
180+
if (!ExecLockTableTuple(erm->relation,&tid,markSlot,
181+
estate->es_snapshot,estate->es_output_cid,
182+
lockmode,erm->waitPolicy,&epq_needed))
183+
gotolnext;
250184

251185
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
252186
erm->curCtid=tid;
@@ -281,6 +215,91 @@ ExecLockRows(PlanState *pstate)
281215
returnslot;
282216
}
283217

218+
/*
219+
* ExecLockTableTuple
220+
* Locks tuple with the specified TID in lockmode following given wait
221+
* policy
222+
*
223+
* Returns true if the tuple was successfully locked. Locked tuple is loaded
224+
* into provided slot.
225+
*/
226+
bool
227+
ExecLockTableTuple(Relationrelation,ItemPointertid,TupleTableSlot*slot,
228+
Snapshotsnapshot,CommandIdcid,
229+
LockTupleModelockmode,LockWaitPolicywaitPolicy,
230+
bool*epq_needed)
231+
{
232+
TM_FailureDatatmfd;
233+
intlockflags=TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS;
234+
TM_Resulttest;
235+
236+
if (!IsolationUsesXactSnapshot())
237+
lockflags |=TUPLE_LOCK_FLAG_FIND_LAST_VERSION;
238+
239+
test=table_tuple_lock(relation,tid,snapshot,slot,cid,lockmode,
240+
waitPolicy,lockflags,&tmfd);
241+
242+
switch (test)
243+
{
244+
caseTM_WouldBlock:
245+
/* couldn't lock tuple in SKIP LOCKED mode */
246+
return false;
247+
248+
caseTM_SelfModified:
249+
250+
/*
251+
* The target tuple was already updated or deleted by the current
252+
* command, or by a later command in the current transaction. We
253+
* *must* ignore the tuple in the former case, so as to avoid the
254+
* "Halloween problem" of repeated update attempts. In the latter
255+
* case it might be sensible to fetch the updated tuple instead,
256+
* but doing so would require changing heap_update and heap_delete
257+
* to not complain about updating "invisible" tuples, which seems
258+
* pretty scary (table_tuple_lock will not complain, but few
259+
* callers expect TM_Invisible, and we're not one of them). So for
260+
* now, treat the tuple as deleted and do not process.
261+
*/
262+
return false;
263+
264+
caseTM_Ok:
265+
266+
/*
267+
* Got the lock successfully, the locked tuple saved in slot for
268+
* EvalPlanQual, if asked by the caller.
269+
*/
270+
if (tmfd.traversed&&epq_needed)
271+
*epq_needed= true;
272+
break;
273+
274+
caseTM_Updated:
275+
if (IsolationUsesXactSnapshot())
276+
ereport(ERROR,
277+
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
278+
errmsg("could not serialize access due to concurrent update")));
279+
elog(ERROR,"unexpected table_tuple_lock status: %u",
280+
test);
281+
break;
282+
283+
caseTM_Deleted:
284+
if (IsolationUsesXactSnapshot())
285+
ereport(ERROR,
286+
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
287+
errmsg("could not serialize access due to concurrent update")));
288+
/* tuple was deleted so don't return it */
289+
return false;
290+
291+
caseTM_Invisible:
292+
elog(ERROR,"attempted to lock invisible tuple");
293+
return false;
294+
295+
default:
296+
elog(ERROR,"unrecognized table_tuple_lock status: %u",test);
297+
return false;
298+
}
299+
300+
return true;
301+
}
302+
284303
/* ----------------------------------------------------------------
285304
*ExecInitLockRows
286305
*

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp