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

Commit15ce775

Browse files
committed
Prevent BEFORE triggers from violating partitioning constraints.
Since tuple-routing implicitly checks the partitioning constraintsat least for the levels of the partitioning hierarchy it traverses,there's normally no need to revalidate the partitioning constraintafter performing tuple routing. However, if there's a BEFORE triggeron the target partition, it could modify the tuple, causing thepartitioning constraint to be violated. Catch that case.Also, instead of checking the root table's partition constraint aftertuple-routing, check it beforehand. Otherwise, the rules for whenthe partitioning constraint gets checked get too complicated, becauseyou sometimes have to check part of the constraint but not all of it.This effectively reverts commit39162b2in favor of a different approach altogether.Report by me. Initial debugging by Jeevan Ladhe. Patch by AmitLangote, reviewed by me.Discussion:http://postgr.es/m/CA+Tgmoa9DTgeVOqopieV8d1QRpddmP65aCdxyjdYDoEO5pS5KA@mail.gmail.com
1 parente6c33d5 commit15ce775

File tree

5 files changed

+128
-75
lines changed

5 files changed

+128
-75
lines changed

‎src/backend/commands/copy.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,9 +2640,24 @@ CopyFrom(CopyState cstate)
26402640
}
26412641
else
26422642
{
2643+
/*
2644+
* We always check the partition constraint, including when
2645+
* the tuple got here via tuple-routing. However we don't
2646+
* need to in the latter case if no BR trigger is defined on
2647+
* the partition. Note that a BR trigger might modify the
2648+
* tuple such that the partition constraint is no longer
2649+
* satisfied, so we need to check in that case.
2650+
*/
2651+
boolcheck_partition_constr=
2652+
(resultRelInfo->ri_PartitionCheck!=NIL);
2653+
2654+
if (saved_resultRelInfo!=NULL&&
2655+
!(resultRelInfo->ri_TrigDesc&&
2656+
resultRelInfo->ri_TrigDesc->trig_insert_before_row))
2657+
check_partition_constr= false;
2658+
26432659
/* Check the constraints of the tuple */
2644-
if (cstate->rel->rd_att->constr||
2645-
resultRelInfo->ri_PartitionCheck)
2660+
if (cstate->rel->rd_att->constr||check_partition_constr)
26462661
ExecConstraints(resultRelInfo,slot,estate);
26472662

26482663
if (useHeapMultiInsert)

‎src/backend/executor/execMain.c

Lines changed: 68 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
103103
intmaxfieldlen);
104104
staticvoidEvalPlanQualStart(EPQState*epqstate,EState*parentestate,
105105
Plan*planTree);
106+
staticvoidExecPartitionCheck(ResultRelInfo*resultRelInfo,
107+
TupleTableSlot*slot,EState*estate);
106108

107109
/*
108110
* Note that GetUpdatedColumns() also exists in commands/trigger.c. There does
@@ -1339,34 +1341,19 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
13391341
resultRelInfo->ri_projectReturning=NULL;
13401342

13411343
/*
1342-
* If partition_root has been specified, that means we are building the
1343-
* ResultRelInfo for one of its leaf partitions. In that case, we need
1344-
* *not* initialize the leaf partition's constraint, but rather the
1345-
* partition_root's (if any). We must do that explicitly like this,
1346-
* because implicit partition constraints are not inherited like user-
1347-
* defined constraints and would fail to be enforced by ExecConstraints()
1348-
* after a tuple is routed to a leaf partition.
1344+
* Partition constraint, which also includes the partition constraint of
1345+
* all the ancestors that are partitions. Note that it will be checked
1346+
* even in the case of tuple-routing where this table is the target leaf
1347+
* partition, if there any BR triggers defined on the table. Although
1348+
* tuple-routing implicitly preserves the partition constraint of the
1349+
* target partition for a given row, the BR triggers may change the row
1350+
* such that the constraint is no longer satisfied, which we must fail
1351+
* for by checking it explicitly.
1352+
*
1353+
* If this is a partitioned table, the partition constraint (if any) of a
1354+
* given row will be checked just before performing tuple-routing.
13491355
*/
1350-
if (partition_root)
1351-
{
1352-
/*
1353-
* Root table itself may or may not be a partition; partition_check
1354-
* would be NIL in the latter case.
1355-
*/
1356-
partition_check=RelationGetPartitionQual(partition_root);
1357-
1358-
/*
1359-
* This is not our own partition constraint, but rather an ancestor's.
1360-
* So any Vars in it bear the ancestor's attribute numbers. We must
1361-
* switch them to our own. (dummy varno = 1)
1362-
*/
1363-
if (partition_check!=NIL)
1364-
partition_check=map_partition_varattnos(partition_check,1,
1365-
resultRelationDesc,
1366-
partition_root);
1367-
}
1368-
else
1369-
partition_check=RelationGetPartitionQual(resultRelationDesc);
1356+
partition_check=RelationGetPartitionQual(resultRelationDesc);
13701357

13711358
resultRelInfo->ri_PartitionCheck=partition_check;
13721359
resultRelInfo->ri_PartitionRoot=partition_root;
@@ -1835,13 +1822,16 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
18351822

18361823
/*
18371824
* ExecPartitionCheck --- check that tuple meets the partition constraint.
1838-
*
1839-
* Note: This is called *iff* resultRelInfo is the main target table.
18401825
*/
1841-
staticbool
1826+
staticvoid
18421827
ExecPartitionCheck(ResultRelInfo*resultRelInfo,TupleTableSlot*slot,
18431828
EState*estate)
18441829
{
1830+
Relationrel=resultRelInfo->ri_RelationDesc;
1831+
TupleDesctupdesc=RelationGetDescr(rel);
1832+
Bitmapset*modifiedCols;
1833+
Bitmapset*insertedCols;
1834+
Bitmapset*updatedCols;
18451835
ExprContext*econtext;
18461836

18471837
/*
@@ -1869,7 +1859,44 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
18691859
* As in case of the catalogued constraints, we treat a NULL result as
18701860
* success here, not a failure.
18711861
*/
1872-
returnExecCheck(resultRelInfo->ri_PartitionCheckExpr,econtext);
1862+
if (!ExecCheck(resultRelInfo->ri_PartitionCheckExpr,econtext))
1863+
{
1864+
char*val_desc;
1865+
Relationorig_rel=rel;
1866+
1867+
/* See the comment above. */
1868+
if (resultRelInfo->ri_PartitionRoot)
1869+
{
1870+
HeapTupletuple=ExecFetchSlotTuple(slot);
1871+
TupleDescold_tupdesc=RelationGetDescr(rel);
1872+
TupleConversionMap*map;
1873+
1874+
rel=resultRelInfo->ri_PartitionRoot;
1875+
tupdesc=RelationGetDescr(rel);
1876+
/* a reverse map */
1877+
map=convert_tuples_by_name(old_tupdesc,tupdesc,
1878+
gettext_noop("could not convert row type"));
1879+
if (map!=NULL)
1880+
{
1881+
tuple=do_convert_tuple(tuple,map);
1882+
ExecStoreTuple(tuple,slot,InvalidBuffer, false);
1883+
}
1884+
}
1885+
1886+
insertedCols=GetInsertedColumns(resultRelInfo,estate);
1887+
updatedCols=GetUpdatedColumns(resultRelInfo,estate);
1888+
modifiedCols=bms_union(insertedCols,updatedCols);
1889+
val_desc=ExecBuildSlotValueDescription(RelationGetRelid(rel),
1890+
slot,
1891+
tupdesc,
1892+
modifiedCols,
1893+
64);
1894+
ereport(ERROR,
1895+
(errcode(ERRCODE_CHECK_VIOLATION),
1896+
errmsg("new row for relation \"%s\" violates partition constraint",
1897+
RelationGetRelationName(orig_rel)),
1898+
val_desc ?errdetail("Failing row contains %s.",val_desc) :0));
1899+
}
18731900
}
18741901

18751902
/*
@@ -1997,47 +2024,11 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
19972024
}
19982025
}
19992026

2000-
if (resultRelInfo->ri_PartitionCheck&&
2001-
!ExecPartitionCheck(resultRelInfo,slot,estate))
2002-
{
2003-
char*val_desc;
2004-
Relationorig_rel=rel;
2005-
2006-
/* See the comment above. */
2007-
if (resultRelInfo->ri_PartitionRoot)
2008-
{
2009-
HeapTupletuple=ExecFetchSlotTuple(slot);
2010-
TupleDescold_tupdesc=RelationGetDescr(rel);
2011-
TupleConversionMap*map;
2012-
2013-
rel=resultRelInfo->ri_PartitionRoot;
2014-
tupdesc=RelationGetDescr(rel);
2015-
/* a reverse map */
2016-
map=convert_tuples_by_name(old_tupdesc,tupdesc,
2017-
gettext_noop("could not convert row type"));
2018-
if (map!=NULL)
2019-
{
2020-
tuple=do_convert_tuple(tuple,map);
2021-
ExecStoreTuple(tuple,slot,InvalidBuffer, false);
2022-
}
2023-
}
2024-
2025-
insertedCols=GetInsertedColumns(resultRelInfo,estate);
2026-
updatedCols=GetUpdatedColumns(resultRelInfo,estate);
2027-
modifiedCols=bms_union(insertedCols,updatedCols);
2028-
val_desc=ExecBuildSlotValueDescription(RelationGetRelid(rel),
2029-
slot,
2030-
tupdesc,
2031-
modifiedCols,
2032-
64);
2033-
ereport(ERROR,
2034-
(errcode(ERRCODE_CHECK_VIOLATION),
2035-
errmsg("new row for relation \"%s\" violates partition constraint",
2036-
RelationGetRelationName(orig_rel)),
2037-
val_desc ?errdetail("Failing row contains %s.",val_desc) :0));
2038-
}
2027+
if (resultRelInfo->ri_PartitionCheck)
2028+
ExecPartitionCheck(resultRelInfo,slot,estate);
20392029
}
20402030

2031+
20412032
/*
20422033
* ExecWithCheckOptions -- check that tuple satisfies any WITH CHECK OPTIONs
20432034
* of the specified kind.
@@ -3317,6 +3308,13 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
33173308
PartitionDispatchData*failed_at;
33183309
TupleTableSlot*failed_slot;
33193310

3311+
/*
3312+
* First check the root table's partition constraint, if any. No point in
3313+
* routing the tuple it if it doesn't belong in the root table itself.
3314+
*/
3315+
if (resultRelInfo->ri_PartitionCheck)
3316+
ExecPartitionCheck(resultRelInfo,slot,estate);
3317+
33203318
result=get_partition_for_tuple(pd,slot,estate,
33213319
&failed_at,&failed_slot);
33223320
if (result<0)

‎src/backend/executor/nodeModifyTable.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,16 @@ ExecInsert(ModifyTableState *mtstate,
414414
}
415415
else
416416
{
417+
/*
418+
* We always check the partition constraint, including when the tuple
419+
* got here via tuple-routing. However we don't need to in the latter
420+
* case if no BR trigger is defined on the partition. Note that a BR
421+
* trigger might modify the tuple such that the partition constraint
422+
* is no longer satisfied, so we need to check in that case.
423+
*/
424+
boolcheck_partition_constr=
425+
(resultRelInfo->ri_PartitionCheck!=NIL);
426+
417427
/*
418428
* Constraints might reference the tableoid column, so initialize
419429
* t_tableOid before evaluating them.
@@ -431,9 +441,16 @@ ExecInsert(ModifyTableState *mtstate,
431441
resultRelInfo,slot,estate);
432442

433443
/*
434-
* Check the constraints of the tuple
444+
* No need though if the tuple has been routed, and a BR trigger
445+
* doesn't exist.
435446
*/
436-
if (resultRelationDesc->rd_att->constr||resultRelInfo->ri_PartitionCheck)
447+
if (saved_resultRelInfo!=NULL&&
448+
!(resultRelInfo->ri_TrigDesc&&
449+
resultRelInfo->ri_TrigDesc->trig_insert_before_row))
450+
check_partition_constr= false;
451+
452+
/* Check the constraints of the tuple */
453+
if (resultRelationDesc->rd_att->constr||check_partition_constr)
437454
ExecConstraints(resultRelInfo,slot,estate);
438455

439456
if (onconflict!=ONCONFLICT_NONE&&resultRelInfo->ri_NumIndices>0)

‎src/test/regress/expected/insert.out

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ drop function mlparted11_trig_fn();
379379
-- checking its partition constraint before inserting into the leaf partition
380380
-- selected by tuple-routing
381381
insert into mlparted1 (a, b) values (2, 3);
382-
ERROR: new row for relation "mlparted11" violates partition constraint
382+
ERROR: new row for relation "mlparted1" violates partition constraint
383383
DETAIL: Failing row contains (3, 2).
384384
-- check routing error through a list partitioned table when the key is null
385385
create table lparted_nonullpart (a int, b char) partition by list (b);
@@ -495,3 +495,16 @@ select tableoid::regclass::text, * from mcrparted order by 1;
495495

496496
-- cleanup
497497
drop table mcrparted;
498+
-- check that a BR constraint can't make partition contain violating rows
499+
create table brtrigpartcon (a int, b text) partition by list (a);
500+
create table brtrigpartcon1 partition of brtrigpartcon for values in (1);
501+
create or replace function brtrigpartcon1trigf() returns trigger as $$begin new.a := 2; return new; end$$ language plpgsql;
502+
create trigger brtrigpartcon1trig before insert on brtrigpartcon1 for each row execute procedure brtrigpartcon1trigf();
503+
insert into brtrigpartcon values (1, 'hi there');
504+
ERROR: new row for relation "brtrigpartcon1" violates partition constraint
505+
DETAIL: Failing row contains (2, hi there).
506+
insert into brtrigpartcon1 values (1, 'hi there');
507+
ERROR: new row for relation "brtrigpartcon1" violates partition constraint
508+
DETAIL: Failing row contains (2, hi there).
509+
drop table brtrigpartcon;
510+
drop function brtrigpartcon1trigf();

‎src/test/regress/sql/insert.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,13 @@ select tableoid::regclass::text, * from mcrparted order by 1;
332332

333333
-- cleanup
334334
droptable mcrparted;
335+
336+
-- check that a BR constraint can't make partition contain violating rows
337+
createtablebrtrigpartcon (aint, btext) partition by list (a);
338+
createtablebrtrigpartcon1 partition of brtrigpartcon forvaluesin (1);
339+
create or replacefunctionbrtrigpartcon1trigf() returns triggeras $$beginnew.a :=2; return new; end$$ language plpgsql;
340+
createtriggerbrtrigpartcon1trig before inserton brtrigpartcon1 for each row execute procedure brtrigpartcon1trigf();
341+
insert into brtrigpartconvalues (1,'hi there');
342+
insert into brtrigpartcon1values (1,'hi there');
343+
droptable brtrigpartcon;
344+
dropfunction brtrigpartcon1trigf();

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp