1#include "duckdb/function/table/system_functions.hpp"
2
3#include "duckdb/catalog/catalog.hpp"
4#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
5#include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp"
6#include "duckdb/parser/qualified_name.hpp"
7#include "duckdb/parser/constraints/not_null_constraint.hpp"
8#include "duckdb/parser/constraints/unique_constraint.hpp"
9#include "duckdb/planner/expression/bound_parameter_expression.hpp"
10#include "duckdb/planner/binder.hpp"
11
12#include "duckdb/common/exception.hpp"
13#include "duckdb/common/limits.hpp"
14
15#include <algorithm>
16
17namespace duckdb {
18
19struct PragmaTableFunctionData : public TableFunctionData {
20 explicit PragmaTableFunctionData(CatalogEntry &entry_p) : entry(entry_p) {
21 }
22
23 CatalogEntry &entry;
24};
25
26struct PragmaTableOperatorData : public GlobalTableFunctionState {
27 PragmaTableOperatorData() : offset(0) {
28 }
29 idx_t offset;
30};
31
32static unique_ptr<FunctionData> PragmaTableInfoBind(ClientContext &context, TableFunctionBindInput &input,
33 vector<LogicalType> &return_types, vector<string> &names) {
34
35 names.emplace_back(args: "cid");
36 return_types.emplace_back(args: LogicalType::INTEGER);
37
38 names.emplace_back(args: "name");
39 return_types.emplace_back(args: LogicalType::VARCHAR);
40
41 names.emplace_back(args: "type");
42 return_types.emplace_back(args: LogicalType::VARCHAR);
43
44 names.emplace_back(args: "notnull");
45 return_types.emplace_back(args: LogicalType::BOOLEAN);
46
47 names.emplace_back(args: "dflt_value");
48 return_types.emplace_back(args: LogicalType::VARCHAR);
49
50 names.emplace_back(args: "pk");
51 return_types.emplace_back(args: LogicalType::BOOLEAN);
52
53 auto qname = QualifiedName::Parse(input: input.inputs[0].GetValue<string>());
54
55 // look up the table name in the catalog
56 Binder::BindSchemaOrCatalog(context, catalog&: qname.catalog, schema&: qname.schema);
57 auto &entry = Catalog::GetEntry(context, type: CatalogType::TABLE_ENTRY, catalog: qname.catalog, schema: qname.schema, name: qname.name);
58 return make_uniq<PragmaTableFunctionData>(args&: entry);
59}
60
61unique_ptr<GlobalTableFunctionState> PragmaTableInfoInit(ClientContext &context, TableFunctionInitInput &input) {
62 return make_uniq<PragmaTableOperatorData>();
63}
64
65static void CheckConstraints(TableCatalogEntry &table, const ColumnDefinition &column, bool &out_not_null,
66 bool &out_pk) {
67 out_not_null = false;
68 out_pk = false;
69 // check all constraints
70 // FIXME: this is pretty inefficient, it probably doesn't matter
71 for (auto &constraint : table.GetConstraints()) {
72 switch (constraint->type) {
73 case ConstraintType::NOT_NULL: {
74 auto &not_null = constraint->Cast<NotNullConstraint>();
75 if (not_null.index == column.Logical()) {
76 out_not_null = true;
77 }
78 break;
79 }
80 case ConstraintType::UNIQUE: {
81 auto &unique = constraint->Cast<UniqueConstraint>();
82 if (unique.is_primary_key) {
83 if (unique.index == column.Logical()) {
84 out_pk = true;
85 }
86 if (std::find(first: unique.columns.begin(), last: unique.columns.end(), val: column.GetName()) != unique.columns.end()) {
87 out_pk = true;
88 }
89 }
90 break;
91 }
92 default:
93 break;
94 }
95 }
96}
97
98static void PragmaTableInfoTable(PragmaTableOperatorData &data, TableCatalogEntry &table, DataChunk &output) {
99 if (data.offset >= table.GetColumns().LogicalColumnCount()) {
100 // finished returning values
101 return;
102 }
103 // start returning values
104 // either fill up the chunk or return all the remaining columns
105 idx_t next = MinValue<idx_t>(a: data.offset + STANDARD_VECTOR_SIZE, b: table.GetColumns().LogicalColumnCount());
106 output.SetCardinality(next - data.offset);
107
108 for (idx_t i = data.offset; i < next; i++) {
109 bool not_null, pk;
110 auto index = i - data.offset;
111 auto &column = table.GetColumn(idx: LogicalIndex(i));
112 D_ASSERT(column.Oid() < (idx_t)NumericLimits<int32_t>::Maximum());
113 CheckConstraints(table, column, out_not_null&: not_null, out_pk&: pk);
114
115 // return values:
116 // "cid", PhysicalType::INT32
117 output.SetValue(col_idx: 0, index, val: Value::INTEGER(value: (int32_t)column.Oid()));
118 // "name", PhysicalType::VARCHAR
119 output.SetValue(col_idx: 1, index, val: Value(column.Name()));
120 // "type", PhysicalType::VARCHAR
121 output.SetValue(col_idx: 2, index, val: Value(column.Type().ToString()));
122 // "notnull", PhysicalType::BOOL
123 output.SetValue(col_idx: 3, index, val: Value::BOOLEAN(value: not_null));
124 // "dflt_value", PhysicalType::VARCHAR
125 Value def_value = column.DefaultValue() ? Value(column.DefaultValue()->ToString()) : Value();
126 output.SetValue(col_idx: 4, index, val: def_value);
127 // "pk", PhysicalType::BOOL
128 output.SetValue(col_idx: 5, index, val: Value::BOOLEAN(value: pk));
129 }
130 data.offset = next;
131}
132
133static void PragmaTableInfoView(PragmaTableOperatorData &data, ViewCatalogEntry &view, DataChunk &output) {
134 if (data.offset >= view.types.size()) {
135 // finished returning values
136 return;
137 }
138 // start returning values
139 // either fill up the chunk or return all the remaining columns
140 idx_t next = MinValue<idx_t>(a: data.offset + STANDARD_VECTOR_SIZE, b: view.types.size());
141 output.SetCardinality(next - data.offset);
142
143 for (idx_t i = data.offset; i < next; i++) {
144 auto index = i - data.offset;
145 auto type = view.types[i];
146 auto &name = view.aliases[i];
147 // return values:
148 // "cid", PhysicalType::INT32
149
150 output.SetValue(col_idx: 0, index, val: Value::INTEGER(value: (int32_t)i));
151 // "name", PhysicalType::VARCHAR
152 output.SetValue(col_idx: 1, index, val: Value(name));
153 // "type", PhysicalType::VARCHAR
154 output.SetValue(col_idx: 2, index, val: Value(type.ToString()));
155 // "notnull", PhysicalType::BOOL
156 output.SetValue(col_idx: 3, index, val: Value::BOOLEAN(value: false));
157 // "dflt_value", PhysicalType::VARCHAR
158 output.SetValue(col_idx: 4, index, val: Value());
159 // "pk", PhysicalType::BOOL
160 output.SetValue(col_idx: 5, index, val: Value::BOOLEAN(value: false));
161 }
162 data.offset = next;
163}
164
165static void PragmaTableInfoFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) {
166 auto &bind_data = data_p.bind_data->Cast<PragmaTableFunctionData>();
167 auto &state = data_p.global_state->Cast<PragmaTableOperatorData>();
168 switch (bind_data.entry.type) {
169 case CatalogType::TABLE_ENTRY:
170 PragmaTableInfoTable(data&: state, table&: bind_data.entry.Cast<TableCatalogEntry>(), output);
171 break;
172 case CatalogType::VIEW_ENTRY:
173 PragmaTableInfoView(data&: state, view&: bind_data.entry.Cast<ViewCatalogEntry>(), output);
174 break;
175 default:
176 throw NotImplementedException("Unimplemented catalog type for pragma_table_info");
177 }
178}
179
180void PragmaTableInfo::RegisterFunction(BuiltinFunctions &set) {
181 set.AddFunction(function: TableFunction("pragma_table_info", {LogicalType::VARCHAR}, PragmaTableInfoFunction,
182 PragmaTableInfoBind, PragmaTableInfoInit));
183}
184
185} // namespace duckdb
186