Skip to content

Commit 0b94a2d

Browse files
committed
Handle anonymous variables. Only allow one per label, and don't allow other variables to get the same variable
1 parent 0b1864d commit 0b94a2d

File tree

2 files changed

+32
-13
lines changed

2 files changed

+32
-13
lines changed

src/include/duckdb/parser/transformer.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,12 @@ class Transformer {
339339
case_insensitive_set_t &global_label_set,
340340
case_insensitive_map_t<string> &table_alias_map);
341341
//! Transform a path pattern (SQL/PGQ)
342-
unique_ptr<PathPattern> TransformPath(duckdb_libpgquery::PGPathPattern *root);
342+
unique_ptr<PathPattern> TransformPath(duckdb_libpgquery::PGPathPattern *root, case_insensitive_map_t<idx_t>& anonymous_variable_map);
343343
//! Transform a path element (SQL/PGQ)
344-
static unique_ptr<PathElement> TransformPathElement(duckdb_libpgquery::PGPathElement *element);
344+
static unique_ptr<PathElement> TransformPathElement(duckdb_libpgquery::PGPathElement *element, case_insensitive_map_t<idx_t>& anonymous_variable_map);
345345
//! Transform a subpath (SQL/PGQ)
346346
unique_ptr<SubPath> TransformSubPathElement(duckdb_libpgquery::PGSubPath *element,
347-
unique_ptr<PathPattern> &path_pattern);
347+
unique_ptr<PathPattern> &path_pattern, case_insensitive_map_t<idx_t>& anonymous_variable_map);
348348

349349
//! Transform a Postgres duckdb_libpgquery::T_PGDropPropertyGraphStmt node into a Drop[Table,Schema]Statement
350350
unique_ptr<SQLStatement> TransformDropPropertyGraph(duckdb_libpgquery::PGDropPropertyGraphStmt &node);

src/parser/transform/tableref/transform_match.cpp

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace 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

4260
unique_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

Comments
 (0)