duckdb 0.7.1-dev240.0 → 0.7.1-dev320.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/src/duckdb/extension/json/json_scan.cpp +1 -4
  4. package/src/duckdb/extension/parquet/column_reader.cpp +7 -0
  5. package/src/duckdb/extension/parquet/include/column_reader.hpp +1 -0
  6. package/src/duckdb/extension/parquet/parquet-extension.cpp +2 -10
  7. package/src/duckdb/src/catalog/catalog.cpp +62 -13
  8. package/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp +8 -7
  9. package/src/duckdb/src/catalog/default/default_views.cpp +1 -1
  10. package/src/duckdb/src/common/file_system.cpp +23 -9
  11. package/src/duckdb/src/common/local_file_system.cpp +4 -4
  12. package/src/duckdb/src/execution/index/art/art.cpp +117 -67
  13. package/src/duckdb/src/execution/index/art/art_key.cpp +24 -12
  14. package/src/duckdb/src/execution/index/art/leaf.cpp +7 -8
  15. package/src/duckdb/src/execution/index/art/node.cpp +13 -27
  16. package/src/duckdb/src/execution/index/art/node16.cpp +5 -8
  17. package/src/duckdb/src/execution/index/art/node256.cpp +3 -5
  18. package/src/duckdb/src/execution/index/art/node4.cpp +4 -7
  19. package/src/duckdb/src/execution/index/art/node48.cpp +5 -8
  20. package/src/duckdb/src/execution/index/art/prefix.cpp +2 -3
  21. package/src/duckdb/src/execution/operator/helper/physical_reset.cpp +1 -9
  22. package/src/duckdb/src/execution/operator/helper/physical_set.cpp +1 -9
  23. package/src/duckdb/src/function/pragma/pragma_queries.cpp +2 -2
  24. package/src/duckdb/src/function/scalar/generic/current_setting.cpp +2 -2
  25. package/src/duckdb/src/function/table/read_csv.cpp +3 -5
  26. package/src/duckdb/src/function/table/table_scan.cpp +3 -0
  27. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  28. package/src/duckdb/src/include/duckdb/catalog/catalog.hpp +7 -1
  29. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp +1 -1
  30. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/index_catalog_entry.hpp +1 -1
  31. package/src/duckdb/src/include/duckdb/common/enums/wal_type.hpp +3 -0
  32. package/src/duckdb/src/include/duckdb/common/file_system.hpp +1 -1
  33. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +37 -41
  34. package/src/duckdb/src/include/duckdb/execution/index/art/art_key.hpp +8 -11
  35. package/src/duckdb/src/include/duckdb/main/{extension_functions.hpp → extension_entries.hpp} +26 -5
  36. package/src/duckdb/src/include/duckdb/main/extension_helper.hpp +3 -0
  37. package/src/duckdb/src/include/duckdb/planner/binder.hpp +3 -0
  38. package/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp +10 -3
  39. package/src/duckdb/src/include/duckdb/storage/data_table.hpp +7 -1
  40. package/src/duckdb/src/include/duckdb/storage/index.hpp +47 -38
  41. package/src/duckdb/src/include/duckdb/storage/write_ahead_log.hpp +7 -0
  42. package/src/duckdb/src/main/database.cpp +4 -2
  43. package/src/duckdb/src/main/extension/extension_load.cpp +22 -3
  44. package/src/duckdb/src/parser/parsed_data/create_index_info.cpp +3 -0
  45. package/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +13 -0
  46. package/src/duckdb/src/planner/expression_binder/index_binder.cpp +32 -1
  47. package/src/duckdb/src/storage/buffer_manager.cpp +30 -3
  48. package/src/duckdb/src/storage/compression/bitpacking.cpp +16 -7
  49. package/src/duckdb/src/storage/data_table.cpp +66 -3
  50. package/src/duckdb/src/storage/index.cpp +1 -1
  51. package/src/duckdb/src/storage/local_storage.cpp +1 -1
  52. package/src/duckdb/src/storage/table_index_list.cpp +1 -2
  53. package/src/duckdb/src/storage/wal_replay.cpp +68 -0
  54. package/src/duckdb/src/storage/write_ahead_log.cpp +21 -1
  55. package/src/duckdb/src/transaction/commit_state.cpp +5 -2
  56. package/src/duckdb/third_party/concurrentqueue/blockingconcurrentqueue.h +2 -2
  57. package/src/statement.cpp +46 -12
  58. package/test/arrow.test.ts +3 -3
  59. package/test/prepare.test.ts +39 -1
  60. package/test/typescript_decls.test.ts +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DuckDB Node Bindings
2
2
 
3
- This package provides a node.js API for [DuckDB](https://github.com/cwida/duckdb), the "SQLite for Analytics". The API for this client is somewhat compliant to the SQLite node.js client for easier transition (and transition you must eventually).
3
+ This package provides a node.js API for [DuckDB](https://github.com/duckdb/duckdb), the "SQLite for Analytics". The API for this client is somewhat compliant to the SQLite node.js client for easier transition (and transition you must eventually).
4
4
 
5
5
  Load the package and create a database object:
6
6
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
4
  "types": "./lib/duckdb.d.ts",
5
- "version": "0.7.1-dev240.0",
5
+ "version": "0.7.1-dev320.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",
44
- "url": "git+https://github.com/cwida/duckdb.git"
44
+ "url": "git+https://github.com/duckdb/duckdb.git"
45
45
  },
46
46
  "ts-node": {
47
47
  "require": [
@@ -56,7 +56,7 @@
56
56
  "author": "Hannes Mühleisen",
57
57
  "license": "MPL-2.0",
58
58
  "bugs": {
59
- "url": "https://github.com/cwida/duckdb/issues"
59
+ "url": "https://github.com/duckdb/duckdb/issues"
60
60
  },
61
61
  "homepage": "https://www.duckdb.org"
62
62
  }
@@ -75,10 +75,7 @@ void JSONScanData::InitializeFilePaths(ClientContext &context, const vector<stri
75
75
  vector<string> &file_paths) {
76
76
  auto &fs = FileSystem::GetFileSystem(context);
77
77
  for (auto &file_pattern : patterns) {
78
- auto found_files = fs.Glob(file_pattern, context);
79
- if (found_files.empty()) {
80
- throw FileSystem::MissingFileException(file_pattern, context);
81
- }
78
+ auto found_files = fs.GlobFiles(file_pattern, context);
82
79
  file_paths.insert(file_paths.end(), found_files.begin(), found_files.end());
83
80
  }
84
81
  }
@@ -589,6 +589,7 @@ void StringColumnReader::PrepareDeltaLengthByteArray(ResizeableBuffer &buffer) {
589
589
  }
590
590
  auto length_data = (uint32_t *)length_buffer->ptr;
591
591
  byte_array_data = make_unique<Vector>(LogicalType::VARCHAR, value_count);
592
+ byte_array_count = value_count;
592
593
  auto string_data = FlatVector::GetData<string_t>(*byte_array_data);
593
594
  for (idx_t i = 0; i < value_count; i++) {
594
595
  auto str_len = length_data[i];
@@ -615,6 +616,7 @@ void StringColumnReader::PrepareDeltaByteArray(ResizeableBuffer &buffer) {
615
616
  auto prefix_data = (uint32_t *)prefix_buffer->ptr;
616
617
  auto suffix_data = (uint32_t *)suffix_buffer->ptr;
617
618
  byte_array_data = make_unique<Vector>(LogicalType::VARCHAR, prefix_count);
619
+ byte_array_count = prefix_count;
618
620
  auto string_data = FlatVector::GetData<string_t>(*byte_array_data);
619
621
  for (idx_t i = 0; i < prefix_count; i++) {
620
622
  auto str_len = prefix_data[i] + suffix_data[i];
@@ -646,6 +648,11 @@ void StringColumnReader::DeltaByteArray(uint8_t *defines, idx_t num_values, parq
646
648
  continue;
647
649
  }
648
650
  if (filter[row_idx + result_offset]) {
651
+ if (delta_offset >= byte_array_count) {
652
+ throw IOException("DELTA_BYTE_ARRAY - length mismatch between values and byte array lengths (attempted "
653
+ "read of %d from %d entries) - corrupt file?",
654
+ delta_offset + 1, byte_array_count);
655
+ }
649
656
  result_ptr[row_idx + result_offset] = string_data[delta_offset++];
650
657
  } else {
651
658
  delta_offset++;
@@ -131,6 +131,7 @@ protected:
131
131
  ParquetReader &reader;
132
132
  LogicalType type;
133
133
  unique_ptr<Vector> byte_array_data;
134
+ idx_t byte_array_count = 0;
134
135
 
135
136
  idx_t pending_skips = 0;
136
137
 
@@ -221,10 +221,7 @@ public:
221
221
  }
222
222
 
223
223
  FileSystem &fs = FileSystem::GetFileSystem(context);
224
- auto files = fs.Glob(info.file_path, context);
225
- if (files.empty()) {
226
- throw FileSystem::MissingFileException(info.file_path, context);
227
- }
224
+ auto files = fs.GlobFiles(info.file_path, context);
228
225
 
229
226
  // The most likely path (Parquet read without union by name option)
230
227
  if (!parquet_options.union_by_name) {
@@ -362,12 +359,7 @@ public:
362
359
  }
363
360
 
364
361
  static vector<string> ParquetGlob(FileSystem &fs, const string &glob, ClientContext &context) {
365
- auto files = fs.Glob(glob, FileSystem::GetFileOpener(context));
366
-
367
- if (files.empty()) {
368
- throw FileSystem::MissingFileException(glob, context);
369
- }
370
- return files;
362
+ return fs.GlobFiles(glob, context);
371
363
  }
372
364
 
373
365
  static unique_ptr<FunctionData> ParquetScanBind(ClientContext &context, TableFunctionBindInput &input,
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include "duckdb/catalog/catalog_search_path.hpp"
4
4
  #include "duckdb/catalog/catalog_entry/list.hpp"
5
+ #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
5
6
  #include "duckdb/catalog/catalog_set.hpp"
6
7
  #include "duckdb/catalog/default/default_schemas.hpp"
7
8
  #include "duckdb/catalog/catalog_entry/type_catalog_entry.hpp"
@@ -26,7 +27,7 @@
26
27
  #include "duckdb/planner/parsed_data/bound_create_table_info.hpp"
27
28
  #include "duckdb/planner/binder.hpp"
28
29
  #include "duckdb/catalog/default/default_types.hpp"
29
- #include "duckdb/main/extension_functions.hpp"
30
+ #include "duckdb/main/extension_entries.hpp"
30
31
  #include "duckdb/main/connection.hpp"
31
32
  #include "duckdb/main/attached_database.hpp"
32
33
  #include "duckdb/main/database_manager.hpp"
@@ -251,6 +252,20 @@ CatalogEntry *Catalog::CreateCollation(CatalogTransaction transaction, SchemaCat
251
252
  return schema->CreateCollation(transaction, info);
252
253
  }
253
254
 
255
+ //===--------------------------------------------------------------------===//
256
+ // Index
257
+ //===--------------------------------------------------------------------===//
258
+ CatalogEntry *Catalog::CreateIndex(CatalogTransaction transaction, CreateIndexInfo *info) {
259
+ auto &context = transaction.GetContext();
260
+ return CreateIndex(context, info);
261
+ }
262
+
263
+ CatalogEntry *Catalog::CreateIndex(ClientContext &context, CreateIndexInfo *info) {
264
+ auto schema = GetSchema(context, info->schema);
265
+ auto table = GetEntry<TableCatalogEntry>(context, schema->name, info->table->table_name);
266
+ return schema->CreateIndex(context, info, table);
267
+ }
268
+
254
269
  //===--------------------------------------------------------------------===//
255
270
  // Lookup Structures
256
271
  //===--------------------------------------------------------------------===//
@@ -317,17 +332,26 @@ SimilarCatalogEntry Catalog::SimilarEntryInSchemas(ClientContext &context, const
317
332
  return result;
318
333
  }
319
334
 
320
- string FindExtension(const string &function_name) {
321
- auto size = sizeof(EXTENSION_FUNCTIONS) / sizeof(ExtensionFunction);
322
- auto it = std::lower_bound(
323
- EXTENSION_FUNCTIONS, EXTENSION_FUNCTIONS + size, function_name,
324
- [](const ExtensionFunction &element, const string &value) { return element.function < value; });
325
- if (it != EXTENSION_FUNCTIONS + size && it->function == function_name) {
335
+ string FindExtensionGeneric(const string &name, const ExtensionEntry entries[], idx_t size) {
336
+ auto lcase = StringUtil::Lower(name);
337
+ auto it = std::lower_bound(entries, entries + size, lcase,
338
+ [](const ExtensionEntry &element, const string &value) { return element.name < value; });
339
+ if (it != entries + size && it->name == lcase) {
326
340
  return it->extension;
327
341
  }
328
342
  return "";
329
343
  }
330
344
 
345
+ string FindExtensionForFunction(const string &name) {
346
+ idx_t size = sizeof(EXTENSION_FUNCTIONS) / sizeof(ExtensionEntry);
347
+ return FindExtensionGeneric(name, EXTENSION_FUNCTIONS, size);
348
+ }
349
+
350
+ string FindExtensionForSetting(const string &name) {
351
+ idx_t size = sizeof(EXTENSION_SETTINGS) / sizeof(ExtensionEntry);
352
+ return FindExtensionGeneric(name, EXTENSION_SETTINGS, size);
353
+ }
354
+
331
355
  vector<CatalogSearchEntry> GetCatalogEntries(ClientContext &context, const string &catalog, const string &schema) {
332
356
  vector<CatalogSearchEntry> entries;
333
357
  auto &search_path = *context.client_data->catalog_search_path;
@@ -392,6 +416,26 @@ void FindMinimalQualification(ClientContext &context, const string &catalog_name
392
416
  qualify_schema = true;
393
417
  }
394
418
 
419
+ CatalogException Catalog::UnrecognizedConfigurationError(ClientContext &context, const string &name) {
420
+ // check if the setting exists in any extensions
421
+ auto extension_name = FindExtensionForSetting(name);
422
+ if (!extension_name.empty()) {
423
+ return CatalogException(
424
+ "Setting with name \"%s\" is not in the catalog, but it exists in the %s extension.\n\nTo "
425
+ "install and load the extension, run:\nINSTALL %s;\nLOAD %s;",
426
+ name, extension_name, extension_name, extension_name);
427
+ }
428
+ // the setting is not in an extension
429
+ // get a list of all options
430
+ vector<string> potential_names = DBConfig::GetOptionNames();
431
+ for (auto &entry : DBConfig::GetConfig(context).extension_parameters) {
432
+ potential_names.push_back(entry.first);
433
+ }
434
+
435
+ throw CatalogException("unrecognized configuration parameter \"%s\"\n%s", name,
436
+ StringUtil::CandidatesErrorMessage(potential_names, name, "Did you mean"));
437
+ }
438
+
395
439
  CatalogException Catalog::CreateMissingEntryException(ClientContext &context, const string &entry_name,
396
440
  CatalogType type,
397
441
  const unordered_set<SchemaCatalogEntry *> &schemas,
@@ -408,13 +452,18 @@ CatalogException Catalog::CreateMissingEntryException(ClientContext &context, co
408
452
  unseen_schemas.insert(current_schema);
409
453
  }
410
454
  }
411
- auto unseen_entry = SimilarEntryInSchemas(context, entry_name, type, unseen_schemas);
412
- auto extension_name = FindExtension(entry_name);
413
- if (!extension_name.empty()) {
414
- return CatalogException("Function with name %s is not on the catalog, but it exists in the %s extension. To "
415
- "Install and Load the extension, run: INSTALL %s; LOAD %s;",
416
- entry_name, extension_name, extension_name, extension_name);
455
+ // check if the entry exists in any extension
456
+ if (type == CatalogType::TABLE_FUNCTION_ENTRY || type == CatalogType::SCALAR_FUNCTION_ENTRY ||
457
+ type == CatalogType::AGGREGATE_FUNCTION_ENTRY) {
458
+ auto extension_name = FindExtensionForFunction(entry_name);
459
+ if (!extension_name.empty()) {
460
+ return CatalogException(
461
+ "Function with name \"%s\" is not in the catalog, but it exists in the %s extension.\n\nTo "
462
+ "install and load the extension, run:\nINSTALL %s;\nLOAD %s;",
463
+ entry_name, extension_name, extension_name, extension_name);
464
+ }
417
465
  }
466
+ auto unseen_entry = SimilarEntryInSchemas(context, entry_name, type, unseen_schemas);
418
467
  string did_you_mean;
419
468
  if (unseen_entry.Found() && unseen_entry.distance < entry.distance) {
420
469
  // the closest matching entry requires qualification as it is not in the default search path
@@ -19,10 +19,11 @@ string IndexCatalogEntry::ToSQL() {
19
19
  return sql;
20
20
  }
21
21
 
22
- void IndexCatalogEntry::Serialize(duckdb::MetaBlockWriter &serializer) {
23
- // Here we serialize the index metadata in the following order:
24
- // schema name, table name, index name, sql, index type, index constraint type, expression list.
25
- // column_ids, unbound_expression
22
+ void IndexCatalogEntry::Serialize(Serializer &serializer) {
23
+ // here we serialize the index metadata in the following order:
24
+ // schema name, table name, index name, sql, index type, index constraint type, expression list, parsed expressions,
25
+ // column IDs
26
+
26
27
  FieldWriter writer(serializer);
27
28
  writer.WriteString(GetSchemaName());
28
29
  writer.WriteString(GetTableName());
@@ -37,9 +38,9 @@ void IndexCatalogEntry::Serialize(duckdb::MetaBlockWriter &serializer) {
37
38
  }
38
39
 
39
40
  unique_ptr<CreateIndexInfo> IndexCatalogEntry::Deserialize(Deserializer &source, ClientContext &context) {
40
- // Here we deserialize the index metadata in the following order:
41
- // root block, root offset, schema name, table name, index name, sql, index type, index constraint type, expression
42
- // list.
41
+ // here we deserialize the index metadata in the following order:
42
+ // schema name, table schema name, table name, index name, sql, index type, index constraint type, expression list,
43
+ // parsed expression list, column IDs
43
44
 
44
45
  auto create_index_info = make_unique<CreateIndexInfo>();
45
46
 
@@ -48,7 +48,7 @@ static DefaultView internal_views[] = {
48
48
  {"pg_catalog", "pg_views", "SELECT schema_name schemaname, view_name viewname, 'duckdb' viewowner, sql definition FROM duckdb_views()"},
49
49
  {"information_schema", "columns", "SELECT database_name table_catalog, schema_name table_schema, table_name, column_name, column_index ordinal_position, column_default, CASE WHEN is_nullable THEN 'YES' ELSE 'NO' END is_nullable, data_type, character_maximum_length, NULL character_octet_length, numeric_precision, numeric_precision_radix, numeric_scale, NULL datetime_precision, NULL interval_type, NULL interval_precision, NULL character_set_catalog, NULL character_set_schema, NULL character_set_name, NULL collation_catalog, NULL collation_schema, NULL collation_name, NULL domain_catalog, NULL domain_schema, NULL domain_name, NULL udt_catalog, NULL udt_schema, NULL udt_name, NULL scope_catalog, NULL scope_schema, NULL scope_name, NULL maximum_cardinality, NULL dtd_identifier, NULL is_self_referencing, NULL is_identity, NULL identity_generation, NULL identity_start, NULL identity_increment, NULL identity_maximum, NULL identity_minimum, NULL identity_cycle, NULL is_generated, NULL generation_expression, NULL is_updatable FROM duckdb_columns;"},
50
50
  {"information_schema", "schemata", "SELECT database_name catalog_name, schema_name, 'duckdb' schema_owner, NULL default_character_set_catalog, NULL default_character_set_schema, NULL default_character_set_name, sql sql_path FROM duckdb_schemas()"},
51
- {"information_schema", "tables", "SELECT database_name table_catalog, schema_name table_schema, table_name, CASE WHEN temporary THEN 'LOCAL TEMPORARY' ELSE 'BASE TABLE' END table_type, NULL self_referencing_column_name, NULL reference_generation, NULL user_defined_type_catalog, NULL user_defined_type_schema, NULL user_defined_type_name, 'YES' is_insertable_into, 'NO' is_typed, CASE WHEN temporary THEN 'PRESERVE' ELSE NULL END commit_action FROM duckdb_tables() UNION ALL SELECT NULL table_catalog, schema_name table_schema, view_name table_name, 'VIEW' table_type, NULL self_referencing_column_name, NULL reference_generation, NULL user_defined_type_catalog, NULL user_defined_type_schema, NULL user_defined_type_name, 'NO' is_insertable_into, 'NO' is_typed, NULL commit_action FROM duckdb_views;"},
51
+ {"information_schema", "tables", "SELECT database_name table_catalog, schema_name table_schema, table_name, CASE WHEN temporary THEN 'LOCAL TEMPORARY' ELSE 'BASE TABLE' END table_type, NULL self_referencing_column_name, NULL reference_generation, NULL user_defined_type_catalog, NULL user_defined_type_schema, NULL user_defined_type_name, 'YES' is_insertable_into, 'NO' is_typed, CASE WHEN temporary THEN 'PRESERVE' ELSE NULL END commit_action FROM duckdb_tables() UNION ALL SELECT database_name table_catalog, schema_name table_schema, view_name table_name, 'VIEW' table_type, NULL self_referencing_column_name, NULL reference_generation, NULL user_defined_type_catalog, NULL user_defined_type_schema, NULL user_defined_type_name, 'NO' is_insertable_into, 'NO' is_typed, NULL commit_action FROM duckdb_views;"},
52
52
  {nullptr, nullptr, nullptr}};
53
53
 
54
54
  static unique_ptr<CreateViewInfo> GetDefaultView(ClientContext &context, const string &input_schema, const string &input_name) {
@@ -10,6 +10,7 @@
10
10
  #include "duckdb/main/client_context.hpp"
11
11
  #include "duckdb/main/client_data.hpp"
12
12
  #include "duckdb/main/database.hpp"
13
+ #include "duckdb/main/extension_helper.hpp"
13
14
 
14
15
  #include <cstdint>
15
16
  #include <cstdio>
@@ -335,18 +336,31 @@ bool FileSystem::CanHandleFile(const string &fpath) {
335
336
  throw NotImplementedException("%s: CanHandleFile is not implemented!", GetName());
336
337
  }
337
338
 
338
- IOException FileSystem::MissingFileException(const string &file_path, ClientContext &context) {
339
- const string prefixes[] = {"http://", "https://", "s3://"};
340
- for (auto &prefix : prefixes) {
341
- if (StringUtil::StartsWith(file_path, prefix)) {
342
- if (!context.db->LoadedExtensions().count("httpfs")) {
343
- return MissingExtensionException("No files found that match the pattern \"%s\", because the httpfs "
344
- "extension is not loaded. Try loading the extension: LOAD HTTPFS",
345
- file_path);
339
+ vector<string> FileSystem::GlobFiles(const string &pattern, ClientContext &context) {
340
+ auto result = Glob(pattern, context);
341
+ if (result.empty()) {
342
+ string required_extension;
343
+ const string prefixes[] = {"http://", "https://", "s3://"};
344
+ for (auto &prefix : prefixes) {
345
+ if (StringUtil::StartsWith(pattern, prefix)) {
346
+ required_extension = "httpfs";
347
+ break;
346
348
  }
347
349
  }
350
+ if (!required_extension.empty() && !context.db->ExtensionIsLoaded(required_extension)) {
351
+ // an extension is required to read this file but it is not loaded - try to load it
352
+ ExtensionHelper::LoadExternalExtension(context, required_extension);
353
+ // success! glob again
354
+ // check the extension is loaded just in case to prevent an infinite loop here
355
+ if (!context.db->ExtensionIsLoaded(required_extension)) {
356
+ throw InternalException("Extension load \"%s\" did not throw but somehow the extension was not loaded",
357
+ required_extension);
358
+ }
359
+ return GlobFiles(pattern, context);
360
+ }
361
+ throw IOException("No files found that match the pattern \"%s\"", pattern);
348
362
  }
349
- return IOException("No files found that match the pattern \"%s\"", file_path);
363
+ return result;
350
364
  }
351
365
 
352
366
  void FileSystem::Seek(FileHandle &handle, idx_t location) {
@@ -833,8 +833,8 @@ static bool HasGlob(const string &str) {
833
833
  return false;
834
834
  }
835
835
 
836
- static void GlobFiles(FileSystem &fs, const string &path, const string &glob, bool match_directory,
837
- vector<string> &result, bool join_path) {
836
+ static void GlobFilesInternal(FileSystem &fs, const string &path, const string &glob, bool match_directory,
837
+ vector<string> &result, bool join_path) {
838
838
  fs.ListFiles(path, [&](const string &fname, bool is_directory) {
839
839
  if (is_directory != match_directory) {
840
840
  return;
@@ -951,12 +951,12 @@ vector<string> LocalFileSystem::Glob(const string &path, FileOpener *opener) {
951
951
  } else {
952
952
  if (previous_directories.empty()) {
953
953
  // no previous directories: list in the current path
954
- GlobFiles(*this, ".", splits[i], !is_last_chunk, result, false);
954
+ GlobFilesInternal(*this, ".", splits[i], !is_last_chunk, result, false);
955
955
  } else {
956
956
  // previous directories
957
957
  // we iterate over each of the previous directories, and apply the glob of the current directory
958
958
  for (auto &prev_directory : previous_directories) {
959
- GlobFiles(*this, prev_directory, splits[i], !is_last_chunk, result, true);
959
+ GlobFilesInternal(*this, prev_directory, splits[i], !is_last_chunk, result, true);
960
960
  }
961
961
  }
962
962
  }