/
githubmirror
/
postgres
Обзор
Документация
Войти
/
githubmirror
/
postgres
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/backend/commands/propgraphcmds.c
1 958 строк
60 KB
Nathan Bossart
Check for USAGE privilege on types used by stored expressions.
10 авг 2026, 16:37
10 авг 2026, 16:37
4fac4c1
Код
Авторство
О чём код?
/*------------------------------------------------------------------------- * * propgraphcmds.c * property graph manipulation * * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/backend/commands/propgraphcmds.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/genam.h" #include "access/htup_details.h" #include "access/nbtree.h" #include "access/table.h" #include "access/xact.h" #include "catalog/catalog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_class.h" #include "catalog/pg_collation_d.h" #include "catalog/pg_operator_d.h" #include "catalog/pg_propgraph_element.h" #include "catalog/pg_propgraph_element_label.h" #include "catalog/pg_propgraph_label.h" #include "catalog/pg_propgraph_label_property.h" #include "catalog/pg_propgraph_property.h" #include "commands/defrem.h" #include "commands/propgraphcmds.h" #include "commands/tablecmds.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/ruleutils.h" #include "utils/syscache.h" struct element_info { Oid elementid; char kind; Oid relid; char *aliasname; ArrayType *key; char *srcvertex; Oid srcvertexid; Oid srcrelid; ArrayType *srckey; ArrayType *srcref; ArrayType *srceqop; char *destvertex; Oid destvertexid; Oid destrelid; ArrayType *destkey; ArrayType *destref; ArrayType *desteqop; List *labels; }; static ArrayType *propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location); static void propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols, Relation edge_rel, Relation ref_rel, const char *aliasname, int location, const char *type, ArrayType **outkey, ArrayType **outref, ArrayType **outeqop); static AttrNumber *array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel); static ArrayType *array_from_attnums(int numattrs, const AttrNumber *attnums); static Oid insert_element_record(ObjectAddress pgaddress, struct element_info *einfo); static Oid insert_label_record(Oid graphid, Oid peoid, const char *label); static void insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties); static void insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr); static void check_element_properties(Oid peoid); static void check_element_label_properties(Oid ellabeloid); static void check_all_labels_properties(Oid pgrelid); static Oid get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location); static Oid get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location); static Oid get_element_relid(Oid peid); static List *get_graph_label_ids(Oid graphid); static List *get_label_element_label_ids(Oid labelid); static List *get_element_label_property_names(Oid ellabeloid); static List *get_graph_property_ids(Oid graphid); /* * CREATE PROPERTY GRAPH */ ObjectAddress CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt) { CreateStmt *cstmt = makeNode(CreateStmt); char components_persistence; ListCell *lc; ObjectAddress pgaddress; List *vertex_infos = NIL; List *edge_infos = NIL; List *element_aliases = NIL; List *element_oids = NIL; if (stmt->pgname->relpersistence == RELPERSISTENCE_UNLOGGED) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("property graphs cannot be unlogged because they do not have storage"))); components_persistence = RELPERSISTENCE_PERMANENT; foreach(lc, stmt->vertex_tables) { PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc); struct element_info *vinfo; Relation rel; vinfo = palloc0_object(struct element_info); vinfo->kind = PGEKIND_VERTEX; vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); rel = table_open(vinfo->relid, NoLock); if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) components_persistence = RELPERSISTENCE_TEMP; if (vertex->vtable->alias) vinfo->aliasname = vertex->vtable->alias->aliasname; else vinfo->aliasname = vertex->vtable->relname; if (list_member(element_aliases, makeString(vinfo->aliasname))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("alias \"%s\" used more than once as element table", vinfo->aliasname), parser_errposition(pstate, vertex->location))); vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location); vinfo->labels = vertex->labels; table_close(rel, NoLock); vertex_infos = lappend(vertex_infos, vinfo); element_aliases = lappend(element_aliases, makeString(vinfo->aliasname)); } foreach(lc, stmt->edge_tables) { PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc); struct element_info *einfo; Relation rel; ListCell *lc2; Oid srcrelid; Oid destrelid; Relation srcrel; Relation destrel; einfo = palloc0_object(struct element_info); einfo->kind = PGEKIND_EDGE; einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); rel = table_open(einfo->relid, NoLock); if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) components_persistence = RELPERSISTENCE_TEMP; if (edge->etable->alias) einfo->aliasname = edge->etable->alias->aliasname; else einfo->aliasname = edge->etable->relname; if (list_member(element_aliases, makeString(einfo->aliasname))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("alias \"%s\" used more than once as element table", einfo->aliasname), parser_errposition(pstate, edge->location))); einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location); einfo->srcvertex = edge->esrcvertex; einfo->destvertex = edge->edestvertex; srcrelid = 0; destrelid = 0; foreach(lc2, vertex_infos) { struct element_info *vinfo = lfirst(lc2); if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0) srcrelid = vinfo->relid; if (strcmp(vinfo->aliasname, edge->edestvertex) == 0) destrelid = vinfo->relid; if (srcrelid && destrelid) break; } if (!srcrelid) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("source vertex \"%s\" of edge \"%s\" does not exist", edge->esrcvertex, einfo->aliasname), parser_errposition(pstate, edge->location))); if (!destrelid) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("destination vertex \"%s\" of edge \"%s\" does not exist", edge->edestvertex, einfo->aliasname), parser_errposition(pstate, edge->location))); srcrel = table_open(srcrelid, NoLock); destrel = table_open(destrelid, NoLock); propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel, einfo->aliasname, edge->location, "SOURCE", &einfo->srckey, &einfo->srcref, &einfo->srceqop); propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel, einfo->aliasname, edge->location, "DESTINATION", &einfo->destkey, &einfo->destref, &einfo->desteqop); einfo->labels = edge->labels; table_close(destrel, NoLock); table_close(srcrel, NoLock); table_close(rel, NoLock); edge_infos = lappend(edge_infos, einfo); element_aliases = lappend(element_aliases, makeString(einfo->aliasname)); } cstmt->relation = stmt->pgname; cstmt->oncommit = ONCOMMIT_NOOP; /* * Automatically make it temporary if any component tables are temporary * (see also DefineView()). */ if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT && components_persistence == RELPERSISTENCE_TEMP) { cstmt->relation = copyObject(cstmt->relation); cstmt->relation->relpersistence = RELPERSISTENCE_TEMP; ereport(NOTICE, (errmsg("property graph \"%s\" will be temporary", stmt->pgname->relname))); } pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL); foreach(lc, vertex_infos) { struct element_info *vinfo = lfirst(lc); Oid peoid; peoid = insert_element_record(pgaddress, vinfo); element_oids = lappend_oid(element_oids, peoid); } foreach(lc, edge_infos) { struct element_info *einfo = lfirst(lc); Oid peoid; ListCell *lc2; /* * Look up the vertices again. Now the vertices have OIDs assigned, * which we need. */ foreach(lc2, vertex_infos) { struct element_info *vinfo = lfirst(lc2); if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0) { einfo->srcvertexid = vinfo->elementid; einfo->srcrelid = vinfo->relid; } if (strcmp(vinfo->aliasname, einfo->destvertex) == 0) { einfo->destvertexid = vinfo->elementid; einfo->destrelid = vinfo->relid; } if (einfo->srcvertexid && einfo->destvertexid) break; } Assert(einfo->srcvertexid); Assert(einfo->destvertexid); Assert(einfo->srcrelid); Assert(einfo->destrelid); peoid = insert_element_record(pgaddress, einfo); element_oids = lappend_oid(element_oids, peoid); } CommandCounterIncrement(); foreach_oid(peoid, element_oids) check_element_properties(peoid); check_all_labels_properties(pgaddress.objectId); return pgaddress; } /* * Process the key clause specified for an element. If key_clause is non-NIL, * then it is a list of column names. Otherwise, the primary key of the * relation is used. The return value is an array of column numbers. */ static ArrayType * propgraph_element_get_key(ParseState *pstate, const List *key_clause, Relation element_rel, const char *aliasname, int location) { ArrayType *a; if (key_clause == NIL) { Oid pkidx = RelationGetPrimaryKeyIndex(element_rel, false); if (!pkidx) ereport(ERROR, errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("no key specified and no suitable primary key exists for definition of element \"%s\"", aliasname), parser_errposition(pstate, location)); else { Relation indexDesc; indexDesc = index_open(pkidx, AccessShareLock); a = array_from_attnums(indexDesc->rd_index->indkey.dim1, indexDesc->rd_index->indkey.values); index_close(indexDesc, NoLock); } } else { a = array_from_attnums(list_length(key_clause), array_from_column_list(pstate, key_clause, location, element_rel)); } return a; } /* * Process the source or destination link of an edge. * * keycols and refcols are column names representing the local and referenced * (vertex) columns. If they are both NIL, a matching foreign key is looked * up. * * edge_rel and ref_rel are the local and referenced element tables. * * aliasname, location, and type are for error messages. type is either * "SOURCE" or "DESTINATION". * * The outputs are arrays of column numbers in outkey and outref. */ static void propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, const List *refcols, Relation edge_rel, Relation ref_rel, const char *aliasname, int location, const char *type, ArrayType **outkey, ArrayType **outref, ArrayType **outeqop) { int nkeys; AttrNumber *keyattnums; AttrNumber *refattnums; Oid *keyeqops; Datum *datums; Assert((keycols && refcols) || (!keycols && !refcols)); if (keycols) { if (list_length(keycols) != list_length(refcols)) ereport(ERROR, errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("mismatching number of columns in %s vertex definition of edge \"%s\"", type, aliasname), parser_errposition(pstate, location)); nkeys = list_length(keycols); keyattnums = array_from_column_list(pstate, keycols, location, edge_rel); refattnums = array_from_column_list(pstate, refcols, location, ref_rel); keyeqops = palloc_array(Oid, nkeys); for (int i = 0; i < nkeys; i++) { Oid keytype; int32 keytypmod; Oid keycoll; Oid reftype; int32 reftypmod; Oid refcoll; Oid opc; Oid opf; StrategyNumber strategy; /* * Lookup equality operator to be used for edge and vertex key. * Vertex key is equivalent to primary key and edge key is similar * to foreign key since edge key references vertex key. Hence * vertex key is used as left operand and edge key is used as * right operand. The method used to find the equality operators * is similar to the method used to find equality operators for * FK/PK comparison in ATAddForeignKeyConstraint() except that * opclass of the vertex key type is used as a starting point. * Since we need only equality operators we use both BT and HASH * strategies. * * If the required operators do not exist, we can not construct * quals linking an edge to its adjacent vertices. */ get_atttypetypmodcoll(RelationGetRelid(edge_rel), keyattnums[i], &keytype, &keytypmod, &keycoll); get_atttypetypmodcoll(RelationGetRelid(ref_rel), refattnums[i], &reftype, &reftypmod, &refcoll); keyeqops[i] = InvalidOid; strategy = BTEqualStrategyNumber; opc = GetDefaultOpClass(reftype, BTREE_AM_OID); if (!OidIsValid(opc)) { opc = GetDefaultOpClass(reftype, HASH_AM_OID); strategy = HTEqualStrategyNumber; } if (OidIsValid(opc)) { opf = get_opclass_family(opc); if (OidIsValid(opf)) { keyeqops[i] = get_opfamily_member(opf, reftype, keytype, strategy); if (!OidIsValid(keyeqops[i])) { /* Last resort, implicit cast. */ if (can_coerce_type(1, &keytype, &reftype, COERCION_IMPLICIT)) keyeqops[i] = get_opfamily_member(opf, reftype, reftype, strategy); } } } if (!OidIsValid(keyeqops[i])) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("no equality operator exists for %s key comparison of edge \"%s\"", type, aliasname), parser_errposition(pstate, location)); /* * If collations of key attribute and referenced attribute are * different, an edge may end up being adjacent to undesired * vertices. Prohibit such a case. * * PK/FK allows different collations as long as they are * deterministic for backward compatibility. But we can be a bit * stricter here and follow SQL standard. */ if (keycoll != refcoll && keycoll != DEFAULT_COLLATION_OID && refcoll != DEFAULT_COLLATION_OID && OidIsValid(keycoll) && OidIsValid(refcoll)) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("collation mismatch in %s key of edge \"%s\": %s vs. %s", type, aliasname, get_collation_name(keycoll), get_collation_name(refcoll)), parser_errposition(pstate, location)); } } else { ForeignKeyCacheInfo *fk = NULL; foreach_node(ForeignKeyCacheInfo, tmp, RelationGetFKeyList(edge_rel)) { if (tmp->confrelid == RelationGetRelid(ref_rel)) { if (fk) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("more than one suitable foreign key exists for %s key of edge \"%s\"", type, aliasname), parser_errposition(pstate, location)); fk = tmp; } } if (!fk) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("no %s key specified and no suitable foreign key exists for definition of edge \"%s\"", type, aliasname), parser_errposition(pstate, location)); nkeys = fk->nkeys; keyattnums = fk->conkey; refattnums = fk->confkey; keyeqops = fk->conpfeqop; } *outkey = array_from_attnums(nkeys, keyattnums); *outref = array_from_attnums(nkeys, refattnums); datums = palloc_array(Datum, nkeys); for (int i = 0; i < nkeys; i++) datums[i] = ObjectIdGetDatum(keyeqops[i]); *outeqop = construct_array_builtin(datums, nkeys, OIDOID); } /* * Convert list of column names in the specified relation into an array of * column numbers. */ static AttrNumber * array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel) { int numattrs; AttrNumber *attnums; int i; ListCell *lc; numattrs = list_length(colnames); attnums = palloc_array(AttrNumber, numattrs); i = 0; foreach(lc, colnames) { char *colname = strVal(lfirst(lc)); Oid relid = RelationGetRelid(element_rel); AttrNumber attnum; attnum = get_attnum(relid, colname); if (!attnum) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", colname, get_rel_name(relid)), parser_errposition(pstate, location))); attnums[i++] = attnum; } for (int j = 0; j < numattrs; j++) { for (int k = j + 1; k < numattrs; k++) { if (attnums[j] == attnums[k]) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("graph key columns list must not contain duplicates"), parser_errposition(pstate, location))); } } return attnums; } static ArrayType * array_from_attnums(int numattrs, const AttrNumber *attnums) { Datum *attnumsd; attnumsd = palloc_array(Datum, numattrs); for (int i = 0; i < numattrs; i++) attnumsd[i] = Int16GetDatum(attnums[i]); return construct_array_builtin(attnumsd, numattrs, INT2OID); } static void array_of_attnums_to_objectaddrs(Oid relid, ArrayType *arr, ObjectAddresses *addrs) { Datum *attnumsd; int numattrs; deconstruct_array_builtin(arr, INT2OID, &attnumsd, NULL, &numattrs); for (int i = 0; i < numattrs; i++) { ObjectAddress referenced; ObjectAddressSubSet(referenced, RelationRelationId, relid, DatumGetInt16(attnumsd[i])); add_exact_object_address(&referenced, addrs); } } static void array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs) { Datum *opersd; int numopers; deconstruct_array_builtin(arr, OIDOID, &opersd, NULL, &numopers); for (int i = 0; i < numopers; i++) { ObjectAddress referenced; ObjectAddressSet(referenced, OperatorRelationId, DatumGetObjectId(opersd[i])); add_exact_object_address(&referenced, addrs); } } /* * Insert a record for an element into the pg_propgraph_element catalog. Also * inserts labels and properties into their respective catalogs. */ static Oid insert_element_record(ObjectAddress pgaddress, struct element_info *einfo) { Oid graphid = pgaddress.objectId; Relation rel; NameData aliasname; Oid peoid; Datum values[Natts_pg_propgraph_element] = {0}; bool nulls[Natts_pg_propgraph_element] = {0}; HeapTuple tup; ObjectAddress myself; ObjectAddress referenced; ObjectAddresses *addrs; rel = table_open(PropgraphElementRelationId, RowExclusiveLock); peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid); einfo->elementid = peoid; values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid); values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid); values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid); namestrcpy(&aliasname, einfo->aliasname); values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname); values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind); values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid); values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid); values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key); if (einfo->srckey) values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey); else nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true; if (einfo->srcref) values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref); else nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true; if (einfo->srceqop) values[Anum_pg_propgraph_element_pgesrceqop - 1] = PointerGetDatum(einfo->srceqop); else nulls[Anum_pg_propgraph_element_pgesrceqop - 1] = true; if (einfo->destkey) values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey); else nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true; if (einfo->destref) values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref); else nulls[Anum_pg_propgraph_element_pgedestref - 1] = true; if (einfo->desteqop) values[Anum_pg_propgraph_element_pgedesteqop - 1] = PointerGetDatum(einfo->desteqop); else nulls[Anum_pg_propgraph_element_pgedesteqop - 1] = true; tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); CatalogTupleInsert(rel, tup); heap_freetuple(tup); ObjectAddressSet(myself, PropgraphElementRelationId, peoid); /* Add dependency on the property graph */ recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO); addrs = new_object_addresses(); /* Add dependency on the relation */ ObjectAddressSet(referenced, RelationRelationId, einfo->relid); add_exact_object_address(&referenced, addrs); array_of_attnums_to_objectaddrs(einfo->relid, einfo->key, addrs); /* * Add dependencies on vertices and equality operators used for key * comparison. */ if (einfo->srcvertexid) { ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid); add_exact_object_address(&referenced, addrs); array_of_attnums_to_objectaddrs(einfo->relid, einfo->srckey, addrs); array_of_attnums_to_objectaddrs(einfo->srcrelid, einfo->srcref, addrs); array_of_opers_to_objectaddrs(einfo->srceqop, addrs); } if (einfo->destvertexid) { ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid); add_exact_object_address(&referenced, addrs); array_of_attnums_to_objectaddrs(einfo->relid, einfo->destkey, addrs); array_of_attnums_to_objectaddrs(einfo->destrelid, einfo->destref, addrs); array_of_opers_to_objectaddrs(einfo->desteqop, addrs); } record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); table_close(rel, NoLock); if (einfo->labels) { ListCell *lc; foreach(lc, einfo->labels) { PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc); Oid ellabeloid; if (lp->label) ellabeloid = insert_label_record(graphid, peoid, lp->label); else ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname); insert_property_records(graphid, ellabeloid, einfo->relid, lp->properties); CommandCounterIncrement(); } } else { Oid ellabeloid; PropGraphProperties *pr = makeNode(PropGraphProperties); pr->all = true; pr->location = -1; ellabeloid = insert_label_record(graphid, peoid, einfo->aliasname); insert_property_records(graphid, ellabeloid, einfo->relid, pr); } return peoid; } /* * Insert records for a label into the pg_propgraph_label and * pg_propgraph_element_label catalogs, and register dependencies. * * Returns the OID of the new pg_propgraph_element_label record. */ static Oid insert_label_record(Oid graphid, Oid peoid, const char *label) { Oid labeloid; Oid ellabeloid; /* * Insert into pg_propgraph_label if not already existing. */ labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(graphid), CStringGetDatum(label)); if (!labeloid) { Relation rel; Datum values[Natts_pg_propgraph_label] = {0}; bool nulls[Natts_pg_propgraph_label] = {0}; NameData labelname; HeapTuple tup; ObjectAddress myself; ObjectAddress referenced; rel = table_open(PropgraphLabelRelationId, RowExclusiveLock); labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid); values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid); values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid); namestrcpy(&labelname, label); values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname); tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); CatalogTupleInsert(rel, tup); heap_freetuple(tup); ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid); ObjectAddressSet(referenced, RelationRelationId, graphid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); table_close(rel, NoLock); } /* * Insert into pg_propgraph_element_label */ if (SearchSysCacheExists2(PROPGRAPHELEMENTLABELELEMENTLABEL, ObjectIdGetDatum(peoid), ObjectIdGetDatum(labeloid))) ereport(ERROR, errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("label \"%s\" already exists", label)); else { Relation rel; Datum values[Natts_pg_propgraph_element_label] = {0}; bool nulls[Natts_pg_propgraph_element_label] = {0}; HeapTuple tup; ObjectAddress myself; ObjectAddress referenced; rel = table_open(PropgraphElementLabelRelationId, RowExclusiveLock); ellabeloid = GetNewOidWithIndex(rel, PropgraphElementLabelObjectIndexId, Anum_pg_propgraph_element_label_oid); values[Anum_pg_propgraph_element_label_oid - 1] = ObjectIdGetDatum(ellabeloid); values[Anum_pg_propgraph_element_label_pgellabelid - 1] = ObjectIdGetDatum(labeloid); values[Anum_pg_propgraph_element_label_pgelelid - 1] = ObjectIdGetDatum(peoid); tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); CatalogTupleInsert(rel, tup); heap_freetuple(tup); ObjectAddressSet(myself, PropgraphElementLabelRelationId, ellabeloid); ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); ObjectAddressSet(referenced, PropgraphElementRelationId, peoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); table_close(rel, NoLock); } return ellabeloid; } /* * Insert records for properties into the pg_propgraph_property catalog. */ static void insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties) { List *proplist = NIL; ParseState *pstate; ParseNamespaceItem *nsitem; List *tp; Relation rel; ListCell *lc; if (properties->all) { Relation attRelation; SysScanDesc scan; ScanKeyData key[1]; HeapTuple attributeTuple; attRelation = table_open(AttributeRelationId, RowShareLock); ScanKeyInit(&key[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(pgerelid)); scan = systable_beginscan(attRelation, AttributeRelidNumIndexId, true, NULL, 1, key); while (HeapTupleIsValid(attributeTuple = systable_getnext(scan))) { Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple); ColumnRef *cr; ResTarget *rt; if (att->attnum <= 0 || att->attisdropped) continue; cr = makeNode(ColumnRef); rt = makeNode(ResTarget); cr->fields = list_make1(makeString(pstrdup(NameStr(att->attname)))); cr->location = -1; rt->name = pstrdup(NameStr(att->attname)); rt->val = (Node *) cr; rt->location = -1; proplist = lappend(proplist, rt); } systable_endscan(scan); table_close(attRelation, RowShareLock); } else { proplist = properties->properties; foreach(lc, proplist) { ResTarget *rt = lfirst_node(ResTarget, lc); if (!rt->name && !IsA(rt->val, ColumnRef)) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("property name required"), parser_errposition(NULL, rt->location)); } } rel = table_open(pgerelid, AccessShareLock); pstate = make_parsestate(NULL); nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock, NULL, false, true); addNSItemToQuery(pstate, nsitem, true, true, true); table_close(rel, NoLock); tp = transformTargetList(pstate, proplist, EXPR_KIND_PROPGRAPH_PROPERTY); if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, tp); assign_expr_collations(pstate, (Node *) tp); /* * When properties are derived from the table's attributes, names are * already unique. Reject duplicate property names within an explicit * PROPERTIES clause. Do this after transformTargetList() so that any * names derived by transformTargetList() are considered. */ if (!properties->all) { List *seen = NIL; foreach_node(TargetEntry, te, tp) { String *name = makeString(te->resname); if (list_member(seen, name)) ereport(ERROR, errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("property \"%s\" specified more than once", te->resname)); seen = lappend(seen, name); } } foreach_node(TargetEntry, te, tp) insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr); } /* * Insert records for a property into the pg_propgraph_property and * pg_propgraph_label_property catalogs, and register dependencies. */ static void insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr) { Oid propoid; Oid exprtypid = exprType((const Node *) expr); int32 exprtypmod = exprTypmod((const Node *) expr); Oid exprcollation = exprCollation((const Node *) expr); /* * Insert into pg_propgraph_property if not already existing. */ propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(graphid), CStringGetDatum(propname)); if (!OidIsValid(propoid)) { Relation rel; NameData propnamedata; Datum values[Natts_pg_propgraph_property] = {0}; bool nulls[Natts_pg_propgraph_property] = {0}; HeapTuple tup; ObjectAddress myself; ObjectAddress referenced; AclResult aclresult; rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock); propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid); values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid); values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid); namestrcpy(&propnamedata, propname); values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata); values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprtypid); values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(exprtypmod); values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(exprcollation); tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); CatalogTupleInsert(rel, tup); heap_freetuple(tup); ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid); ObjectAddressSet(referenced, RelationRelationId, graphid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); aclresult = object_aclcheck(TypeRelationId, exprtypid, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, exprtypid); ObjectAddressSet(referenced, TypeRelationId, exprtypid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); if (OidIsValid(exprcollation) && exprcollation != DEFAULT_COLLATION_OID) { ObjectAddressSet(referenced, CollationRelationId, exprcollation); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } table_close(rel, NoLock); } else { HeapTuple pgptup = SearchSysCache1(PROPGRAPHPROPOID, ObjectIdGetDatum(propoid)); Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup); Oid proptypid = pgpform->pgptypid; int32 proptypmod = pgpform->pgptypmod; Oid propcollation = pgpform->pgpcollation; ReleaseSysCache(pgptup); /* * Check that in the graph, all properties with the same name have the * same type (independent of which label they are on). (See SQL/PGQ * subclause "Consistency check of a tabular property graph * descriptor".) */ if (proptypid != exprtypid || proptypmod != exprtypmod) { ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("property \"%s\" data type mismatch: %s vs. %s", propname, format_type_with_typemod(proptypid, proptypmod), format_type_with_typemod(exprtypid, exprtypmod)), errdetail("In a property graph, a property of the same name has to have the same data type in each label.")); } /* Similarly for collation */ if (propcollation != exprcollation) { ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("property \"%s\" collation mismatch: %s vs. %s", propname, get_collation_name(propcollation), get_collation_name(exprcollation)), errdetail("In a property graph, a property of the same name has to have the same collation in each label.")); } } /* * Insert into pg_propgraph_label_property */ if (SearchSysCacheExists2(PROPGRAPHLABELPROP, ObjectIdGetDatum(ellabeloid), ObjectIdGetDatum(propoid))) ereport(ERROR, errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("property \"%s\" already exists", propname)); else { Relation rel; Datum values[Natts_pg_propgraph_label_property] = {0}; bool nulls[Natts_pg_propgraph_label_property] = {0}; Oid plpoid; HeapTuple tup; ObjectAddress myself; ObjectAddress referenced; rel = table_open(PropgraphLabelPropertyRelationId, RowExclusiveLock); plpoid = GetNewOidWithIndex(rel, PropgraphLabelPropertyObjectIndexId, Anum_pg_propgraph_label_property_oid); values[Anum_pg_propgraph_label_property_oid - 1] = ObjectIdGetDatum(plpoid); values[Anum_pg_propgraph_label_property_plppropid - 1] = ObjectIdGetDatum(propoid); values[Anum_pg_propgraph_label_property_plpellabelid - 1] = ObjectIdGetDatum(ellabeloid); values[Anum_pg_propgraph_label_property_plpexpr - 1] = CStringGetTextDatum(nodeToString(expr)); tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); CatalogTupleInsert(rel, tup); heap_freetuple(tup); ObjectAddressSet(myself, PropgraphLabelPropertyRelationId, plpoid); ObjectAddressSet(referenced, PropgraphPropertyRelationId, propoid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); ObjectAddressSet(referenced, PropgraphElementLabelRelationId, ellabeloid); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); CheckUsageOnTypesInSingleRelExpr((Node *) expr, pgerelid, GetUserId()); recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false); table_close(rel, NoLock); } } /* * Check that for the given graph element, all properties with the same name * have the same expression for each label. (See SQL/PGQ subclause "Creation * of an element table descriptor".) * * We check this after all the catalog records are already inserted. This * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER * PROPERTY GRAPH. We pass in the element OID so that ALTER PROPERTY GRAPH * only has to check the element it has just operated on. CREATE PROPERTY * GRAPH checks all elements it has created. */ static void check_element_properties(Oid peoid) { Relation rel1; ScanKeyData key1[1]; SysScanDesc scan1; HeapTuple tuple1; List *propoids = NIL; List *propexprs = NIL; rel1 = table_open(PropgraphElementLabelRelationId, AccessShareLock); ScanKeyInit(&key1[0], Anum_pg_propgraph_element_label_pgelelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(peoid)); scan1 = systable_beginscan(rel1, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, key1); while (HeapTupleIsValid(tuple1 = systable_getnext(scan1))) { Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple1); Relation rel2; ScanKeyData key2[1]; SysScanDesc scan2; HeapTuple tuple2; rel2 = table_open(PropgraphLabelPropertyRelationId, AccessShareLock); ScanKeyInit(&key2[0], Anum_pg_propgraph_label_property_plpellabelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ellabel->oid)); scan2 = systable_beginscan(rel2, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key2); while (HeapTupleIsValid(tuple2 = systable_getnext(scan2))) { Form_pg_propgraph_label_property lprop = (Form_pg_propgraph_label_property) GETSTRUCT(tuple2); Oid propoid; Datum datum; bool isnull; char *propexpr; ListCell *lc1, *lc2; bool found; propoid = lprop->plppropid; datum = heap_getattr(tuple2, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(rel2), &isnull); Assert(!isnull); propexpr = TextDatumGetCString(datum); found = false; forboth(lc1, propoids, lc2, propexprs) { if (propoid == lfirst_oid(lc1)) { Node *na, *nb; na = stringToNode(propexpr); nb = stringToNode(lfirst(lc2)); found = true; if (!equal(na, nb)) { HeapTuple tuple3; Form_pg_propgraph_element elform; List *dpcontext; char *dpa, *dpb; tuple3 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peoid)); if (!tuple3) elog(ERROR, "cache lookup failed for property graph element %u", peoid); elform = (Form_pg_propgraph_element) GETSTRUCT(tuple3); dpcontext = deparse_context_for(get_rel_name(elform->pgerelid), elform->pgerelid); dpa = deparse_expression(na, dpcontext, false, false); dpb = deparse_expression(nb, dpcontext, false, false); /* * show in sorted order to keep output independent of * index order */ if (strcmp(dpa, dpb) > 0) { char *tmp; tmp = dpa; dpa = dpb; dpb = tmp; } ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("element \"%s\" property \"%s\" expression mismatch: %s vs. %s", NameStr(elform->pgealias), get_propgraph_property_name(propoid), dpa, dpb), errdetail("In a property graph element, a property of the same name has to have the same expression in each label.")); ReleaseSysCache(tuple3); } break; } } if (!found) { propoids = lappend_oid(propoids, propoid); propexprs = lappend(propexprs, propexpr); } } systable_endscan(scan2); table_close(rel2, AccessShareLock); } systable_endscan(scan1); table_close(rel1, AccessShareLock); } /* * Check that for the given element label, all labels of the same name in the * graph have the same number and names of properties (independent of which * element they are on). (See SQL/PGQ subclause "Consistency check of a * tabular property graph descriptor".) * * We check this after all the catalog records are already inserted. This * makes it easier to share this code between CREATE PROPERTY GRAPH and ALTER * PROPERTY GRAPH. We pass in the element label OID so that some variants of * ALTER PROPERTY GRAPH only have to check the element label it has just * operated on. CREATE PROPERTY GRAPH and other ALTER PROPERTY GRAPH variants * check all labels. */ static void check_element_label_properties(Oid ellabeloid) { Relation rel; SysScanDesc scan; ScanKeyData key[1]; HeapTuple tuple; Oid labelid = InvalidOid; Oid ref_ellabeloid = InvalidOid; List *myprops, *refprops; List *diff1, *diff2; rel = table_open(PropgraphElementLabelRelationId, AccessShareLock); /* * Get element label info */ ScanKeyInit(&key[0], Anum_pg_propgraph_element_label_oid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ellabeloid)); scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key); if (HeapTupleIsValid(tuple = systable_getnext(scan))) { Form_pg_propgraph_element_label ellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple); labelid = ellabel->pgellabelid; } systable_endscan(scan); if (!labelid) elog(ERROR, "element label %u not found", ellabeloid); /* * Find a reference element label to fetch label properties. The * reference element label has to have the same label OID as the one being * checked but a different element OID. */ ScanKeyInit(&key[0], Anum_pg_propgraph_element_label_pgellabelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(labelid)); scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { Form_pg_propgraph_element_label otherellabel = (Form_pg_propgraph_element_label) GETSTRUCT(tuple); if (otherellabel->oid != ellabeloid) { ref_ellabeloid = otherellabel->oid; break; } } systable_endscan(scan); table_close(rel, AccessShareLock); /* * If there is no previous definition of this label, then we are done. */ if (!ref_ellabeloid) return; /* * Now check number and names. * * XXX We could provide more detail in the error messages, but that would * probably only be useful for some ALTER commands, because otherwise it's * not really clear which label definition is the wrong one, and so you'd * have to construct a rather verbose report to be of any use. Let's keep * it simple for now. */ myprops = get_element_label_property_names(ellabeloid); refprops = get_element_label_property_names(ref_ellabeloid); if (list_length(refprops) != list_length(myprops)) ereport(ERROR, errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("mismatching number of properties in definition of label \"%s\"", get_propgraph_label_name(labelid))); diff1 = list_difference(myprops, refprops); diff2 = list_difference(refprops, myprops); if (diff1 || diff2) ereport(ERROR, errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("mismatching property names in definition of label \"%s\"", get_propgraph_label_name(labelid))); } /* * As above, but check all labels of a graph. */ static void check_all_labels_properties(Oid pgrelid) { foreach_oid(labeloid, get_graph_label_ids(pgrelid)) { foreach_oid(ellabeloid, get_label_element_label_ids(labeloid)) { check_element_label_properties(ellabeloid); } } } /* * ALTER PROPERTY GRAPH */ ObjectAddress AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) { Oid pgrelid; ListCell *lc; ObjectAddress pgaddress; /* * ShareRowExclusiveLock is required because this command runs some * graph-wide consistency checks that wouldn't work if more than one ALTER * PROPERTY GRAPH could operate on the same graph at once. */ pgrelid = RangeVarGetRelidExtended(stmt->pgname, ShareRowExclusiveLock, stmt->missing_ok ? RVR_MISSING_OK : 0, RangeVarCallbackOwnsRelation, NULL); if (pgrelid == InvalidOid) { ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", stmt->pgname->relname))); return InvalidObjectAddress; } ObjectAddressSet(pgaddress, RelationRelationId, pgrelid); foreach(lc, stmt->add_vertex_tables) { PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc); struct element_info *vinfo; Relation rel; Oid peoid; vinfo = palloc0_object(struct element_info); vinfo->kind = PGEKIND_VERTEX; vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); rel = table_open(vinfo->relid, NoLock); if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("cannot add temporary element table to non-temporary property graph"), errdetail("Table \"%s\" is a temporary table.", get_rel_name(vinfo->relid)), parser_errposition(pstate, vertex->vtable->location))); if (vertex->vtable->alias) vinfo->aliasname = vertex->vtable->alias->aliasname; else vinfo->aliasname = vertex->vtable->relname; vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, rel, vinfo->aliasname, vertex->location); vinfo->labels = vertex->labels; table_close(rel, NoLock); if (SearchSysCacheExists2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(vinfo->aliasname))) ereport(ERROR, errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alias \"%s\" already exists in property graph \"%s\"", vinfo->aliasname, stmt->pgname->relname), parser_errposition(pstate, vertex->vtable->location)); peoid = insert_element_record(pgaddress, vinfo); CommandCounterIncrement(); check_element_properties(peoid); check_all_labels_properties(pgrelid); } foreach(lc, stmt->add_edge_tables) { PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc); struct element_info *einfo; Relation rel; Relation srcrel; Relation destrel; Oid peoid; einfo = palloc0_object(struct element_info); einfo->kind = PGEKIND_EDGE; einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL); rel = table_open(einfo->relid, NoLock); if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && get_rel_persistence(pgrelid) != RELPERSISTENCE_TEMP) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("cannot add temporary element table to non-temporary property graph"), errdetail("Table \"%s\" is a temporary table.", get_rel_name(einfo->relid)), parser_errposition(pstate, edge->etable->location))); if (edge->etable->alias) einfo->aliasname = edge->etable->alias->aliasname; else einfo->aliasname = edge->etable->relname; einfo->key = propgraph_element_get_key(pstate, edge->ekey, rel, einfo->aliasname, edge->location); einfo->srcvertexid = get_vertex_oid(pstate, pgrelid, edge->esrcvertex, edge->location); einfo->destvertexid = get_vertex_oid(pstate, pgrelid, edge->edestvertex, edge->location); einfo->srcrelid = get_element_relid(einfo->srcvertexid); einfo->destrelid = get_element_relid(einfo->destvertexid); srcrel = table_open(einfo->srcrelid, AccessShareLock); destrel = table_open(einfo->destrelid, AccessShareLock); propgraph_edge_get_ref_keys(pstate, edge->esrckey, edge->esrcvertexcols, rel, srcrel, einfo->aliasname, edge->location, "SOURCE", &einfo->srckey, &einfo->srcref, &einfo->srceqop); propgraph_edge_get_ref_keys(pstate, edge->edestkey, edge->edestvertexcols, rel, destrel, einfo->aliasname, edge->location, "DESTINATION", &einfo->destkey, &einfo->destref, &einfo->desteqop); einfo->labels = edge->labels; table_close(destrel, NoLock); table_close(srcrel, NoLock); table_close(rel, NoLock); if (SearchSysCacheExists2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(einfo->aliasname))) ereport(ERROR, errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("alias \"%s\" already exists in property graph \"%s\"", einfo->aliasname, stmt->pgname->relname), parser_errposition(pstate, edge->etable->location)); peoid = insert_element_record(pgaddress, einfo); CommandCounterIncrement(); check_element_properties(peoid); check_all_labels_properties(pgrelid); } foreach(lc, stmt->drop_vertex_tables) { char *alias = strVal(lfirst(lc)); Oid peoid; ObjectAddress obj; peoid = get_vertex_oid(pstate, pgrelid, alias, -1); ObjectAddressSet(obj, PropgraphElementRelationId, peoid); performDeletion(&obj, stmt->drop_behavior, 0); } foreach(lc, stmt->drop_edge_tables) { char *alias = strVal(lfirst(lc)); Oid peoid; ObjectAddress obj; peoid = get_edge_oid(pstate, pgrelid, alias, -1); ObjectAddressSet(obj, PropgraphElementRelationId, peoid); performDeletion(&obj, stmt->drop_behavior, 0); } /* Remove any orphaned pg_propgraph_label entries */ if (stmt->drop_vertex_tables || stmt->drop_edge_tables) { foreach_oid(labeloid, get_graph_label_ids(pgrelid)) { if (!get_label_element_label_ids(labeloid)) { ObjectAddress obj; ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid); performDeletion(&obj, stmt->drop_behavior, 0); } } } foreach(lc, stmt->add_labels) { PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc); Oid peoid; Oid pgerelid; Oid ellabeloid; Assert(lp->label); if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); else peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); pgerelid = get_element_relid(peoid); ellabeloid = insert_label_record(pgrelid, peoid, lp->label); insert_property_records(pgrelid, ellabeloid, pgerelid, lp->properties); CommandCounterIncrement(); check_element_properties(peoid); check_element_label_properties(ellabeloid); } if (stmt->drop_label) { Oid peoid; Oid labeloid; Oid ellabeloid = InvalidOid; ObjectAddress obj; Relation ellabelrel; SysScanDesc ellabelscan; ScanKeyData ellabelkey[1]; int nlabels; HeapTuple tuple; if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); else peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(pgrelid), CStringGetDatum(stmt->drop_label)); if (!labeloid) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label), parser_errposition(pstate, -1)); /* * Is the given label associated with the element? Is this the only * label associated with the element? Scan the * pg_propgraph_element_label table to find answers to these * questions. Stop scanning when we know both answers. */ ellabelrel = table_open(PropgraphElementLabelRelationId, AccessShareLock); ScanKeyInit(&ellabelkey[0], Anum_pg_propgraph_element_label_pgelelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(peoid)); ellabelscan = systable_beginscan(ellabelrel, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, ellabelkey); nlabels = 0; while (HeapTupleIsValid(tuple = systable_getnext(ellabelscan))) { Form_pg_propgraph_element_label ellabelform = (Form_pg_propgraph_element_label) GETSTRUCT(tuple); nlabels++; if (ellabelform->pgellabelid == labeloid) ellabeloid = ellabelform->oid; if (nlabels > 1 && ellabeloid) break; } systable_endscan(ellabelscan); table_close(ellabelrel, AccessShareLock); if (!ellabeloid) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label), parser_errposition(pstate, -1)); /* * Prevent dropping the last label from an element. Every element must * have at least one label associated with it. */ if (nlabels == 1) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot drop the last label from element \"%s\"", stmt->element_alias), errhint("Every element must have at least one label."))); ObjectAddressSet(obj, PropgraphElementLabelRelationId, ellabeloid); performDeletion(&obj, stmt->drop_behavior, 0); /* Remove any orphaned pg_propgraph_label entries */ if (!get_label_element_label_ids(labeloid)) { ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid); performDeletion(&obj, stmt->drop_behavior, 0); } } if (stmt->add_properties) { Oid peoid; Oid pgerelid; Oid labeloid; Oid ellabeloid = InvalidOid; if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); else peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(pgrelid), CStringGetDatum(stmt->alter_label)); if (labeloid) ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL, Anum_pg_propgraph_element_label_oid, ObjectIdGetDatum(peoid), ObjectIdGetDatum(labeloid)); if (!ellabeloid) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label), parser_errposition(pstate, -1)); pgerelid = get_element_relid(peoid); insert_property_records(pgrelid, ellabeloid, pgerelid, stmt->add_properties); CommandCounterIncrement(); check_element_properties(peoid); check_element_label_properties(ellabeloid); } if (stmt->drop_properties) { Oid peoid; Oid labeloid; Oid ellabeloid = InvalidOid; ObjectAddress obj; if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX) peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1); else peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1); labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(pgrelid), CStringGetDatum(stmt->alter_label)); if (labeloid) ellabeloid = GetSysCacheOid2(PROPGRAPHELEMENTLABELELEMENTLABEL, Anum_pg_propgraph_element_label_oid, ObjectIdGetDatum(peoid), ObjectIdGetDatum(labeloid)); if (!ellabeloid) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"", get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label), parser_errposition(pstate, -1)); foreach(lc, stmt->drop_properties) { char *propname = strVal(lfirst(lc)); Oid propoid; Oid plpoid = InvalidOid; propoid = GetSysCacheOid2(PROPGRAPHPROPNAME, Anum_pg_propgraph_property_oid, ObjectIdGetDatum(pgrelid), CStringGetDatum(propname)); if (propoid) plpoid = GetSysCacheOid2(PROPGRAPHLABELPROP, Anum_pg_propgraph_label_property_oid, ObjectIdGetDatum(ellabeloid), ObjectIdGetDatum(propoid)); if (!plpoid) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" element \"%s\" label \"%s\" has no property \"%s\"", get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label, propname), parser_errposition(pstate, -1)); ObjectAddressSet(obj, PropgraphLabelPropertyRelationId, plpoid); performDeletion(&obj, stmt->drop_behavior, 0); } check_element_label_properties(ellabeloid); } /* Remove any orphaned pg_propgraph_property entries */ if (stmt->drop_properties || stmt->drop_vertex_tables || stmt->drop_edge_tables || stmt->drop_label) { foreach_oid(propoid, get_graph_property_ids(pgrelid)) { Relation rel; SysScanDesc scan; ScanKeyData key[1]; rel = table_open(PropgraphLabelPropertyRelationId, RowShareLock); ScanKeyInit(&key[0], Anum_pg_propgraph_label_property_plppropid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(propoid)); /* XXX no suitable index */ scan = systable_beginscan(rel, InvalidOid, true, NULL, 1, key); if (!systable_getnext(scan)) { ObjectAddress obj; ObjectAddressSet(obj, PropgraphPropertyRelationId, propoid); performDeletion(&obj, stmt->drop_behavior, 0); } systable_endscan(scan); table_close(rel, RowShareLock); } } /* * Invalidate relcache entry of the property graph so that the queries in * the cached plans referencing the property graph will be rewritten * considering changes to the property graph. */ CacheInvalidateRelcacheByRelid(pgrelid); return pgaddress; } /* * Get OID of vertex from graph OID and element alias. Element must be a * vertex, otherwise error. */ static Oid get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location) { HeapTuple tuple; Oid peoid; tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias)); if (!tuple) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" has no element with alias \"%s\"", get_rel_name(pgrelid), alias), parser_errposition(pstate, location)); if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("element \"%s\" of property graph \"%s\" is not a vertex", alias, get_rel_name(pgrelid)), parser_errposition(pstate, location)); peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid; ReleaseSysCache(tuple); return peoid; } /* * Get OID of edge from graph OID and element alias. Element must be an edge, * otherwise error. */ static Oid get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location) { HeapTuple tuple; Oid peoid; tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias)); if (!tuple) ereport(ERROR, errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("property graph \"%s\" has no element with alias \"%s\"", get_rel_name(pgrelid), alias), parser_errposition(pstate, location)); if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("element \"%s\" of property graph \"%s\" is not an edge", alias, get_rel_name(pgrelid)), parser_errposition(pstate, location)); peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid; ReleaseSysCache(tuple); return peoid; } /* * Get the element table relation OID from the OID of the element. */ static Oid get_element_relid(Oid peid) { HeapTuple tuple; Oid pgerelid; tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peid)); if (!tuple) elog(ERROR, "cache lookup failed for property graph element %u", peid); pgerelid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgerelid; ReleaseSysCache(tuple); return pgerelid; } /* * Get a list of all label OIDs of a graph. */ static List * get_graph_label_ids(Oid graphid) { Relation rel; SysScanDesc scan; ScanKeyData key[1]; HeapTuple tuple; List *result = NIL; rel = table_open(PropgraphLabelRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_propgraph_label_pglpgid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(graphid)); scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId, true, NULL, 1, key); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tuple))->oid); } systable_endscan(scan); table_close(rel, AccessShareLock); return result; } /* * Get a list of all element label OIDs for a label. */ static List * get_label_element_label_ids(Oid labelid) { Relation rel; SysScanDesc scan; ScanKeyData key[1]; HeapTuple tuple; List *result = NIL; rel = table_open(PropgraphElementLabelRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_propgraph_element_label_pgellabelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(labelid)); scan = systable_beginscan(rel, PropgraphElementLabelLabelIndexId, true, NULL, 1, key); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { result = lappend_oid(result, ((Form_pg_propgraph_element_label) GETSTRUCT(tuple))->oid); } systable_endscan(scan); table_close(rel, AccessShareLock); return result; } /* * Get the names of properties associated with the given element label OID. * * The result is a list of String nodes (so we can use list functions to * detect differences). */ static List * get_element_label_property_names(Oid ellabeloid) { Relation rel; SysScanDesc scan; ScanKeyData key[1]; HeapTuple tuple; List *result = NIL; rel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_propgraph_label_property_plpellabelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ellabeloid)); scan = systable_beginscan(rel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, key); while ((tuple = systable_getnext(scan))) { Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tuple); result = lappend(result, makeString(get_propgraph_property_name(plpform->plppropid))); } systable_endscan(scan); table_close(rel, AccessShareLock); return result; } /* * Get a list of all property OIDs of a graph. */ static List * get_graph_property_ids(Oid graphid) { Relation rel; SysScanDesc scan; ScanKeyData key[1]; HeapTuple tuple; List *result = NIL; rel = table_open(PropgraphPropertyRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_propgraph_property_pgppgid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(graphid)); scan = systable_beginscan(rel, PropgraphPropertyNameIndexId, true, NULL, 1, key); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { result = lappend_oid(result, ((Form_pg_propgraph_property) GETSTRUCT(tuple))->oid); } systable_endscan(scan); table_close(rel, AccessShareLock); return result; }