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-2016, 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 "catalog/indexing.h"
17 #include "catalog/objectaccess.h"
18 #include "catalog/pg_db_role_setting.h"
19 #include "utils/fmgroids.h"
20 #include "utils/rel.h"
21 #include "utils/tqual.h"
22 
23 void
AlterSetting(Oid databaseid,Oid roleid,VariableSetStmt * setstmt)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 = heap_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 				simple_heap_update(rel, &tuple->t_self, newtuple);
92 
93 				/* Update indexes */
94 				CatalogUpdateIndexes(rel, newtuple);
95 			}
96 			else
97 				simple_heap_delete(rel, &tuple->t_self);
98 		}
99 	}
100 	else if (HeapTupleIsValid(tuple))
101 	{
102 		Datum		repl_val[Natts_pg_db_role_setting];
103 		bool		repl_null[Natts_pg_db_role_setting];
104 		bool		repl_repl[Natts_pg_db_role_setting];
105 		HeapTuple	newtuple;
106 		Datum		datum;
107 		bool		isnull;
108 		ArrayType  *a;
109 
110 		memset(repl_repl, false, sizeof(repl_repl));
111 		repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true;
112 		repl_null[Anum_pg_db_role_setting_setconfig - 1] = false;
113 
114 		/* Extract old value of setconfig */
115 		datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,
116 							 RelationGetDescr(rel), &isnull);
117 		a = isnull ? NULL : DatumGetArrayTypeP(datum);
118 
119 		/* Update (valuestr is NULL in RESET cases) */
120 		if (valuestr)
121 			a = GUCArrayAdd(a, setstmt->name, valuestr);
122 		else
123 			a = GUCArrayDelete(a, setstmt->name);
124 
125 		if (a)
126 		{
127 			repl_val[Anum_pg_db_role_setting_setconfig - 1] =
128 				PointerGetDatum(a);
129 
130 			newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
131 										 repl_val, repl_null, repl_repl);
132 			simple_heap_update(rel, &tuple->t_self, newtuple);
133 
134 			/* Update indexes */
135 			CatalogUpdateIndexes(rel, newtuple);
136 		}
137 		else
138 			simple_heap_delete(rel, &tuple->t_self);
139 	}
140 	else if (valuestr)
141 	{
142 		/* non-null valuestr means it's not RESET, so insert a new tuple */
143 		HeapTuple	newtuple;
144 		Datum		values[Natts_pg_db_role_setting];
145 		bool		nulls[Natts_pg_db_role_setting];
146 		ArrayType  *a;
147 
148 		memset(nulls, false, sizeof(nulls));
149 
150 		a = GUCArrayAdd(NULL, setstmt->name, valuestr);
151 
152 		values[Anum_pg_db_role_setting_setdatabase - 1] =
153 			ObjectIdGetDatum(databaseid);
154 		values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid);
155 		values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a);
156 		newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
157 
158 		simple_heap_insert(rel, newtuple);
159 
160 		/* Update indexes */
161 		CatalogUpdateIndexes(rel, newtuple);
162 	}
163 
164 	InvokeObjectPostAlterHookArg(DbRoleSettingRelationId,
165 								 databaseid, 0, roleid, false);
166 
167 	systable_endscan(scan);
168 
169 	/* Close pg_db_role_setting, but keep lock till commit */
170 	heap_close(rel, NoLock);
171 }
172 
173 /*
174  * Drop some settings from the catalog.  These can be for a particular
175  * database, or for a particular role.  (It is of course possible to do both
176  * too, but it doesn't make sense for current uses.)
177  */
178 void
DropSetting(Oid databaseid,Oid roleid)179 DropSetting(Oid databaseid, Oid roleid)
180 {
181 	Relation	relsetting;
182 	HeapScanDesc scan;
183 	ScanKeyData keys[2];
184 	HeapTuple	tup;
185 	int			numkeys = 0;
186 
187 	relsetting = heap_open(DbRoleSettingRelationId, RowExclusiveLock);
188 
189 	if (OidIsValid(databaseid))
190 	{
191 		ScanKeyInit(&keys[numkeys],
192 					Anum_pg_db_role_setting_setdatabase,
193 					BTEqualStrategyNumber,
194 					F_OIDEQ,
195 					ObjectIdGetDatum(databaseid));
196 		numkeys++;
197 	}
198 	if (OidIsValid(roleid))
199 	{
200 		ScanKeyInit(&keys[numkeys],
201 					Anum_pg_db_role_setting_setrole,
202 					BTEqualStrategyNumber,
203 					F_OIDEQ,
204 					ObjectIdGetDatum(roleid));
205 		numkeys++;
206 	}
207 
208 	scan = heap_beginscan_catalog(relsetting, numkeys, keys);
209 	while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
210 	{
211 		simple_heap_delete(relsetting, &tup->t_self);
212 	}
213 	heap_endscan(scan);
214 
215 	heap_close(relsetting, RowExclusiveLock);
216 }
217 
218 /*
219  * Scan pg_db_role_setting looking for applicable settings, and load them on
220  * the current process.
221  *
222  * relsetting is pg_db_role_setting, already opened and locked.
223  *
224  * Note: we only consider setting for the exact databaseid/roleid combination.
225  * This probably needs to be called more than once, with InvalidOid passed as
226  * databaseid/roleid.
227  */
228 void
ApplySetting(Snapshot snapshot,Oid databaseid,Oid roleid,Relation relsetting,GucSource source)229 ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid,
230 			 Relation relsetting, GucSource source)
231 {
232 	SysScanDesc scan;
233 	ScanKeyData keys[2];
234 	HeapTuple	tup;
235 
236 	ScanKeyInit(&keys[0],
237 				Anum_pg_db_role_setting_setdatabase,
238 				BTEqualStrategyNumber,
239 				F_OIDEQ,
240 				ObjectIdGetDatum(databaseid));
241 	ScanKeyInit(&keys[1],
242 				Anum_pg_db_role_setting_setrole,
243 				BTEqualStrategyNumber,
244 				F_OIDEQ,
245 				ObjectIdGetDatum(roleid));
246 
247 	scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true,
248 							  snapshot, 2, keys);
249 	while (HeapTupleIsValid(tup = systable_getnext(scan)))
250 	{
251 		bool		isnull;
252 		Datum		datum;
253 
254 		datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig,
255 							 RelationGetDescr(relsetting), &isnull);
256 		if (!isnull)
257 		{
258 			ArrayType  *a = DatumGetArrayTypeP(datum);
259 
260 			/*
261 			 * We process all the options at SUSET level.  We assume that the
262 			 * right to insert an option into pg_db_role_setting was checked
263 			 * when it was inserted.
264 			 */
265 			ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET);
266 		}
267 	}
268 
269 	systable_endscan(scan);
270 }
271