On Wed, Jul 8, 2026 at 6:59 PM Ashutosh Bapat <[email protected]> wrote: > > On Wed, Jul 8, 2026 at 2:35 PM Peter Eisentraut <[email protected]> wrote: > > > > I would like this code to be organized differently. Note that the > > existing insert_*_record functions don't do any error checking; they are > > just there to make catalog modifications. There are various check_* > > functions that pretty much correspond to syntax rule checks, but these > > are run after the catalog changes, hence the present issue. Maybe we > > should have a set of "pre-check" functions in addition? > >
If we consolidate all the pre-checks together similar to post checks, we duplicate the catalog lookups. But a future code may miss calling pre-checks before calling insert_*_record(). I tried to write one precheck function for insert_label_record and insert_property_record respectively and ended up calling it close to the insert_*_record itself. The effort is attached as v20260716-0001-pre-check-Report-duplicate-property-and-label-names-.patch.nocibot I don't like the result. > > Do you expect insert_element_record() to perform sanity checks of > labels or insert_label_record() to perform sanity checks on > properties? Or do you expect a hierarchy of pre_check_ functions > cascading from elements to labels to properties? If I understand your intention to keep functions inserting records into catalogs simple, Attached v20260716-0001-simple-insert-Report-duplicate-property-and-label-names.patch.nocibot is what I could come up with. It adds one insert_*_record() function for every property graph catalog, and then an add_* functions which call the insert_*_record functions after necessary pre-checks. I like this version better. It doesn't collect all the checks like check_* functions but create a hierarchy of checks and catalog inserts keeping the latter simple. I have also rearranged the code so that all the catalog insert functions are together and their wrappers are together respectively. -- Best Wishes, Ashutosh Bapat
v20260716-0001-pre-check-Report-duplicate-property-and-label-names-.patch.nocibot
Description: Binary data
From 048e06179cec4b2fefed653a49bd3fc9b106083b Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <[email protected]> Date: Thu, 16 Jul 2026 15:30:19 +0530 Subject: [PATCH v20260716] Report duplicate property and label names with a proper error Adding a property with the same name multiple times to a label on an element, either within a single PROPERTIES clause or across statements via ALTER PROPERTY GRAPH ... ADD PROPERTIES, previously failed with a unique-index violation on pg_propgraph_label_property. The same class of bug existed for labels: listing the same label multiple times on one element in CREATE PROPERTY GRAPH, or adding a label to an element that already has it via ALTER PROPERTY GRAPH ... ADD LABEL, failed with a unique-index violation on pg_propgraph_element_label. Detect the duplicates up front and raise a friendlier error in both cases. For properties, the cross-statement duplicate is caught by a syscache probe before insertion. The in-clause duplicate could have been caught by the same probe if we issued a CommandCounterIncrement() between property inserts, but that forces a catalog invalidation per property purely to detect a condition we can check for free on the in-memory target list. The list-based check is only needed when the properties are listed explicitly; when they are derived from the table's attributes, names are already unique. For labels, a single syscache probe on pg_propgraph_element_label suffices for both the in-clause and cross-statement cases because insert_element_record() already issues CommandCounterIncrement() between successive label inserts. Author: Ashutosh Bapat <[email protected]> Reported by: Noah Misch <[email protected]> Discussion: https://www.postgresql.org/message-id/[email protected] diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c index c87c519b032..9c9f4f2b299 100644 --- a/src/backend/commands/propgraphcmds.c +++ b/src/backend/commands/propgraphcmds.c @@ -784,6 +784,13 @@ insert_label_record(Oid graphid, Oid peoid, const char *label) /* * 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}; @@ -901,12 +908,30 @@ insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGra resolveTargetListUnknowns(pstate, tp); assign_expr_collations(pstate, (Node *) tp); - foreach(lc, 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) { - TargetEntry *te = lfirst_node(TargetEntry, lc); + List *seen = NIL; - insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr); + 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); } /* @@ -1003,6 +1028,12 @@ insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *pr /* * 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}; diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out index 7387751eac2..4a8b3d91219 100644 --- a/src/test/regress/expected/create_property_graph.out +++ b/src/test/regress/expected/create_property_graph.out @@ -207,6 +207,20 @@ CREATE PROPERTY GRAPH gx ); DROP PROPERTY GRAPH gx; DROP TABLE t1x, t2x; +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a AS p, a AS p) -- duplicate property on label + ); +ERROR: property "p" specified more than once +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS i_j); -- duplicate property on label +ERROR: property "i_j" already exists +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 LABEL l1 -- duplicate label on element + ); +ERROR: label "l1" already exists +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t3 ADD LABEL t3l1 NO PROPERTIES; -- duplicate label on element +ERROR: label "t3l1" already exists CREATE PROPERTY GRAPH gx VERTEX TABLES ( t1 KEY (a) LABEL l1 PROPERTIES (a, a AS aa), diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql index 3494390b923..1667896f558 100644 --- a/src/test/regress/sql/create_property_graph.sql +++ b/src/test/regress/sql/create_property_graph.sql @@ -161,6 +161,18 @@ CREATE PROPERTY GRAPH gx DROP PROPERTY GRAPH gx; DROP TABLE t1x, t2x; +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a AS p, a AS p) -- duplicate property on label + ); +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS i_j); -- duplicate property on label + +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 LABEL l1 -- duplicate label on element + ); +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t3 ADD LABEL t3l1 NO PROPERTIES; -- duplicate label on element + CREATE PROPERTY GRAPH gx VERTEX TABLES ( t1 KEY (a) LABEL l1 PROPERTIES (a, a AS aa), --- src/backend/commands/propgraphcmds.c | 487 +++++++++++------- .../expected/create_property_graph.out | 14 + .../regress/sql/create_property_graph.sql | 12 + 3 files changed, 322 insertions(+), 191 deletions(-) diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c index c87c519b032..2938e8bd9ca 100644 --- a/src/backend/commands/propgraphcmds.c +++ b/src/backend/commands/propgraphcmds.c @@ -82,9 +82,20 @@ static void propgraph_edge_get_ref_keys(ParseState *pstate, const List *keycols, 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 Oid insert_label_record(Oid graphid, const char *label); +static Oid insert_property_record(Oid graphid, const char *propname, + Oid typid, int32 typmod, Oid collation); +static Oid insert_element_label_record(Oid peoid, Oid labeloid); +static void insert_label_property_record(Oid ellabeloid, Oid propoid, Oid pgerelid, + const Expr *expr); +static Oid add_element(ObjectAddress pgaddress, struct element_info *einfo); +static Oid add_element_label(Oid graphid, Oid peoid, const char *label); +static Oid add_element_label_properties(Oid graphid, Oid peoid, Oid pgerelid, + const char *label, + const PropGraphProperties *properties); +static void add_label_properties(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties); +static void add_label_property(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); @@ -268,7 +279,7 @@ CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt) struct element_info *vinfo = lfirst(lc); Oid peoid; - peoid = insert_element_record(pgaddress, vinfo); + peoid = add_element(pgaddress, vinfo); element_oids = lappend_oid(element_oids, peoid); } @@ -303,7 +314,7 @@ CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt) Assert(einfo->destvertexid); Assert(einfo->srcrelid); Assert(einfo->destrelid); - peoid = insert_element_record(pgaddress, einfo); + peoid = add_element(pgaddress, einfo); element_oids = lappend_oid(element_oids, peoid); } @@ -605,8 +616,8 @@ array_of_opers_to_objectaddrs(ArrayType *arr, ObjectAddresses *addrs) } /* - * Insert a record for an element into the pg_propgraph_element catalog. Also - * inserts labels and properties into their respective catalogs. + * Insert a record into pg_propgraph_element catalog with the required + * dependencies. */ static Oid insert_element_record(ObjectAddress pgaddress, struct element_info *einfo) @@ -702,6 +713,182 @@ insert_element_record(ObjectAddress pgaddress, struct element_info *einfo) table_close(rel, NoLock); + return peoid; +} + +/* + * Insert a record into pg_propgraph_label catalog for given label with required + * dependencies. + */ +static Oid +insert_label_record(Oid graphid, const char *label) +{ + Relation rel; + Datum values[Natts_pg_propgraph_label] = {0}; + bool nulls[Natts_pg_propgraph_label] = {0}; + NameData labelname; + Oid labeloid; + 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); + + return labeloid; +} + +/* + * Insert a record into pg_propgraph_property catalog for the given property + * with required dependencies. + */ +static Oid +insert_property_record(Oid graphid, const char *propname, + Oid typid, int32 typmod, Oid collation) +{ + Relation rel; + NameData propnamedata; + Datum values[Natts_pg_propgraph_property] = {0}; + bool nulls[Natts_pg_propgraph_property] = {0}; + Oid propoid; + HeapTuple tup; + ObjectAddress myself; + ObjectAddress referenced; + + 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(typid); + values[Anum_pg_propgraph_property_pgptypmod - 1] = Int32GetDatum(typmod); + values[Anum_pg_propgraph_property_pgpcollation - 1] = ObjectIdGetDatum(collation); + + 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); + ObjectAddressSet(referenced, TypeRelationId, typid); + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID) + { + ObjectAddressSet(referenced, CollationRelationId, collation); + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + table_close(rel, NoLock); + + return propoid; +} + +/* + * Insert a record into pg_propgraph_element_label catalog associating the given label + * with the given element with required dependencies. + */ +static Oid +insert_element_label_record(Oid peoid, Oid labeloid) +{ + Relation rel; + Datum values[Natts_pg_propgraph_element_label] = {0}; + bool nulls[Natts_pg_propgraph_element_label] = {0}; + Oid ellabeloid; + 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 a record into pg_propgraph_label_property catalog associating the given + * property with the given element label with required dependencies. + */ +static void +insert_label_property_record(Oid ellabeloid, Oid propoid, Oid pgerelid, + const Expr *expr) +{ + 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); + + recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false); + + table_close(rel, NoLock); +} + +/* + * Add a property graph element associating it with the given labels and + * properties. + */ +static Oid +add_element(ObjectAddress pgaddress, struct element_info *einfo) +{ + Oid graphid = pgaddress.objectId; + Oid peoid; + + peoid = insert_element_record(pgaddress, einfo); + if (einfo->labels) { ListCell *lc; @@ -709,109 +896,76 @@ insert_element_record(ObjectAddress pgaddress, struct element_info *einfo) 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); + add_element_label_properties(graphid, peoid, einfo->relid, + lp->label ? lp->label : einfo->aliasname, + 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); + add_element_label_properties(graphid, peoid, einfo->relid, einfo->aliasname, pr); } return peoid; } /* - * Insert records for a label into the pg_propgraph_label and - * pg_propgraph_element_label catalogs, and register dependencies. + * Add the given label to the given property graph element. + * + * ... creating a new pg_propgraph_label record if necessary. Throw an error if + * the label already exists for the element. * - * Returns the OID of the new pg_propgraph_element_label record. + * Returns the OID of the new pg_propgraph_element_label row. */ static Oid -insert_label_record(Oid graphid, Oid peoid, const char *label) +add_element_label(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) + labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME, + Anum_pg_propgraph_label_oid, + ObjectIdGetDatum(graphid), + CStringGetDatum(label)); + if (OidIsValid(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); + if (SearchSysCacheExists2(PROPGRAPHELEMENTLABELELEMENTLABEL, + ObjectIdGetDatum(peoid), + ObjectIdGetDatum(labeloid))) + ereport(ERROR, + errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("label \"%s\" already exists", label)); } + else + labeloid = insert_label_record(graphid, label); - /* - * Insert into pg_propgraph_element_label - */ - { - 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); + return insert_element_label_record(peoid, labeloid); +} - ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid); - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); - ObjectAddressSet(referenced, PropgraphElementRelationId, peoid); - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); +/* + * Add a label to a property graph element along with its properties. + * + * pgerelid is the OID of the element's underlying relation required to resolve + * column references in the property expressions. + * + * Returns the OID of the pg_propgraph_element_label. + */ +static Oid +add_element_label_properties(Oid graphid, Oid peoid, Oid pgerelid, + const char *label, + const PropGraphProperties *properties) +{ + Oid ellabeloid; - table_close(rel, NoLock); - } + ellabeloid = add_element_label(graphid, peoid, label); + add_label_properties(graphid, ellabeloid, pgerelid, properties); return ellabeloid; } @@ -820,7 +974,7 @@ insert_label_record(Oid graphid, Oid peoid, const char *label) * Insert records for properties into the pg_propgraph_property catalog. */ static void -insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties) +add_label_properties(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGraphProperties *properties) { List *proplist = NIL; ParseState *pstate; @@ -901,141 +1055,94 @@ insert_property_records(Oid graphid, Oid ellabeloid, Oid pgerelid, const PropGra resolveTargetListUnknowns(pstate, tp); assign_expr_collations(pstate, (Node *) tp); - foreach(lc, tp) + /* + * Reject duplicate property names within an explicit PROPERTIES clause. + * add_label_property()'s syscache probe cannot see property rows added + * earlier in this same call, since there is no CommandCounterIncrement() + * between them. Done after transformTargetList() so that names it derives + * are considered. When properties are derived from the table's + * attributes, names are already unique. + */ + if (!properties->all) { - TargetEntry *te = lfirst_node(TargetEntry, lc); + List *seen = NIL; - insert_property_record(graphid, ellabeloid, pgerelid, te->resname, te->expr); + 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) + add_label_property(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. + * Add a property to an element label + * + * ... creating a new pg_propgraph_property record if necessary. Throw an error + * if the property already exists for the element label, or if the property is + * defined already with a different type or collation. */ static void -insert_property_record(Oid graphid, Oid ellabeloid, Oid pgerelid, const char *propname, const Expr *expr) +add_label_property(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); + HeapTuple pgptup; + Oid propoid; - /* - * 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; - - 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); - 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 + pgptup = SearchSysCache2(PROPGRAPHPROPNAME, + ObjectIdGetDatum(graphid), + CStringGetDatum(propname)); + if (HeapTupleIsValid(pgptup)) { - 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; + propoid = pgpform->oid; 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 (SearchSysCacheExists2(PROPGRAPHLABELPROP, + ObjectIdGetDatum(ellabeloid), + ObjectIdGetDatum(propoid))) + ereport(ERROR, + errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("property \"%s\" already exists", propname)); + 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)), + 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)), + 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.")); - } } + else + propoid = insert_property_record(graphid, propname, + exprtypid, exprtypmod, exprcollation); - /* - * Insert into pg_propgraph_label_property - */ - { - 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); - - recordDependencyOnSingleRelExpr(&myself, (Node *) copyObject(expr), pgerelid, DEPENDENCY_NORMAL, DEPENDENCY_NORMAL, false); - - table_close(rel, NoLock); - } + insert_label_property_record(ellabeloid, propoid, pgerelid, expr); } /* @@ -1357,7 +1464,7 @@ AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) vinfo->aliasname, stmt->pgname->relname), parser_errposition(pstate, vertex->vtable->location)); - peoid = insert_element_record(pgaddress, vinfo); + peoid = add_element(pgaddress, vinfo); CommandCounterIncrement(); check_element_properties(peoid); @@ -1426,7 +1533,7 @@ AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) einfo->aliasname, stmt->pgname->relname), parser_errposition(pstate, edge->etable->location)); - peoid = insert_element_record(pgaddress, einfo); + peoid = add_element(pgaddress, einfo); CommandCounterIncrement(); check_element_properties(peoid); @@ -1474,7 +1581,6 @@ AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) { PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc); Oid peoid; - Oid pgerelid; Oid ellabeloid; Assert(lp->label); @@ -1484,10 +1590,9 @@ AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) 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); + ellabeloid = add_element_label_properties(pgrelid, peoid, + get_element_relid(peoid), + lp->label, lp->properties); CommandCounterIncrement(); check_element_properties(peoid); @@ -1610,7 +1715,7 @@ AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt) pgerelid = get_element_relid(peoid); - insert_property_records(pgrelid, ellabeloid, pgerelid, stmt->add_properties); + add_label_properties(pgrelid, ellabeloid, pgerelid, stmt->add_properties); CommandCounterIncrement(); check_element_properties(peoid); diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out index 7387751eac2..4a8b3d91219 100644 --- a/src/test/regress/expected/create_property_graph.out +++ b/src/test/regress/expected/create_property_graph.out @@ -207,6 +207,20 @@ CREATE PROPERTY GRAPH gx ); DROP PROPERTY GRAPH gx; DROP TABLE t1x, t2x; +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a AS p, a AS p) -- duplicate property on label + ); +ERROR: property "p" specified more than once +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS i_j); -- duplicate property on label +ERROR: property "i_j" already exists +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 LABEL l1 -- duplicate label on element + ); +ERROR: label "l1" already exists +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t3 ADD LABEL t3l1 NO PROPERTIES; -- duplicate label on element +ERROR: label "t3l1" already exists CREATE PROPERTY GRAPH gx VERTEX TABLES ( t1 KEY (a) LABEL l1 PROPERTIES (a, a AS aa), diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql index 3494390b923..1667896f558 100644 --- a/src/test/regress/sql/create_property_graph.sql +++ b/src/test/regress/sql/create_property_graph.sql @@ -161,6 +161,18 @@ CREATE PROPERTY GRAPH gx DROP PROPERTY GRAPH gx; DROP TABLE t1x, t2x; +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 PROPERTIES (a AS p, a AS p) -- duplicate property on label + ); +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS i_j); -- duplicate property on label + +CREATE PROPERTY GRAPH gx + VERTEX TABLES ( + t1 KEY (a) LABEL l1 LABEL l1 -- duplicate label on element + ); +ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t3 ADD LABEL t3l1 NO PROPERTIES; -- duplicate label on element + CREATE PROPERTY GRAPH gx VERTEX TABLES ( t1 KEY (a) LABEL l1 PROPERTIES (a, a AS aa), base-commit: f4f77f557bb1a173f82be42770a28758732f4cc0 -- 2.34.1
