| 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 | |