Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)