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 | |
17 | namespace duckdb { |
18 | |
19 | struct PragmaTableFunctionData : public TableFunctionData { |
20 | explicit PragmaTableFunctionData(CatalogEntry &entry_p) : entry(entry_p) { |
21 | } |
22 | |
23 | CatalogEntry &entry; |
24 | }; |
25 | |
26 | struct PragmaTableOperatorData : public GlobalTableFunctionState { |
27 | PragmaTableOperatorData() : offset(0) { |
28 | } |
29 | idx_t offset; |
30 | }; |
31 | |
32 | static 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 | |
61 | unique_ptr<GlobalTableFunctionState> PragmaTableInfoInit(ClientContext &context, TableFunctionInitInput &input) { |
62 | return make_uniq<PragmaTableOperatorData>(); |
63 | } |
64 | |
65 | static 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 ¬_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 | |
98 | static 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 | |
133 | static 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 | |
165 | static 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 | |
180 | void PragmaTableInfo::RegisterFunction(BuiltinFunctions &set) { |
181 | set.AddFunction(function: TableFunction("pragma_table_info" , {LogicalType::VARCHAR}, PragmaTableInfoFunction, |
182 | PragmaTableInfoBind, PragmaTableInfoInit)); |
183 | } |
184 | |
185 | } // namespace duckdb |
186 | |