| /* |
| * pg_db_role_setting.c |
| * Routines to support manipulation of the pg_db_role_setting relation |
| * |
| * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * IDENTIFICATION |
| * src/backend/catalog/pg_db_role_setting.c |
| */ |
| #include "postgres.h" |
| |
| #include "access/genam.h" |
| #include "access/heapam.h" |
| #include "access/htup_details.h" |
| #include "access/tableam.h" |
| #include "catalog/indexing.h" |
| #include "catalog/objectaccess.h" |
| #include "catalog/pg_db_role_setting.h" |
| #include "utils/fmgroids.h" |
| #include "utils/rel.h" |
| |
| #include "catalog/heap.h" |
| #include "catalog/pg_authid.h" |
| #include "catalog/pg_database.h" |
| #include "cdb/cdbdisp_query.h" |
| #include "cdb/cdbvars.h" |
| #include "utils/builtins.h" |
| #include "utils/syscache.h" |
| |
| void |
| AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) |
| { |
| char *valuestr; |
| HeapTuple tuple; |
| Relation rel; |
| ScanKeyData scankey[2]; |
| SysScanDesc scan; |
| |
| valuestr = ExtractSetVariableArgs(setstmt); |
| |
| /* Get the old tuple, if any. */ |
| |
| rel = table_open(DbRoleSettingRelationId, RowExclusiveLock); |
| ScanKeyInit(&scankey[0], |
| Anum_pg_db_role_setting_setdatabase, |
| BTEqualStrategyNumber, F_OIDEQ, |
| ObjectIdGetDatum(databaseid)); |
| ScanKeyInit(&scankey[1], |
| Anum_pg_db_role_setting_setrole, |
| BTEqualStrategyNumber, F_OIDEQ, |
| ObjectIdGetDatum(roleid)); |
| scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true, |
| NULL, 2, scankey); |
| tuple = systable_getnext(scan); |
| |
| /* |
| * There are three cases: |
| * |
| * - in RESET ALL, request GUC to reset the settings array and update the |
| * catalog if there's anything left, delete it otherwise |
| * |
| * - in other commands, if there's a tuple in pg_db_role_setting, update |
| * it; if it ends up empty, delete it |
| * |
| * - otherwise, insert a new pg_db_role_setting tuple, but only if the |
| * command is not RESET |
| */ |
| if (setstmt->kind == VAR_RESET_ALL) |
| { |
| if (HeapTupleIsValid(tuple)) |
| { |
| ArrayType *new = NULL; |
| Datum datum; |
| bool isnull; |
| |
| datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, |
| RelationGetDescr(rel), &isnull); |
| |
| if (!isnull) |
| new = GUCArrayReset(DatumGetArrayTypeP(datum)); |
| |
| if (new) |
| { |
| Datum repl_val[Natts_pg_db_role_setting]; |
| bool repl_null[Natts_pg_db_role_setting]; |
| bool repl_repl[Natts_pg_db_role_setting]; |
| HeapTuple newtuple; |
| |
| memset(repl_repl, false, sizeof(repl_repl)); |
| |
| repl_val[Anum_pg_db_role_setting_setconfig - 1] = |
| PointerGetDatum(new); |
| repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; |
| repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; |
| |
| newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), |
| repl_val, repl_null, repl_repl); |
| CatalogTupleUpdate(rel, &tuple->t_self, newtuple); |
| } |
| else |
| CatalogTupleDelete(rel, &tuple->t_self); |
| } |
| } |
| else if (HeapTupleIsValid(tuple)) |
| { |
| Datum repl_val[Natts_pg_db_role_setting]; |
| bool repl_null[Natts_pg_db_role_setting]; |
| bool repl_repl[Natts_pg_db_role_setting]; |
| HeapTuple newtuple; |
| Datum datum; |
| bool isnull; |
| ArrayType *a; |
| |
| memset(repl_repl, false, sizeof(repl_repl)); |
| repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; |
| repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; |
| |
| /* Extract old value of setconfig */ |
| datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, |
| RelationGetDescr(rel), &isnull); |
| a = isnull ? NULL : DatumGetArrayTypeP(datum); |
| |
| /* Update (valuestr is NULL in RESET cases) */ |
| if (valuestr) |
| a = GUCArrayAdd(a, setstmt->name, valuestr); |
| else |
| a = GUCArrayDelete(a, setstmt->name); |
| |
| if (a) |
| { |
| repl_val[Anum_pg_db_role_setting_setconfig - 1] = |
| PointerGetDatum(a); |
| |
| newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), |
| repl_val, repl_null, repl_repl); |
| CatalogTupleUpdate(rel, &tuple->t_self, newtuple); |
| } |
| else |
| CatalogTupleDelete(rel, &tuple->t_self); |
| } |
| else if (valuestr) |
| { |
| /* non-null valuestr means it's not RESET, so insert a new tuple */ |
| HeapTuple newtuple; |
| Datum values[Natts_pg_db_role_setting]; |
| bool nulls[Natts_pg_db_role_setting]; |
| ArrayType *a; |
| |
| memset(nulls, false, sizeof(nulls)); |
| |
| a = GUCArrayAdd(NULL, setstmt->name, valuestr); |
| |
| values[Anum_pg_db_role_setting_setdatabase - 1] = |
| ObjectIdGetDatum(databaseid); |
| values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid); |
| values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a); |
| newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); |
| |
| CatalogTupleInsert(rel, newtuple); |
| } |
| |
| InvokeObjectPostAlterHookArg(DbRoleSettingRelationId, |
| databaseid, 0, roleid, false); |
| |
| systable_endscan(scan); |
| |
| /* update pg_stat_last_shoperation for metadata tracking */ |
| if (Gp_role == GP_ROLE_DISPATCH) |
| { |
| char *alter_subtype; |
| |
| if (setstmt->kind == VAR_RESET_ALL) |
| alter_subtype = "RESET ALL"; |
| else if (setstmt->kind == VAR_RESET) |
| alter_subtype = "RESET"; |
| else |
| alter_subtype = "SET"; |
| |
| /* roleoid is only valid for ALTER ROLE */ |
| MetaTrackUpdObject(DatabaseRelationId, |
| OidIsValid(roleid) ? roleid : databaseid, |
| GetUserId(), |
| "ALTER", alter_subtype); |
| } |
| |
| if (Gp_role == GP_ROLE_DISPATCH) |
| { |
| StringInfoData buffer; |
| char *rolename; |
| char *dbname; |
| HeapTuple htup; |
| |
| if (roleid) |
| { |
| htup = SearchSysCache1(AUTHOID, roleid); |
| if (!HeapTupleIsValid(htup)) |
| elog(ERROR, "cache lookup failed for role %u", roleid); |
| rolename = pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname)); |
| ReleaseSysCache(htup); |
| } |
| else |
| rolename = NULL; |
| |
| if (databaseid) |
| { |
| htup = SearchSysCache1(DATABASEOID, databaseid); |
| if (!htup) |
| elog(ERROR, "cache lookup failed for database %u", databaseid); |
| dbname = pstrdup(NameStr(((Form_pg_database) GETSTRUCT(htup))->datname)); |
| ReleaseSysCache(htup); |
| } |
| else |
| dbname = NULL; |
| |
| initStringInfo(&buffer); |
| |
| if (databaseid && roleid) |
| appendStringInfo(&buffer, "ALTER ROLE %s IN DATABASE %s ", |
| quote_identifier(rolename), quote_identifier(dbname)); |
| else if (roleid) |
| appendStringInfo(&buffer, "ALTER ROLE %s ", |
| quote_identifier(rolename)); |
| else if (databaseid) |
| appendStringInfo(&buffer, "ALTER DATABASE %s ", |
| quote_identifier(dbname)); |
| else |
| appendStringInfo(&buffer, "ALTER ROLE ALL "); |
| |
| if (setstmt->kind == VAR_RESET_ALL) |
| appendStringInfo(&buffer, "RESET ALL"); |
| else if (valuestr == NULL) |
| appendStringInfo(&buffer, "RESET %s", quote_identifier(setstmt->name)); |
| else if (setstmt->kind == VAR_SET_CURRENT) |
| appendStringInfo(&buffer, "SET %s TO %s", quote_identifier(setstmt->name), quote_literal_cstr(valuestr)); |
| else |
| { |
| ListCell *l; |
| bool first; |
| |
| appendStringInfo(&buffer, "SET %s TO ", quote_identifier(setstmt->name)); |
| |
| /* Parse string into list of identifiers */ |
| first = true; |
| foreach(l, setstmt->args) |
| { |
| A_Const *arg = (A_Const *) lfirst(l); |
| |
| if (!first) |
| appendStringInfo(&buffer, ","); |
| first = false; |
| |
| switch (nodeTag(&arg->val)) |
| { |
| case T_Integer: |
| appendStringInfo(&buffer, "%d", intVal(&arg->val)); |
| break; |
| case T_Float: |
| /* represented as a string, so just copy it */ |
| appendStringInfoString(&buffer, strVal(&arg->val)); |
| break; |
| case T_String: |
| appendStringInfoString(&buffer, quote_literal_cstr(strVal(&arg->val))); |
| break; |
| default: |
| elog(ERROR, "unexpected constant type: %d", nodeTag(&arg->val)); |
| } |
| } |
| } |
| |
| CdbDispatchCommand(buffer.data, |
| DF_CANCEL_ON_ERROR| |
| DF_NEED_TWO_PHASE| |
| DF_WITH_SNAPSHOT, |
| NULL); |
| |
| pfree(buffer.data); |
| } |
| |
| /* Close pg_db_role_setting, but keep lock till commit */ |
| table_close(rel, NoLock); |
| } |
| |
| /* |
| * Drop some settings from the catalog. These can be for a particular |
| * database, or for a particular role. (It is of course possible to do both |
| * too, but it doesn't make sense for current uses.) |
| */ |
| void |
| DropSetting(Oid databaseid, Oid roleid) |
| { |
| Relation relsetting; |
| TableScanDesc scan; |
| ScanKeyData keys[2]; |
| HeapTuple tup; |
| int numkeys = 0; |
| |
| relsetting = table_open(DbRoleSettingRelationId, RowExclusiveLock); |
| |
| if (OidIsValid(databaseid)) |
| { |
| ScanKeyInit(&keys[numkeys], |
| Anum_pg_db_role_setting_setdatabase, |
| BTEqualStrategyNumber, |
| F_OIDEQ, |
| ObjectIdGetDatum(databaseid)); |
| numkeys++; |
| } |
| if (OidIsValid(roleid)) |
| { |
| ScanKeyInit(&keys[numkeys], |
| Anum_pg_db_role_setting_setrole, |
| BTEqualStrategyNumber, |
| F_OIDEQ, |
| ObjectIdGetDatum(roleid)); |
| numkeys++; |
| } |
| |
| scan = table_beginscan_catalog(relsetting, numkeys, keys); |
| while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) |
| { |
| CatalogTupleDelete(relsetting, &tup->t_self); |
| } |
| table_endscan(scan); |
| |
| table_close(relsetting, RowExclusiveLock); |
| } |
| |
| /* |
| * Scan pg_db_role_setting looking for applicable settings, and load them on |
| * the current process. |
| * |
| * relsetting is pg_db_role_setting, already opened and locked. |
| * |
| * Note: we only consider setting for the exact databaseid/roleid combination. |
| * This probably needs to be called more than once, with InvalidOid passed as |
| * databaseid/roleid. |
| */ |
| void |
| ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid, |
| Relation relsetting, GucSource source) |
| { |
| SysScanDesc scan; |
| ScanKeyData keys[2]; |
| HeapTuple tup; |
| |
| ScanKeyInit(&keys[0], |
| Anum_pg_db_role_setting_setdatabase, |
| BTEqualStrategyNumber, |
| F_OIDEQ, |
| ObjectIdGetDatum(databaseid)); |
| ScanKeyInit(&keys[1], |
| Anum_pg_db_role_setting_setrole, |
| BTEqualStrategyNumber, |
| F_OIDEQ, |
| ObjectIdGetDatum(roleid)); |
| |
| scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true, |
| snapshot, 2, keys); |
| while (HeapTupleIsValid(tup = systable_getnext(scan))) |
| { |
| bool isnull; |
| Datum datum; |
| |
| datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig, |
| RelationGetDescr(relsetting), &isnull); |
| if (!isnull) |
| { |
| ArrayType *a = DatumGetArrayTypeP(datum); |
| |
| /* |
| * We process all the options at SUSET level. We assume that the |
| * right to insert an option into pg_db_role_setting was checked |
| * when it was inserted. |
| */ |
| ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET); |
| } |
| } |
| |
| systable_endscan(scan); |
| } |