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
11namespace duckdb {
12
13struct 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
22struct DuckDBExtensionsData : public GlobalTableFunctionState {
23 DuckDBExtensionsData() : offset(0) {
24 }
25
26 vector<ExtensionInformation> entries;
27 idx_t offset;
28};
29
30static 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
53unique_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
121void 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
153void 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