duckdb 0.8.2-dev3300.0 → 0.8.2-dev3456.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 (34) hide show
  1. package/binding.gyp +3 -1
  2. package/configure.py +3 -0
  3. package/package.json +1 -1
  4. package/src/duckdb/src/catalog/catalog.cpp +144 -63
  5. package/src/duckdb/src/common/exception.cpp +13 -0
  6. package/src/duckdb/src/common/file_system.cpp +21 -6
  7. package/src/duckdb/src/core_functions/scalar/enum/enum_functions.cpp +16 -12
  8. package/src/duckdb/src/core_functions/scalar/generic/current_setting.cpp +3 -1
  9. package/src/duckdb/src/execution/operator/helper/physical_load.cpp +2 -1
  10. package/src/duckdb/src/execution/operator/helper/physical_reset.cpp +3 -1
  11. package/src/duckdb/src/execution/operator/helper/physical_set.cpp +3 -1
  12. package/src/duckdb/src/function/pragma/pragma_queries.cpp +2 -1
  13. package/src/duckdb/src/function/table/read_csv.cpp +8 -3
  14. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  15. package/src/duckdb/src/include/duckdb/catalog/catalog.hpp +20 -5
  16. package/src/duckdb/src/include/duckdb/common/exception.hpp +15 -1
  17. package/src/duckdb/src/include/duckdb/main/client_config.hpp +2 -0
  18. package/src/duckdb/src/include/duckdb/main/config.hpp +12 -0
  19. package/src/duckdb/src/include/duckdb/main/extension/generated_extension_loader.hpp +6 -1
  20. package/src/duckdb/src/include/duckdb/main/extension_entries.hpp +209 -151
  21. package/src/duckdb/src/include/duckdb/main/extension_helper.hpp +34 -3
  22. package/src/duckdb/src/include/duckdb/main/settings.hpp +30 -0
  23. package/src/duckdb/src/include/duckdb/parser/parsed_data/load_info.hpp +4 -0
  24. package/src/duckdb/src/include/duckdb/planner/binder.hpp +4 -0
  25. package/src/duckdb/src/main/config.cpp +3 -0
  26. package/src/duckdb/src/main/extension/extension_helper.cpp +57 -0
  27. package/src/duckdb/src/main/extension/extension_install.cpp +46 -7
  28. package/src/duckdb/src/main/settings/settings.cpp +46 -0
  29. package/src/duckdb/src/parser/transform/statement/transform_load.cpp +1 -0
  30. package/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +69 -23
  31. package/src/duckdb/src/planner/expression_binder/order_binder.cpp +5 -4
  32. package/src/duckdb/src/storage/serialization/serialize_parse_info.cpp +2 -0
  33. package/src/duckdb/third_party/libpg_query/include/nodes/parsenodes.hpp +1 -0
  34. package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +8747 -8821
package/binding.gyp CHANGED
@@ -313,7 +313,9 @@
313
313
  "NAPI_VERSION=6",
314
314
  "DUCKDB_EXTENSION_PARQUET_LINKED",
315
315
  "DUCKDB_EXTENSION_ICU_LINKED",
316
- "DUCKDB_EXTENSION_JSON_LINKED"
316
+ "DUCKDB_EXTENSION_JSON_LINKED",
317
+ "DUCKDB_EXTENSION_AUTOLOAD_DEFAULT=1",
318
+ "DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT=1"
317
319
  ],
318
320
  "cflags_cc": [
319
321
  "-frtti",
package/configure.py CHANGED
@@ -22,6 +22,9 @@ import package_build
22
22
 
23
23
  defines = ['DUCKDB_EXTENSION_{}_LINKED'.format(ext.upper()) for ext in extensions]
24
24
 
25
+ # Autoloading is on by default for node distributions
26
+ defines.extend(['DUCKDB_EXTENSION_AUTOLOAD_DEFAULT=1', 'DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT=1'])
27
+
25
28
  if os.environ.get('DUCKDB_NODE_BUILD_CACHE') == '1' and os.path.isfile(cache_file):
26
29
  with open(cache_file, 'rb') as f:
27
30
  cache = pickle.load(f)
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.8.2-dev3300.0",
5
+ "version": "0.8.2-dev3456.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -11,6 +11,7 @@
11
11
  #include "duckdb/main/client_data.hpp"
12
12
  #include "duckdb/main/database.hpp"
13
13
  #include "duckdb/parser/expression/function_expression.hpp"
14
+ #include "duckdb/main/extension_helper.hpp"
14
15
  #include "duckdb/parser/parsed_data/alter_table_info.hpp"
15
16
  #include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp"
16
17
  #include "duckdb/parser/parsed_data/create_collation_info.hpp"
@@ -28,6 +29,7 @@
28
29
  #include "duckdb/planner/binder.hpp"
29
30
  #include "duckdb/catalog/default/default_types.hpp"
30
31
  #include "duckdb/main/extension_entries.hpp"
32
+ #include "duckdb/main/extension/generated_extension_loader.hpp"
31
33
  #include "duckdb/main/connection.hpp"
32
34
  #include "duckdb/main/attached_database.hpp"
33
35
  #include "duckdb/main/database_manager.hpp"
@@ -297,6 +299,7 @@ struct CatalogLookup {
297
299
  struct CatalogEntryLookup {
298
300
  optional_ptr<SchemaCatalogEntry> schema;
299
301
  optional_ptr<CatalogEntry> entry;
302
+ PreservedError error;
300
303
 
301
304
  DUCKDB_API bool Found() const {
302
305
  return entry;
@@ -315,6 +318,7 @@ void Catalog::DropEntry(ClientContext &context, DropInfo &info) {
315
318
  }
316
319
 
317
320
  auto lookup = LookupEntry(context, info.type, info.schema, info.name, info.if_not_found);
321
+
318
322
  if (!lookup.Found()) {
319
323
  return;
320
324
  }
@@ -363,26 +367,6 @@ SimilarCatalogEntry Catalog::SimilarEntryInSchemas(ClientContext &context, const
363
367
  return result;
364
368
  }
365
369
 
366
- string FindExtensionGeneric(const string &name, const ExtensionEntry entries[], idx_t size) {
367
- auto lcase = StringUtil::Lower(name);
368
- auto it = std::lower_bound(entries, entries + size, lcase,
369
- [](const ExtensionEntry &element, const string &value) { return element.name < value; });
370
- if (it != entries + size && it->name == lcase) {
371
- return it->extension;
372
- }
373
- return "";
374
- }
375
-
376
- string FindExtensionForFunction(const string &name) {
377
- idx_t size = sizeof(EXTENSION_FUNCTIONS) / sizeof(ExtensionEntry);
378
- return FindExtensionGeneric(name, EXTENSION_FUNCTIONS, size);
379
- }
380
-
381
- string FindExtensionForSetting(const string &name) {
382
- idx_t size = sizeof(EXTENSION_SETTINGS) / sizeof(ExtensionEntry);
383
- return FindExtensionGeneric(name, EXTENSION_SETTINGS, size);
384
- }
385
-
386
370
  vector<CatalogSearchEntry> GetCatalogEntries(ClientContext &context, const string &catalog, const string &schema) {
387
371
  vector<CatalogSearchEntry> entries;
388
372
  auto &search_path = *context.client_data->catalog_search_path;
@@ -447,14 +431,53 @@ void FindMinimalQualification(ClientContext &context, const string &catalog_name
447
431
  qualify_schema = true;
448
432
  }
449
433
 
434
+ void Catalog::AutoloadExtensionByConfigName(ClientContext &context, const string &configuration_name) {
435
+ #ifndef DUCKDB_DISABLE_EXTENSION_LOAD
436
+ auto &dbconfig = DBConfig::GetConfig(context);
437
+ if (dbconfig.options.autoload_known_extensions) {
438
+ auto extension_name = ExtensionHelper::FindExtensionInEntries(configuration_name, EXTENSION_SETTINGS);
439
+ if (ExtensionHelper::CanAutoloadExtension(extension_name)) {
440
+ ExtensionHelper::AutoLoadExtension(context, extension_name);
441
+ return;
442
+ }
443
+ }
444
+ #endif
445
+
446
+ throw Catalog::UnrecognizedConfigurationError(context, configuration_name);
447
+ }
448
+
449
+ bool Catalog::AutoLoadExtensionByCatalogEntry(ClientContext &context, CatalogType type, const string &entry_name) {
450
+ #ifndef DUCKDB_DISABLE_EXTENSION_LOAD
451
+ auto &dbconfig = DBConfig::GetConfig(context);
452
+ if (dbconfig.options.autoload_known_extensions) {
453
+ string extension_name;
454
+ if (type == CatalogType::TABLE_FUNCTION_ENTRY || type == CatalogType::SCALAR_FUNCTION_ENTRY ||
455
+ type == CatalogType::AGGREGATE_FUNCTION_ENTRY || type == CatalogType::PRAGMA_FUNCTION_ENTRY) {
456
+ extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_FUNCTIONS);
457
+ } else if (type == CatalogType::COPY_FUNCTION_ENTRY) {
458
+ extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_COPY_FUNCTIONS);
459
+ } else if (type == CatalogType::TYPE_ENTRY) {
460
+ extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_TYPES);
461
+ }
462
+
463
+ if (!extension_name.empty() && ExtensionHelper::CanAutoloadExtension(extension_name)) {
464
+ ExtensionHelper::AutoLoadExtension(context, extension_name);
465
+ return true;
466
+ }
467
+ }
468
+ #endif
469
+
470
+ return false;
471
+ }
472
+
450
473
  CatalogException Catalog::UnrecognizedConfigurationError(ClientContext &context, const string &name) {
451
474
  // check if the setting exists in any extensions
452
- auto extension_name = FindExtensionForSetting(name);
475
+ auto extension_name = ExtensionHelper::FindExtensionInEntries(name, EXTENSION_SETTINGS);
453
476
  if (!extension_name.empty()) {
454
- return CatalogException(
455
- "Setting with name \"%s\" is not in the catalog, but it exists in the %s extension.\n\nTo "
456
- "install and load the extension, run:\nINSTALL %s;\nLOAD %s;",
457
- name, extension_name, extension_name, extension_name);
477
+ auto error_message = "Setting with name \"" + name + "\" is not in the catalog, but it exists in the " +
478
+ extension_name + " extension.";
479
+ error_message = ExtensionHelper::AddExtensionInstallHintToErrorMsg(context, error_message, extension_name);
480
+ return CatalogException(error_message);
458
481
  }
459
482
  // the setting is not in an extension
460
483
  // get a list of all options
@@ -484,16 +507,24 @@ CatalogException Catalog::CreateMissingEntryException(ClientContext &context, co
484
507
  }
485
508
  }
486
509
  // check if the entry exists in any extension
510
+ string extension_name;
487
511
  if (type == CatalogType::TABLE_FUNCTION_ENTRY || type == CatalogType::SCALAR_FUNCTION_ENTRY ||
488
- type == CatalogType::AGGREGATE_FUNCTION_ENTRY) {
489
- auto extension_name = FindExtensionForFunction(entry_name);
490
- if (!extension_name.empty()) {
491
- return CatalogException(
492
- "Function with name \"%s\" is not in the catalog, but it exists in the %s extension.\n\nTo "
493
- "install and load the extension, run:\nINSTALL %s;\nLOAD %s;",
494
- entry_name, extension_name, extension_name, extension_name);
495
- }
512
+ type == CatalogType::AGGREGATE_FUNCTION_ENTRY || type == CatalogType::PRAGMA_FUNCTION_ENTRY) {
513
+ extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_FUNCTIONS);
514
+ } else if (type == CatalogType::TYPE_ENTRY) {
515
+ extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_TYPES);
516
+ } else if (type == CatalogType::COPY_FUNCTION_ENTRY) {
517
+ extension_name = ExtensionHelper::FindExtensionInEntries(entry_name, EXTENSION_COPY_FUNCTIONS);
518
+ }
519
+
520
+ // if we found an extension that can handle this catalog entry, create an error hinting the user
521
+ if (!extension_name.empty()) {
522
+ auto error_message = CatalogTypeToString(type) + " with name \"" + entry_name +
523
+ "\" is not in the catalog, but it exists in the " + extension_name + " extension.";
524
+ error_message = ExtensionHelper::AddExtensionInstallHintToErrorMsg(context, error_message, extension_name);
525
+ return CatalogException(error_message);
496
526
  }
527
+
497
528
  auto unseen_entry = SimilarEntryInSchemas(context, entry_name, type, unseen_schemas);
498
529
  string did_you_mean;
499
530
  if (unseen_entry.Found() && unseen_entry.distance < entry.distance) {
@@ -513,22 +544,22 @@ CatalogException Catalog::CreateMissingEntryException(ClientContext &context, co
513
544
  entry_name, did_you_mean));
514
545
  }
515
546
 
516
- CatalogEntryLookup Catalog::LookupEntryInternal(CatalogTransaction transaction, CatalogType type, const string &schema,
517
- const string &name) {
547
+ CatalogEntryLookup Catalog::TryLookupEntryInternal(CatalogTransaction transaction, CatalogType type,
548
+ const string &schema, const string &name) {
518
549
  auto schema_entry = GetSchema(transaction, schema, OnEntryNotFound::RETURN_NULL);
519
550
  if (!schema_entry) {
520
- return {nullptr, nullptr};
551
+ return {nullptr, nullptr, PreservedError()};
521
552
  }
522
553
  auto entry = schema_entry->GetEntry(transaction, type, name);
523
554
  if (!entry) {
524
- return {schema_entry, nullptr};
555
+ return {schema_entry, nullptr, PreservedError()};
525
556
  }
526
- return {schema_entry, entry};
557
+ return {schema_entry, entry, PreservedError()};
527
558
  }
528
559
 
529
- CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type, const string &schema,
530
- const string &name, OnEntryNotFound if_not_found,
531
- QueryErrorContext error_context) {
560
+ CatalogEntryLookup Catalog::TryLookupEntry(ClientContext &context, CatalogType type, const string &schema,
561
+ const string &name, OnEntryNotFound if_not_found,
562
+ QueryErrorContext error_context) {
532
563
  reference_set_t<SchemaCatalogEntry> schemas;
533
564
  if (IsInvalidSchema(schema)) {
534
565
  // try all schemas for this catalog
@@ -536,7 +567,7 @@ CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type
536
567
  for (auto &entry : entries) {
537
568
  auto &candidate_schema = entry.schema;
538
569
  auto transaction = GetCatalogTransaction(context);
539
- auto result = LookupEntryInternal(transaction, type, candidate_schema, name);
570
+ auto result = TryLookupEntryInternal(transaction, type, candidate_schema, name);
540
571
  if (result.Found()) {
541
572
  return result;
542
573
  }
@@ -546,7 +577,7 @@ CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type
546
577
  }
547
578
  } else {
548
579
  auto transaction = GetCatalogTransaction(context);
549
- auto result = LookupEntryInternal(transaction, type, schema, name);
580
+ auto result = TryLookupEntryInternal(transaction, type, schema, name);
550
581
  if (result.Found()) {
551
582
  return result;
552
583
  }
@@ -554,19 +585,34 @@ CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type
554
585
  schemas.insert(*result.schema);
555
586
  }
556
587
  }
588
+
557
589
  if (if_not_found == OnEntryNotFound::RETURN_NULL) {
558
- return {nullptr, nullptr};
590
+ return {nullptr, nullptr, PreservedError()};
591
+ } else {
592
+ auto except = CreateMissingEntryException(context, name, type, schemas, error_context);
593
+ return {nullptr, nullptr, PreservedError(except)};
559
594
  }
560
- throw CreateMissingEntryException(context, name, type, schemas, error_context);
561
595
  }
562
596
 
563
- CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, vector<CatalogLookup> &lookups, CatalogType type,
597
+ CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type, const string &schema,
564
598
  const string &name, OnEntryNotFound if_not_found,
565
599
  QueryErrorContext error_context) {
600
+ auto res = TryLookupEntry(context, type, schema, name, if_not_found, error_context);
601
+
602
+ if (res.error) {
603
+ res.error.Throw();
604
+ }
605
+
606
+ return res;
607
+ }
608
+
609
+ CatalogEntryLookup Catalog::TryLookupEntry(ClientContext &context, vector<CatalogLookup> &lookups, CatalogType type,
610
+ const string &name, OnEntryNotFound if_not_found,
611
+ QueryErrorContext error_context) {
566
612
  reference_set_t<SchemaCatalogEntry> schemas;
567
613
  for (auto &lookup : lookups) {
568
614
  auto transaction = lookup.catalog.GetCatalogTransaction(context);
569
- auto result = lookup.catalog.LookupEntryInternal(transaction, type, lookup.schema, name);
615
+ auto result = lookup.catalog.TryLookupEntryInternal(transaction, type, lookup.schema, name);
570
616
  if (result.Found()) {
571
617
  return result;
572
618
  }
@@ -574,10 +620,33 @@ CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, vector<CatalogLo
574
620
  schemas.insert(*result.schema);
575
621
  }
576
622
  }
623
+
577
624
  if (if_not_found == OnEntryNotFound::RETURN_NULL) {
578
- return {nullptr, nullptr};
625
+ return {nullptr, nullptr, PreservedError()};
626
+ } else {
627
+ auto except = CreateMissingEntryException(context, name, type, schemas, error_context);
628
+ return {nullptr, nullptr, PreservedError(except)};
579
629
  }
580
- throw CreateMissingEntryException(context, name, type, schemas, error_context);
630
+ }
631
+
632
+ CatalogEntryLookup Catalog::TryLookupEntry(ClientContext &context, CatalogType type, const string &catalog,
633
+ const string &schema, const string &name, OnEntryNotFound if_not_found,
634
+ QueryErrorContext error_context) {
635
+ auto entries = GetCatalogEntries(context, catalog, schema);
636
+ vector<CatalogLookup> lookups;
637
+ lookups.reserve(entries.size());
638
+ for (auto &entry : entries) {
639
+ if (if_not_found == OnEntryNotFound::RETURN_NULL) {
640
+ auto catalog_entry = Catalog::GetCatalogEntry(context, entry.catalog);
641
+ if (!catalog_entry) {
642
+ return {nullptr, nullptr, PreservedError()};
643
+ }
644
+ lookups.emplace_back(*catalog_entry, entry.schema);
645
+ } else {
646
+ lookups.emplace_back(Catalog::GetCatalog(context, entry.catalog), entry.schema);
647
+ }
648
+ }
649
+ return Catalog::TryLookupEntry(context, lookups, type, name, if_not_found, error_context);
581
650
  }
582
651
 
583
652
  CatalogEntry &Catalog::GetEntry(ClientContext &context, const string &schema, const string &name) {
@@ -596,7 +665,20 @@ CatalogEntry &Catalog::GetEntry(ClientContext &context, const string &schema, co
596
665
  optional_ptr<CatalogEntry> Catalog::GetEntry(ClientContext &context, CatalogType type, const string &schema_name,
597
666
  const string &name, OnEntryNotFound if_not_found,
598
667
  QueryErrorContext error_context) {
599
- return LookupEntry(context, type, schema_name, name, if_not_found, error_context).entry.get();
668
+ auto lookup_entry = TryLookupEntry(context, type, schema_name, name, if_not_found, error_context);
669
+
670
+ // Try autoloading extension to resolve lookup
671
+ if (!lookup_entry.Found()) {
672
+ if (AutoLoadExtensionByCatalogEntry(context, type, name)) {
673
+ lookup_entry = TryLookupEntry(context, type, schema_name, name, if_not_found, error_context);
674
+ }
675
+ }
676
+
677
+ if (lookup_entry.error) {
678
+ lookup_entry.error.Throw();
679
+ }
680
+
681
+ return lookup_entry.entry.get();
600
682
  }
601
683
 
602
684
  CatalogEntry &Catalog::GetEntry(ClientContext &context, CatalogType type, const string &schema, const string &name,
@@ -607,21 +689,19 @@ CatalogEntry &Catalog::GetEntry(ClientContext &context, CatalogType type, const
607
689
  optional_ptr<CatalogEntry> Catalog::GetEntry(ClientContext &context, CatalogType type, const string &catalog,
608
690
  const string &schema, const string &name, OnEntryNotFound if_not_found,
609
691
  QueryErrorContext error_context) {
610
- auto entries = GetCatalogEntries(context, catalog, schema);
611
- vector<CatalogLookup> lookups;
612
- lookups.reserve(entries.size());
613
- for (auto &entry : entries) {
614
- if (if_not_found == OnEntryNotFound::RETURN_NULL) {
615
- auto catalog_entry = Catalog::GetCatalogEntry(context, entry.catalog);
616
- if (!catalog_entry) {
617
- return nullptr;
618
- }
619
- lookups.emplace_back(*catalog_entry, entry.schema);
620
- } else {
621
- lookups.emplace_back(Catalog::GetCatalog(context, entry.catalog), entry.schema);
692
+ auto result = TryLookupEntry(context, type, catalog, schema, name, if_not_found, error_context);
693
+
694
+ // Try autoloading extension to resolve lookup
695
+ if (!result.Found()) {
696
+ if (AutoLoadExtensionByCatalogEntry(context, type, name)) {
697
+ result = TryLookupEntry(context, type, catalog, schema, name, if_not_found, error_context);
622
698
  }
623
699
  }
624
- auto result = LookupEntry(context, lookups, type, name, if_not_found, error_context);
700
+
701
+ if (result.error) {
702
+ result.error.Throw();
703
+ }
704
+
625
705
  if (!result.Found()) {
626
706
  D_ASSERT(if_not_found == OnEntryNotFound::RETURN_NULL);
627
707
  return nullptr;
@@ -724,6 +804,7 @@ vector<reference<SchemaCatalogEntry>> Catalog::GetAllSchemas(ClientContext &cont
724
804
  void Catalog::Alter(ClientContext &context, AlterInfo &info) {
725
805
  ModifyCatalog();
726
806
  auto lookup = LookupEntry(context, info.GetCatalogType(), info.schema, info.name, info.if_not_found);
807
+
727
808
  if (!lookup.Found()) {
728
809
  return;
729
810
  }
@@ -158,8 +158,12 @@ string Exception::ExceptionTypeToString(ExceptionType type) {
158
158
  return "Parameter Not Allowed";
159
159
  case ExceptionType::DEPENDENCY:
160
160
  return "Dependency";
161
+ case ExceptionType::MISSING_EXTENSION:
162
+ return "Missing Extension";
161
163
  case ExceptionType::HTTP:
162
164
  return "HTTP";
165
+ case ExceptionType::AUTOLOAD:
166
+ return "Extension Autoloading";
163
167
  default:
164
168
  return "Unknown";
165
169
  }
@@ -225,6 +229,8 @@ void Exception::ThrowAsTypeWithMessage(ExceptionType type, const string &message
225
229
  case ExceptionType::HTTP: {
226
230
  original->AsHTTPException().Throw();
227
231
  }
232
+ case ExceptionType::MISSING_EXTENSION:
233
+ throw MissingExtensionException(message);
228
234
  default:
229
235
  throw Exception(type, message);
230
236
  }
@@ -347,6 +353,13 @@ MissingExtensionException::MissingExtensionException(const string &msg)
347
353
  : Exception(ExceptionType::MISSING_EXTENSION, msg) {
348
354
  }
349
355
 
356
+ AutoloadException::AutoloadException(const string &extension_name, Exception &e)
357
+ : Exception(ExceptionType::AUTOLOAD,
358
+ "An error occured while trying to automatically install the required extension '" + extension_name +
359
+ "':\n" + e.RawMessage()),
360
+ wrapped_exception(e) {
361
+ }
362
+
350
363
  SerializationException::SerializationException(const string &msg) : Exception(ExceptionType::SERIALIZATION, msg) {
351
364
  }
352
365
 
@@ -397,16 +397,31 @@ bool FileSystem::CanHandleFile(const string &fpath) {
397
397
  throw NotImplementedException("%s: CanHandleFile is not implemented!", GetName());
398
398
  }
399
399
 
400
+ static string LookupExtensionForPattern(const string &pattern) {
401
+ for (const auto &entry : EXTENSION_FILE_PREFIXES) {
402
+ if (StringUtil::StartsWith(pattern, entry.name)) {
403
+ return entry.extension;
404
+ }
405
+ }
406
+ return "";
407
+ }
408
+
400
409
  vector<string> FileSystem::GlobFiles(const string &pattern, ClientContext &context, FileGlobOptions options) {
401
410
  auto result = Glob(pattern);
402
411
  if (result.empty()) {
403
- string required_extension;
404
- if (FileSystem::IsRemoteFile(pattern)) {
405
- required_extension = "httpfs";
406
- }
412
+ string required_extension = LookupExtensionForPattern(pattern);
407
413
  if (!required_extension.empty() && !context.db->ExtensionIsLoaded(required_extension)) {
408
- // an extension is required to read this file but it is not loaded - try to load it
409
- ExtensionHelper::LoadExternalExtension(context, required_extension);
414
+ auto &dbconfig = DBConfig::GetConfig(context);
415
+ if (!ExtensionHelper::CanAutoloadExtension(required_extension) ||
416
+ !dbconfig.options.autoload_known_extensions) {
417
+ auto error_message =
418
+ "File " + pattern + " requires the extension " + required_extension + " to be loaded";
419
+ error_message =
420
+ ExtensionHelper::AddExtensionInstallHintToErrorMsg(context, error_message, required_extension);
421
+ throw MissingExtensionException(error_message);
422
+ }
423
+ // an extension is required to read this file, but it is not loaded - try to load it
424
+ ExtensionHelper::AutoLoadExtension(context, required_extension);
410
425
  // success! glob again
411
426
  // check the extension is loaded just in case to prevent an infinite loop here
412
427
  if (!context.db->ExtensionIsLoaded(required_extension)) {
@@ -3,24 +3,27 @@
3
3
  namespace duckdb {
4
4
 
5
5
  static void EnumFirstFunction(DataChunk &input, ExpressionState &state, Vector &result) {
6
- D_ASSERT(input.GetTypes().size() == 1);
7
- auto &enum_vector = EnumType::GetValuesInsertOrder(input.GetTypes()[0]);
6
+ auto types = input.GetTypes();
7
+ D_ASSERT(types.size() == 1);
8
+ auto &enum_vector = EnumType::GetValuesInsertOrder(types[0]);
8
9
  auto val = Value(enum_vector.GetValue(0));
9
10
  result.Reference(val);
10
11
  }
11
12
 
12
13
  static void EnumLastFunction(DataChunk &input, ExpressionState &state, Vector &result) {
13
- D_ASSERT(input.GetTypes().size() == 1);
14
- auto enum_size = EnumType::GetSize(input.GetTypes()[0]);
15
- auto &enum_vector = EnumType::GetValuesInsertOrder(input.GetTypes()[0]);
14
+ auto types = input.GetTypes();
15
+ D_ASSERT(types.size() == 1);
16
+ auto enum_size = EnumType::GetSize(types[0]);
17
+ auto &enum_vector = EnumType::GetValuesInsertOrder(types[0]);
16
18
  auto val = Value(enum_vector.GetValue(enum_size - 1));
17
19
  result.Reference(val);
18
20
  }
19
21
 
20
22
  static void EnumRangeFunction(DataChunk &input, ExpressionState &state, Vector &result) {
21
- D_ASSERT(input.GetTypes().size() == 1);
22
- auto enum_size = EnumType::GetSize(input.GetTypes()[0]);
23
- auto &enum_vector = EnumType::GetValuesInsertOrder(input.GetTypes()[0]);
23
+ auto types = input.GetTypes();
24
+ D_ASSERT(types.size() == 1);
25
+ auto enum_size = EnumType::GetSize(types[0]);
26
+ auto &enum_vector = EnumType::GetValuesInsertOrder(types[0]);
24
27
  vector<Value> enum_values;
25
28
  for (idx_t i = 0; i < enum_size; i++) {
26
29
  enum_values.emplace_back(enum_vector.GetValue(i));
@@ -30,13 +33,14 @@ static void EnumRangeFunction(DataChunk &input, ExpressionState &state, Vector &
30
33
  }
31
34
 
32
35
  static void EnumRangeBoundaryFunction(DataChunk &input, ExpressionState &state, Vector &result) {
33
- D_ASSERT(input.GetTypes().size() == 2);
36
+ auto types = input.GetTypes();
37
+ D_ASSERT(types.size() == 2);
34
38
  idx_t start, end;
35
39
  auto first_param = input.GetValue(0, 0);
36
40
  auto second_param = input.GetValue(1, 0);
37
41
 
38
- auto &enum_vector = first_param.IsNull() ? EnumType::GetValuesInsertOrder(input.GetTypes()[1])
39
- : EnumType::GetValuesInsertOrder(input.GetTypes()[0]);
42
+ auto &enum_vector =
43
+ first_param.IsNull() ? EnumType::GetValuesInsertOrder(types[1]) : EnumType::GetValuesInsertOrder(types[0]);
40
44
 
41
45
  if (first_param.IsNull()) {
42
46
  start = 0;
@@ -44,7 +48,7 @@ static void EnumRangeBoundaryFunction(DataChunk &input, ExpressionState &state,
44
48
  start = first_param.GetValue<uint32_t>();
45
49
  }
46
50
  if (second_param.IsNull()) {
47
- end = EnumType::GetSize(input.GetTypes()[0]);
51
+ end = EnumType::GetSize(types[0]);
48
52
  } else {
49
53
  end = second_param.GetValue<uint32_t>() + 1;
50
54
  }
@@ -51,7 +51,9 @@ unique_ptr<FunctionData> CurrentSettingBind(ClientContext &context, ScalarFuncti
51
51
  auto key = StringUtil::Lower(key_str);
52
52
  Value val;
53
53
  if (!context.TryGetCurrentSetting(key, val)) {
54
- throw Catalog::UnrecognizedConfigurationError(context, key);
54
+ Catalog::AutoloadExtensionByConfigName(context, key);
55
+ // If autoloader didn't throw, the config is now available
56
+ context.TryGetCurrentSetting(key, val);
55
57
  }
56
58
 
57
59
  bound_function.return_type = val.type();
@@ -5,7 +5,8 @@ namespace duckdb {
5
5
 
6
6
  SourceResultType PhysicalLoad::GetData(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input) const {
7
7
  if (info->load_type == LoadType::INSTALL || info->load_type == LoadType::FORCE_INSTALL) {
8
- ExtensionHelper::InstallExtension(context.client, info->filename, info->load_type == LoadType::FORCE_INSTALL);
8
+ ExtensionHelper::InstallExtension(context.client, info->filename, info->load_type == LoadType::FORCE_INSTALL,
9
+ info->repository);
9
10
  } else {
10
11
  ExtensionHelper::LoadExternalExtension(context.client, info->filename);
11
12
  }
@@ -30,7 +30,9 @@ SourceResultType PhysicalReset::GetData(ExecutionContext &context, DataChunk &ch
30
30
  // check if this is an extra extension variable
31
31
  auto entry = config.extension_parameters.find(name);
32
32
  if (entry == config.extension_parameters.end()) {
33
- throw Catalog::UnrecognizedConfigurationError(context.client, name);
33
+ Catalog::AutoloadExtensionByConfigName(context.client, name);
34
+ entry = config.extension_parameters.find(name);
35
+ D_ASSERT(entry != config.extension_parameters.end());
34
36
  }
35
37
  ResetExtensionVariable(context, config, entry->second);
36
38
  return SourceResultType::FINISHED;
@@ -33,7 +33,9 @@ SourceResultType PhysicalSet::GetData(ExecutionContext &context, DataChunk &chun
33
33
  // check if this is an extra extension variable
34
34
  auto entry = config.extension_parameters.find(name);
35
35
  if (entry == config.extension_parameters.end()) {
36
- throw Catalog::UnrecognizedConfigurationError(context.client, name);
36
+ Catalog::AutoloadExtensionByConfigName(context.client, name);
37
+ entry = config.extension_parameters.find(name);
38
+ D_ASSERT(entry != config.extension_parameters.end());
37
39
  }
38
40
  SetExtensionVariable(context.client, entry->second, name, scope, value);
39
41
  return SourceResultType::FINISHED;
@@ -123,7 +123,8 @@ string PragmaShow(ClientContext &context, const FunctionParameters &parameters)
123
123
  LEFT JOIN duckdb_columns cols
124
124
  ON cols.column_name = pragma_table_info.name
125
125
  AND cols.table_name='%table_name%'
126
- AND cols.schema_name='%table_schema%';)";
126
+ AND cols.schema_name='%table_schema%'
127
+ ORDER BY column_index;)";
127
128
  // clang-format on
128
129
 
129
130
  sql = StringUtil::Replace(sql, "%func_param_table%", parameters.values[0].ToString());
@@ -325,9 +325,6 @@ public:
325
325
  file_handle->ReadLine();
326
326
  }
327
327
  first_position = current_csv_position;
328
- current_buffer = make_shared<CSVBuffer>(context, buffer_size, *file_handle, current_csv_position, file_number);
329
- next_buffer = shared_ptr<CSVBuffer>(
330
- current_buffer->Next(*file_handle, buffer_size, current_csv_position, file_number).release());
331
328
  running_threads = MaxThreads();
332
329
 
333
330
  // Initialize all the book-keeping variables
@@ -441,6 +438,8 @@ private:
441
438
  vector<column_t> column_ids;
442
439
  //! Line Info used in error messages
443
440
  LineInfo line_info;
441
+ //! Have we initialized our reading
442
+ bool initialized = false;
444
443
  };
445
444
 
446
445
  idx_t ParallelCSVGlobalState::MaxThreads() const {
@@ -543,6 +542,12 @@ void LineInfo::Verify(idx_t file_idx, idx_t batch_idx, idx_t cur_first_pos) {
543
542
  bool ParallelCSVGlobalState::Next(ClientContext &context, const ReadCSVData &bind_data,
544
543
  unique_ptr<ParallelCSVReader> &reader) {
545
544
  lock_guard<mutex> parallel_lock(main_mutex);
545
+ if (!initialized && file_handle) {
546
+ current_buffer = make_shared<CSVBuffer>(context, buffer_size, *file_handle, current_csv_position, file_number);
547
+ next_buffer = shared_ptr<CSVBuffer>(
548
+ current_buffer->Next(*file_handle, buffer_size, current_csv_position, file_number).release());
549
+ initialized = true;
550
+ }
546
551
  if (!current_buffer) {
547
552
  // This means we are done with the current file, we need to go to the next one (if exists).
548
553
  if (file_index < bind_data.files.size()) {
@@ -1,8 +1,8 @@
1
1
  #ifndef DUCKDB_VERSION
2
- #define DUCKDB_VERSION "0.8.2-dev3300"
2
+ #define DUCKDB_VERSION "0.8.2-dev3456"
3
3
  #endif
4
4
  #ifndef DUCKDB_SOURCE_ID
5
- #define DUCKDB_SOURCE_ID "26931f7ac6"
5
+ #define DUCKDB_SOURCE_ID "44fec4a812"
6
6
  #endif
7
7
  #include "duckdb/function/table/system_functions.hpp"
8
8
  #include "duckdb/main/database.hpp"
@@ -301,18 +301,33 @@ public:
301
301
 
302
302
  static CatalogException UnrecognizedConfigurationError(ClientContext &context, const string &name);
303
303
 
304
+ //! Autoload the extension required for `configuration_name` or throw a CatalogException
305
+ static void AutoloadExtensionByConfigName(ClientContext &context, const string &configuration_name);
306
+ //! Autoload the extension required for `function_name` or throw a CatalogException
307
+ static bool AutoLoadExtensionByCatalogEntry(ClientContext &context, CatalogType type, const string &entry_name);
308
+
304
309
  protected:
305
310
  //! Reference to the database
306
311
  AttachedDatabase &db;
307
312
 
308
313
  private:
309
- CatalogEntryLookup LookupEntryInternal(CatalogTransaction transaction, CatalogType type, const string &schema,
310
- const string &name);
314
+ //! Lookup an entry in the schema, returning a lookup with the entry and schema if they exist
315
+ CatalogEntryLookup TryLookupEntryInternal(CatalogTransaction transaction, CatalogType type, const string &schema,
316
+ const string &name);
317
+ //! Calls LookupEntryInternal on the schema, trying other schemas if the schema is invalid. Sets
318
+ //! CatalogEntryLookup->error depending on if_not_found when no entry is found
319
+ CatalogEntryLookup TryLookupEntry(ClientContext &context, CatalogType type, const string &schema,
320
+ const string &name, OnEntryNotFound if_not_found,
321
+ QueryErrorContext error_context = QueryErrorContext());
322
+ //! Lookup an entry using TryLookupEntry, throws if entry not found and if_not_found == THROW_EXCEPTION
311
323
  CatalogEntryLookup LookupEntry(ClientContext &context, CatalogType type, const string &schema, const string &name,
312
324
  OnEntryNotFound if_not_found, QueryErrorContext error_context = QueryErrorContext());
313
- static CatalogEntryLookup LookupEntry(ClientContext &context, vector<CatalogLookup> &lookups, CatalogType type,
314
- const string &name, OnEntryNotFound if_not_found,
315
- QueryErrorContext error_context = QueryErrorContext());
325
+ static CatalogEntryLookup TryLookupEntry(ClientContext &context, vector<CatalogLookup> &lookups, CatalogType type,
326
+ const string &name, OnEntryNotFound if_not_found,
327
+ QueryErrorContext error_context = QueryErrorContext());
328
+ static CatalogEntryLookup TryLookupEntry(ClientContext &context, CatalogType type, const string &catalog,
329
+ const string &schema, const string &name, OnEntryNotFound if_not_found,
330
+ QueryErrorContext error_context);
316
331
 
317
332
  //! Return an exception with did-you-mean suggestion.
318
333
  static CatalogException CreateMissingEntryException(ClientContext &context, const string &entry_name,