1#include "duckdb/planner/expression_binder/check_binder.hpp"
2
3#include "duckdb/planner/table_binding.hpp"
4#include "duckdb/parser/expression/columnref_expression.hpp"
5#include "duckdb/planner/expression/bound_reference_expression.hpp"
6
7namespace duckdb {
8
9CheckBinder::CheckBinder(Binder &binder, ClientContext &context, string table_p, const ColumnList &columns,
10 physical_index_set_t &bound_columns)
11 : ExpressionBinder(binder, context), table(std::move(table_p)), columns(columns), bound_columns(bound_columns) {
12 target_type = LogicalType::INTEGER;
13}
14
15BindResult CheckBinder::BindExpression(unique_ptr<ParsedExpression> &expr_ptr, idx_t depth, bool root_expression) {
16 auto &expr = *expr_ptr;
17 switch (expr.GetExpressionClass()) {
18 case ExpressionClass::WINDOW:
19 return BindResult("window functions are not allowed in check constraints");
20 case ExpressionClass::SUBQUERY:
21 return BindResult("cannot use subquery in check constraint");
22 case ExpressionClass::COLUMN_REF:
23 return BindCheckColumn(expr&: expr.Cast<ColumnRefExpression>());
24 default:
25 return ExpressionBinder::BindExpression(expr_ptr, depth);
26 }
27}
28
29string CheckBinder::UnsupportedAggregateMessage() {
30 return "aggregate functions are not allowed in check constraints";
31}
32
33BindResult ExpressionBinder::BindQualifiedColumnName(ColumnRefExpression &colref, const string &table_name) {
34 idx_t struct_start = 0;
35 if (colref.column_names[0] == table_name) {
36 struct_start++;
37 }
38 auto result = make_uniq_base<ParsedExpression, ColumnRefExpression>(args&: colref.column_names.back());
39 for (idx_t i = struct_start; i + 1 < colref.column_names.size(); i++) {
40 result = CreateStructExtract(base: std::move(result), field_name: colref.column_names[i]);
41 }
42 return BindExpression(expr_ptr&: result, depth: 0);
43}
44
45BindResult CheckBinder::BindCheckColumn(ColumnRefExpression &colref) {
46
47 // if this is a lambda parameters, then we temporarily add a BoundLambdaRef,
48 // which we capture and remove later
49 if (lambda_bindings) {
50 for (idx_t i = 0; i < lambda_bindings->size(); i++) {
51 if (colref.GetColumnName() == (*lambda_bindings)[i].dummy_name) {
52 // FIXME: support lambdas in CHECK constraints
53 // FIXME: like so: return (*lambda_bindings)[i].Bind(colref, i, depth);
54 throw NotImplementedException("Lambda functions are currently not supported in CHECK constraints.");
55 }
56 }
57 }
58
59 if (colref.column_names.size() > 1) {
60 return BindQualifiedColumnName(colref, table_name: table);
61 }
62 if (!columns.ColumnExists(name: colref.column_names[0])) {
63 throw BinderException("Table does not contain column %s referenced in check constraint!",
64 colref.column_names[0]);
65 }
66 auto &col = columns.GetColumn(name: colref.column_names[0]);
67 if (col.Generated()) {
68 auto bound_expression = col.GeneratedExpression().Copy();
69 return BindExpression(expr_ptr&: bound_expression, depth: 0, root_expression: false);
70 }
71 bound_columns.insert(x: col.Physical());
72 D_ASSERT(col.StorageOid() != DConstants::INVALID_INDEX);
73 return BindResult(make_uniq<BoundReferenceExpression>(args: col.Type(), args: col.StorageOid()));
74}
75
76} // namespace duckdb
77