| 1 | /* |
| 2 | * pg_db_role_setting.c |
| 3 | * Routines to support manipulation of the pg_db_role_setting relation |
| 4 | * |
| 5 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 6 | * Portions Copyright (c) 1994, Regents of the University of California |
| 7 | * |
| 8 | * IDENTIFICATION |
| 9 | * src/backend/catalog/pg_db_role_setting.c |
| 10 | */ |
| 11 | #include "postgres.h" |
| 12 | |
| 13 | #include "access/genam.h" |
| 14 | #include "access/heapam.h" |
| 15 | #include "access/htup_details.h" |
| 16 | #include "access/tableam.h" |
| 17 | #include "catalog/indexing.h" |
| 18 | #include "catalog/objectaccess.h" |
| 19 | #include "catalog/pg_db_role_setting.h" |
| 20 | #include "utils/fmgroids.h" |
| 21 | #include "utils/rel.h" |
| 22 | |
| 23 | void |
| 24 | AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) |
| 25 | { |
| 26 | char *valuestr; |
| 27 | HeapTuple tuple; |
| 28 | Relation rel; |
| 29 | ScanKeyData scankey[2]; |
| 30 | SysScanDesc scan; |
| 31 | |
| 32 | valuestr = ExtractSetVariableArgs(setstmt); |
| 33 | |
| 34 | /* Get the old tuple, if any. */ |
| 35 | |
| 36 | rel = table_open(DbRoleSettingRelationId, RowExclusiveLock); |
| 37 | ScanKeyInit(&scankey[0], |
| 38 | Anum_pg_db_role_setting_setdatabase, |
| 39 | BTEqualStrategyNumber, F_OIDEQ, |
| 40 | ObjectIdGetDatum(databaseid)); |
| 41 | ScanKeyInit(&scankey[1], |
| 42 | Anum_pg_db_role_setting_setrole, |
| 43 | BTEqualStrategyNumber, F_OIDEQ, |
| 44 | ObjectIdGetDatum(roleid)); |
| 45 | scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true, |
| 46 | NULL, 2, scankey); |
| 47 | tuple = systable_getnext(scan); |
| 48 | |
| 49 | /* |
| 50 | * There are three cases: |
| 51 | * |
| 52 | * - in RESET ALL, request GUC to reset the settings array and update the |
| 53 | * catalog if there's anything left, delete it otherwise |
| 54 | * |
| 55 | * - in other commands, if there's a tuple in pg_db_role_setting, update |
| 56 | * it; if it ends up empty, delete it |
| 57 | * |
| 58 | * - otherwise, insert a new pg_db_role_setting tuple, but only if the |
| 59 | * command is not RESET |
| 60 | */ |
| 61 | if (setstmt->kind == VAR_RESET_ALL) |
| 62 | { |
| 63 | if (HeapTupleIsValid(tuple)) |
| 64 | { |
| 65 | ArrayType *new = NULL; |
| 66 | Datum datum; |
| 67 | bool isnull; |
| 68 | |
| 69 | datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, |
| 70 | RelationGetDescr(rel), &isnull); |
| 71 | |
| 72 | if (!isnull) |
| 73 | new = GUCArrayReset(DatumGetArrayTypeP(datum)); |
| 74 | |
| 75 | if (new) |
| 76 | { |
| 77 | Datum repl_val[Natts_pg_db_role_setting]; |
| 78 | bool repl_null[Natts_pg_db_role_setting]; |
| 79 | bool repl_repl[Natts_pg_db_role_setting]; |
| 80 | HeapTuple newtuple; |
| 81 | |
| 82 | memset(repl_repl, false, sizeof(repl_repl)); |
| 83 | |
| 84 | repl_val[Anum_pg_db_role_setting_setconfig - 1] = |
| 85 | PointerGetDatum(new); |
| 86 | repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; |
| 87 | repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; |
| 88 | |
| 89 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), |
| 90 | repl_val, repl_null, repl_repl); |
| 91 | CatalogTupleUpdate(rel, &tuple->t_self, newtuple); |
| 92 | } |
| 93 | else |
| 94 | CatalogTupleDelete(rel, &tuple->t_self); |
| 95 | } |
| 96 | } |
| 97 | else if (HeapTupleIsValid(tuple)) |
| 98 | { |
| 99 | Datum repl_val[Natts_pg_db_role_setting]; |
| 100 | bool repl_null[Natts_pg_db_role_setting]; |
| 101 | bool repl_repl[Natts_pg_db_role_setting]; |
| 102 | HeapTuple newtuple; |
| 103 | Datum datum; |
| 104 | bool isnull; |
| 105 | ArrayType *a; |
| 106 | |
| 107 | memset(repl_repl, false, sizeof(repl_repl)); |
| 108 | repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; |
| 109 | repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; |
| 110 | |
| 111 | /* Extract old value of setconfig */ |
| 112 | datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, |
| 113 | RelationGetDescr(rel), &isnull); |
| 114 | a = isnull ? NULL : DatumGetArrayTypeP(datum); |
| 115 | |
| 116 | /* Update (valuestr is NULL in RESET cases) */ |
| 117 | if (valuestr) |
| 118 | a = GUCArrayAdd(a, setstmt->name, valuestr); |
| 119 | else |
| 120 | a = GUCArrayDelete(a, setstmt->name); |
| 121 | |
| 122 | if (a) |
| 123 | { |
| 124 | repl_val[Anum_pg_db_role_setting_setconfig - 1] = |
| 125 | PointerGetDatum(a); |
| 126 | |
| 127 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), |
| 128 | repl_val, repl_null, repl_repl); |
| 129 | CatalogTupleUpdate(rel, &tuple->t_self, newtuple); |
| 130 | } |
| 131 | else |
| 132 | CatalogTupleDelete(rel, &tuple->t_self); |
| 133 | } |
| 134 | else if (valuestr) |
| 135 | { |
| 136 | /* non-null valuestr means it's not RESET, so insert a new tuple */ |
| 137 | HeapTuple newtuple; |
| 138 | Datum values[Natts_pg_db_role_setting]; |
| 139 | bool nulls[Natts_pg_db_role_setting]; |
| 140 | ArrayType *a; |
| 141 | |
| 142 | memset(nulls, false, sizeof(nulls)); |
| 143 | |
| 144 | a = GUCArrayAdd(NULL, setstmt->name, valuestr); |
| 145 | |
| 146 | values[Anum_pg_db_role_setting_setdatabase - 1] = |
| 147 | ObjectIdGetDatum(databaseid); |
| 148 | values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid); |
| 149 | values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a); |
| 150 | newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); |
| 151 | |
| 152 | CatalogTupleInsert(rel, newtuple); |
| 153 | } |
| 154 | |
| 155 | InvokeObjectPostAlterHookArg(DbRoleSettingRelationId, |
| 156 | databaseid, 0, roleid, false); |
| 157 | |
| 158 | systable_endscan(scan); |
| 159 | |
| 160 | /* Close pg_db_role_setting, but keep lock till commit */ |
| 161 | table_close(rel, NoLock); |
| 162 | } |
| 163 | |
| 164 | /* |
| 165 | * Drop some settings from the catalog. These can be for a particular |
| 166 | * database, or for a particular role. (It is of course possible to do both |
| 167 | * too, but it doesn't make sense for current uses.) |
| 168 | */ |
| 169 | void |
| 170 | DropSetting(Oid databaseid, Oid roleid) |
| 171 | { |
| 172 | Relation relsetting; |
| 173 | TableScanDesc scan; |
| 174 | ScanKeyData keys[2]; |
| 175 | HeapTuple tup; |
| 176 | int numkeys = 0; |
| 177 | |
| 178 | relsetting = table_open(DbRoleSettingRelationId, RowExclusiveLock); |
| 179 | |
| 180 | if (OidIsValid(databaseid)) |
| 181 | { |
| 182 | ScanKeyInit(&keys[numkeys], |
| 183 | Anum_pg_db_role_setting_setdatabase, |
| 184 | BTEqualStrategyNumber, |
| 185 | F_OIDEQ, |
| 186 | ObjectIdGetDatum(databaseid)); |
| 187 | numkeys++; |
| 188 | } |
| 189 | if (OidIsValid(roleid)) |
| 190 | { |
| 191 | ScanKeyInit(&keys[numkeys], |
| 192 | Anum_pg_db_role_setting_setrole, |
| 193 | BTEqualStrategyNumber, |
| 194 | F_OIDEQ, |
| 195 | ObjectIdGetDatum(roleid)); |
| 196 | numkeys++; |
| 197 | } |
| 198 | |
| 199 | scan = table_beginscan_catalog(relsetting, numkeys, keys); |
| 200 | while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) |
| 201 | { |
| 202 | CatalogTupleDelete(relsetting, &tup->t_self); |
| 203 | } |
| 204 | table_endscan(scan); |
| 205 | |
| 206 | table_close(relsetting, RowExclusiveLock); |
| 207 | } |
| 208 | |
| 209 | /* |
| 210 | * Scan pg_db_role_setting looking for applicable settings, and load them on |
| 211 | * the current process. |
| 212 | * |
| 213 | * relsetting is pg_db_role_setting, already opened and locked. |
| 214 | * |
| 215 | * Note: we only consider setting for the exact databaseid/roleid combination. |
| 216 | * This probably needs to be called more than once, with InvalidOid passed as |
| 217 | * databaseid/roleid. |
| 218 | */ |
| 219 | void |
| 220 | ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid, |
| 221 | Relation relsetting, GucSource source) |
| 222 | { |
| 223 | SysScanDesc scan; |
| 224 | ScanKeyData keys[2]; |
| 225 | HeapTuple tup; |
| 226 | |
| 227 | ScanKeyInit(&keys[0], |
| 228 | Anum_pg_db_role_setting_setdatabase, |
| 229 | BTEqualStrategyNumber, |
| 230 | F_OIDEQ, |
| 231 | ObjectIdGetDatum(databaseid)); |
| 232 | ScanKeyInit(&keys[1], |
| 233 | Anum_pg_db_role_setting_setrole, |
| 234 | BTEqualStrategyNumber, |
| 235 | F_OIDEQ, |
| 236 | ObjectIdGetDatum(roleid)); |
| 237 | |
| 238 | scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true, |
| 239 | snapshot, 2, keys); |
| 240 | while (HeapTupleIsValid(tup = systable_getnext(scan))) |
| 241 | { |
| 242 | bool isnull; |
| 243 | Datum datum; |
| 244 | |
| 245 | datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig, |
| 246 | RelationGetDescr(relsetting), &isnull); |
| 247 | if (!isnull) |
| 248 | { |
| 249 | ArrayType *a = DatumGetArrayTypeP(datum); |
| 250 | |
| 251 | /* |
| 252 | * We process all the options at SUSET level. We assume that the |
| 253 | * right to insert an option into pg_db_role_setting was checked |
| 254 | * when it was inserted. |
| 255 | */ |
| 256 | ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | systable_endscan(scan); |
| 261 | } |
| 262 | |