📄 rewritedefine.c
字号:
ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("SELECT rule's target list has too many entries"))); attr = event_relation->rd_att->attrs[i - 1]; attname = NameStr(attr->attname); /* * Disallow dropped columns in the relation. This won't happen in * the cases we actually care about (namely creating a view via * CREATE TABLE then CREATE RULE). Trying to cope with it is much * more trouble than it's worth, because we'd have to modify the * rule to insert dummy NULLs at the right positions. */ if (attr->attisdropped) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert relation containing dropped columns to view"))); if (strcmp(tle->resname, attname) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname))); if (attr->atttypid != exprType((Node *) tle->expr)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("SELECT rule's target entry %d has different type from column \"%s\"", i, attname))); /* * Allow typmods to be different only if one of them is -1, ie, * "unspecified". This is necessary for cases like "numeric", * where the table will have a filled-in default length but the * select rule's expression will probably have typmod = -1. */ tletypmod = exprTypmod((Node *) tle->expr); if (attr->atttypmod != tletypmod && attr->atttypmod != -1 && tletypmod != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("SELECT rule's target entry %d has different size from column \"%s\"", i, attname))); } if (i != event_relation->rd_att->natts) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("SELECT rule's target list has too few entries"))); /* * ... there must not be another ON SELECT rule already ... */ if (!replace && event_relation->rd_rules != NULL) { for (i = 0; i < event_relation->rd_rules->numLocks; i++) { RewriteRule *rule; rule = event_relation->rd_rules->rules[i]; if (rule->event == CMD_SELECT) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("\"%s\" is already a view", RelationGetRelationName(event_relation)))); } } /* * ... and finally the rule must be named _RETURN. */ if (strcmp(stmt->rulename, ViewSelectRuleName) != 0) { /* * In versions before 7.3, the expected name was _RETviewname. For * backwards compatibility with old pg_dump output, accept that * and silently change it to _RETURN. Since this is just a quick * backwards-compatibility hack, limit the number of characters * checked to a few less than NAMEDATALEN; this saves having to * worry about where a multibyte character might have gotten * truncated. */ if (strncmp(stmt->rulename, "_RET", 4) != 0 || strncmp(stmt->rulename + 4, event_obj->relname, NAMEDATALEN - 4 - 4) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("view rule for \"%s\" must be named \"%s\"", event_obj->relname, ViewSelectRuleName))); stmt->rulename = pstrdup(ViewSelectRuleName); } /* * Are we converting a relation to a view? * * If so, check that the relation is empty because the storage for the * relation is going to be deleted. Also insist that the rel not have * any triggers, indexes, or child tables. */ if (event_relation->rd_rel->relkind != RELKIND_VIEW) { HeapScanDesc scanDesc; scanDesc = heap_beginscan(event_relation, SnapshotNow, 0, NULL); if (heap_getnext(scanDesc, ForwardScanDirection) != NULL) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not convert table \"%s\" to a view because it is not empty", event_obj->relname))); heap_endscan(scanDesc); if (event_relation->rd_rel->reltriggers != 0) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not convert table \"%s\" to a view because it has triggers", event_obj->relname), errhint("In particular, the table may not be involved in any foreign key relationships."))); if (event_relation->rd_rel->relhasindex) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not convert table \"%s\" to a view because it has indexes", event_obj->relname))); if (event_relation->rd_rel->relhassubclass) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not convert table \"%s\" to a view because it has child tables", event_obj->relname))); RelisBecomingView = true; } } /* * This rule is allowed - prepare to install it. */ event_attno = -1; /* * We want the rule's table references to be checked as though by the rule * owner, not the user referencing the rule. Therefore, scan through the * rule's rtables and set the checkAsUser field on all rtable entries. We * have to look at event_qual as well, in case it contains sublinks. */ foreach(l, action) { query = (Query *) lfirst(l); setRuleCheckAsUser_Query(query, GetUserId()); } setRuleCheckAsUser_Expr(event_qual, GetUserId()); /* discard rule if it's null action and not INSTEAD; it's a no-op */ if (action != NIL || is_instead) { ruleId = InsertRule(stmt->rulename, event_type, ev_relid, event_attno, is_instead, event_qual, action, replace); /* * Set pg_class 'relhasrules' field TRUE for event relation. If * appropriate, also modify the 'relkind' field to show that the * relation is now a view. * * Important side effect: an SI notice is broadcast to force all * backends (including me!) to update relcache entries with the new * rule. */ SetRelationRuleStatus(ev_relid, true, RelisBecomingView); } /* * IF the relation is becoming a view, delete the storage files associated * with it. NB: we had better have AccessExclusiveLock to do this ... * * XXX what about getting rid of its TOAST table? For now, we don't. */ if (RelisBecomingView) { RelationOpenSmgr(event_relation); smgrscheduleunlink(event_relation->rd_smgr, event_relation->rd_istemp); } /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock);}/* * setRuleCheckAsUser_Query * Recursively scan a query and set the checkAsUser field to the * given userid in all rtable entries. * * Note: for a view (ON SELECT rule), the checkAsUser field of the *OLD* * RTE entry will be overridden when the view rule is expanded, and the * checkAsUser field of the *NEW* entry is irrelevant because that entry's * requiredPerms bits will always be zero. However, for other types of rules * it's important to set these fields to match the rule owner. So we just set * them always. */static voidsetRuleCheckAsUser_Query(Query *qry, Oid userid){ ListCell *l; /* Set all the RTEs in this query node */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) { /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); } else rte->checkAsUser = userid; } /* If there are sublinks, search for them and process their RTEs */ /* ignore subqueries in rtable because we already processed them */ if (qry->hasSubLinks) query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid, QTW_IGNORE_RT_SUBQUERIES);}/* * Expression-tree walker to find sublink queries */static voidsetRuleCheckAsUser_Expr(Node *node, Oid userid){ (void) setRuleCheckAsUser_walker(node, &userid);}static boolsetRuleCheckAsUser_walker(Node *node, Oid *context){ if (node == NULL) return false; if (IsA(node, Query)) { Query *qry = (Query *) node; setRuleCheckAsUser_Query(qry, *context); return false; } return expression_tree_walker(node, setRuleCheckAsUser_walker, (void *) context);}/* * Rename an existing rewrite rule. * * This is unused code at the moment. */voidRenameRewriteRule(Oid owningRel, const char *oldName, const char *newName){ Relation pg_rewrite_desc; HeapTuple ruletup; pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); ruletup = SearchSysCacheCopy(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(oldName), 0, 0); if (!HeapTupleIsValid(ruletup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", oldName, get_rel_name(owningRel)))); /* should not already exist */ if (IsDefinedRewriteRule(owningRel, newName)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", newName, get_rel_name(owningRel)))); namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName); simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); /* keep system catalog indexes current */ CatalogUpdateIndexes(pg_rewrite_desc, ruletup); heap_freetuple(ruletup); heap_close(pg_rewrite_desc, RowExclusiveLock);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -