/* * BuildIndexValueDescription * * Construct a string describing the contents of an index entry, in the * form "(key_name, ...)=(key_value, ...)". This is currently used * for building unique-constraint and exclusion-constraint error messages. * * The passed-in values/nulls arrays are the "raw" input to the index AM, * e.g. results of FormIndexDatum --- this is not necessarily what is stored * in the index, but it's what the user perceives to be stored. */ char * BuildIndexValueDescription(Relation indexRelation, Datum *values, bool *isnull) { StringInfoData buf; int natts = indexRelation->rd_rel->relnatts; int i; initStringInfo(&buf); appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(RelationGetRelid(indexRelation), true)); for (i = 0; i < natts; i++) { char *val; if (isnull[i]) val = "null"; else { Oid foutoid; bool typisvarlena; /* * The provided data is not necessarily of the type stored in the * index; rather it is of the index opclass's input type. So look * at rd_opcintype not the index tupdesc. * * Note: this is a bit shaky for opclasses that have pseudotype * input types such as ANYARRAY or RECORD. Currently, the * typoutput functions associated with the pseudotypes will work * okay, but we might have to try harder in future. */ getTypeOutputInfo(indexRelation->rd_opcintype[i], &foutoid, &typisvarlena); val = OidOutputFunctionCall(foutoid, values[i]); } if (i > 0) appendStringInfoString(&buf, ", "); appendStringInfoString(&buf, val); } appendStringInfoChar(&buf, ')'); return buf.data; }
/* * BuildIndexValueDescription * * Construct a string describing the contents of an index entry, in the * form "(key_name, ...)=(key_value, ...)". This is currently used * only for building unique-constraint error messages, but we don't want * to hardwire the spelling of the messages here. */ char * BuildIndexValueDescription(Relation indexRelation, Datum *values, bool *isnull) { /* * XXX for the moment we use the index's tupdesc as a guide to the * datatypes of the values. This is okay for btree indexes but is in * fact the wrong thing in general. This will have to be fixed if we * are ever to support non-btree unique indexes. */ TupleDesc tupdesc = RelationGetDescr(indexRelation); StringInfoData buf; int i; initStringInfo(&buf); appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(RelationGetRelid(indexRelation), true)); for (i = 0; i < tupdesc->natts; i++) { char *val; if (isnull[i]) val = "null"; else { Oid foutoid; bool typisvarlena; getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &foutoid, &typisvarlena); val = OidOutputFunctionCall(foutoid, values[i]); } if (i > 0) appendStringInfoString(&buf, ", "); appendStringInfoString(&buf, val); } appendStringInfoChar(&buf, ')'); return buf.data; }
/* * BuildIndexValueDescription * * Construct a string describing the contents of an index entry, in the * form "(key_name, ...)=(key_value, ...)". This is currently used * for building unique-constraint and exclusion-constraint error messages. * * Note that if the user does not have permissions to view all of the * columns involved then a NULL is returned. Returning a partial key seems * unlikely to be useful and we have no way to know which of the columns the * user provided (unlike in ExecBuildSlotValueDescription). * * The passed-in values/nulls arrays are the "raw" input to the index AM, * e.g. results of FormIndexDatum --- this is not necessarily what is stored * in the index, but it's what the user perceives to be stored. */ char * BuildIndexValueDescription(Relation indexRelation, Datum *values, bool *isnull) { StringInfoData buf; Form_pg_index idxrec; HeapTuple ht_idx; int natts = indexRelation->rd_rel->relnatts; int i; int keyno; Oid indexrelid = RelationGetRelid(indexRelation); Oid indrelid; AclResult aclresult; /* * Check permissions- if the user does not have access to view all of the * key columns then return NULL to avoid leaking data. * * First check if RLS is enabled for the relation. If so, return NULL to * avoid leaking data. * * Next we need to check table-level SELECT access and then, if there is * no access there, check column-level permissions. */ /* * Fetch the pg_index tuple by the Oid of the index */ ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid)); if (!HeapTupleIsValid(ht_idx)) elog(ERROR, "cache lookup failed for index %u", indexrelid); idxrec = (Form_pg_index) GETSTRUCT(ht_idx); indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); /* RLS check- if RLS is enabled then we don't return anything. */ if (check_enable_rls(indrelid, GetUserId(), true) == RLS_ENABLED) { ReleaseSysCache(ht_idx); return NULL; } /* Table-level SELECT is enough, if the user has it */ aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) { /* * No table-level access, so step through the columns in the index and * make sure the user has SELECT rights on all of them. */ for (keyno = 0; keyno < idxrec->indnatts; keyno++) { AttrNumber attnum = idxrec->indkey.values[keyno]; /* * Note that if attnum == InvalidAttrNumber, then this is an index * based on an expression and we return no detail rather than try * to figure out what column(s) the expression includes and if the * user has SELECT rights on them. */ if (attnum == InvalidAttrNumber || pg_attribute_aclcheck(indrelid, attnum, GetUserId(), ACL_SELECT) != ACLCHECK_OK) { /* No access, so clean up and return */ ReleaseSysCache(ht_idx); return NULL; } } } ReleaseSysCache(ht_idx); initStringInfo(&buf); appendStringInfo(&buf, "(%s)=(", pg_get_indexdef_columns(indexrelid, true)); for (i = 0; i < natts; i++) { char *val; if (isnull[i]) val = "null"; else { Oid foutoid; bool typisvarlena; /* * The provided data is not necessarily of the type stored in the * index; rather it is of the index opclass's input type. So look * at rd_opcintype not the index tupdesc. * * Note: this is a bit shaky for opclasses that have pseudotype * input types such as ANYARRAY or RECORD. Currently, the * typoutput functions associated with the pseudotypes will work * okay, but we might have to try harder in future. */ getTypeOutputInfo(indexRelation->rd_opcintype[i], &foutoid, &typisvarlena); val = OidOutputFunctionCall(foutoid, values[i]); } if (i > 0) appendStringInfoString(&buf, ", "); appendStringInfoString(&buf, val); } appendStringInfoChar(&buf, ')'); return buf.data; }