1#include "duckdb/function/table/system_functions.hpp"
2
3#include "duckdb/catalog/catalog.hpp"
4#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp"
5#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
6#include "duckdb/common/exception.hpp"
7#include "duckdb/main/client_context.hpp"
8#include "duckdb/main/client_data.hpp"
9#include "duckdb/parser/constraint.hpp"
10#include "duckdb/parser/constraints/unique_constraint.hpp"
11#include "duckdb/storage/data_table.hpp"
12#include "duckdb/storage/table_storage_info.hpp"
13
14namespace duckdb {
15
16struct DuckDBTablesData : public GlobalTableFunctionState {
17 DuckDBTablesData() : offset(0) {
18 }
19
20 vector<reference<CatalogEntry>> entries;
21 idx_t offset;
22};
23
24static unique_ptr<FunctionData> DuckDBTablesBind(ClientContext &context, TableFunctionBindInput &input,
25 vector<LogicalType> &return_types, vector<string> &names) {
26 names.emplace_back(args: "database_name");
27 return_types.emplace_back(args: LogicalType::VARCHAR);
28
29 names.emplace_back(args: "database_oid");
30 return_types.emplace_back(args: LogicalType::BIGINT);
31
32 names.emplace_back(args: "schema_name");
33 return_types.emplace_back(args: LogicalType::VARCHAR);
34
35 names.emplace_back(args: "schema_oid");
36 return_types.emplace_back(args: LogicalType::BIGINT);
37
38 names.emplace_back(args: "table_name");
39 return_types.emplace_back(args: LogicalType::VARCHAR);
40
41 names.emplace_back(args: "table_oid");
42 return_types.emplace_back(args: LogicalType::BIGINT);
43
44 names.emplace_back(args: "internal");
45 return_types.emplace_back(args: LogicalType::BOOLEAN);
46
47 names.emplace_back(args: "temporary");
48 return_types.emplace_back(args: LogicalType::BOOLEAN);
49
50 names.emplace_back(args: "has_primary_key");
51 return_types.emplace_back(args: LogicalType::BOOLEAN);
52
53 names.emplace_back(args: "estimated_size");
54 return_types.emplace_back(args: LogicalType::BIGINT);
55
56 names.emplace_back(args: "column_count");
57 return_types.emplace_back(args: LogicalType::BIGINT);
58
59 names.emplace_back(args: "index_count");
60 return_types.emplace_back(args: LogicalType::BIGINT);
61
62 names.emplace_back(args: "check_constraint_count");
63 return_types.emplace_back(args: LogicalType::BIGINT);
64
65 names.emplace_back(args: "sql");
66 return_types.emplace_back(args: LogicalType::VARCHAR);
67
68 return nullptr;
69}
70
71unique_ptr<GlobalTableFunctionState> DuckDBTablesInit(ClientContext &context, TableFunctionInitInput &input) {
72 auto result = make_uniq<DuckDBTablesData>();
73
74 // scan all the schemas for tables and collect themand collect them
75 auto schemas = Catalog::GetAllSchemas(context);
76 for (auto &schema : schemas) {
77 schema.get().Scan(context, type: CatalogType::TABLE_ENTRY,
78 callback: [&](CatalogEntry &entry) { result->entries.push_back(x: entry); });
79 };
80 return std::move(result);
81}
82
83static bool TableHasPrimaryKey(TableCatalogEntry &table) {
84 for (auto &constraint : table.GetConstraints()) {
85 if (constraint->type == ConstraintType::UNIQUE) {
86 auto &unique = constraint->Cast<UniqueConstraint>();
87 if (unique.is_primary_key) {
88 return true;
89 }
90 }
91 }
92 return false;
93}
94
95static idx_t CheckConstraintCount(TableCatalogEntry &table) {
96 idx_t check_count = 0;
97 for (auto &constraint : table.GetConstraints()) {
98 if (constraint->type == ConstraintType::CHECK) {
99 check_count++;
100 }
101 }
102 return check_count;
103}
104
105void DuckDBTablesFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) {
106 auto &data = data_p.global_state->Cast<DuckDBTablesData>();
107 if (data.offset >= data.entries.size()) {
108 // finished returning values
109 return;
110 }
111 // start returning values
112 // either fill up the chunk or return all the remaining columns
113 idx_t count = 0;
114 while (data.offset < data.entries.size() && count < STANDARD_VECTOR_SIZE) {
115 auto &entry = data.entries[data.offset++].get();
116
117 if (entry.type != CatalogType::TABLE_ENTRY) {
118 continue;
119 }
120 auto &table = entry.Cast<TableCatalogEntry>();
121 auto storage_info = table.GetStorageInfo(context);
122 // return values:
123 idx_t col = 0;
124 // database_name, VARCHAR
125 output.SetValue(col_idx: col++, index: count, val: table.catalog.GetName());
126 // database_oid, BIGINT
127 output.SetValue(col_idx: col++, index: count, val: Value::BIGINT(value: table.catalog.GetOid()));
128 // schema_name, LogicalType::VARCHAR
129 output.SetValue(col_idx: col++, index: count, val: Value(table.schema.name));
130 // schema_oid, LogicalType::BIGINT
131 output.SetValue(col_idx: col++, index: count, val: Value::BIGINT(value: table.schema.oid));
132 // table_name, LogicalType::VARCHAR
133 output.SetValue(col_idx: col++, index: count, val: Value(table.name));
134 // table_oid, LogicalType::BIGINT
135 output.SetValue(col_idx: col++, index: count, val: Value::BIGINT(value: table.oid));
136 // internal, LogicalType::BOOLEAN
137 output.SetValue(col_idx: col++, index: count, val: Value::BOOLEAN(value: table.internal));
138 // temporary, LogicalType::BOOLEAN
139 output.SetValue(col_idx: col++, index: count, val: Value::BOOLEAN(value: table.temporary));
140 // has_primary_key, LogicalType::BOOLEAN
141 output.SetValue(col_idx: col++, index: count, val: Value::BOOLEAN(value: TableHasPrimaryKey(table)));
142 // estimated_size, LogicalType::BIGINT
143 Value card_val =
144 storage_info.cardinality == DConstants::INVALID_INDEX ? Value() : Value::BIGINT(value: storage_info.cardinality);
145 output.SetValue(col_idx: col++, index: count, val: card_val);
146 // column_count, LogicalType::BIGINT
147 output.SetValue(col_idx: col++, index: count, val: Value::BIGINT(value: table.GetColumns().LogicalColumnCount()));
148 // index_count, LogicalType::BIGINT
149 output.SetValue(col_idx: col++, index: count, val: Value::BIGINT(value: storage_info.index_info.size()));
150 // check_constraint_count, LogicalType::BIGINT
151 output.SetValue(col_idx: col++, index: count, val: Value::BIGINT(value: CheckConstraintCount(table)));
152 // sql, LogicalType::VARCHAR
153 output.SetValue(col_idx: col++, index: count, val: Value(table.ToSQL()));
154
155 count++;
156 }
157 output.SetCardinality(count);
158}
159
160void DuckDBTablesFun::RegisterFunction(BuiltinFunctions &set) {
161 set.AddFunction(function: TableFunction("duckdb_tables", {}, DuckDBTablesFunction, DuckDBTablesBind, DuckDBTablesInit));
162}
163
164} // namespace duckdb
165