/* * InsertRule - * takes the arguments and inserts them as a row into the system * relation "pg_rewrite" */ static Oid InsertRule(char *rulname, int evtype, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead, Node *event_qual, List *action, bool replace) { char *evqual = nodeToString(event_qual); char *actiontree = nodeToString((Node *) action); int i; Datum values[Natts_pg_rewrite]; bool nulls[Natts_pg_rewrite]; bool replaces[Natts_pg_rewrite]; NameData rname; Relation pg_rewrite_desc; HeapTuple tup, oldtup; Oid rewriteObjectId; ObjectAddress myself, referenced; bool is_update = false; /* * Set up *nulls and *values arrays */ MemSet(nulls, false, sizeof(nulls)); i = 0; namestrcpy(&rname, rulname); values[i++] = NameGetDatum(&rname); /* rulename */ values[i++] = ObjectIdGetDatum(eventrel_oid); /* ev_class */ values[i++] = Int16GetDatum(evslot_index); /* ev_attr */ values[i++] = CharGetDatum(evtype + '0'); /* ev_type */ values[i++] = CharGetDatum(RULE_FIRES_ON_ORIGIN); /* ev_enabled */ values[i++] = BoolGetDatum(evinstead); /* is_instead */ values[i++] = CStringGetTextDatum(evqual); /* ev_qual */ values[i++] = CStringGetTextDatum(actiontree); /* ev_action */ /* * Ready to store new pg_rewrite tuple */ pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); /* * Check to see if we are replacing an existing tuple */ oldtup = SearchSysCache2(RULERELNAME, ObjectIdGetDatum(eventrel_oid), PointerGetDatum(rulname)); if (HeapTupleIsValid(oldtup)) { if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", rulname, get_rel_name(eventrel_oid)))); /* * When replacing, we don't need to replace every attribute */ MemSet(replaces, false, sizeof(replaces)); replaces[Anum_pg_rewrite_ev_attr - 1] = true; replaces[Anum_pg_rewrite_ev_type - 1] = true; replaces[Anum_pg_rewrite_is_instead - 1] = true; replaces[Anum_pg_rewrite_ev_qual - 1] = true; replaces[Anum_pg_rewrite_ev_action - 1] = true; tup = heap_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc), values, nulls, replaces); simple_heap_update(pg_rewrite_desc, &tup->t_self, tup); ReleaseSysCache(oldtup); rewriteObjectId = HeapTupleGetOid(tup); is_update = true; } else { tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls); rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup); } /* Need to update indexes in either case */ CatalogUpdateIndexes(pg_rewrite_desc, tup); heap_freetuple(tup); /* If replacing, get rid of old dependencies and make new ones */ if (is_update) deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false); /* * Install dependency on rule's relation to ensure it will go away on * relation deletion. If the rule is ON SELECT, make the dependency * implicit --- this prevents deleting a view's SELECT rule. Other kinds * of rules can be AUTO. */ myself.classId = RewriteRelationId; myself.objectId = rewriteObjectId; myself.objectSubId = 0; referenced.classId = RelationRelationId; referenced.objectId = eventrel_oid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); /* * Also install dependencies on objects referenced in action and qual. */ recordDependencyOnExpr(&myself, (Node *) action, NIL, DEPENDENCY_NORMAL); if (event_qual != NULL) { /* Find query containing OLD/NEW rtable entries */ Query *qry = (Query *) linitial(action); qry = getInsertSelectQuery(qry, NULL); recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL); } /* Post creation hook for new rule */ InvokeObjectAccessHook(OAT_POST_CREATE, RewriteRelationId, rewriteObjectId, 0); heap_close(pg_rewrite_desc, RowExclusiveLock); return rewriteObjectId; }
/* * GenerateTypeDependencies: build the dependencies needed for a type * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. We don't remove an existing extension dependency, though. * (That means an extension can't absorb a shell type created in another * extension, nor ALTER a type created by another extension. Also, if it * replaces a free-standing shell type or ALTERs a free-standing type, * that type will become a member of the extension.) */ void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, Oid relationOid, /* only for relation rowtypes */ char relationKind, /* ditto */ Oid owner, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid typmodinProcedure, Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, bool isImplicitArray, Oid baseType, Oid typeCollation, Node *defaultExpr, bool rebuild) { ObjectAddress myself, referenced; /* If rebuild, first flush old dependencies, except extension deps */ if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } myself.classId = TypeRelationId; myself.objectId = typeObjectId; myself.objectSubId = 0; /* * Make dependencies on namespace, owner, extension. * * For a relation rowtype (that's not a composite type), we should skip * these because we'll depend on them indirectly through the pg_class * entry. Likewise, skip for implicit arrays since we'll depend on them * through the element type. */ if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && !isImplicitArray) { referenced.classId = NamespaceRelationId; referenced.objectId = typeNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ if (OidIsValid(inputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = inputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(outputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = outputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(receiveProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = receiveProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(sendProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = sendProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typmodinProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = typmodinProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typmodoutProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = typmodoutProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(analyzeProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = analyzeProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type * relation. For the latter case, we have to reverse the dependency. * * In the former case, this allows the type to be auto-dropped when the * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ if (OidIsValid(relationOid)) { referenced.classId = RelationRelationId; referenced.objectId = relationOid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); else recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } /* * If the type is an implicitly-created array type, mark it as internally * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ if (OidIsValid(elementType)) { referenced.classId = TypeRelationId; referenced.objectId = elementType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ if (OidIsValid(baseType)) { referenced.classId = TypeRelationId; referenced.objectId = baseType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its collation. */ /* We know the default collation is pinned, so don't bother recording it */ if (OidIsValid(typeCollation) && typeCollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = typeCollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); }
/* * GenerateTypeDependencies: build the dependencies needed for a type * * Most of what this function needs to know about the type is passed as the * new pg_type row, typeForm. But we can't get at the varlena fields through * that, so defaultExpr and typacl are passed separately. (typacl is really * "Acl *", but we declare it "void *" to avoid including acl.h in pg_type.h.) * * relationKind and isImplicitArray aren't visible in the pg_type row either, * so they're also passed separately. * * isDependentType is true if this is an implicit array or relation rowtype; * that means it doesn't need its own dependencies on owner etc. * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. We don't remove an existing extension dependency, though. * (That means an extension can't absorb a shell type created in another * extension, nor ALTER a type created by another extension. Also, if it * replaces a free-standing shell type or ALTERs a free-standing type, * that type will become a member of the extension.) */ void GenerateTypeDependencies(Oid typeObjectId, Form_pg_type typeForm, Node *defaultExpr, void *typacl, char relationKind, /* only for relation rowtypes */ bool isImplicitArray, bool isDependentType, bool rebuild) { ObjectAddress myself, referenced; /* If rebuild, first flush old dependencies, except extension deps */ if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } myself.classId = TypeRelationId; myself.objectId = typeObjectId; myself.objectSubId = 0; /* * Make dependencies on namespace, owner, ACL, extension. * * Skip these for a dependent type, since it will have such dependencies * indirectly through its depended-on type or relation. */ if (!isDependentType) { referenced.classId = NamespaceRelationId; referenced.objectId = typeForm->typnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, typeForm->typowner); recordDependencyOnNewAcl(TypeRelationId, typeObjectId, 0, typeForm->typowner, typacl); recordDependencyOnCurrentExtension(&myself, rebuild); } /* Normal dependencies on the I/O functions */ if (OidIsValid(typeForm->typinput)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typinput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typoutput)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typoutput; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typreceive)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typreceive; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typsend)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typsend; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typmodin)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typmodin; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typmodout)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typmodout; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(typeForm->typanalyze)) { referenced.classId = ProcedureRelationId; referenced.objectId = typeForm->typanalyze; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type * relation. For the latter case, we have to reverse the dependency. * * In the former case, this allows the type to be auto-dropped when the * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ if (OidIsValid(typeForm->typrelid)) { referenced.classId = RelationRelationId; referenced.objectId = typeForm->typrelid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); else recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } /* * If the type is an implicitly-created array type, mark it as internally * dependent on the element type. Otherwise, if it has an element type, * the dependency is a normal one. */ if (OidIsValid(typeForm->typelem)) { referenced.classId = TypeRelationId; referenced.objectId = typeForm->typelem; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ if (OidIsValid(typeForm->typbasetype)) { referenced.classId = TypeRelationId; referenced.objectId = typeForm->typbasetype; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its collation. */ /* We know the default collation is pinned, so don't bother recording it */ if (OidIsValid(typeForm->typcollation) && typeForm->typcollation != DEFAULT_COLLATION_OID) { referenced.classId = CollationRelationId; referenced.objectId = typeForm->typcollation; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); }
/* * InsertRule - * takes the arguments and inserts them as a row into the system * relation "pg_rewrite" */ static Oid InsertRule(char *rulname, int evtype, Oid eventrel_oid, AttrNumber evslot_index, bool evinstead, Node *event_qual, List *action, bool replace, Oid ruleOid) { char *evqual = nodeToString(event_qual); char *actiontree = nodeToString((Node *) action); int i; Datum values[Natts_pg_rewrite]; bool nulls[Natts_pg_rewrite]; bool replaces[Natts_pg_rewrite]; NameData rname; HeapTuple tup, oldtup; Oid rewriteObjectId; ObjectAddress myself, referenced; bool is_update = false; cqContext *pcqCtx; /* * Set up *nulls and *values arrays */ MemSet(nulls, false, sizeof(nulls)); i = 0; namestrcpy(&rname, rulname); values[i++] = NameGetDatum(&rname); /* rulename */ values[i++] = ObjectIdGetDatum(eventrel_oid); /* ev_class */ values[i++] = Int16GetDatum(evslot_index); /* ev_attr */ values[i++] = CharGetDatum(evtype + '0'); /* ev_type */ values[i++] = BoolGetDatum(evinstead); /* is_instead */ values[i++] = CStringGetTextDatum(evqual); /* ev_qual */ values[i++] = CStringGetTextDatum(actiontree); /* ev_action */ /* * Ready to store new pg_rewrite tuple */ /* * Check to see if we are replacing an existing tuple */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE ev_class = :1 " " AND rulename = :2 " " FOR UPDATE ", ObjectIdGetDatum(eventrel_oid), CStringGetDatum(rulname))); oldtup = caql_getnext(pcqCtx); if (HeapTupleIsValid(oldtup)) { if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", rulname, get_rel_name(eventrel_oid)))); /* * When replacing, we don't need to replace every attribute */ MemSet(replaces, false, sizeof(replaces)); replaces[Anum_pg_rewrite_ev_attr - 1] = true; replaces[Anum_pg_rewrite_ev_type - 1] = true; replaces[Anum_pg_rewrite_is_instead - 1] = true; replaces[Anum_pg_rewrite_ev_qual - 1] = true; replaces[Anum_pg_rewrite_ev_action - 1] = true; tup = caql_modify_current(pcqCtx, values, nulls, replaces); caql_update_current(pcqCtx, tup); /* and Update indexes (implicit) */ rewriteObjectId = HeapTupleGetOid(tup); is_update = true; } else { tup = caql_form_tuple(pcqCtx, values, nulls); if (OidIsValid(ruleOid)) HeapTupleSetOid(tup, ruleOid); rewriteObjectId = caql_insert(pcqCtx, tup); /* and Update indexes (implicit) */ } heap_freetuple(tup); /* If replacing, get rid of old dependencies and make new ones */ if (is_update) deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId); /* * Install dependency on rule's relation to ensure it will go away on * relation deletion. If the rule is ON SELECT, make the dependency * implicit --- this prevents deleting a view's SELECT rule. Other kinds * of rules can be AUTO. */ myself.classId = RewriteRelationId; myself.objectId = rewriteObjectId; myself.objectSubId = 0; referenced.classId = RelationRelationId; referenced.objectId = eventrel_oid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO); /* * Also install dependencies on objects referenced in action and qual. */ recordDependencyOnExpr(&myself, (Node *) action, NIL, DEPENDENCY_NORMAL); if (event_qual != NULL) { /* Find query containing OLD/NEW rtable entries */ Query *qry = (Query *) linitial(action); qry = getInsertSelectQuery(qry, NULL); recordDependencyOnExpr(&myself, event_qual, qry->rtable, DEPENDENCY_NORMAL); } caql_endscan(pcqCtx); return rewriteObjectId; }
/* * GenerateTypeDependencies: build the dependencies needed for a type * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. */ void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, Oid relationOid, /* only for 'c'atalog types */ char relationKind, /* ditto */ Oid owner, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, Oid analyzeProcedure, Oid elementType, Oid baseType, Node *defaultExpr, bool rebuild) { ObjectAddress myself, referenced; if (rebuild) { deleteDependencyRecordsFor(TypeRelationId, typeObjectId); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId); } myself.classId = TypeRelationId; myself.objectId = typeObjectId; myself.objectSubId = 0; /* dependency on namespace */ /* skip for relation rowtype, since we have indirect dependency */ if (!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) { referenced.classId = NamespaceRelationId; referenced.objectId = typeNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); } /* Normal dependencies on the I/O functions */ if (OidIsValid(inputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = inputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(outputProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = outputProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(receiveProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = receiveProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(sendProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = sendProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } if (OidIsValid(analyzeProcedure)) { referenced.classId = ProcedureRelationId; referenced.objectId = analyzeProcedure; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* * If the type is a rowtype for a relation, mark it as internally * dependent on the relation, *unless* it is a stand-alone composite type * relation. For the latter case, we have to reverse the dependency. * * In the former case, this allows the type to be auto-dropped when the * relation is, and not otherwise. And in the latter, of course we get the * opposite effect. */ if (OidIsValid(relationOid)) { referenced.classId = RelationRelationId; referenced.objectId = relationOid; referenced.objectSubId = 0; if (relationKind != RELKIND_COMPOSITE_TYPE) recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); else recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } /* * If the type is an array type, mark it auto-dependent on the base type. * (This is a compromise between the typical case where the array type is * automatically generated and the case where it is manually created: we'd * prefer INTERNAL for the former case and NORMAL for the latter.) */ if (OidIsValid(elementType)) { referenced.classId = TypeRelationId; referenced.objectId = elementType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } /* Normal dependency from a domain to its base type. */ if (OidIsValid(baseType)) { referenced.classId = TypeRelationId; referenced.objectId = baseType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); }