diff options
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r-- | src/backend/executor/execMain.c | 193 |
1 files changed, 156 insertions, 37 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b9f21c560f9..20b3188dfdc 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -56,6 +56,7 @@ #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" +#include "utils/rls.h" #include "utils/snapmgr.h" #include "utils/tqual.h" @@ -82,12 +83,23 @@ static void ExecutePlan(EState *estate, PlanState *planstate, DestReceiver *dest); static bool ExecCheckRTEPerms(RangeTblEntry *rte); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); -static char *ExecBuildSlotValueDescription(TupleTableSlot *slot, +static char *ExecBuildSlotValueDescription(Oid reloid, + TupleTableSlot *slot, TupleDesc tupdesc, + Bitmapset *modifiedCols, int maxfieldlen); static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree); +/* + * Note that this macro also exists in commands/trigger.c. There does not + * appear to be any good header to put it into, given the structures that + * it uses, so we let them be duplicated. Be sure to update both if one needs + * to be changed, however. + */ +#define GetModifiedColumns(relinfo, estate) \ + (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols) + /* end of local decls */ @@ -1607,15 +1619,24 @@ ExecConstraints(ResultRelInfo *resultRelInfo, { if (tupdesc->attrs[attrChk - 1]->attnotnull && slot_attisnull(slot, attrChk)) + { + char *val_desc; + Bitmapset *modifiedCols; + + modifiedCols = GetModifiedColumns(resultRelInfo, estate); + val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), + slot, + tupdesc, + modifiedCols, + 64); + ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("null value in column \"%s\" violates not-null constraint", NameStr(tupdesc->attrs[attrChk - 1]->attname)), - errdetail("Failing row contains %s.", - ExecBuildSlotValueDescription(slot, - tupdesc, - 64)), + val_desc ? errdetail("Failing row contains %s.", val_desc) : 0, errtablecol(rel, attrChk))); + } } } @@ -1624,15 +1645,23 @@ ExecConstraints(ResultRelInfo *resultRelInfo, const char *failed; if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL) + { + char *val_desc; + Bitmapset *modifiedCols; + + modifiedCols = GetModifiedColumns(resultRelInfo, estate); + val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), + slot, + tupdesc, + modifiedCols, + 64); ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("new row for relation \"%s\" violates check constraint \"%s\"", RelationGetRelationName(rel), failed), - errdetail("Failing row contains %s.", - ExecBuildSlotValueDescription(slot, - tupdesc, - 64)), + val_desc ? errdetail("Failing row contains %s.", val_desc) : 0, errtableconstraint(rel, failed))); + } } } @@ -1643,6 +1672,8 @@ void ExecWithCheckOptions(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate) { + Relation rel = resultRelInfo->ri_RelationDesc; + TupleDesc tupdesc = RelationGetDescr(rel); ExprContext *econtext; ListCell *l1, *l2; @@ -1673,14 +1704,24 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo, * case (the opposite of what we do above for CHECK constraints). */ if (!ExecQual((List *) wcoExpr, econtext, false)) + { + char *val_desc; + Bitmapset *modifiedCols; + + modifiedCols = GetModifiedColumns(resultRelInfo, estate); + val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel), + slot, + tupdesc, + modifiedCols, + 64); + ereport(ERROR, (errcode(ERRCODE_WITH_CHECK_OPTION_VIOLATION), errmsg("new row violates WITH CHECK OPTION for \"%s\"", wco->viewname), - errdetail("Failing row contains %s.", - ExecBuildSlotValueDescription(slot, - RelationGetDescr(resultRelInfo->ri_RelationDesc), - 64)))); + val_desc ? errdetail("Failing row contains %s.", val_desc) : + 0)); + } } } @@ -1696,25 +1737,64 @@ ExecWithCheckOptions(ResultRelInfo *resultRelInfo, * dropped columns. We used to use the slot's tuple descriptor to decode the * data, but the slot's descriptor doesn't identify dropped columns, so we * now need to be passed the relation's descriptor. + * + * Note that, like BuildIndexValueDescription, if the user does not have + * permission to view any of the columns involved, a NULL is returned. Unlike + * BuildIndexValueDescription, if the user has access to view a subset of the + * column involved, that subset will be returned with a key identifying which + * columns they are. */ static char * -ExecBuildSlotValueDescription(TupleTableSlot *slot, +ExecBuildSlotValueDescription(Oid reloid, + TupleTableSlot *slot, TupleDesc tupdesc, + Bitmapset *modifiedCols, int maxfieldlen) { StringInfoData buf; + StringInfoData collist; bool write_comma = false; + bool write_comma_collist = false; int i; + AclResult aclresult; + bool table_perm = false; + bool any_perm = false; - /* Make sure the tuple is fully deconstructed */ - slot_getallattrs(slot); + /* + * Check if RLS is enabled and should be active for the relation; if so, + * then don't return anything. Otherwise, go through normal permission + * checks. + */ + if (check_enable_rls(reloid, GetUserId(), true) == RLS_ENABLED) + return NULL; initStringInfo(&buf); appendStringInfoChar(&buf, '('); + /* + * Check if the user has permissions to see the row. Table-level SELECT + * allows access to all columns. If the user does not have table-level + * SELECT then we check each column and include those the user has SELECT + * rights on. Additionally, we always include columns the user provided + * data for. + */ + aclresult = pg_class_aclcheck(reloid, GetUserId(), ACL_SELECT); + if (aclresult != ACLCHECK_OK) + { + /* Set up the buffer for the column list */ + initStringInfo(&collist); + appendStringInfoChar(&collist, '('); + } + else + table_perm = any_perm = true; + + /* Make sure the tuple is fully deconstructed */ + slot_getallattrs(slot); + for (i = 0; i < tupdesc->natts; i++) { + bool column_perm = false; char *val; int vallen; @@ -1722,37 +1802,76 @@ ExecBuildSlotValueDescription(TupleTableSlot *slot, if (tupdesc->attrs[i]->attisdropped) continue; - if (slot->tts_isnull[i]) - val = "null"; - else + if (!table_perm) { - Oid foutoid; - bool typisvarlena; + /* + * No table-level SELECT, so need to make sure they either have + * SELECT rights on the column or that they have provided the + * data for the column. If not, omit this column from the error + * message. + */ + aclresult = pg_attribute_aclcheck(reloid, tupdesc->attrs[i]->attnum, + GetUserId(), ACL_SELECT); + if (bms_is_member(tupdesc->attrs[i]->attnum - FirstLowInvalidHeapAttributeNumber, + modifiedCols) || aclresult == ACLCHECK_OK) + { + column_perm = any_perm = true; - getTypeOutputInfo(tupdesc->attrs[i]->atttypid, - &foutoid, &typisvarlena); - val = OidOutputFunctionCall(foutoid, slot->tts_values[i]); - } + if (write_comma_collist) + appendStringInfoString(&collist, ", "); + else + write_comma_collist = true; - if (write_comma) - appendStringInfoString(&buf, ", "); - else - write_comma = true; + appendStringInfoString(&collist, NameStr(tupdesc->attrs[i]->attname)); + } + } - /* truncate if needed */ - vallen = strlen(val); - if (vallen <= maxfieldlen) - appendStringInfoString(&buf, val); - else + if (table_perm || column_perm) { - vallen = pg_mbcliplen(val, vallen, maxfieldlen); - appendBinaryStringInfo(&buf, val, vallen); - appendStringInfoString(&buf, "..."); + if (slot->tts_isnull[i]) + val = "null"; + else + { + Oid foutoid; + bool typisvarlena; + + getTypeOutputInfo(tupdesc->attrs[i]->atttypid, + &foutoid, &typisvarlena); + val = OidOutputFunctionCall(foutoid, slot->tts_values[i]); + } + + if (write_comma) + appendStringInfoString(&buf, ", "); + else + write_comma = true; + + /* truncate if needed */ + vallen = strlen(val); + if (vallen <= maxfieldlen) + appendStringInfoString(&buf, val); + else + { + vallen = pg_mbcliplen(val, vallen, maxfieldlen); + appendBinaryStringInfo(&buf, val, vallen); + appendStringInfoString(&buf, "..."); + } } } + /* If we end up with zero columns being returned, then return NULL. */ + if (!any_perm) + return NULL; + appendStringInfoChar(&buf, ')'); + if (!table_perm) + { + appendStringInfoString(&collist, ") = "); + appendStringInfoString(&collist, buf.data); + + return collist.data; + } + return buf.data; } |