duckdb 0.8.2-dev3334.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.
- package/binding.gyp +3 -1
- package/configure.py +3 -0
- package/package.json +1 -1
- package/src/duckdb/src/catalog/catalog.cpp +144 -63
- package/src/duckdb/src/common/exception.cpp +13 -0
- package/src/duckdb/src/common/file_system.cpp +21 -6
- package/src/duckdb/src/core_functions/scalar/generic/current_setting.cpp +3 -1
- package/src/duckdb/src/execution/operator/helper/physical_load.cpp +2 -1
- package/src/duckdb/src/execution/operator/helper/physical_reset.cpp +3 -1
- package/src/duckdb/src/execution/operator/helper/physical_set.cpp +3 -1
- package/src/duckdb/src/function/pragma/pragma_queries.cpp +2 -1
- package/src/duckdb/src/function/table/read_csv.cpp +8 -3
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/catalog/catalog.hpp +20 -5
- package/src/duckdb/src/include/duckdb/common/exception.hpp +15 -1
- package/src/duckdb/src/include/duckdb/main/client_config.hpp +2 -0
- package/src/duckdb/src/include/duckdb/main/config.hpp +12 -0
- package/src/duckdb/src/include/duckdb/main/extension/generated_extension_loader.hpp +6 -1
- package/src/duckdb/src/include/duckdb/main/extension_entries.hpp +209 -151
- package/src/duckdb/src/include/duckdb/main/extension_helper.hpp +34 -3
- package/src/duckdb/src/include/duckdb/main/settings.hpp +30 -0
- package/src/duckdb/src/include/duckdb/parser/parsed_data/load_info.hpp +4 -0
- package/src/duckdb/src/include/duckdb/planner/binder.hpp +4 -0
- package/src/duckdb/src/main/config.cpp +3 -0
- package/src/duckdb/src/main/extension/extension_helper.cpp +57 -0
- package/src/duckdb/src/main/extension/extension_install.cpp +46 -7
- package/src/duckdb/src/main/settings/settings.cpp +46 -0
- package/src/duckdb/src/parser/transform/statement/transform_load.cpp +1 -0
- package/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +69 -23
- package/src/duckdb/src/planner/expression_binder/order_binder.cpp +5 -4
- package/src/duckdb/src/storage/serialization/serialize_parse_info.cpp +2 -0
- package/src/duckdb/third_party/libpg_query/include/nodes/parsenodes.hpp +1 -0
- 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
@@ -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 =
|
475
|
+
auto extension_name = ExtensionHelper::FindExtensionInEntries(name, EXTENSION_SETTINGS);
|
453
476
|
if (!extension_name.empty()) {
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
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
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
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::
|
517
|
-
|
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::
|
530
|
-
|
531
|
-
|
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 =
|
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 =
|
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,
|
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.
|
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
|
-
|
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
|
-
|
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
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
if (
|
615
|
-
|
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
|
-
|
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
|
-
|
409
|
-
ExtensionHelper::
|
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)) {
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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 ¶meters)
|
|
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-
|
2
|
+
#define DUCKDB_VERSION "0.8.2-dev3456"
|
3
3
|
#endif
|
4
4
|
#ifndef DUCKDB_SOURCE_ID
|
5
|
-
#define DUCKDB_SOURCE_ID "
|
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
|
-
|
310
|
-
|
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
|
314
|
-
|
315
|
-
|
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,
|
@@ -80,7 +80,8 @@ enum class ExceptionType {
|
|
80
80
|
PARAMETER_NOT_ALLOWED = 36, // parameter types not allowed
|
81
81
|
DEPENDENCY = 37, // dependency
|
82
82
|
HTTP = 38,
|
83
|
-
MISSING_EXTENSION = 39 // Thrown when an extension is used but not loaded
|
83
|
+
MISSING_EXTENSION = 39, // Thrown when an extension is used but not loaded
|
84
|
+
AUTOLOAD = 40 // Thrown when an extension is used but not loaded
|
84
85
|
};
|
85
86
|
class HTTPException;
|
86
87
|
|
@@ -290,6 +291,19 @@ public:
|
|
290
291
|
}
|
291
292
|
};
|
292
293
|
|
294
|
+
class AutoloadException : public Exception {
|
295
|
+
public:
|
296
|
+
DUCKDB_API explicit AutoloadException(const string &extension_name, Exception &e);
|
297
|
+
|
298
|
+
template <typename... Args>
|
299
|
+
explicit AutoloadException(const string &extension_name, Exception &e, Args... params)
|
300
|
+
: AutoloadException(ConstructMessage(extension_name, e, params...)) {
|
301
|
+
}
|
302
|
+
|
303
|
+
protected:
|
304
|
+
Exception &wrapped_exception;
|
305
|
+
};
|
306
|
+
|
293
307
|
class HTTPException : public IOException {
|
294
308
|
public:
|
295
309
|
template <typename>
|
@@ -91,6 +91,8 @@ struct ClientConfig {
|
|
91
91
|
|
92
92
|
//! Override for the default extension repository
|
93
93
|
string custom_extension_repo = "";
|
94
|
+
//! Override for the default autoload extensoin repository
|
95
|
+
string autoinstall_extension_repo = "";
|
94
96
|
|
95
97
|
//! The explain output type used when none is specified (default: PHYSICAL_ONLY)
|
96
98
|
ExplainOutputType explain_output_type = ExplainOutputType::PHYSICAL_ONLY;
|
@@ -95,6 +95,18 @@ struct DBConfigOptions {
|
|
95
95
|
bool use_direct_io = false;
|
96
96
|
//! Whether extensions should be loaded on start-up
|
97
97
|
bool load_extensions = true;
|
98
|
+
#ifdef DUCKDB_EXTENSION_AUTOLOAD_DEFAULT
|
99
|
+
//! Whether known extensions are allowed to be automatically loaded when a query depends on them
|
100
|
+
bool autoload_known_extensions = DUCKDB_EXTENSION_AUTOLOAD_DEFAULT;
|
101
|
+
#else
|
102
|
+
bool autoload_known_extensions = false;
|
103
|
+
#endif
|
104
|
+
#ifdef DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT
|
105
|
+
//! Whether known extensions are allowed to be automatically installed when a query depends on them
|
106
|
+
bool autoinstall_known_extensions = DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT;
|
107
|
+
#else
|
108
|
+
bool autoinstall_known_extensions = false;
|
109
|
+
#endif
|
98
110
|
//! The maximum memory used by the database system (in bytes). Default: 80% of System available memory
|
99
111
|
idx_t maximum_memory = (idx_t)-1;
|
100
112
|
//! The maximum amount of CPU threads used by the database system. Default: all available.
|