Coverage Report

Created: 2025-06-12 07:25

/src/duckdb/src/planner/binder/statement/bind_vacuum.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "duckdb/parser/expression/columnref_expression.hpp"
2
#include "duckdb/parser/statement/vacuum_statement.hpp"
3
#include "duckdb/planner/binder.hpp"
4
#include "duckdb/planner/operator/logical_get.hpp"
5
#include "duckdb/planner/operator/logical_projection.hpp"
6
#include "duckdb/planner/operator/logical_vacuum.hpp"
7
#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
8
9
namespace duckdb {
10
11
44
void Binder::BindVacuumTable(LogicalVacuum &vacuum, unique_ptr<LogicalOperator> &root) {
12
44
  auto &info = vacuum.GetInfo();
13
44
  if (!info.has_table) {
14
23
    return;
15
23
  }
16
17
21
  D_ASSERT(vacuum.column_id_map.empty());
18
21
  auto bound_table = Bind(*info.ref);
19
21
  if (bound_table->type != TableReferenceType::BASE_TABLE) {
20
0
    throw InvalidInputException("can only vacuum or analyze base tables");
21
0
  }
22
21
  auto ref = unique_ptr_cast<BoundTableRef, BoundBaseTableRef>(std::move(bound_table));
23
21
  auto &table = ref->table;
24
21
  vacuum.SetTable(table);
25
26
21
  vector<unique_ptr<Expression>> select_list;
27
21
  auto &columns = info.columns;
28
21
  if (columns.empty()) {
29
    // Empty means ALL columns should be vacuumed/analyzed
30
0
    for (auto &col : table.GetColumns().Physical()) {
31
0
      columns.push_back(col.GetName());
32
0
    }
33
0
  }
34
35
21
  case_insensitive_set_t column_name_set;
36
21
  vector<string> non_generated_column_names;
37
21
  for (auto &col_name : columns) {
38
0
    if (column_name_set.count(col_name) > 0) {
39
0
      throw BinderException("cannot vacuum or analyze the same column twice, i.e., there is a duplicate entry in "
40
0
                            "the list of column names");
41
0
    }
42
0
    column_name_set.insert(col_name);
43
0
    if (!table.ColumnExists(col_name)) {
44
0
      throw BinderException("Column with name \"%s\" does not exist", col_name);
45
0
    }
46
0
    auto &col = table.GetColumn(col_name);
47
    // ignore generated column
48
0
    if (col.Generated()) {
49
0
      throw BinderException(
50
0
          "cannot vacuum or analyze generated column \"%s\" - specify non-generated columns to vacuum or analyze",
51
0
          col.GetName());
52
0
    }
53
0
    non_generated_column_names.push_back(col_name);
54
0
    ColumnRefExpression colref(col_name, table.name);
55
0
    auto result = bind_context.BindColumn(colref, 0);
56
0
    if (result.HasError()) {
57
0
      result.error.Throw();
58
0
    }
59
0
    select_list.push_back(std::move(result.expression));
60
0
  }
61
21
  info.columns = std::move(non_generated_column_names);
62
63
21
  auto table_scan = CreatePlan(*ref);
64
21
  D_ASSERT(table_scan->type == LogicalOperatorType::LOGICAL_GET);
65
66
21
  auto &get = table_scan->Cast<LogicalGet>();
67
68
21
  auto &column_ids = get.GetColumnIds();
69
21
  D_ASSERT(select_list.size() == column_ids.size());
70
21
  D_ASSERT(info.columns.size() == column_ids.size());
71
21
  for (idx_t i = 0; i < column_ids.size(); i++) {
72
0
    vacuum.column_id_map[i] = table.GetColumns().LogicalToPhysical(column_ids[i].ToLogical()).index;
73
0
  }
74
75
21
  auto projection = make_uniq<LogicalProjection>(GenerateTableIndex(), std::move(select_list));
76
21
  projection->children.push_back(std::move(table_scan));
77
78
21
  root = std::move(projection);
79
21
}
80
81
44
BoundStatement Binder::Bind(VacuumStatement &stmt) {
82
44
  BoundStatement result;
83
84
44
  unique_ptr<LogicalOperator> root;
85
86
44
  auto vacuum = make_uniq<LogicalVacuum>(std::move(stmt.info));
87
44
  BindVacuumTable(*vacuum, root);
88
44
  if (root) {
89
0
    vacuum->children.push_back(std::move(root));
90
0
  }
91
92
44
  result.names = {"Success"};
93
44
  result.types = {LogicalType::BOOLEAN};
94
44
  result.plan = std::move(vacuum);
95
96
44
  auto &properties = GetStatementProperties();
97
44
  properties.return_type = StatementReturnType::NOTHING;
98
44
  return result;
99
44
}
100
101
} // namespace duckdb