Coverage Report

Created: 2026-06-30 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/src/main/extension/extension_loader.cpp
Line
Count
Source
1
#include "duckdb/main/extension/extension_loader.hpp"
2
3
#include "duckdb/function/scalar_function.hpp"
4
#include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp"
5
#include "duckdb/parser/parsed_data/create_schema_info.hpp"
6
#include "duckdb/parser/parsed_data/create_type_info.hpp"
7
#include "duckdb/parser/parsed_data/create_copy_function_info.hpp"
8
#include "duckdb/parser/parsed_data/create_pragma_function_info.hpp"
9
#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
10
#include "duckdb/parser/parsed_data/create_table_function_info.hpp"
11
#include "duckdb/parser/parsed_data/create_window_function_info.hpp"
12
#include "duckdb/parser/parsed_data/create_macro_info.hpp"
13
#include "duckdb/catalog/catalog_entry/scalar_function_catalog_entry.hpp"
14
#include "duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp"
15
#include "duckdb/parser/parsed_data/create_collation_info.hpp"
16
#include "duckdb/main/extension_install_info.hpp"
17
#include "duckdb/catalog/catalog.hpp"
18
#include "duckdb/main/client_data.hpp"
19
#include "duckdb/main/config.hpp"
20
#include "duckdb/main/secret/secret_manager.hpp"
21
#include "duckdb/main/database.hpp"
22
#include "duckdb/main/profiler/metrics_manager.hpp"
23
24
#include "duckdb/main/extension_callback_manager.hpp"
25
#include "re2/re2.h"
26
27
namespace duckdb {
28
29
ExtensionLoader::ExtensionLoader(const ExtensionActiveLoad &load_info)
30
19.7k
    : db(load_info.db), extension_info(load_info.info) {
31
19.7k
  loader_info.extension_name = load_info.extension_name;
32
19.7k
  loader_info.extension_alias = load_info.alias;
33
19.7k
}
34
35
0
ExtensionLoader::ExtensionLoader(DatabaseInstance &db, const string &name) : db(db) {
36
0
  loader_info.extension_name = Identifier(name);
37
0
}
38
39
539k
DatabaseInstance &ExtensionLoader::GetDatabaseInstance() const {
40
539k
  return db;
41
539k
}
42
43
0
void ExtensionLoader::SetDescription(const string &description) {
44
0
  loader_info.extension_description = description;
45
0
}
46
47
0
void ExtensionLoader::UseDedicatedSchemaForExtension(const Identifier &extension_schema_name) {
48
0
  CreateSchema(extension_schema_name);
49
0
  UseDefaultSchema(extension_schema_name);
50
0
  AddSchemaToSearchPath(extension_schema_name);
51
0
}
52
53
0
void ExtensionLoader::UseDedicatedSchemaForExtension() {
54
0
  auto registered_ext_name = GetRegisteredExtensionName();
55
0
  UseDedicatedSchemaForExtension(registered_ext_name);
56
0
}
57
58
0
void ExtensionLoader::CreateSchema(const Identifier &name) const {
59
0
  auto &system_catalog = Catalog::GetSystemCatalog(db);
60
0
  auto data = CatalogTransaction::GetSystemTransaction(db);
61
62
0
  CreateSchemaInfo info;
63
0
  info.SetQualifiedName(QualifiedName(info.GetQualifiedName().Catalog(), name, info.GetQualifiedName().Name()));
64
0
  info.internal = true;
65
  // TODO; we can give the user more control here
66
0
  info.on_conflict = OnCreateConflict::ERROR_ON_CONFLICT;
67
0
  system_catalog.CreateSchema(data, info);
68
0
}
69
70
0
void ExtensionLoader::UseDefaultSchema(const Identifier &name) {
71
0
  if (loader_info.extension_schema != DEFAULT_SCHEMA && name != DEFAULT_SCHEMA &&
72
0
      loader_info.extension_schema != name) {
73
0
    throw InvalidInputException("Cannot set extension schema to '%s', schema is already set to '%s'", name,
74
0
                                loader_info.extension_schema);
75
0
  }
76
0
  if (name == "pg_catalog") {
77
0
    throw InvalidInputException("Cannot set default extension schema to '%s'", name);
78
0
  }
79
0
  if (name == DEFAULT_SCHEMA) {
80
0
    loader_info.extension_schema = Identifier::DefaultSchema();
81
0
    return;
82
0
  }
83
0
  loader_info.extension_schema = name;
84
0
}
85
86
0
void ExtensionLoader::AddSchemaToSearchPath(const Identifier &schema_name) const {
87
  // adds an explicitly set extension schema to the search path
88
0
  if (loader_info.extension_schema != schema_name || schema_name == DEFAULT_SCHEMA ||
89
0
      loader_info.extension_schema == DEFAULT_SCHEMA) {
90
0
    throw InvalidInputException("Cannot add schema '%s' to search path, first set the extension schema explicitly "
91
0
                                "with UseDefaultSchema()",
92
0
                                schema_name);
93
0
  }
94
95
  // check if schema already exists in the system catalog
96
0
  auto &system_catalog = Catalog::GetSystemCatalog(db);
97
0
  auto data = CatalogTransaction::GetSystemTransaction(db);
98
0
  auto schema = system_catalog.GetSchema(data, schema_name, OnEntryNotFound::RETURN_NULL);
99
0
  if (!schema) {
100
0
    throw InvalidInputException("Cannot add schema '%s' to search path: schema does not exist. "
101
0
                                "Call CreateExtensionSchema() first.",
102
0
                                schema_name);
103
0
  }
104
105
  // TODO: remove extension schema from search path if loading extension failed
106
0
  ExtensionCallbackManager::Get(db).AddExtensionSchema(loader_info.extension_schema);
107
0
}
108
109
0
void ExtensionLoader::RefreshSearchPath(ClientContext &context) {
110
0
  ClientData::Get(context).catalog_search_path->RefreshSetPaths();
111
0
}
112
113
19.7k
void ExtensionLoader::FinalizeLoad() {
114
  // Set extension description, if provided
115
19.7k
  if (!loader_info.extension_description.empty() && extension_info) {
116
0
    auto info = make_uniq<ExtensionLoadedInfo>();
117
0
    info->description = loader_info.extension_description;
118
0
    extension_info->load_info = std::move(info);
119
0
  }
120
19.7k
}
121
122
13.1k
void ExtensionLoader::RegisterFunction(ScalarFunction function) {
123
13.1k
  ScalarFunctionSet set {function.name};
124
13.1k
  set.AddFunction(std::move(function));
125
13.1k
  RegisterFunction(std::move(set));
126
13.1k
}
127
128
230k
void ExtensionLoader::RegisterFunction(ScalarFunctionSet function) {
129
230k
  CreateScalarFunctionInfo info(std::move(function));
130
230k
  info.SetQualifiedName(
131
230k
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
132
230k
  info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
133
230k
  RegisterFunction(std::move(info));
134
230k
}
135
136
2.21M
void ExtensionLoader::RegisterFunction(CreateScalarFunctionInfo function) {
137
2.21M
  D_ASSERT(!function.functions.name.empty());
138
2.21M
  function.extension_name = GetRegisteredExtensionName();
139
2.21M
  if (function.GetQualifiedName().Schema() == DEFAULT_SCHEMA) {
140
2.21M
    function.SetQualifiedName(QualifiedName(function.GetQualifiedName().Catalog(), loader_info.extension_schema,
141
2.21M
                                            function.GetQualifiedName().Name()));
142
2.21M
  }
143
2.21M
  auto &system_catalog = Catalog::GetSystemCatalog(db);
144
2.21M
  auto data = CatalogTransaction::GetSystemTransaction(db);
145
2.21M
  system_catalog.CreateFunction(data, function);
146
2.21M
}
147
148
0
void ExtensionLoader::RegisterFunction(AggregateFunction function) {
149
0
  AggregateFunctionSet set {function.name};
150
0
  set.AddFunction(std::move(function));
151
0
  RegisterFunction(std::move(set));
152
0
}
153
154
0
void ExtensionLoader::RegisterFunction(AggregateFunctionSet function) {
155
0
  CreateAggregateFunctionInfo info(std::move(function));
156
0
  info.SetQualifiedName(
157
0
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
158
0
  info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
159
0
  RegisterFunction(std::move(info));
160
0
}
161
162
441k
void ExtensionLoader::RegisterFunction(CreateAggregateFunctionInfo function) {
163
441k
  D_ASSERT(!function.functions.name.empty());
164
441k
  if (function.GetQualifiedName().Schema() == DEFAULT_SCHEMA) {
165
441k
    function.SetQualifiedName(QualifiedName(function.GetQualifiedName().Catalog(), loader_info.extension_schema,
166
441k
                                            function.GetQualifiedName().Name()));
167
441k
  }
168
441k
  function.extension_name = GetRegisteredExtensionName();
169
441k
  auto &system_catalog = Catalog::GetSystemCatalog(db);
170
441k
  auto data = CatalogTransaction::GetSystemTransaction(db);
171
441k
  system_catalog.CreateFunction(data, function);
172
441k
}
173
174
0
void ExtensionLoader::RegisterFunction(WindowFunction function) {
175
0
  WindowFunctionSet set {function.name};
176
0
  set.AddFunction(std::move(function));
177
0
  RegisterFunction(std::move(set));
178
0
}
179
180
0
void ExtensionLoader::RegisterFunction(WindowFunctionSet function) {
181
0
  CreateWindowFunctionInfo info(std::move(function));
182
0
  info.SetQualifiedName(
183
0
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
184
0
  info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
185
0
  RegisterFunction(std::move(info));
186
0
}
187
188
0
void ExtensionLoader::RegisterFunction(CreateWindowFunctionInfo function) {
189
0
  D_ASSERT(!function.functions.name.empty());
190
0
  if (function.GetQualifiedName().Schema() == DEFAULT_SCHEMA) {
191
0
    function.SetQualifiedName(QualifiedName(function.GetQualifiedName().Catalog(), loader_info.extension_schema,
192
0
                                            function.GetQualifiedName().Name()));
193
0
  }
194
0
  function.extension_name = GetRegisteredExtensionName();
195
0
  auto &system_catalog = Catalog::GetSystemCatalog(db);
196
0
  auto data = CatalogTransaction::GetSystemTransaction(db);
197
0
  system_catalog.CreateFunction(data, function);
198
0
}
199
200
0
void ExtensionLoader::RegisterFunction(CreateSecretFunction function) {
201
0
  D_ASSERT(!function.secret_type.empty());
202
0
  auto &config = DBConfig::GetConfig(db);
203
0
  config.secret_manager->RegisterSecretFunction(std::move(function), OnCreateConflict::ERROR_ON_CONFLICT);
204
0
}
205
206
0
void ExtensionLoader::RegisterFunction(TableFunction function) {
207
0
  TableFunctionSet set {function.name};
208
0
  set.AddFunction(std::move(function));
209
0
  RegisterFunction(std::move(set));
210
0
}
211
212
118k
void ExtensionLoader::RegisterFunction(TableFunctionSet function) {
213
118k
  D_ASSERT(!function.name.empty());
214
118k
  CreateTableFunctionInfo info(std::move(function));
215
118k
  info.SetQualifiedName(
216
118k
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
217
118k
  info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
218
118k
  RegisterFunction(std::move(info));
219
118k
}
220
221
118k
void ExtensionLoader::RegisterFunction(CreateTableFunctionInfo info) {
222
118k
  D_ASSERT(!info.functions.name.empty());
223
118k
  info.extension_name = GetRegisteredExtensionName();
224
118k
  if (info.GetQualifiedName().Schema() == DEFAULT_SCHEMA) {
225
118k
    info.SetQualifiedName(QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema,
226
118k
                                        info.GetQualifiedName().Name()));
227
118k
  }
228
118k
  auto &system_catalog = Catalog::GetSystemCatalog(db);
229
118k
  auto data = CatalogTransaction::GetSystemTransaction(db);
230
118k
  system_catalog.CreateFunction(data, info);
231
118k
}
232
233
6.58k
void ExtensionLoader::RegisterFunction(PragmaFunction function) {
234
6.58k
  D_ASSERT(!function.name.empty());
235
6.58k
  PragmaFunctionSet set {function.name};
236
6.58k
  set.AddFunction(std::move(function));
237
6.58k
  RegisterFunction(std::move(set));
238
6.58k
}
239
240
13.1k
void ExtensionLoader::RegisterFunction(PragmaFunctionSet function) {
241
13.1k
  D_ASSERT(!function.name.empty());
242
13.1k
  CreatePragmaFunctionInfo info(std::move(function));
243
13.1k
  info.extension_name = GetRegisteredExtensionName();
244
13.1k
  info.SetQualifiedName(
245
13.1k
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
246
13.1k
  auto &system_catalog = Catalog::GetSystemCatalog(db);
247
13.1k
  auto data = CatalogTransaction::GetSystemTransaction(db);
248
13.1k
  system_catalog.CreatePragmaFunction(data, info);
249
13.1k
}
250
251
26.3k
void ExtensionLoader::RegisterFunction(CopyFunction function) {
252
26.3k
  CreateCopyFunctionInfo info(std::move(function));
253
26.3k
  info.extension_name = GetRegisteredExtensionName();
254
26.3k
  info.SetQualifiedName(
255
26.3k
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
256
26.3k
  auto &system_catalog = Catalog::GetSystemCatalog(db);
257
26.3k
  auto data = CatalogTransaction::GetSystemTransaction(db);
258
26.3k
  system_catalog.CreateCopyFunction(data, info);
259
26.3k
}
260
261
39.4k
void ExtensionLoader::RegisterFunction(CreateMacroInfo &info) {
262
39.4k
  info.extension_name = GetRegisteredExtensionName();
263
39.4k
  if (info.GetQualifiedName().Schema() == DEFAULT_SCHEMA) {
264
39.4k
    info.SetQualifiedName(QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema,
265
39.4k
                                        info.GetQualifiedName().Name()));
266
39.4k
  }
267
39.4k
  auto &system_catalog = Catalog::GetSystemCatalog(db);
268
39.4k
  auto data = CatalogTransaction::GetSystemTransaction(db);
269
39.4k
  system_catalog.CreateFunction(data, info);
270
39.4k
}
271
272
0
void ExtensionLoader::RegisterCollation(CreateCollationInfo &info) {
273
0
  info.extension_name = GetRegisteredExtensionName();
274
0
  auto &system_catalog = Catalog::GetSystemCatalog(db);
275
0
  auto data = CatalogTransaction::GetSystemTransaction(db);
276
0
  if (info.GetQualifiedName().Schema() == DEFAULT_SCHEMA) {
277
0
    info.SetQualifiedName(QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema,
278
0
                                        info.GetQualifiedName().Name()));
279
0
  }
280
0
  info.on_conflict = OnCreateConflict::IGNORE_ON_CONFLICT;
281
0
  system_catalog.CreateCollation(data, info);
282
283
  // Also register as a function for serialisation
284
0
  CreateScalarFunctionInfo finfo(info.function);
285
0
  finfo.extension_name = GetRegisteredExtensionName();
286
0
  finfo.SetQualifiedName(QualifiedName(finfo.GetQualifiedName().Catalog(), loader_info.extension_schema,
287
0
                                       finfo.GetQualifiedName().Name()));
288
0
  finfo.on_conflict = OnCreateConflict::IGNORE_ON_CONFLICT;
289
0
  system_catalog.CreateFunction(data, finfo);
290
0
}
291
292
0
void ExtensionLoader::RegisterCoordinateSystem(CreateCoordinateSystemInfo &info) {
293
0
  auto &system_catalog = Catalog::GetSystemCatalog(db);
294
0
  auto data = CatalogTransaction::GetSystemTransaction(db);
295
0
  system_catalog.CreateCoordinateSystem(data, info);
296
0
}
297
298
0
void ExtensionLoader::AddFunctionOverload(ScalarFunction function) {
299
0
  auto &scalar_function = GetFunction(function.name);
300
0
  scalar_function.functions.AddFunction(std::move(function));
301
0
}
302
303
0
void ExtensionLoader::AddFunctionOverload(ScalarFunctionSet functions) { // NOLINT
304
0
  D_ASSERT(!functions.name.empty());
305
0
  auto &scalar_function = GetFunction(functions.name);
306
0
  for (auto &function : functions.functions) {
307
0
    function.name = functions.name;
308
0
    scalar_function.functions.AddFunction(std::move(function));
309
0
  }
310
0
}
311
312
0
void ExtensionLoader::AddFunctionOverload(TableFunctionSet functions) { // NOLINT
313
0
  auto &table_function = GetTableFunction(functions.name);
314
0
  for (auto &function : functions.functions) {
315
0
    function.name = functions.name;
316
0
    table_function.functions.AddFunction(std::move(function));
317
0
  }
318
0
}
319
320
0
static optional_ptr<CatalogEntry> TryGetEntry(DatabaseInstance &db, const Identifier &name, CatalogType type) {
321
0
  D_ASSERT(!name.empty());
322
0
  auto &system_catalog = Catalog::GetSystemCatalog(db);
323
0
  auto data = CatalogTransaction::GetSystemTransaction(db);
324
0
  auto &schema = system_catalog.GetSchema(data, Identifier::DefaultSchema());
325
0
  return schema.GetEntry(data, type, name);
326
0
}
327
328
0
optional_ptr<CatalogEntry> ExtensionLoader::TryGetFunction(const Identifier &name) {
329
0
  return TryGetEntry(db, name, CatalogType::SCALAR_FUNCTION_ENTRY);
330
0
}
331
332
0
ScalarFunctionCatalogEntry &ExtensionLoader::GetFunction(const Identifier &name) {
333
0
  auto catalog_entry = TryGetFunction(name);
334
0
  if (!catalog_entry) {
335
0
    throw InvalidInputException("Function with name \"%s\" not found in ExtensionLoader::GetFunction", name);
336
0
  }
337
0
  return catalog_entry->Cast<ScalarFunctionCatalogEntry>();
338
0
}
339
340
0
optional_ptr<CatalogEntry> ExtensionLoader::TryGetTableFunction(const Identifier &name) {
341
0
  return TryGetEntry(db, name, CatalogType::TABLE_FUNCTION_ENTRY);
342
0
}
343
344
0
TableFunctionCatalogEntry &ExtensionLoader::GetTableFunction(const Identifier &name) {
345
0
  auto catalog_entry = TryGetTableFunction(name);
346
0
  if (!catalog_entry) {
347
0
    throw InvalidInputException("Function with name \"%s\" not found in ExtensionLoader::GetTableFunction", name);
348
0
  }
349
0
  return catalog_entry->Cast<TableFunctionCatalogEntry>();
350
0
}
351
352
6.58k
void ExtensionLoader::RegisterType(string type_name, LogicalType type, bind_logical_type_function_t bind_modifiers) {
353
6.58k
  D_ASSERT(!type_name.empty());
354
6.58k
  CreateTypeInfo info(std::move(type_name), std::move(type), bind_modifiers);
355
6.58k
  info.temporary = true;
356
6.58k
  info.internal = true;
357
6.58k
  info.extension_name = GetRegisteredExtensionName();
358
6.58k
  info.SetQualifiedName(
359
6.58k
      QualifiedName(info.GetQualifiedName().Catalog(), loader_info.extension_schema, info.GetQualifiedName().Name()));
360
6.58k
  auto &system_catalog = Catalog::GetSystemCatalog(db);
361
6.58k
  auto data = CatalogTransaction::GetSystemTransaction(db);
362
6.58k
  system_catalog.CreateType(data, info);
363
6.58k
}
364
365
0
void ExtensionLoader::RegisterSecretType(SecretType secret_type) {
366
0
  auto &config = DBConfig::GetConfig(db);
367
0
  config.secret_manager->RegisterSecretType(secret_type);
368
0
}
369
370
void ExtensionLoader::RegisterCastFunction(const LogicalType &source, const LogicalType &target,
371
513k
                                           bind_cast_function_t bind_function, int64_t implicit_cast_cost) {
372
513k
  auto &config = DBConfig::GetConfig(db);
373
513k
  auto &casts = config.GetCastFunctions();
374
513k
  casts.RegisterCastFunction(source, target, bind_function, implicit_cast_cost);
375
513k
}
376
377
void ExtensionLoader::RegisterCastFunction(const LogicalType &source, const LogicalType &target, BoundCastInfo function,
378
39.4k
                                           int64_t implicit_cast_cost) {
379
39.4k
  auto &config = DBConfig::GetConfig(db);
380
39.4k
  auto &casts = config.GetCastFunctions();
381
39.4k
  casts.RegisterCastFunction(source, target, std::move(function), implicit_cast_cost);
382
39.4k
}
383
384
0
void ExtensionLoader::RegisterCombineTypesRule(CombineTypesRule rule) {
385
0
  DBConfig::GetConfig(db).GetCastFunctions().RegisterCombineTypesRule(rule);
386
0
}
387
388
0
void ExtensionLoader::RegisterMetric(MetricInfo info) {
389
0
  MetricsManager::Get(db).RegisterMetric(std::move(info));
390
0
}
391
392
} // namespace duckdb