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.
- package/README.md +1 -1
- package/package.json +3 -3
- package/src/duckdb/extension/json/json_scan.cpp +1 -4
- package/src/duckdb/extension/parquet/column_reader.cpp +7 -0
- package/src/duckdb/extension/parquet/include/column_reader.hpp +1 -0
- package/src/duckdb/extension/parquet/parquet-extension.cpp +2 -10
- package/src/duckdb/src/catalog/catalog.cpp +62 -13
- package/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp +8 -7
- package/src/duckdb/src/catalog/default/default_views.cpp +1 -1
- package/src/duckdb/src/common/file_system.cpp +23 -9
- package/src/duckdb/src/common/local_file_system.cpp +4 -4
- package/src/duckdb/src/execution/index/art/art.cpp +117 -67
- package/src/duckdb/src/execution/index/art/art_key.cpp +24 -12
- package/src/duckdb/src/execution/index/art/leaf.cpp +7 -8
- package/src/duckdb/src/execution/index/art/node.cpp +13 -27
- package/src/duckdb/src/execution/index/art/node16.cpp +5 -8
- package/src/duckdb/src/execution/index/art/node256.cpp +3 -5
- package/src/duckdb/src/execution/index/art/node4.cpp +4 -7
- package/src/duckdb/src/execution/index/art/node48.cpp +5 -8
- package/src/duckdb/src/execution/index/art/prefix.cpp +2 -3
- package/src/duckdb/src/execution/operator/helper/physical_reset.cpp +1 -9
- package/src/duckdb/src/execution/operator/helper/physical_set.cpp +1 -9
- package/src/duckdb/src/function/pragma/pragma_queries.cpp +2 -2
- package/src/duckdb/src/function/scalar/generic/current_setting.cpp +2 -2
- package/src/duckdb/src/function/table/read_csv.cpp +3 -5
- package/src/duckdb/src/function/table/table_scan.cpp +3 -0
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/catalog/catalog.hpp +7 -1
- package/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp +1 -1
- package/src/duckdb/src/include/duckdb/catalog/catalog_entry/index_catalog_entry.hpp +1 -1
- package/src/duckdb/src/include/duckdb/common/enums/wal_type.hpp +3 -0
- package/src/duckdb/src/include/duckdb/common/file_system.hpp +1 -1
- package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +37 -41
- package/src/duckdb/src/include/duckdb/execution/index/art/art_key.hpp +8 -11
- package/src/duckdb/src/include/duckdb/main/{extension_functions.hpp → extension_entries.hpp} +26 -5
- package/src/duckdb/src/include/duckdb/main/extension_helper.hpp +3 -0
- package/src/duckdb/src/include/duckdb/planner/binder.hpp +3 -0
- package/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp +10 -3
- package/src/duckdb/src/include/duckdb/storage/data_table.hpp +7 -1
- package/src/duckdb/src/include/duckdb/storage/index.hpp +47 -38
- package/src/duckdb/src/include/duckdb/storage/write_ahead_log.hpp +7 -0
- package/src/duckdb/src/main/database.cpp +4 -2
- package/src/duckdb/src/main/extension/extension_load.cpp +22 -3
- package/src/duckdb/src/parser/parsed_data/create_index_info.cpp +3 -0
- package/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +13 -0
- package/src/duckdb/src/planner/expression_binder/index_binder.cpp +32 -1
- package/src/duckdb/src/storage/buffer_manager.cpp +30 -3
- package/src/duckdb/src/storage/compression/bitpacking.cpp +16 -7
- package/src/duckdb/src/storage/data_table.cpp +66 -3
- package/src/duckdb/src/storage/index.cpp +1 -1
- package/src/duckdb/src/storage/local_storage.cpp +1 -1
- package/src/duckdb/src/storage/table_index_list.cpp +1 -2
- package/src/duckdb/src/storage/wal_replay.cpp +68 -0
- package/src/duckdb/src/storage/write_ahead_log.cpp +21 -1
- package/src/duckdb/src/transaction/commit_state.cpp +5 -2
- package/src/duckdb/third_party/concurrentqueue/blockingconcurrentqueue.h +2 -2
- package/src/statement.cpp +46 -12
- package/test/arrow.test.ts +3 -3
- package/test/prepare.test.ts +39 -1
- 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/
|
|
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-
|
|
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/
|
|
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/
|
|
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.
|
|
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++;
|
|
@@ -221,10 +221,7 @@ public:
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
FileSystem &fs = FileSystem::GetFileSystem(context);
|
|
224
|
-
auto files = fs.
|
|
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
|
-
|
|
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/
|
|
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
|
|
321
|
-
auto
|
|
322
|
-
auto it = std::lower_bound(
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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(
|
|
23
|
-
//
|
|
24
|
-
// schema name, table name, index name, sql, index type, index constraint type, expression list
|
|
25
|
-
//
|
|
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
|
-
//
|
|
41
|
-
//
|
|
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
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
|
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
|
|
837
|
-
|
|
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
|
-
|
|
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
|
-
|
|
959
|
+
GlobFilesInternal(*this, prev_directory, splits[i], !is_last_chunk, result, true);
|
|
960
960
|
}
|
|
961
961
|
}
|
|
962
962
|
}
|