Skip to content

Commit 5a73e17

Browse files
committed
Improve error reporting for tuple-routing failures.
Currently, the whole row is shown without column names. Instead, adopt a style similar to _bt_check_unique() in ExecFindPartition() and show the failing key: (key1, ...) = (val1, ...). Amit Langote, per a complaint from Simon Riggs. Reviewed by me; I also adjusted the grammar in one of the comments. Discussion: http://postgr.es/m/9f9dc7ae-14f0-4a25-5485-964d9bfc19bd@lab.ntt.co.jp
1 parent be6ed64 commit 5a73e17

File tree

8 files changed

+231
-50
lines changed

8 files changed

+231
-50
lines changed

src/backend/access/index/genam.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ IndexScanEnd(IndexScanDesc scan)
168168
* The passed-in values/nulls arrays are the "raw" input to the index AM,
169169
* e.g. results of FormIndexDatum --- this is not necessarily what is stored
170170
* in the index, but it's what the user perceives to be stored.
171+
*
172+
* Note: if you change anything here, check whether
173+
* ExecBuildSlotPartitionKeyDescription() in execMain.c needs a similar
174+
* change.
171175
*/
172176
char *
173177
BuildIndexValueDescription(Relation indexRelation,

src/backend/catalog/partition.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,6 @@ static int partition_bound_bsearch(PartitionKey key,
140140
PartitionBoundInfo boundinfo,
141141
void *probe, bool probe_is_bound, bool *is_equal);
142142

143-
/* Support get_partition_for_tuple() */
144-
static void FormPartitionKeyDatum(PartitionDispatch pd,
145-
TupleTableSlot *slot,
146-
EState *estate,
147-
Datum *values,
148-
bool *isnull);
149-
150143
/*
151144
* RelationBuildPartitionDesc
152145
* Form rel's partition descriptor
@@ -1608,7 +1601,7 @@ generate_partition_qual(Relation rel)
16081601
* the heap tuple passed in.
16091602
* ----------------
16101603
*/
1611-
static void
1604+
void
16121605
FormPartitionKeyDatum(PartitionDispatch pd,
16131606
TupleTableSlot *slot,
16141607
EState *estate,
@@ -1672,7 +1665,8 @@ int
16721665
get_partition_for_tuple(PartitionDispatch *pd,
16731666
TupleTableSlot *slot,
16741667
EState *estate,
1675-
Oid *failed_at)
1668+
PartitionDispatchData **failed_at,
1669+
TupleTableSlot **failed_slot)
16761670
{
16771671
PartitionDispatch parent;
16781672
Datum values[PARTITION_MAX_KEYS];
@@ -1693,13 +1687,6 @@ get_partition_for_tuple(PartitionDispatch *pd,
16931687
TupleTableSlot *myslot = parent->tupslot;
16941688
TupleConversionMap *map = parent->tupmap;
16951689

1696-
/* Quick exit */
1697-
if (partdesc->nparts == 0)
1698-
{
1699-
*failed_at = RelationGetRelid(parent->reldesc);
1700-
return -1;
1701-
}
1702-
17031690
if (myslot != NULL && map != NULL)
17041691
{
17051692
HeapTuple tuple = ExecFetchSlotTuple(slot);
@@ -1710,6 +1697,14 @@ get_partition_for_tuple(PartitionDispatch *pd,
17101697
slot = myslot;
17111698
}
17121699

1700+
/* Quick exit */
1701+
if (partdesc->nparts == 0)
1702+
{
1703+
*failed_at = parent;
1704+
*failed_slot = slot;
1705+
return -1;
1706+
}
1707+
17131708
/*
17141709
* Extract partition key from tuple. Expression evaluation machinery
17151710
* that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
@@ -1774,7 +1769,8 @@ get_partition_for_tuple(PartitionDispatch *pd,
17741769
if (cur_index < 0)
17751770
{
17761771
result = -1;
1777-
*failed_at = RelationGetRelid(parent->reldesc);
1772+
*failed_at = parent;
1773+
*failed_slot = slot;
17781774
break;
17791775
}
17801776
else if (parent->indexes[cur_index] >= 0)

src/backend/executor/execMain.c

Lines changed: 113 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "utils/lsyscache.h"
6161
#include "utils/memutils.h"
6262
#include "utils/rls.h"
63+
#include "utils/ruleutils.h"
6364
#include "utils/snapmgr.h"
6465
#include "utils/tqual.h"
6566

@@ -95,6 +96,10 @@ static char *ExecBuildSlotValueDescription(Oid reloid,
9596
TupleDesc tupdesc,
9697
Bitmapset *modifiedCols,
9798
int maxfieldlen);
99+
static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
100+
Datum *values,
101+
bool *isnull,
102+
int maxfieldlen);
98103
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
99104
Plan *planTree);
100105

@@ -3189,33 +3194,122 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
31893194
TupleTableSlot *slot, EState *estate)
31903195
{
31913196
int result;
3192-
Oid failed_at;
3197+
PartitionDispatchData *failed_at;
3198+
TupleTableSlot *failed_slot;
31933199

3194-
result = get_partition_for_tuple(pd, slot, estate, &failed_at);
3200+
result = get_partition_for_tuple(pd, slot, estate,
3201+
&failed_at, &failed_slot);
31953202
if (result < 0)
31963203
{
3197-
Relation rel = resultRelInfo->ri_RelationDesc;
3204+
Relation failed_rel;
3205+
Datum key_values[PARTITION_MAX_KEYS];
3206+
bool key_isnull[PARTITION_MAX_KEYS];
31983207
char *val_desc;
3199-
Bitmapset *insertedCols,
3200-
*updatedCols,
3201-
*modifiedCols;
3202-
TupleDesc tupDesc = RelationGetDescr(rel);
3203-
3204-
insertedCols = GetInsertedColumns(resultRelInfo, estate);
3205-
updatedCols = GetUpdatedColumns(resultRelInfo, estate);
3206-
modifiedCols = bms_union(insertedCols, updatedCols);
3207-
val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
3208-
slot,
3209-
tupDesc,
3210-
modifiedCols,
3211-
64);
3212-
Assert(OidIsValid(failed_at));
3208+
ExprContext *ecxt = GetPerTupleExprContext(estate);
3209+
3210+
failed_rel = failed_at->reldesc;
3211+
ecxt->ecxt_scantuple = failed_slot;
3212+
FormPartitionKeyDatum(failed_at, failed_slot, estate,
3213+
key_values, key_isnull);
3214+
val_desc = ExecBuildSlotPartitionKeyDescription(failed_rel,
3215+
key_values,
3216+
key_isnull,
3217+
64);
3218+
Assert(OidIsValid(RelationGetRelid(failed_rel)));
32133219
ereport(ERROR,
32143220
(errcode(ERRCODE_CHECK_VIOLATION),
32153221
errmsg("no partition of relation \"%s\" found for row",
3216-
get_rel_name(failed_at)),
3217-
val_desc ? errdetail("Failing row contains %s.", val_desc) : 0));
3222+
RelationGetRelationName(failed_rel)),
3223+
val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0));
32183224
}
32193225

32203226
return result;
32213227
}
3228+
3229+
/*
3230+
* BuildSlotPartitionKeyDescription
3231+
*
3232+
* This works very much like BuildIndexValueDescription() and is currently
3233+
* used for building error messages when ExecFindPartition() fails to find
3234+
* partition for a row.
3235+
*/
3236+
static char *
3237+
ExecBuildSlotPartitionKeyDescription(Relation rel,
3238+
Datum *values,
3239+
bool *isnull,
3240+
int maxfieldlen)
3241+
{
3242+
StringInfoData buf;
3243+
PartitionKey key = RelationGetPartitionKey(rel);
3244+
int partnatts = get_partition_natts(key);
3245+
int i;
3246+
Oid relid = RelationGetRelid(rel);
3247+
AclResult aclresult;
3248+
3249+
if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
3250+
return NULL;
3251+
3252+
/* If the user has table-level access, just go build the description. */
3253+
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
3254+
if (aclresult != ACLCHECK_OK)
3255+
{
3256+
/*
3257+
* Step through the columns of the partition key and make sure the
3258+
* user has SELECT rights on all of them.
3259+
*/
3260+
for (i = 0; i < partnatts; i++)
3261+
{
3262+
AttrNumber attnum = get_partition_col_attnum(key, i);
3263+
3264+
/*
3265+
* If this partition key column is an expression, we return no
3266+
* detail rather than try to figure out what column(s) the
3267+
* expression includes and if the user has SELECT rights on them.
3268+
*/
3269+
if (attnum == InvalidAttrNumber ||
3270+
pg_attribute_aclcheck(relid, attnum, GetUserId(),
3271+
ACL_SELECT) != ACLCHECK_OK)
3272+
return NULL;
3273+
}
3274+
}
3275+
3276+
initStringInfo(&buf);
3277+
appendStringInfo(&buf, "(%s) = (",
3278+
pg_get_partkeydef_columns(relid, true));
3279+
3280+
for (i = 0; i < partnatts; i++)
3281+
{
3282+
char *val;
3283+
int vallen;
3284+
3285+
if (isnull[i])
3286+
val = "null";
3287+
else
3288+
{
3289+
Oid foutoid;
3290+
bool typisvarlena;
3291+
3292+
getTypeOutputInfo(get_partition_col_typid(key, i),
3293+
&foutoid, &typisvarlena);
3294+
val = OidOutputFunctionCall(foutoid, values[i]);
3295+
}
3296+
3297+
if (i > 0)
3298+
appendStringInfoString(&buf, ", ");
3299+
3300+
/* truncate if needed */
3301+
vallen = strlen(val);
3302+
if (vallen <= maxfieldlen)
3303+
appendStringInfoString(&buf, val);
3304+
else
3305+
{
3306+
vallen = pg_mbcliplen(val, vallen, maxfieldlen);
3307+
appendBinaryStringInfo(&buf, val, vallen);
3308+
appendStringInfoString(&buf, "...");
3309+
}
3310+
}
3311+
3312+
appendStringInfoChar(&buf, ')');
3313+
3314+
return buf.data;
3315+
}

src/backend/utils/adt/ruleutils.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,8 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
317317
const Oid *excludeOps,
318318
bool attrsOnly, bool showTblSpc,
319319
int prettyFlags, bool missing_ok);
320-
static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags);
320+
static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
321+
bool attrsOnly);
321322
static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
322323
int prettyFlags, bool missing_ok);
323324
static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
@@ -1431,14 +1432,26 @@ pg_get_partkeydef(PG_FUNCTION_ARGS)
14311432
Oid relid = PG_GETARG_OID(0);
14321433

14331434
PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid,
1434-
PRETTYFLAG_INDENT)));
1435+
PRETTYFLAG_INDENT,
1436+
false)));
1437+
}
1438+
1439+
/* Internal version that just reports the column definitions */
1440+
char *
1441+
pg_get_partkeydef_columns(Oid relid, bool pretty)
1442+
{
1443+
int prettyFlags;
1444+
1445+
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
1446+
return pg_get_partkeydef_worker(relid, prettyFlags, true);
14351447
}
14361448

14371449
/*
14381450
* Internal workhorse to decompile a partition key definition.
14391451
*/
14401452
static char *
1441-
pg_get_partkeydef_worker(Oid relid, int prettyFlags)
1453+
pg_get_partkeydef_worker(Oid relid, int prettyFlags,
1454+
bool attrsOnly)
14421455
{
14431456
Form_pg_partitioned_table form;
14441457
HeapTuple tuple;
@@ -1508,17 +1521,20 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags)
15081521
switch (form->partstrat)
15091522
{
15101523
case PARTITION_STRATEGY_LIST:
1511-
appendStringInfo(&buf, "LIST");
1524+
if (!attrsOnly)
1525+
appendStringInfo(&buf, "LIST");
15121526
break;
15131527
case PARTITION_STRATEGY_RANGE:
1514-
appendStringInfo(&buf, "RANGE");
1528+
if (!attrsOnly)
1529+
appendStringInfo(&buf, "RANGE");
15151530
break;
15161531
default:
15171532
elog(ERROR, "unexpected partition strategy: %d",
15181533
(int) form->partstrat);
15191534
}
15201535

1521-
appendStringInfo(&buf, " (");
1536+
if (!attrsOnly)
1537+
appendStringInfo(&buf, " (");
15221538
sep = "";
15231539
for (keyno = 0; keyno < form->partnatts; keyno++)
15241540
{
@@ -1561,14 +1577,17 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags)
15611577

15621578
/* Add collation, if not default for column */
15631579
partcoll = partcollation->values[keyno];
1564-
if (OidIsValid(partcoll) && partcoll != keycolcollation)
1580+
if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
15651581
appendStringInfo(&buf, " COLLATE %s",
15661582
generate_collation_name((partcoll)));
15671583

15681584
/* Add the operator class name, if not default */
1569-
get_opclass_name(partclass->values[keyno], keycoltype, &buf);
1585+
if (!attrsOnly)
1586+
get_opclass_name(partclass->values[keyno], keycoltype, &buf);
15701587
}
1571-
appendStringInfoChar(&buf, ')');
1588+
1589+
if (!attrsOnly)
1590+
appendStringInfoChar(&buf, ')');
15721591

15731592
/* Clean up */
15741593
ReleaseSysCache(tuple);

src/include/catalog/partition.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,14 @@ extern List *RelationGetPartitionQual(Relation rel);
8585
extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
8686
int lockmode, int *num_parted,
8787
List **leaf_part_oids);
88+
extern void FormPartitionKeyDatum(PartitionDispatch pd,
89+
TupleTableSlot *slot,
90+
EState *estate,
91+
Datum *values,
92+
bool *isnull);
8893
extern int get_partition_for_tuple(PartitionDispatch *pd,
8994
TupleTableSlot *slot,
9095
EState *estate,
91-
Oid *failed_at);
96+
PartitionDispatchData **failed_at,
97+
TupleTableSlot **failed_slot);
9298
#endif /* PARTITION_H */

src/include/utils/ruleutils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
extern char *pg_get_indexdef_string(Oid indexrelid);
2222
extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
2323

24+
extern char *pg_get_partkeydef_columns(Oid relid, bool pretty);
25+
2426
extern char *pg_get_constraintdef_command(Oid constraintId);
2527
extern char *deparse_expression(Node *expr, List *dpcontext,
2628
bool forceprefix, bool showimplicit);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy