1 | #include "duckdb/function/table/system_functions.hpp" |
2 | |
3 | #include "duckdb/common/file_system.hpp" |
4 | #include "duckdb/common/map.hpp" |
5 | #include "duckdb/common/string_util.hpp" |
6 | #include "duckdb/function/function_set.hpp" |
7 | #include "duckdb/main/client_context.hpp" |
8 | #include "duckdb/main/database.hpp" |
9 | #include "duckdb/main/extension_helper.hpp" |
10 | |
11 | namespace duckdb { |
12 | |
13 | struct ExtensionInformation { |
14 | string name; |
15 | bool loaded = false; |
16 | bool installed = false; |
17 | string file_path; |
18 | string description; |
19 | vector<Value> aliases; |
20 | }; |
21 | |
22 | struct DuckDBExtensionsData : public GlobalTableFunctionState { |
23 | DuckDBExtensionsData() : offset(0) { |
24 | } |
25 | |
26 | vector<ExtensionInformation> entries; |
27 | idx_t offset; |
28 | }; |
29 | |
30 | static unique_ptr<FunctionData> DuckDBExtensionsBind(ClientContext &context, TableFunctionBindInput &input, |
31 | vector<LogicalType> &return_types, vector<string> &names) { |
32 | names.emplace_back(args: "extension_name" ); |
33 | return_types.emplace_back(args: LogicalType::VARCHAR); |
34 | |
35 | names.emplace_back(args: "loaded" ); |
36 | return_types.emplace_back(args: LogicalType::BOOLEAN); |
37 | |
38 | names.emplace_back(args: "installed" ); |
39 | return_types.emplace_back(args: LogicalType::BOOLEAN); |
40 | |
41 | names.emplace_back(args: "install_path" ); |
42 | return_types.emplace_back(args: LogicalType::VARCHAR); |
43 | |
44 | names.emplace_back(args: "description" ); |
45 | return_types.emplace_back(args: LogicalType::VARCHAR); |
46 | |
47 | names.emplace_back(args: "aliases" ); |
48 | return_types.emplace_back(args: LogicalType::LIST(child: LogicalType::VARCHAR)); |
49 | |
50 | return nullptr; |
51 | } |
52 | |
53 | unique_ptr<GlobalTableFunctionState> DuckDBExtensionsInit(ClientContext &context, TableFunctionInitInput &input) { |
54 | auto result = make_uniq<DuckDBExtensionsData>(); |
55 | |
56 | auto &fs = FileSystem::GetFileSystem(context); |
57 | auto &db = DatabaseInstance::GetDatabase(context); |
58 | |
59 | map<string, ExtensionInformation> installed_extensions; |
60 | auto extension_count = ExtensionHelper::DefaultExtensionCount(); |
61 | auto alias_count = ExtensionHelper::ExtensionAliasCount(); |
62 | for (idx_t i = 0; i < extension_count; i++) { |
63 | auto extension = ExtensionHelper::GetDefaultExtension(index: i); |
64 | ExtensionInformation info; |
65 | info.name = extension.name; |
66 | info.installed = extension.statically_loaded; |
67 | info.loaded = false; |
68 | info.file_path = extension.statically_loaded ? "(BUILT-IN)" : string(); |
69 | info.description = extension.description; |
70 | for (idx_t k = 0; k < alias_count; k++) { |
71 | auto alias = ExtensionHelper::GetExtensionAlias(index: k); |
72 | if (info.name == alias.extension) { |
73 | info.aliases.emplace_back(args&: alias.alias); |
74 | } |
75 | } |
76 | installed_extensions[info.name] = std::move(info); |
77 | } |
78 | #ifndef WASM_LOADABLE_EXTENSIONS |
79 | // scan the install directory for installed extensions |
80 | auto ext_directory = ExtensionHelper::ExtensionDirectory(context); |
81 | fs.ListFiles(directory: ext_directory, callback: [&](const string &path, bool is_directory) { |
82 | if (!StringUtil::EndsWith(str: path, suffix: ".duckdb_extension" )) { |
83 | return; |
84 | } |
85 | ExtensionInformation info; |
86 | info.name = fs.ExtractBaseName(path); |
87 | info.loaded = false; |
88 | info.file_path = fs.JoinPath(a: ext_directory, path); |
89 | auto entry = installed_extensions.find(x: info.name); |
90 | if (entry == installed_extensions.end()) { |
91 | installed_extensions[info.name] = std::move(info); |
92 | } else { |
93 | if (!entry->second.loaded) { |
94 | entry->second.file_path = info.file_path; |
95 | } |
96 | entry->second.installed = true; |
97 | } |
98 | }); |
99 | #endif |
100 | // now check the list of currently loaded extensions |
101 | auto &loaded_extensions = db.LoadedExtensions(); |
102 | for (auto &ext_name : loaded_extensions) { |
103 | auto entry = installed_extensions.find(x: ext_name); |
104 | if (entry == installed_extensions.end()) { |
105 | ExtensionInformation info; |
106 | info.name = ext_name; |
107 | info.loaded = true; |
108 | installed_extensions[ext_name] = std::move(info); |
109 | } else { |
110 | entry->second.loaded = true; |
111 | } |
112 | } |
113 | |
114 | result->entries.reserve(n: installed_extensions.size()); |
115 | for (auto &kv : installed_extensions) { |
116 | result->entries.push_back(x: std::move(kv.second)); |
117 | } |
118 | return std::move(result); |
119 | } |
120 | |
121 | void DuckDBExtensionsFunction(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) { |
122 | auto &data = data_p.global_state->Cast<DuckDBExtensionsData>(); |
123 | if (data.offset >= data.entries.size()) { |
124 | // finished returning values |
125 | return; |
126 | } |
127 | // start returning values |
128 | // either fill up the chunk or return all the remaining columns |
129 | idx_t count = 0; |
130 | while (data.offset < data.entries.size() && count < STANDARD_VECTOR_SIZE) { |
131 | auto &entry = data.entries[data.offset]; |
132 | |
133 | // return values: |
134 | // extension_name LogicalType::VARCHAR |
135 | output.SetValue(col_idx: 0, index: count, val: Value(entry.name)); |
136 | // loaded LogicalType::BOOLEAN |
137 | output.SetValue(col_idx: 1, index: count, val: Value::BOOLEAN(value: entry.loaded)); |
138 | // installed LogicalType::BOOLEAN |
139 | output.SetValue(col_idx: 2, index: count, val: !entry.installed && entry.loaded ? Value() : Value::BOOLEAN(value: entry.installed)); |
140 | // install_path LogicalType::VARCHAR |
141 | output.SetValue(col_idx: 3, index: count, val: Value(entry.file_path)); |
142 | // description LogicalType::VARCHAR |
143 | output.SetValue(col_idx: 4, index: count, val: Value(entry.description)); |
144 | // aliases LogicalType::LIST(LogicalType::VARCHAR) |
145 | output.SetValue(col_idx: 5, index: count, val: Value::LIST(child_type: LogicalType::VARCHAR, values: entry.aliases)); |
146 | |
147 | data.offset++; |
148 | count++; |
149 | } |
150 | output.SetCardinality(count); |
151 | } |
152 | |
153 | void DuckDBExtensionsFun::RegisterFunction(BuiltinFunctions &set) { |
154 | TableFunctionSet functions("duckdb_extensions" ); |
155 | functions.AddFunction(function: TableFunction({}, DuckDBExtensionsFunction, DuckDBExtensionsBind, DuckDBExtensionsInit)); |
156 | set.AddFunction(set: functions); |
157 | } |
158 | |
159 | } // namespace duckdb |
160 | |