231{
237
238 /*
239 * If we are installing an ON SELECT rule, we had better grab
240 * AccessExclusiveLock to ensure no SELECTs are currently running on the
241 * event relation. For other types of rules, it would be sufficient to
242 * grab ShareRowExclusiveLock to lock out insert/update/delete actions and
243 * to ensure that we lock out current CREATE RULE statements; but because
244 * of race conditions in access to catalog entries, we can't do that yet.
245 *
246 * Note that this lock level should match the one used in DefineRule.
247 */
249
250 /*
251 * Verify relation is of a type that rules can sensibly be applied to.
252 * Internal callers can target materialized views, but transformRuleStmt()
253 * blocks them for users. Don't mention them in the error message.
254 */
255 if (event_relation->
rd_rel->relkind != RELKIND_RELATION &&
256 event_relation->
rd_rel->relkind != RELKIND_MATVIEW &&
257 event_relation->
rd_rel->relkind != RELKIND_VIEW &&
258 event_relation->
rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
260 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
261 errmsg(
"relation \"%s\" cannot have rules",
264
267 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
268 errmsg(
"permission denied: \"%s\" is a system catalog",
270
271 /*
272 * Check user has permission to apply rules to this relation.
273 */
277
278 /*
279 * No rule actions that modify OLD or NEW
280 */
282 {
284 if (query->resultRelation == 0)
285 continue;
286 /* Don't be fooled by INSERT/SELECT */
288 continue;
291 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
292 errmsg(
"rule actions on OLD are not implemented"),
293 errhint(
"Use views or triggers instead.")));
296 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
297 errmsg(
"rule actions on NEW are not implemented"),
298 errhint(
"Use triggers instead.")));
299 }
300
302 {
303 /*
304 * Rules ON SELECT are restricted to view definitions
305 *
306 * So this had better be a view, ...
307 */
308 if (event_relation->
rd_rel->relkind != RELKIND_VIEW &&
309 event_relation->
rd_rel->relkind != RELKIND_MATVIEW)
311 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
312 errmsg(
"relation \"%s\" cannot have ON SELECT rules",
315
316 /*
317 * ... there cannot be INSTEAD NOTHING, ...
318 */
321 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
322 errmsg(
"INSTEAD NOTHING rules on SELECT are not implemented"),
323 errhint(
"Use views instead.")));
324
325 /*
326 * ... there cannot be multiple actions, ...
327 */
330 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
331 errmsg(
"multiple actions for rules on SELECT are not implemented")));
332
333 /*
334 * ... the one action must be a SELECT, ...
335 */
337 if (!is_instead ||
340 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
341 errmsg(
"rules on SELECT must have action INSTEAD SELECT")));
342
343 /*
344 * ... it cannot contain data-modifying WITH ...
345 */
346 if (query->hasModifyingCTE)
348 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
349 errmsg(
"rules on SELECT must not contain data-modifying statements in WITH")));
350
351 /*
352 * ... there can be no rule qual, ...
353 */
354 if (event_qual != NULL)
356 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
357 errmsg(
"event qualifications are not implemented for rules on SELECT")));
358
359 /*
360 * ... the targetlist of the SELECT action must exactly match the
361 * event relation, ...
362 */
365 true,
366 event_relation->
rd_rel->relkind !=
367 RELKIND_MATVIEW);
368
369 /*
370 * ... there must not be another ON SELECT rule already ...
371 */
372 if (!replace && event_relation->
rd_rules != NULL)
373 {
375
377 {
379
383 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
384 errmsg(
"\"%s\" is already a view",
386 }
387 }
388
389 /*
390 * ... and finally the rule must be named _RETURN.
391 */
393 {
394 /*
395 * In versions before 7.3, the expected name was _RETviewname. For
396 * backwards compatibility with old pg_dump output, accept that
397 * and silently change it to _RETURN. Since this is just a quick
398 * backwards-compatibility hack, limit the number of characters
399 * checked to a few less than NAMEDATALEN; this saves having to
400 * worry about where a multibyte character might have gotten
401 * truncated.
402 */
403 if (strncmp(rulename, "_RET", 4) != 0 ||
407 (
errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
408 errmsg(
"view rule for \"%s\" must be named \"%s\"",
412 }
413 }
414 else
415 {
416 /*
417 * For non-SELECT rules, a RETURNING list can appear in at most one of
418 * the actions ... and there can't be any RETURNING list at all in a
419 * conditional or non-INSTEAD rule. (Actually, there can be at most
420 * one RETURNING list across all rules on the same event, but it seems
421 * best to enforce that at rule expansion time.) If there is a
422 * RETURNING list, it must match the event relation.
423 */
424 bool haveReturning = false;
425
427 {
429
431 continue;
432 if (haveReturning)
434 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
435 errmsg(
"cannot have multiple RETURNING lists in a rule")));
436 haveReturning = true;
437 if (event_qual != NULL)
439 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
440 errmsg(
"RETURNING lists are not supported in conditional rules")));
441 if (!is_instead)
443 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
444 errmsg(
"RETURNING lists are not supported in non-INSTEAD rules")));
447 false, false);
448 }
449
450 /*
451 * And finally, if it's not an ON SELECT rule then it must *not* be
452 * named _RETURN. This prevents accidentally or maliciously replacing
453 * a view's ON SELECT rule with some other kind of rule.
454 */
457 (
errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
458 errmsg(
"non-view rule for \"%s\" must not be named \"%s\"",
461 }
462
463 /*
464 * This rule is allowed - prepare to install it.
465 */
466
467 /* discard rule if it's null action and not INSTEAD; it's a no-op */
469 {
471 event_type,
472 event_relid,
473 is_instead,
474 event_qual,
476 replace);
477
478 /*
479 * Set pg_class 'relhasrules' field true for event relation.
480 *
481 * Important side effect: an SI notice is broadcast to force all
482 * backends (including me!) to update relcache entries with the new
483 * rule.
484 */
486 }
487
489
490 /* Close rel, but keep lock till commit... */
492
493 return address;
494}
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
bool IsSystemRelation(Relation relation)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
bool allowSystemTableMods
#define AccessExclusiveLock
char * pstrdup(const char *in)
ObjectType get_relkind_objtype(char relkind)
#define ObjectAddressSet(addr, class_id, object_id)
int errdetail_relkind_not_supported(char relkind)
#define lfirst_node(type, lc)
static int list_length(const List *l)
#define linitial_node(type, l)
#define RelationGetDescr(relation)
#define RelationGetRelationName(relation)
static Oid InsertRule(const char *rulname, int evtype, Oid eventrel_oid, bool evinstead, Node *event_qual, List *action, bool replace)
static void checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect, bool requireColumnNameMatch)
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
void SetRelationRuleStatus(Oid relationId, bool relHasRules)
#define ViewSelectRuleName
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)