@@ -186,159 +186,139 @@ policy_role_list_to_array(List *roles, int *num_roles)
186
186
/*
187
187
* Load row security policy from the catalog, and store it in
188
188
* the relation's relcache entry.
189
+ *
190
+ * Note that caller should have verified that pg_class.relrowsecurity
191
+ * is true for this relation.
189
192
*/
190
193
void
191
194
RelationBuildRowSecurity (Relation relation )
192
195
{
193
196
MemoryContext rscxt ;
194
197
MemoryContext oldcxt = CurrentMemoryContext ;
195
- RowSecurityDesc * volatile rsdesc = NULL ;
198
+ RowSecurityDesc * rsdesc ;
199
+ Relation catalog ;
200
+ ScanKeyData skey ;
201
+ SysScanDesc sscan ;
202
+ HeapTuple tuple ;
196
203
197
204
/*
198
205
* Create a memory context to hold everything associated with this
199
206
* relation's row security policy. This makes it easy to clean up during
200
- * a relcache flush.
207
+ * a relcache flush. However, to cover the possibility of an error
208
+ * partway through, we don't make the context long-lived till we're done.
201
209
*/
202
- rscxt = AllocSetContextCreate (CacheMemoryContext ,
210
+ rscxt = AllocSetContextCreate (CurrentMemoryContext ,
203
211
"row security descriptor" ,
204
212
ALLOCSET_SMALL_SIZES );
213
+ MemoryContextCopyAndSetIdentifier (rscxt ,
214
+ RelationGetRelationName (relation ));
215
+
216
+ rsdesc = MemoryContextAllocZero (rscxt , sizeof (RowSecurityDesc ));
217
+ rsdesc -> rscxt = rscxt ;
205
218
206
219
/*
207
- * Since rscxt lives under CacheMemoryContext, it is long-lived. Use a
208
- * PG_TRY block to ensure it'll get freed if we fail partway through.
220
+ * Now scan pg_policy for RLS policies associated with this relation.
221
+ * Because we use the index on (polrelid, polname), we should consistently
222
+ * visit the rel's policies in name order, at least when system indexes
223
+ * aren't disabled. This simplifies equalRSDesc().
209
224
*/
210
- PG_TRY ();
211
- {
212
- Relation catalog ;
213
- ScanKeyData skey ;
214
- SysScanDesc sscan ;
215
- HeapTuple tuple ;
216
-
217
- MemoryContextCopyAndSetIdentifier (rscxt ,
218
- RelationGetRelationName (relation ));
225
+ catalog = heap_open (PolicyRelationId , AccessShareLock );
219
226
220
- rsdesc = MemoryContextAllocZero (rscxt , sizeof (RowSecurityDesc ));
221
- rsdesc -> rscxt = rscxt ;
227
+ ScanKeyInit (& skey ,
228
+ Anum_pg_policy_polrelid ,
229
+ BTEqualStrategyNumber , F_OIDEQ ,
230
+ ObjectIdGetDatum (RelationGetRelid (relation )));
222
231
223
- catalog = heap_open (PolicyRelationId , AccessShareLock );
232
+ sscan = systable_beginscan (catalog , PolicyPolrelidPolnameIndexId , true,
233
+ NULL , 1 , & skey );
224
234
225
- ScanKeyInit (& skey ,
226
- Anum_pg_policy_polrelid ,
227
- BTEqualStrategyNumber , F_OIDEQ ,
228
- ObjectIdGetDatum (RelationGetRelid (relation )));
235
+ while (HeapTupleIsValid (tuple = systable_getnext (sscan )))
236
+ {
237
+ Form_pg_policy policy_form = (Form_pg_policy ) GETSTRUCT (tuple );
238
+ RowSecurityPolicy * policy ;
239
+ Datum datum ;
240
+ bool isnull ;
241
+ char * str_value ;
229
242
230
- sscan = systable_beginscan (catalog , PolicyPolrelidPolnameIndexId , true,
231
- NULL , 1 , & skey );
243
+ policy = MemoryContextAllocZero (rscxt , sizeof (RowSecurityPolicy ));
232
244
233
245
/*
234
- * Loop through the row level security policies for this relation, if
235
- * any.
246
+ * Note: we must be sure that pass-by-reference data gets copied into
247
+ * rscxt. We avoid making that context current over wider spans than
248
+ * we have to, though.
236
249
*/
237
- while (HeapTupleIsValid (tuple = systable_getnext (sscan )))
238
- {
239
- Datum value_datum ;
240
- char cmd_value ;
241
- bool permissive_value ;
242
- Datum roles_datum ;
243
- char * qual_value ;
244
- Expr * qual_expr ;
245
- char * with_check_value ;
246
- Expr * with_check_qual ;
247
- char * policy_name_value ;
248
- bool isnull ;
249
- RowSecurityPolicy * policy ;
250
-
251
- /*
252
- * Note: all the pass-by-reference data we collect here is either
253
- * still stored in the tuple, or constructed in the caller's
254
- * short-lived memory context. We must copy it into rscxt
255
- * explicitly below.
256
- */
257
-
258
- /* Get policy command */
259
- value_datum = heap_getattr (tuple , Anum_pg_policy_polcmd ,
260
- RelationGetDescr (catalog ), & isnull );
261
- Assert (!isnull );
262
- cmd_value = DatumGetChar (value_datum );
263
-
264
- /* Get policy permissive or restrictive */
265
- value_datum = heap_getattr (tuple , Anum_pg_policy_polpermissive ,
266
- RelationGetDescr (catalog ), & isnull );
267
- Assert (!isnull );
268
- permissive_value = DatumGetBool (value_datum );
269
-
270
- /* Get policy name */
271
- value_datum = heap_getattr (tuple , Anum_pg_policy_polname ,
272
- RelationGetDescr (catalog ), & isnull );
273
- Assert (!isnull );
274
- policy_name_value = NameStr (* (DatumGetName (value_datum )));
275
-
276
- /* Get policy roles */
277
- roles_datum = heap_getattr (tuple , Anum_pg_policy_polroles ,
278
- RelationGetDescr (catalog ), & isnull );
279
- /* shouldn't be null, but initdb doesn't mark it so, so check */
280
- if (isnull )
281
- elog (ERROR , "unexpected null value in pg_policy.polroles" );
282
-
283
- /* Get policy qual */
284
- value_datum = heap_getattr (tuple , Anum_pg_policy_polqual ,
285
- RelationGetDescr (catalog ), & isnull );
286
- if (!isnull )
287
- {
288
- qual_value = TextDatumGetCString (value_datum );
289
- qual_expr = (Expr * ) stringToNode (qual_value );
290
- }
291
- else
292
- qual_expr = NULL ;
293
250
294
- /* Get WITH CHECK qual */
295
- value_datum = heap_getattr (tuple , Anum_pg_policy_polwithcheck ,
296
- RelationGetDescr (catalog ), & isnull );
297
- if (!isnull )
298
- {
299
- with_check_value = TextDatumGetCString (value_datum );
300
- with_check_qual = (Expr * ) stringToNode (with_check_value );
301
- }
302
- else
303
- with_check_qual = NULL ;
251
+ /* Get policy command */
252
+ policy -> polcmd = policy_form -> polcmd ;
304
253
305
- /* Now copy everything into the cache context */
306
- MemoryContextSwitchTo ( rscxt ) ;
254
+ /* Get policy, permissive or restrictive */
255
+ policy -> permissive = policy_form -> polpermissive ;
307
256
308
- policy = palloc0 (sizeof (RowSecurityPolicy ));
309
- policy -> policy_name = pstrdup (policy_name_value );
310
- policy -> polcmd = cmd_value ;
311
- policy -> permissive = permissive_value ;
312
- policy -> roles = DatumGetArrayTypePCopy (roles_datum );
313
- policy -> qual = copyObject (qual_expr );
314
- policy -> with_check_qual = copyObject (with_check_qual );
315
- policy -> hassublinks = checkExprHasSubLink ((Node * ) qual_expr ) ||
316
- checkExprHasSubLink ((Node * ) with_check_qual );
257
+ /* Get policy name */
258
+ policy -> policy_name =
259
+ MemoryContextStrdup (rscxt , NameStr (policy_form -> polname ));
317
260
318
- rsdesc -> policies = lcons (policy , rsdesc -> policies );
261
+ /* Get policy roles */
262
+ datum = heap_getattr (tuple , Anum_pg_policy_polroles ,
263
+ RelationGetDescr (catalog ), & isnull );
264
+ /* shouldn't be null, but let's check for luck */
265
+ if (isnull )
266
+ elog (ERROR , "unexpected null value in pg_policy.polroles" );
267
+ MemoryContextSwitchTo (rscxt );
268
+ policy -> roles = DatumGetArrayTypePCopy (datum );
269
+ MemoryContextSwitchTo (oldcxt );
319
270
271
+ /* Get policy qual */
272
+ datum = heap_getattr (tuple , Anum_pg_policy_polqual ,
273
+ RelationGetDescr (catalog ), & isnull );
274
+ if (!isnull )
275
+ {
276
+ str_value = TextDatumGetCString (datum );
277
+ MemoryContextSwitchTo (rscxt );
278
+ policy -> qual = (Expr * ) stringToNode (str_value );
320
279
MemoryContextSwitchTo (oldcxt );
280
+ pfree (str_value );
281
+ }
282
+ else
283
+ policy -> qual = NULL ;
321
284
322
- /* clean up some (not all) of the junk ... */
323
- if (qual_expr != NULL )
324
- pfree (qual_expr );
325
- if (with_check_qual != NULL )
326
- pfree (with_check_qual );
285
+ /* Get WITH CHECK qual */
286
+ datum = heap_getattr (tuple , Anum_pg_policy_polwithcheck ,
287
+ RelationGetDescr (catalog ), & isnull );
288
+ if (!isnull )
289
+ {
290
+ str_value = TextDatumGetCString (datum );
291
+ MemoryContextSwitchTo (rscxt );
292
+ policy -> with_check_qual = (Expr * ) stringToNode (str_value );
293
+ MemoryContextSwitchTo (oldcxt );
294
+ pfree (str_value );
327
295
}
296
+ else
297
+ policy -> with_check_qual = NULL ;
328
298
329
- systable_endscan (sscan );
330
- heap_close (catalog , AccessShareLock );
331
- }
332
- PG_CATCH ();
333
- {
334
- /* Delete rscxt, first making sure it isn't active */
299
+ /* We want to cache whether there are SubLinks in these expressions */
300
+ policy -> hassublinks = checkExprHasSubLink ((Node * ) policy -> qual ) ||
301
+ checkExprHasSubLink ((Node * ) policy -> with_check_qual );
302
+
303
+ /*
304
+ * Add this object to list. For historical reasons, the list is built
305
+ * in reverse order.
306
+ */
307
+ MemoryContextSwitchTo (rscxt );
308
+ rsdesc -> policies = lcons (policy , rsdesc -> policies );
335
309
MemoryContextSwitchTo (oldcxt );
336
- MemoryContextDelete (rscxt );
337
- PG_RE_THROW ();
338
310
}
339
- PG_END_TRY ();
340
311
341
- /* Success --- attach the policy descriptor to the relcache entry */
312
+ systable_endscan (sscan );
313
+ heap_close (catalog , AccessShareLock );
314
+
315
+ /*
316
+ * Success. Reparent the descriptor's memory context under
317
+ * CacheMemoryContext so that it will live indefinitely, then attach the
318
+ * policy descriptor to the relcache entry.
319
+ */
320
+ MemoryContextSetParent (rscxt , CacheMemoryContext );
321
+
342
322
relation -> rd_rsdesc = rsdesc ;
343
323
}
344
324
0 commit comments