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 | |