1#include "duckdb/function/table/sqlite_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/transaction/transaction.hpp"
8
9#include <algorithm>
10
11using namespace std;
12
13namespace duckdb {
14
15struct SQLiteMasterData : public TableFunctionData {
16 SQLiteMasterData() : initialized(false), offset(0) {
17 }
18
19 bool initialized;
20 vector<CatalogEntry *> entries;
21 idx_t offset;
22};
23
24string GenerateQuery(CatalogEntry *entry) {
25 // generate a query from a catalog entry
26 if (entry->type == CatalogType::TABLE) {
27 // FIXME: constraints
28 stringstream ss;
29 auto table = (TableCatalogEntry *)entry;
30 ss << "CREATE TABLE " << table->name << "(";
31
32 for (idx_t i = 0; i < table->columns.size(); i++) {
33 auto &column = table->columns[i];
34 ss << column.name << " " << SQLTypeToString(column.type);
35 if (i + 1 < table->columns.size()) {
36 ss << ", ";
37 }
38 }
39
40 ss << ");";
41 return ss.str();
42 } else {
43 return "[Unknown]";
44 }
45}
46
47static unique_ptr<FunctionData> sqlite_master_bind(ClientContext &context, vector<Value> inputs,
48 vector<SQLType> &return_types, vector<string> &names) {
49 names.push_back("type");
50 return_types.push_back(SQLType::VARCHAR);
51
52 names.push_back("name");
53 return_types.push_back(SQLType::VARCHAR);
54
55 names.push_back("tbl_name");
56 return_types.push_back(SQLType::VARCHAR);
57
58 names.push_back("rootpage");
59 return_types.push_back(SQLType::INTEGER);
60
61 names.push_back("sql");
62 return_types.push_back(SQLType::VARCHAR);
63
64 // initialize the function data structure
65 return make_unique<SQLiteMasterData>();
66}
67
68void sqlite_master(ClientContext &context, vector<Value> &input, DataChunk &output, FunctionData *dataptr) {
69 auto &data = *((SQLiteMasterData *)dataptr);
70 assert(input.size() == 0);
71 if (!data.initialized) {
72 // scan all the schemas
73 auto &transaction = Transaction::GetTransaction(context);
74 Catalog::GetCatalog(context).schemas.Scan(transaction, [&](CatalogEntry *entry) {
75 auto schema = (SchemaCatalogEntry *)entry;
76 schema->tables.Scan(transaction, [&](CatalogEntry *entry) { data.entries.push_back(entry); });
77 });
78 data.initialized = true;
79 }
80
81 if (data.offset >= data.entries.size()) {
82 // finished returning values
83 return;
84 }
85 idx_t next = min(data.offset + STANDARD_VECTOR_SIZE, (idx_t)data.entries.size());
86 output.SetCardinality(next - data.offset);
87
88 // start returning values
89 // either fill up the chunk or return all the remaining columns
90 for (idx_t i = data.offset; i < next; i++) {
91 auto index = i - data.offset;
92 auto &entry = data.entries[i];
93
94 // return values:
95 // "type", TypeId::VARCHAR
96 const char *type_str;
97 switch (entry->type) {
98 case CatalogType::TABLE:
99 type_str = "table";
100 break;
101 case CatalogType::SCHEMA:
102 type_str = "schema";
103 break;
104 case CatalogType::TABLE_FUNCTION:
105 type_str = "function";
106 break;
107 case CatalogType::VIEW:
108 type_str = "view";
109 break;
110 default:
111 type_str = "unknown";
112 }
113 output.SetValue(0, index, Value(type_str));
114 // "name", TypeId::VARCHAR
115 output.SetValue(1, index, Value(entry->name));
116 // "tbl_name", TypeId::VARCHAR
117 output.SetValue(2, index, Value(entry->name));
118 // "rootpage", TypeId::INT32
119 output.SetValue(3, index, Value::INTEGER(0));
120 // "sql", TypeId::VARCHAR
121 output.SetValue(4, index, Value(GenerateQuery(entry)));
122 }
123 data.offset = next;
124}
125
126void SQLiteMaster::RegisterFunction(BuiltinFunctions &set) {
127 set.AddFunction(TableFunction("sqlite_master", {}, sqlite_master_bind, sqlite_master, nullptr));
128}
129
130} // namespace duckdb
131