44
55namespace duckdb {
66
7- unique_ptr<PathElement> Transformer::TransformPathElement (duckdb_libpgquery::PGPathElement *element) {
7+ unique_ptr<PathElement> Transformer::TransformPathElement (duckdb_libpgquery::PGPathElement *element, case_insensitive_map_t < idx_t >& anonymous_variable_map ) {
88 // ! Vertex or edge pattern
99 auto result = make_uniq<PathElement>(PGQPathReferenceType::PATH_ELEMENT);
1010 switch (element->match_type ) {
@@ -33,14 +33,32 @@ unique_ptr<PathElement> Transformer::TransformPathElement(duckdb_libpgquery::PGP
3333 std::string label_name = StringUtil::Lower (label_expression->name );
3434 result->label = label_name;
3535 if (!element->element_var ) {
36- throw ConstraintException (" All patterns must bind to a variable, %s is missing a variable" , result->label );
36+ // Case: Anonymous node with label `:N`
37+ if (anonymous_variable_map.find (label_name) != anonymous_variable_map.end ()) {
38+ throw ConstraintException (" Ambiguous anonymous variable pattern detected for label '" + label_name + " '. "
39+ " Please provide an explicit variable name." );
40+ }
41+ anonymous_variable_map[label_name] = 1 ;
42+ result->variable_binding = label_name;
43+ } else {
44+ // Case: Explicitly named node (e.g., `n:N`)
45+ std::string variable_name = element->element_var ;
46+ // Check if this label was already used as an anonymous variable
47+ if (anonymous_variable_map.find (variable_name) != anonymous_variable_map.end () &&
48+ anonymous_variable_map[variable_name] == 1 ) {
49+ throw ConstraintException (" Conflicting variable bindings: anonymous node with variable '" + variable_name +
50+ " ' was already assigned. Provide explicit variable names for all occurrences." );
51+ }
52+ anonymous_variable_map[variable_name] = 1 ;
53+
54+ // Register this explicitly named variable
55+ result->variable_binding = variable_name;
3756 }
38- result->variable_binding = element->element_var ;
3957 return result;
4058}
4159
4260unique_ptr<SubPath> Transformer::TransformSubPathElement (duckdb_libpgquery::PGSubPath *root,
43- unique_ptr<PathPattern> &path_pattern) {
61+ unique_ptr<PathPattern> &path_pattern, case_insensitive_map_t < idx_t >& anonymous_variable_map ) {
4462 auto result = make_uniq<SubPath>(PGQPathReferenceType::SUBPATH);
4563
4664 result->where_clause = TransformExpression (root->where_clause );
@@ -85,18 +103,18 @@ unique_ptr<SubPath> Transformer::TransformSubPathElement(duckdb_libpgquery::PGSu
85103 auto path_node = reinterpret_cast <duckdb_libpgquery::PGNode *>(node->data .ptr_value );
86104 if (path_node->type == duckdb_libpgquery::T_PGPathElement) {
87105 auto element = reinterpret_cast <duckdb_libpgquery::PGPathElement *>(path_node);
88- auto path_element = TransformPathElement (element);
106+ auto path_element = TransformPathElement (element, anonymous_variable_map );
89107 result->path_list .push_back (std::move (path_element));
90108 } else if (path_node->type == duckdb_libpgquery::T_PGSubPath) {
91109 auto subpath = reinterpret_cast <duckdb_libpgquery::PGSubPath *>(path_node);
92- auto subpath_element = TransformSubPathElement (subpath, path_pattern);
110+ auto subpath_element = TransformSubPathElement (subpath, path_pattern, anonymous_variable_map );
93111 result->path_list .push_back (std::move (subpath_element));
94112 }
95113 }
96114 return result;
97115}
98116
99- unique_ptr<PathPattern> Transformer::TransformPath (duckdb_libpgquery::PGPathPattern *root) {
117+ unique_ptr<PathPattern> Transformer::TransformPath (duckdb_libpgquery::PGPathPattern *root, case_insensitive_map_t < idx_t >& anonymous_variable_map ) {
100118 auto result = make_uniq<PathPattern>();
101119 result->all = root->all ;
102120 result->shortest = root->shortest ;
@@ -117,11 +135,11 @@ unique_ptr<PathPattern> Transformer::TransformPath(duckdb_libpgquery::PGPathPatt
117135 auto path_node = reinterpret_cast <duckdb_libpgquery::PGNode *>(node->data .ptr_value );
118136 if (path_node->type == duckdb_libpgquery::T_PGPathElement) {
119137 auto element = reinterpret_cast <duckdb_libpgquery::PGPathElement *>(path_node);
120- auto path_element = TransformPathElement (element);
138+ auto path_element = TransformPathElement (element, anonymous_variable_map );
121139 result->path_elements .push_back (std::move (path_element));
122140 } else if (path_node->type == duckdb_libpgquery::T_PGSubPath) {
123141 auto subpath = reinterpret_cast <duckdb_libpgquery::PGSubPath *>(path_node);
124- auto subpath_element = TransformSubPathElement (subpath, result);
142+ auto subpath_element = TransformSubPathElement (subpath, result, anonymous_variable_map );
125143 result->path_elements .push_back (std::move (subpath_element));
126144 } else {
127145 throw NotImplementedException (" Path node type " + NodetypeToString (path_node->type ) + " not recognized" );
@@ -135,6 +153,7 @@ unique_ptr<TableRef> Transformer::TransformMatch(duckdb_libpgquery::PGMatchClaus
135153 auto match_info = make_uniq<MatchExpression>();
136154 match_info->pg_name = root.pg_name ; // Name of the property graph to bind to
137155 string alias_name;
156+ case_insensitive_map_t <idx_t > anonymous_variable_map; // Map of anonymous variables to their bound names
138157 if (root.graph_table ) {
139158 alias_name = TransformQualifiedName (*root.graph_table ).name ;
140159 }
@@ -146,7 +165,7 @@ unique_ptr<TableRef> Transformer::TransformMatch(duckdb_libpgquery::PGMatchClaus
146165
147166 for (auto node = root.paths ->head ; node != nullptr ; node = lnext (node)) {
148167 auto path = reinterpret_cast <duckdb_libpgquery::PGPathPattern *>(node->data .ptr_value );
149- auto transformed_path = TransformPath (path);
168+ auto transformed_path = TransformPath (path, anonymous_variable_map );
150169 match_info->path_patterns .push_back (std::move (transformed_path));
151170 }
152171
0 commit comments