1 | #include "duckdb/planner/bind_context.hpp" |
2 | |
3 | #include "duckdb/catalog/catalog_entry/table_column_type.hpp" |
4 | #include "duckdb/catalog/standard_entry.hpp" |
5 | #include "duckdb/common/pair.hpp" |
6 | #include "duckdb/common/string_util.hpp" |
7 | #include "duckdb/parser/expression/columnref_expression.hpp" |
8 | #include "duckdb/parser/expression/operator_expression.hpp" |
9 | #include "duckdb/parser/expression/positional_reference_expression.hpp" |
10 | #include "duckdb/parser/expression/star_expression.hpp" |
11 | #include "duckdb/parser/tableref/subqueryref.hpp" |
12 | #include "duckdb/parser/tableref/table_function_ref.hpp" |
13 | #include "duckdb/planner/bound_query_node.hpp" |
14 | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
15 | #include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp" |
16 | #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" |
17 | #include "duckdb/planner/expression_binder/constant_binder.hpp" |
18 | |
19 | #include <algorithm> |
20 | |
21 | namespace duckdb { |
22 | |
23 | string BindContext::GetMatchingBinding(const string &column_name) { |
24 | string result; |
25 | for (auto &kv : bindings) { |
26 | auto binding = kv.second.get(); |
27 | auto is_using_binding = GetUsingBinding(column_name, binding_name: kv.first); |
28 | if (is_using_binding) { |
29 | continue; |
30 | } |
31 | if (binding->HasMatchingBinding(column_name)) { |
32 | if (!result.empty() || is_using_binding) { |
33 | throw BinderException("Ambiguous reference to column name \"%s\" (use: \"%s.%s\" " |
34 | "or \"%s.%s\")" , |
35 | column_name, result, column_name, kv.first, column_name); |
36 | } |
37 | result = kv.first; |
38 | } |
39 | } |
40 | return result; |
41 | } |
42 | |
43 | vector<string> BindContext::GetSimilarBindings(const string &column_name) { |
44 | vector<pair<string, idx_t>> scores; |
45 | for (auto &kv : bindings) { |
46 | auto binding = kv.second.get(); |
47 | for (auto &name : binding->names) { |
48 | idx_t distance = StringUtil::SimilarityScore(s1: name, s2: column_name); |
49 | scores.emplace_back(args: binding->alias + "." + name, args&: distance); |
50 | } |
51 | } |
52 | return StringUtil::TopNStrings(scores); |
53 | } |
54 | |
55 | void BindContext::AddUsingBinding(const string &column_name, UsingColumnSet &set) { |
56 | using_columns[column_name].insert(x: set); |
57 | } |
58 | |
59 | void BindContext::AddUsingBindingSet(unique_ptr<UsingColumnSet> set) { |
60 | using_column_sets.push_back(x: std::move(set)); |
61 | } |
62 | |
63 | optional_ptr<UsingColumnSet> BindContext::GetUsingBinding(const string &column_name) { |
64 | auto entry = using_columns.find(x: column_name); |
65 | if (entry == using_columns.end()) { |
66 | return nullptr; |
67 | } |
68 | auto &using_bindings = entry->second; |
69 | if (using_bindings.size() > 1) { |
70 | string error = "Ambiguous column reference: column \"" + column_name + "\" can refer to either:\n" ; |
71 | for (auto &using_set_ref : using_bindings) { |
72 | auto &using_set = using_set_ref.get(); |
73 | string result_bindings; |
74 | for (auto &binding : using_set.bindings) { |
75 | if (result_bindings.empty()) { |
76 | result_bindings = "[" ; |
77 | } else { |
78 | result_bindings += ", " ; |
79 | } |
80 | result_bindings += binding; |
81 | result_bindings += "." ; |
82 | result_bindings += GetActualColumnName(binding, column_name); |
83 | } |
84 | error += result_bindings + "]" ; |
85 | } |
86 | throw BinderException(error); |
87 | } |
88 | for (auto &using_set : using_bindings) { |
89 | return &using_set.get(); |
90 | } |
91 | throw InternalException("Using binding found but no entries" ); |
92 | } |
93 | |
94 | optional_ptr<UsingColumnSet> BindContext::GetUsingBinding(const string &column_name, const string &binding_name) { |
95 | if (binding_name.empty()) { |
96 | throw InternalException("GetUsingBinding: expected non-empty binding_name" ); |
97 | } |
98 | auto entry = using_columns.find(x: column_name); |
99 | if (entry == using_columns.end()) { |
100 | return nullptr; |
101 | } |
102 | auto &using_bindings = entry->second; |
103 | for (auto &using_set_ref : using_bindings) { |
104 | auto &using_set = using_set_ref.get(); |
105 | auto &bindings = using_set.bindings; |
106 | if (bindings.find(x: binding_name) != bindings.end()) { |
107 | return &using_set; |
108 | } |
109 | } |
110 | return nullptr; |
111 | } |
112 | |
113 | void BindContext::RemoveUsingBinding(const string &column_name, UsingColumnSet &set) { |
114 | auto entry = using_columns.find(x: column_name); |
115 | if (entry == using_columns.end()) { |
116 | throw InternalException("Attempting to remove using binding that is not there" ); |
117 | } |
118 | auto &bindings = entry->second; |
119 | if (bindings.find(x: set) != bindings.end()) { |
120 | bindings.erase(x: set); |
121 | } |
122 | if (bindings.empty()) { |
123 | using_columns.erase(x: column_name); |
124 | } |
125 | } |
126 | |
127 | void BindContext::TransferUsingBinding(BindContext ¤t_context, optional_ptr<UsingColumnSet> current_set, |
128 | UsingColumnSet &new_set, const string &binding, const string &using_column) { |
129 | AddUsingBinding(column_name: using_column, set&: new_set); |
130 | if (current_set) { |
131 | current_context.RemoveUsingBinding(column_name: using_column, set&: *current_set); |
132 | } |
133 | } |
134 | |
135 | string BindContext::GetActualColumnName(const string &binding_name, const string &column_name) { |
136 | string error; |
137 | auto binding = GetBinding(name: binding_name, out_error&: error); |
138 | if (!binding) { |
139 | throw InternalException("No binding with name \"%s\"" , binding_name); |
140 | } |
141 | column_t binding_index; |
142 | if (!binding->TryGetBindingIndex(column_name, result&: binding_index)) { // LCOV_EXCL_START |
143 | throw InternalException("Binding with name \"%s\" does not have a column named \"%s\"" , binding_name, |
144 | column_name); |
145 | } // LCOV_EXCL_STOP |
146 | return binding->names[binding_index]; |
147 | } |
148 | |
149 | unordered_set<string> BindContext::GetMatchingBindings(const string &column_name) { |
150 | unordered_set<string> result; |
151 | for (auto &kv : bindings) { |
152 | auto binding = kv.second.get(); |
153 | if (binding->HasMatchingBinding(column_name)) { |
154 | result.insert(x: kv.first); |
155 | } |
156 | } |
157 | return result; |
158 | } |
159 | |
160 | unique_ptr<ParsedExpression> BindContext::ExpandGeneratedColumn(const string &table_name, const string &column_name) { |
161 | string error_message; |
162 | |
163 | auto binding = GetBinding(name: table_name, out_error&: error_message); |
164 | D_ASSERT(binding); |
165 | auto &table_binding = binding->Cast<TableBinding>(); |
166 | auto result = table_binding.ExpandGeneratedColumn(column_name); |
167 | result->alias = column_name; |
168 | return result; |
169 | } |
170 | |
171 | unique_ptr<ParsedExpression> BindContext::CreateColumnReference(const string &table_name, const string &column_name) { |
172 | string schema_name; |
173 | return CreateColumnReference(schema_name, table_name, column_name); |
174 | } |
175 | |
176 | static bool ColumnIsGenerated(Binding &binding, column_t index) { |
177 | if (binding.binding_type != BindingType::TABLE) { |
178 | return false; |
179 | } |
180 | auto &table_binding = binding.Cast<TableBinding>(); |
181 | auto catalog_entry = table_binding.GetStandardEntry(); |
182 | if (!catalog_entry) { |
183 | return false; |
184 | } |
185 | if (index == COLUMN_IDENTIFIER_ROW_ID) { |
186 | return false; |
187 | } |
188 | D_ASSERT(catalog_entry->type == CatalogType::TABLE_ENTRY); |
189 | auto &table_entry = catalog_entry->Cast<TableCatalogEntry>(); |
190 | return table_entry.GetColumn(idx: LogicalIndex(index)).Generated(); |
191 | } |
192 | |
193 | unique_ptr<ParsedExpression> BindContext::CreateColumnReference(const string &catalog_name, const string &schema_name, |
194 | const string &table_name, const string &column_name) { |
195 | string error_message; |
196 | vector<string> names; |
197 | if (!catalog_name.empty()) { |
198 | names.push_back(x: catalog_name); |
199 | } |
200 | if (!schema_name.empty()) { |
201 | names.push_back(x: schema_name); |
202 | } |
203 | names.push_back(x: table_name); |
204 | names.push_back(x: column_name); |
205 | |
206 | auto result = make_uniq<ColumnRefExpression>(args: std::move(names)); |
207 | auto binding = GetBinding(name: table_name, out_error&: error_message); |
208 | if (!binding) { |
209 | return std::move(result); |
210 | } |
211 | auto column_index = binding->GetBindingIndex(column_name); |
212 | if (ColumnIsGenerated(binding&: *binding, index: column_index)) { |
213 | return ExpandGeneratedColumn(table_name, column_name); |
214 | } else if (column_index < binding->names.size() && binding->names[column_index] != column_name) { |
215 | // because of case insensitivity in the binder we rename the column to the original name |
216 | // as it appears in the binding itself |
217 | result->alias = binding->names[column_index]; |
218 | } |
219 | return std::move(result); |
220 | } |
221 | |
222 | unique_ptr<ParsedExpression> BindContext::CreateColumnReference(const string &schema_name, const string &table_name, |
223 | const string &column_name) { |
224 | string catalog_name; |
225 | return CreateColumnReference(catalog_name, schema_name, table_name, column_name); |
226 | } |
227 | |
228 | optional_ptr<Binding> BindContext::GetCTEBinding(const string &ctename) { |
229 | auto match = cte_bindings.find(x: ctename); |
230 | if (match == cte_bindings.end()) { |
231 | return nullptr; |
232 | } |
233 | return match->second.get(); |
234 | } |
235 | |
236 | optional_ptr<Binding> BindContext::GetBinding(const string &name, string &out_error) { |
237 | auto match = bindings.find(x: name); |
238 | if (match == bindings.end()) { |
239 | // alias not found in this BindContext |
240 | vector<string> candidates; |
241 | for (auto &kv : bindings) { |
242 | candidates.push_back(x: kv.first); |
243 | } |
244 | string candidate_str = |
245 | StringUtil::CandidatesMessage(candidates: StringUtil::TopNLevenshtein(strings: candidates, target: name), candidate: "Candidate tables" ); |
246 | out_error = StringUtil::Format(fmt_str: "Referenced table \"%s\" not found!%s" , params: name, params: candidate_str); |
247 | return nullptr; |
248 | } |
249 | return match->second.get(); |
250 | } |
251 | |
252 | BindResult BindContext::BindColumn(ColumnRefExpression &colref, idx_t depth) { |
253 | if (!colref.IsQualified()) { |
254 | throw InternalException("Could not bind alias \"%s\"!" , colref.GetColumnName()); |
255 | } |
256 | |
257 | string error; |
258 | auto binding = GetBinding(name: colref.GetTableName(), out_error&: error); |
259 | if (!binding) { |
260 | return BindResult(error); |
261 | } |
262 | return binding->Bind(colref, depth); |
263 | } |
264 | |
265 | string BindContext::BindColumn(PositionalReferenceExpression &ref, string &table_name, string &column_name) { |
266 | idx_t total_columns = 0; |
267 | idx_t current_position = ref.index - 1; |
268 | for (auto &entry : bindings_list) { |
269 | auto &binding = entry.get(); |
270 | idx_t entry_column_count = binding.names.size(); |
271 | if (ref.index == 0) { |
272 | // this is a row id |
273 | table_name = binding.alias; |
274 | column_name = "rowid" ; |
275 | return string(); |
276 | } |
277 | if (current_position < entry_column_count) { |
278 | table_name = binding.alias; |
279 | column_name = binding.names[current_position]; |
280 | return string(); |
281 | } else { |
282 | total_columns += entry_column_count; |
283 | current_position -= entry_column_count; |
284 | } |
285 | } |
286 | return StringUtil::Format(fmt_str: "Positional reference %d out of range (total %d columns)" , params: ref.index, params: total_columns); |
287 | } |
288 | |
289 | unique_ptr<ColumnRefExpression> BindContext::PositionToColumn(PositionalReferenceExpression &ref) { |
290 | string table_name, column_name; |
291 | |
292 | string error = BindColumn(ref, table_name, column_name); |
293 | if (!error.empty()) { |
294 | throw BinderException(error); |
295 | } |
296 | return make_uniq<ColumnRefExpression>(args&: column_name, args&: table_name); |
297 | } |
298 | |
299 | bool BindContext::CheckExclusionList(StarExpression &expr, const string &column_name, |
300 | vector<unique_ptr<ParsedExpression>> &new_select_list, |
301 | case_insensitive_set_t &excluded_columns) { |
302 | if (expr.exclude_list.find(x: column_name) != expr.exclude_list.end()) { |
303 | excluded_columns.insert(x: column_name); |
304 | return true; |
305 | } |
306 | auto entry = expr.replace_list.find(x: column_name); |
307 | if (entry != expr.replace_list.end()) { |
308 | auto new_entry = entry->second->Copy(); |
309 | new_entry->alias = entry->first; |
310 | excluded_columns.insert(x: entry->first); |
311 | new_select_list.push_back(x: std::move(new_entry)); |
312 | return true; |
313 | } |
314 | return false; |
315 | } |
316 | |
317 | void BindContext::GenerateAllColumnExpressions(StarExpression &expr, |
318 | vector<unique_ptr<ParsedExpression>> &new_select_list) { |
319 | if (bindings_list.empty()) { |
320 | throw BinderException("* expression without FROM clause!" ); |
321 | } |
322 | case_insensitive_set_t excluded_columns; |
323 | if (expr.relation_name.empty()) { |
324 | // SELECT * case |
325 | // bind all expressions of each table in-order |
326 | reference_set_t<UsingColumnSet> handled_using_columns; |
327 | for (auto &entry : bindings_list) { |
328 | auto &binding = entry.get(); |
329 | for (auto &column_name : binding.names) { |
330 | if (CheckExclusionList(expr, column_name, new_select_list, excluded_columns)) { |
331 | continue; |
332 | } |
333 | // check if this column is a USING column |
334 | auto using_binding_ptr = GetUsingBinding(column_name, binding_name: binding.alias); |
335 | if (using_binding_ptr) { |
336 | auto &using_binding = *using_binding_ptr; |
337 | // it is! |
338 | // check if we have already emitted the using column |
339 | if (handled_using_columns.find(x: using_binding) != handled_using_columns.end()) { |
340 | // we have! bail out |
341 | continue; |
342 | } |
343 | // we have not! output the using column |
344 | if (using_binding.primary_binding.empty()) { |
345 | // no primary binding: output a coalesce |
346 | auto coalesce = make_uniq<OperatorExpression>(args: ExpressionType::OPERATOR_COALESCE); |
347 | for (auto &child_binding : using_binding.bindings) { |
348 | coalesce->children.push_back(x: make_uniq<ColumnRefExpression>(args&: column_name, args: child_binding)); |
349 | } |
350 | coalesce->alias = column_name; |
351 | new_select_list.push_back(x: std::move(coalesce)); |
352 | } else { |
353 | // primary binding: output the qualified column ref |
354 | new_select_list.push_back( |
355 | x: make_uniq<ColumnRefExpression>(args&: column_name, args&: using_binding.primary_binding)); |
356 | } |
357 | handled_using_columns.insert(x: using_binding); |
358 | continue; |
359 | } |
360 | new_select_list.push_back(x: make_uniq<ColumnRefExpression>(args&: column_name, args&: binding.alias)); |
361 | } |
362 | } |
363 | } else { |
364 | // SELECT tbl.* case |
365 | // SELECT struct.* case |
366 | string error; |
367 | auto binding = GetBinding(name: expr.relation_name, out_error&: error); |
368 | bool is_struct_ref = false; |
369 | if (!binding) { |
370 | auto binding_name = GetMatchingBinding(column_name: expr.relation_name); |
371 | if (binding_name.empty()) { |
372 | throw BinderException(error); |
373 | } |
374 | binding = bindings[binding_name].get(); |
375 | is_struct_ref = true; |
376 | } |
377 | |
378 | if (is_struct_ref) { |
379 | auto col_idx = binding->GetBindingIndex(column_name: expr.relation_name); |
380 | auto col_type = binding->types[col_idx]; |
381 | if (col_type.id() != LogicalTypeId::STRUCT) { |
382 | throw BinderException(StringUtil::Format( |
383 | fmt_str: "Cannot extract field from expression \"%s\" because it is not a struct" , params: expr.ToString())); |
384 | } |
385 | auto &struct_children = StructType::GetChildTypes(type: col_type); |
386 | vector<string> column_names(3); |
387 | column_names[0] = binding->alias; |
388 | column_names[1] = expr.relation_name; |
389 | for (auto &child : struct_children) { |
390 | if (CheckExclusionList(expr, column_name: child.first, new_select_list, excluded_columns)) { |
391 | continue; |
392 | } |
393 | column_names[2] = child.first; |
394 | new_select_list.push_back(x: make_uniq<ColumnRefExpression>(args&: column_names)); |
395 | } |
396 | } else { |
397 | for (auto &column_name : binding->names) { |
398 | if (CheckExclusionList(expr, column_name, new_select_list, excluded_columns)) { |
399 | continue; |
400 | } |
401 | |
402 | new_select_list.push_back(x: make_uniq<ColumnRefExpression>(args&: column_name, args&: binding->alias)); |
403 | } |
404 | } |
405 | } |
406 | for (auto &excluded : expr.exclude_list) { |
407 | if (excluded_columns.find(x: excluded) == excluded_columns.end()) { |
408 | throw BinderException("Column \"%s\" in EXCLUDE list not found in %s" , excluded, |
409 | expr.relation_name.empty() ? "FROM clause" : expr.relation_name.c_str()); |
410 | } |
411 | } |
412 | for (auto &entry : expr.replace_list) { |
413 | if (excluded_columns.find(x: entry.first) == excluded_columns.end()) { |
414 | throw BinderException("Column \"%s\" in REPLACE list not found in %s" , entry.first, |
415 | expr.relation_name.empty() ? "FROM clause" : expr.relation_name.c_str()); |
416 | } |
417 | } |
418 | } |
419 | |
420 | void BindContext::GetTypesAndNames(vector<string> &result_names, vector<LogicalType> &result_types) { |
421 | for (auto &binding_entry : bindings_list) { |
422 | auto &binding = binding_entry.get(); |
423 | D_ASSERT(binding.names.size() == binding.types.size()); |
424 | for (idx_t i = 0; i < binding.names.size(); i++) { |
425 | result_names.push_back(x: binding.names[i]); |
426 | result_types.push_back(x: binding.types[i]); |
427 | } |
428 | } |
429 | } |
430 | |
431 | void BindContext::AddBinding(const string &alias, unique_ptr<Binding> binding) { |
432 | if (bindings.find(x: alias) != bindings.end()) { |
433 | throw BinderException("Duplicate alias \"%s\" in query!" , alias); |
434 | } |
435 | bindings_list.push_back(x: *binding); |
436 | bindings[alias] = std::move(binding); |
437 | } |
438 | |
439 | void BindContext::AddBaseTable(idx_t index, const string &alias, const vector<string> &names, |
440 | const vector<LogicalType> &types, vector<column_t> &bound_column_ids, |
441 | StandardEntry *entry, bool add_row_id) { |
442 | AddBinding(alias, binding: make_uniq<TableBinding>(args: alias, args: types, args: names, args&: bound_column_ids, args&: entry, args&: index, args&: add_row_id)); |
443 | } |
444 | |
445 | void BindContext::AddTableFunction(idx_t index, const string &alias, const vector<string> &names, |
446 | const vector<LogicalType> &types, vector<column_t> &bound_column_ids, |
447 | StandardEntry *entry) { |
448 | AddBinding(alias, binding: make_uniq<TableBinding>(args: alias, args: types, args: names, args&: bound_column_ids, args&: entry, args&: index)); |
449 | } |
450 | |
451 | static string AddColumnNameToBinding(const string &base_name, case_insensitive_set_t ¤t_names) { |
452 | idx_t index = 1; |
453 | string name = base_name; |
454 | while (current_names.find(x: name) != current_names.end()) { |
455 | name = base_name + ":" + std::to_string(val: index++); |
456 | } |
457 | current_names.insert(x: name); |
458 | return name; |
459 | } |
460 | |
461 | vector<string> BindContext::AliasColumnNames(const string &table_name, const vector<string> &names, |
462 | const vector<string> &column_aliases) { |
463 | vector<string> result; |
464 | if (column_aliases.size() > names.size()) { |
465 | throw BinderException("table \"%s\" has %lld columns available but %lld columns specified" , table_name, |
466 | names.size(), column_aliases.size()); |
467 | } |
468 | case_insensitive_set_t current_names; |
469 | // use any provided column aliases first |
470 | for (idx_t i = 0; i < column_aliases.size(); i++) { |
471 | result.push_back(x: AddColumnNameToBinding(base_name: column_aliases[i], current_names)); |
472 | } |
473 | // if not enough aliases were provided, use the default names for remaining columns |
474 | for (idx_t i = column_aliases.size(); i < names.size(); i++) { |
475 | result.push_back(x: AddColumnNameToBinding(base_name: names[i], current_names)); |
476 | } |
477 | return result; |
478 | } |
479 | |
480 | void BindContext::AddSubquery(idx_t index, const string &alias, SubqueryRef &ref, BoundQueryNode &subquery) { |
481 | auto names = AliasColumnNames(table_name: alias, names: subquery.names, column_aliases: ref.column_name_alias); |
482 | AddGenericBinding(index, alias, names, types: subquery.types); |
483 | } |
484 | |
485 | void BindContext::AddEntryBinding(idx_t index, const string &alias, const vector<string> &names, |
486 | const vector<LogicalType> &types, StandardEntry &entry) { |
487 | AddBinding(alias, binding: make_uniq<EntryBinding>(args: alias, args: types, args: names, args&: index, args&: entry)); |
488 | } |
489 | |
490 | void BindContext::AddView(idx_t index, const string &alias, SubqueryRef &ref, BoundQueryNode &subquery, |
491 | ViewCatalogEntry *view) { |
492 | auto names = AliasColumnNames(table_name: alias, names: subquery.names, column_aliases: ref.column_name_alias); |
493 | AddEntryBinding(index, alias, names, types: subquery.types, entry&: view->Cast<StandardEntry>()); |
494 | } |
495 | |
496 | void BindContext::AddSubquery(idx_t index, const string &alias, TableFunctionRef &ref, BoundQueryNode &subquery) { |
497 | auto names = AliasColumnNames(table_name: alias, names: subquery.names, column_aliases: ref.column_name_alias); |
498 | AddGenericBinding(index, alias, names, types: subquery.types); |
499 | } |
500 | |
501 | void BindContext::AddGenericBinding(idx_t index, const string &alias, const vector<string> &names, |
502 | const vector<LogicalType> &types) { |
503 | AddBinding(alias, binding: make_uniq<Binding>(args: BindingType::BASE, args: alias, args: types, args: names, args&: index)); |
504 | } |
505 | |
506 | void BindContext::AddCTEBinding(idx_t index, const string &alias, const vector<string> &names, |
507 | const vector<LogicalType> &types) { |
508 | auto binding = make_shared<Binding>(args: BindingType::BASE, args: alias, args: types, args: names, args&: index); |
509 | |
510 | if (cte_bindings.find(x: alias) != cte_bindings.end()) { |
511 | throw BinderException("Duplicate alias \"%s\" in query!" , alias); |
512 | } |
513 | cte_bindings[alias] = std::move(binding); |
514 | cte_references[alias] = std::make_shared<idx_t>(args: 0); |
515 | } |
516 | |
517 | void BindContext::AddContext(BindContext other) { |
518 | for (auto &binding : other.bindings) { |
519 | if (bindings.find(x: binding.first) != bindings.end()) { |
520 | throw BinderException("Duplicate alias \"%s\" in query!" , binding.first); |
521 | } |
522 | bindings[binding.first] = std::move(binding.second); |
523 | } |
524 | for (auto &binding : other.bindings_list) { |
525 | bindings_list.push_back(x: binding); |
526 | } |
527 | for (auto &entry : other.using_columns) { |
528 | for (auto &alias : entry.second) { |
529 | #ifdef DEBUG |
530 | for (auto &other_alias : using_columns[entry.first]) { |
531 | for (auto &col : alias.get().bindings) { |
532 | D_ASSERT(other_alias.get().bindings.find(col) == other_alias.get().bindings.end()); |
533 | } |
534 | } |
535 | #endif |
536 | using_columns[entry.first].insert(x: alias); |
537 | } |
538 | } |
539 | } |
540 | |
541 | void BindContext::RemoveContext(vector<reference<Binding>> &other_bindings_list) { |
542 | for (auto &other_binding : other_bindings_list) { |
543 | auto it = std::remove_if(first: bindings_list.begin(), last: bindings_list.end(), pred: [other_binding](reference<Binding> x) { |
544 | return x.get().alias == other_binding.get().alias; |
545 | }); |
546 | bindings_list.erase(first: it, last: bindings_list.end()); |
547 | } |
548 | |
549 | for (auto &other_binding : other_bindings_list) { |
550 | auto &alias = other_binding.get().alias; |
551 | if (bindings.find(x: alias) != bindings.end()) { |
552 | bindings.erase(x: alias); |
553 | } |
554 | } |
555 | } |
556 | |
557 | } // namespace duckdb |
558 | |