Coverage Report

Created: 2025-11-15 07:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/src/planner/binder/statement/bind_simple.cpp
Line
Count
Source
1
#include "duckdb/catalog/catalog.hpp"
2
#include "duckdb/catalog/catalog_entry/duck_table_entry.hpp"
3
#include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp"
4
#include "duckdb/catalog/duck_catalog.hpp"
5
#include "duckdb/execution/index/art/art.hpp"
6
#include "duckdb/function/table/table_scan.hpp"
7
#include "duckdb/parser/constraints/unique_constraint.hpp"
8
#include "duckdb/parser/parsed_data/comment_on_column_info.hpp"
9
#include "duckdb/parser/statement/alter_statement.hpp"
10
#include "duckdb/parser/statement/transaction_statement.hpp"
11
#include "duckdb/parser/tableref/basetableref.hpp"
12
#include "duckdb/planner/binder.hpp"
13
#include "duckdb/planner/constraints/bound_unique_constraint.hpp"
14
#include "duckdb/planner/expression_binder/index_binder.hpp"
15
#include "duckdb/planner/operator/logical_create_index.hpp"
16
#include "duckdb/planner/operator/logical_get.hpp"
17
#include "duckdb/planner/operator/logical_simple.hpp"
18
19
namespace duckdb {
20
21
unique_ptr<LogicalOperator> DuckCatalog::BindAlterAddIndex(Binder &binder, TableCatalogEntry &table_entry,
22
                                                           unique_ptr<LogicalOperator> plan,
23
                                                           unique_ptr<CreateIndexInfo> create_info,
24
0
                                                           unique_ptr<AlterTableInfo> alter_info) {
25
0
  D_ASSERT(plan->type == LogicalOperatorType::LOGICAL_GET);
26
0
  IndexBinder index_binder(binder, binder.context);
27
0
  return index_binder.BindCreateIndex(binder.context, std::move(create_info), table_entry, std::move(plan),
28
0
                                      std::move(alter_info));
29
0
}
30
31
BoundStatement Binder::BindAlterAddIndex(BoundStatement &result, CatalogEntry &entry,
32
0
                                         unique_ptr<AlterInfo> alter_info) {
33
0
  auto &table_info = alter_info->Cast<AlterTableInfo>();
34
0
  auto &constraint_info = table_info.Cast<AddConstraintInfo>();
35
0
  auto &table = entry.Cast<TableCatalogEntry>();
36
0
  auto &column_list = table.GetColumns();
37
38
0
  auto bound_constraint = BindUniqueConstraint(*constraint_info.constraint, table_info.name, column_list);
39
0
  auto &bound_unique = bound_constraint->Cast<BoundUniqueConstraint>();
40
41
  // Create the CreateIndexInfo.
42
0
  auto create_index_info = make_uniq<CreateIndexInfo>();
43
0
  create_index_info->table = table_info.name;
44
0
  create_index_info->index_type = ART::TYPE_NAME;
45
0
  create_index_info->constraint_type = IndexConstraintType::PRIMARY;
46
47
0
  for (const auto &physical_index : bound_unique.keys) {
48
0
    auto &col = column_list.GetColumn(physical_index);
49
0
    unique_ptr<ParsedExpression> parsed = make_uniq<ColumnRefExpression>(col.GetName(), table_info.name);
50
0
    create_index_info->expressions.push_back(parsed->Copy());
51
0
    create_index_info->parsed_expressions.push_back(parsed->Copy());
52
0
  }
53
54
0
  auto unique_constraint = constraint_info.constraint->Cast<UniqueConstraint>();
55
0
  auto index_name = unique_constraint.GetName(table_info.name);
56
0
  create_index_info->index_name = index_name;
57
0
  D_ASSERT(!create_index_info->index_name.empty());
58
59
  // Plan the table scan.
60
0
  TableDescription table_description(table_info.catalog, table_info.schema, table_info.name);
61
0
  auto table_ref = make_uniq<BaseTableRef>(table_description);
62
0
  auto bound_table = Bind(*table_ref);
63
0
  if (bound_table.plan->type != LogicalOperatorType::LOGICAL_GET) {
64
0
    throw BinderException("can only add an index to a base table");
65
0
  }
66
0
  auto &get = bound_table.plan->Cast<LogicalGet>();
67
0
  get.names = column_list.GetColumnNames();
68
69
0
  auto alter_table_info = unique_ptr_cast<AlterInfo, AlterTableInfo>(std::move(alter_info));
70
0
  result.plan = table.catalog.BindAlterAddIndex(*this, table, std::move(bound_table.plan),
71
0
                                                std::move(create_index_info), std::move(alter_table_info));
72
0
  return std::move(result);
73
0
}
74
75
14
BoundStatement Binder::Bind(AlterStatement &stmt) {
76
14
  BoundStatement result;
77
14
  result.names = {"Success"};
78
14
  result.types = {LogicalType::BOOLEAN};
79
80
  // Special handling for ALTER DATABASE - doesn't use schema binding
81
14
  if (stmt.info->type == AlterType::ALTER_DATABASE) {
82
0
    auto &properties = GetStatementProperties();
83
0
    properties.return_type = StatementReturnType::NOTHING;
84
0
    properties.RegisterDBModify(Catalog::GetSystemCatalog(context), context);
85
0
    result.plan = make_uniq<LogicalSimple>(LogicalOperatorType::LOGICAL_ALTER, std::move(stmt.info));
86
0
    return result;
87
0
  }
88
89
14
  BindSchemaOrCatalog(stmt.info->catalog, stmt.info->schema);
90
91
14
  optional_ptr<CatalogEntry> entry;
92
14
  if (stmt.info->type == AlterType::SET_COLUMN_COMMENT) {
93
    // Extra step for column comments: They can alter a table or a view, and we resolve that here.
94
0
    auto &info = stmt.info->Cast<SetColumnCommentInfo>();
95
0
    entry = info.TryResolveCatalogEntry(entry_retriever);
96
97
14
  } else {
98
    // For any other ALTER, we retrieve the catalog entry directly.
99
14
    EntryLookupInfo lookup_info(stmt.info->GetCatalogType(), stmt.info->name);
100
14
    entry = entry_retriever.GetEntry(stmt.info->catalog, stmt.info->schema, lookup_info, stmt.info->if_not_found);
101
14
  }
102
103
14
  auto &properties = GetStatementProperties();
104
14
  properties.return_type = StatementReturnType::NOTHING;
105
14
  if (!entry) {
106
0
    result.plan = make_uniq<LogicalSimple>(LogicalOperatorType::LOGICAL_ALTER, std::move(stmt.info));
107
0
    return result;
108
0
  }
109
110
14
  D_ASSERT(!entry->deleted);
111
14
  auto &catalog = entry->ParentCatalog();
112
14
  if (catalog.IsSystemCatalog()) {
113
0
    throw BinderException("Can not comment on System Catalog entries");
114
0
  }
115
14
  if (!entry->temporary) {
116
    // We can only alter temporary tables and views in read-only mode.
117
0
    properties.RegisterDBModify(catalog, context);
118
0
  }
119
14
  stmt.info->catalog = catalog.GetName();
120
14
  stmt.info->schema = entry->ParentSchema().name;
121
122
14
  if (!stmt.info->IsAddPrimaryKey()) {
123
0
    result.plan = make_uniq<LogicalSimple>(LogicalOperatorType::LOGICAL_ALTER, std::move(stmt.info));
124
0
    return result;
125
0
  }
126
127
14
  return BindAlterAddIndex(result, *entry, std::move(stmt.info));
128
14
}
129
130
18.8k
BoundStatement Binder::Bind(TransactionStatement &stmt) {
131
18.8k
  auto &properties = GetStatementProperties();
132
133
  // Transaction statements do not require a valid transaction.
134
18.8k
  properties.requires_valid_transaction = stmt.info->type == TransactionType::BEGIN_TRANSACTION;
135
136
18.8k
  BoundStatement result;
137
18.8k
  result.names = {"Success"};
138
18.8k
  result.types = {LogicalType::BOOLEAN};
139
18.8k
  result.plan = make_uniq<LogicalSimple>(LogicalOperatorType::LOGICAL_TRANSACTION, std::move(stmt.info));
140
18.8k
  properties.return_type = StatementReturnType::NOTHING;
141
18.8k
  return result;
142
18.8k
}
143
144
} // namespace duckdb