duckdb 0.7.2-dev1684.0 → 0.7.2-dev1734.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 (24) hide show
  1. package/package.json +1 -1
  2. package/src/duckdb/extension/icu/icu-datefunc.cpp +20 -8
  3. package/src/duckdb/extension/icu/icu-strptime.cpp +117 -29
  4. package/src/duckdb/extension/icu/include/icu-datefunc.hpp +2 -0
  5. package/src/duckdb/src/common/local_file_system.cpp +13 -2
  6. package/src/duckdb/src/common/sort/partition_state.cpp +644 -0
  7. package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +77 -849
  8. package/src/duckdb/src/function/table/system/duckdb_extensions.cpp +2 -2
  9. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  10. package/src/duckdb/src/include/duckdb/common/sort/partition_state.hpp +247 -0
  11. package/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp +1 -2
  12. package/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp +77 -0
  13. package/src/duckdb/src/include/duckdb/storage/buffer/temporary_file_information.hpp +12 -0
  14. package/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +3 -59
  15. package/src/duckdb/src/main/extension/extension_install.cpp +11 -0
  16. package/src/duckdb/src/main/extension/extension_load.cpp +29 -3
  17. package/src/duckdb/src/storage/buffer/block_handle.cpp +128 -0
  18. package/src/duckdb/src/storage/buffer/block_manager.cpp +81 -0
  19. package/src/duckdb/src/storage/buffer/buffer_pool.cpp +132 -0
  20. package/src/duckdb/src/storage/buffer/buffer_pool_reservation.cpp +32 -0
  21. package/src/duckdb/src/storage/buffer_manager.cpp +0 -351
  22. package/src/duckdb/third_party/libpg_query/postgres_parser.cpp +3 -5
  23. package/src/duckdb/ub_src_common_sort.cpp +2 -0
  24. package/src/duckdb/ub_src_storage_buffer.cpp +8 -0
@@ -75,7 +75,7 @@ unique_ptr<GlobalTableFunctionState> DuckDBExtensionsInit(ClientContext &context
75
75
  }
76
76
  installed_extensions[info.name] = std::move(info);
77
77
  }
78
-
78
+ #ifndef WASM_LOADABLE_EXTENSIONS
79
79
  // scan the install directory for installed extensions
80
80
  auto ext_directory = ExtensionHelper::ExtensionDirectory(context);
81
81
  fs.ListFiles(ext_directory, [&](const string &path, bool is_directory) {
@@ -96,7 +96,7 @@ unique_ptr<GlobalTableFunctionState> DuckDBExtensionsInit(ClientContext &context
96
96
  entry->second.installed = true;
97
97
  }
98
98
  });
99
-
99
+ #endif
100
100
  // now check the list of currently loaded extensions
101
101
  auto &loaded_extensions = db.LoadedExtensions();
102
102
  for (auto &ext_name : loaded_extensions) {
@@ -1,8 +1,8 @@
1
1
  #ifndef DUCKDB_VERSION
2
- #define DUCKDB_VERSION "0.7.2-dev1684"
2
+ #define DUCKDB_VERSION "0.7.2-dev1734"
3
3
  #endif
4
4
  #ifndef DUCKDB_SOURCE_ID
5
- #define DUCKDB_SOURCE_ID "754ff32005"
5
+ #define DUCKDB_SOURCE_ID "e8610c85fb"
6
6
  #endif
7
7
  #include "duckdb/function/table/system_functions.hpp"
8
8
  #include "duckdb/main/database.hpp"
@@ -0,0 +1,247 @@
1
+ //===----------------------------------------------------------------------===//
2
+ // DuckDB
3
+ //
4
+ // duckdb/common/sort/partition_state.hpp
5
+ //
6
+ //
7
+ //===----------------------------------------------------------------------===//
8
+
9
+ #pragma once
10
+
11
+ #include "duckdb/common/sort/sort.hpp"
12
+ #include "duckdb/common/types/partitioned_column_data.hpp"
13
+ #include "duckdb/common/radix_partitioning.hpp"
14
+ #include "duckdb/parallel/base_pipeline_event.hpp"
15
+
16
+ namespace duckdb {
17
+
18
+ class PartitionGlobalHashGroup {
19
+ public:
20
+ using GlobalSortStatePtr = unique_ptr<GlobalSortState>;
21
+ using LocalSortStatePtr = unique_ptr<LocalSortState>;
22
+ using Orders = vector<BoundOrderByNode>;
23
+ using Types = vector<LogicalType>;
24
+
25
+ PartitionGlobalHashGroup(BufferManager &buffer_manager, const Orders &partitions, const Orders &orders,
26
+ const Types &payload_types, bool external);
27
+
28
+ void ComputeMasks(ValidityMask &partition_mask, ValidityMask &order_mask);
29
+
30
+ GlobalSortStatePtr global_sort;
31
+ atomic<idx_t> count;
32
+
33
+ // Mask computation
34
+ SortLayout partition_layout;
35
+ };
36
+
37
+ class PartitionGlobalSinkState {
38
+ public:
39
+ using HashGroupPtr = unique_ptr<PartitionGlobalHashGroup>;
40
+ using Orders = vector<BoundOrderByNode>;
41
+ using Types = vector<LogicalType>;
42
+
43
+ using GroupingPartition = unique_ptr<PartitionedColumnData>;
44
+ using GroupingAppend = unique_ptr<PartitionedColumnDataAppendState>;
45
+
46
+ PartitionGlobalSinkState(ClientContext &context, const vector<unique_ptr<Expression>> &partitions_p,
47
+ const vector<BoundOrderByNode> &orders_p, const Types &payload_types,
48
+ const vector<unique_ptr<BaseStatistics>> &partitions_stats, idx_t estimated_cardinality);
49
+
50
+ void UpdateLocalPartition(GroupingPartition &local_partition, GroupingAppend &local_append);
51
+ void CombineLocalPartition(GroupingPartition &local_partition, GroupingAppend &local_append);
52
+
53
+ void BuildSortState(ColumnDataCollection &group_data, PartitionGlobalHashGroup &global_sort);
54
+
55
+ ClientContext &context;
56
+ BufferManager &buffer_manager;
57
+ Allocator &allocator;
58
+ mutex lock;
59
+
60
+ // OVER(PARTITION BY...) (hash grouping)
61
+ unique_ptr<RadixPartitionedColumnData> grouping_data;
62
+ //! Payload plus hash column
63
+ Types grouping_types;
64
+
65
+ // OVER(...) (sorting)
66
+ Orders partitions;
67
+ Orders orders;
68
+ const Types payload_types;
69
+ vector<HashGroupPtr> hash_groups;
70
+ bool external;
71
+
72
+ // OVER() (no sorting)
73
+ unique_ptr<RowDataCollection> rows;
74
+ unique_ptr<RowDataCollection> strings;
75
+
76
+ // Threading
77
+ idx_t memory_per_thread;
78
+ atomic<idx_t> count;
79
+
80
+ private:
81
+ void ResizeGroupingData(idx_t cardinality);
82
+ void SyncLocalPartition(GroupingPartition &local_partition, GroupingAppend &local_append);
83
+ };
84
+
85
+ class PartitionLocalSinkState {
86
+ public:
87
+ PartitionLocalSinkState(ClientContext &context, PartitionGlobalSinkState &gstate_p);
88
+
89
+ // Global state
90
+ PartitionGlobalSinkState &gstate;
91
+ Allocator &allocator;
92
+
93
+ // OVER(PARTITION BY...) (hash grouping)
94
+ ExpressionExecutor executor;
95
+ DataChunk group_chunk;
96
+ DataChunk payload_chunk;
97
+ unique_ptr<PartitionedColumnData> local_partition;
98
+ unique_ptr<PartitionedColumnDataAppendState> local_append;
99
+
100
+ // OVER(...) (sorting)
101
+ size_t sort_cols;
102
+
103
+ // OVER() (no sorting)
104
+ RowLayout payload_layout;
105
+ unique_ptr<RowDataCollection> rows;
106
+ unique_ptr<RowDataCollection> strings;
107
+
108
+ //! Compute the hash values
109
+ void Hash(DataChunk &input_chunk, Vector &hash_vector);
110
+ //! Sink an input chunk
111
+ void Sink(DataChunk &input_chunk);
112
+ //! Merge the state into the global state.
113
+ void Combine();
114
+ };
115
+
116
+ enum class PartitionSortStage : uint8_t { INIT, PREPARE, MERGE, SORTED };
117
+
118
+ class PartitionLocalMergeState;
119
+
120
+ class PartitionGlobalMergeState {
121
+ public:
122
+ using GroupDataPtr = unique_ptr<ColumnDataCollection>;
123
+
124
+ explicit PartitionGlobalMergeState(PartitionGlobalSinkState &sink, GroupDataPtr group_data);
125
+
126
+ bool IsSorted() const {
127
+ lock_guard<mutex> guard(lock);
128
+ return stage == PartitionSortStage::SORTED;
129
+ }
130
+
131
+ bool AssignTask(PartitionLocalMergeState &local_state);
132
+ bool TryPrepareNextStage();
133
+ void CompleteTask();
134
+
135
+ PartitionGlobalSinkState &sink;
136
+ GroupDataPtr group_data;
137
+ PartitionGlobalHashGroup *hash_group;
138
+ GlobalSortState *global_sort;
139
+
140
+ private:
141
+ mutable mutex lock;
142
+ PartitionSortStage stage;
143
+ idx_t total_tasks;
144
+ idx_t tasks_assigned;
145
+ idx_t tasks_completed;
146
+ };
147
+
148
+ class PartitionLocalMergeState {
149
+ public:
150
+ PartitionLocalMergeState() : merge_state(nullptr), stage(PartitionSortStage::INIT) {
151
+ finished = true;
152
+ }
153
+
154
+ bool TaskFinished() {
155
+ return finished;
156
+ }
157
+
158
+ void Prepare();
159
+ void Merge();
160
+
161
+ void ExecuteTask();
162
+
163
+ PartitionGlobalMergeState *merge_state;
164
+ PartitionSortStage stage;
165
+ atomic<bool> finished;
166
+ };
167
+
168
+ class PartitionGlobalMergeStates {
169
+ public:
170
+ using PartitionGlobalMergeStatePtr = unique_ptr<PartitionGlobalMergeState>;
171
+
172
+ explicit PartitionGlobalMergeStates(PartitionGlobalSinkState &sink);
173
+
174
+ vector<PartitionGlobalMergeStatePtr> states;
175
+ };
176
+
177
+ class PartitionMergeEvent : public BasePipelineEvent {
178
+ public:
179
+ PartitionMergeEvent(PartitionGlobalSinkState &gstate_p, Pipeline &pipeline_p)
180
+ : BasePipelineEvent(pipeline_p), gstate(gstate_p), merge_states(gstate_p) {
181
+ }
182
+
183
+ PartitionGlobalSinkState &gstate;
184
+ PartitionGlobalMergeStates merge_states;
185
+
186
+ public:
187
+ void Schedule() override;
188
+ };
189
+
190
+ class PartitionGlobalSourceState {
191
+ public:
192
+ explicit PartitionGlobalSourceState(PartitionGlobalSinkState &gsink_p) : gsink(gsink_p), next_bin(0) {
193
+ }
194
+
195
+ PartitionGlobalSinkState &gsink;
196
+ //! The output read position.
197
+ atomic<idx_t> next_bin;
198
+
199
+ public:
200
+ idx_t MaxThreads() {
201
+ // If there is only one partition, we have to process it on one thread.
202
+ if (!gsink.grouping_data) {
203
+ return 1;
204
+ }
205
+
206
+ // If there is not a lot of data, process serially.
207
+ if (gsink.count < STANDARD_ROW_GROUPS_SIZE) {
208
+ return 1;
209
+ }
210
+
211
+ return gsink.hash_groups.size();
212
+ }
213
+ };
214
+
215
+ // Per-thread read state
216
+ class PartitionLocalSourceState {
217
+ public:
218
+ using HashGroupPtr = unique_ptr<PartitionGlobalHashGroup>;
219
+
220
+ explicit PartitionLocalSourceState(PartitionGlobalSinkState &gstate_p);
221
+
222
+ void MaterializeSortedData();
223
+ idx_t GeneratePartition(const idx_t hash_bin);
224
+
225
+ PartitionGlobalSinkState &gstate;
226
+
227
+ //! The read partition
228
+ idx_t hash_bin;
229
+ HashGroupPtr hash_group;
230
+
231
+ //! The generated input chunks
232
+ unique_ptr<RowDataCollection> rows;
233
+ unique_ptr<RowDataCollection> heap;
234
+ RowLayout layout;
235
+ //! The partition boundary mask
236
+ vector<validity_t> partition_bits;
237
+ ValidityMask partition_mask;
238
+ //! The order boundary mask
239
+ vector<validity_t> order_bits;
240
+ ValidityMask order_mask;
241
+ //! The read cursor
242
+ unique_ptr<RowDataCollectionScanner> scanner;
243
+ //! Buffer for the inputs
244
+ DataChunk input_chunk;
245
+ };
246
+
247
+ } // namespace duckdb
@@ -12,14 +12,13 @@
12
12
  #include "duckdb/common/common.hpp"
13
13
  #include "duckdb/common/mutex.hpp"
14
14
  #include "duckdb/storage/storage_info.hpp"
15
+ #include "duckdb/common/file_buffer.hpp"
15
16
 
16
17
  namespace duckdb {
17
18
  class BlockManager;
18
19
  class BufferHandle;
19
- class BufferManager;
20
20
  class BufferPool;
21
21
  class DatabaseInstance;
22
- class FileBuffer;
23
22
 
24
23
  enum class BlockState : uint8_t { BLOCK_UNLOADED = 0, BLOCK_LOADED = 1 };
25
24
 
@@ -0,0 +1,77 @@
1
+ #pragma once
2
+
3
+ #include "duckdb/common/mutex.hpp"
4
+ #include "duckdb/common/file_buffer.hpp"
5
+ #include "duckdb/storage/buffer/block_handle.hpp"
6
+
7
+ namespace duckdb {
8
+
9
+ struct EvictionQueue;
10
+
11
+ struct BufferEvictionNode {
12
+ BufferEvictionNode() {
13
+ }
14
+ BufferEvictionNode(weak_ptr<BlockHandle> handle_p, idx_t timestamp_p)
15
+ : handle(std::move(handle_p)), timestamp(timestamp_p) {
16
+ D_ASSERT(!handle.expired());
17
+ }
18
+
19
+ weak_ptr<BlockHandle> handle;
20
+ idx_t timestamp;
21
+
22
+ bool CanUnload(BlockHandle &handle_p);
23
+
24
+ shared_ptr<BlockHandle> TryGetBlockHandle();
25
+ };
26
+
27
+ //! The BufferPool is in charge of handling memory management for one or more databases. It defines memory limits
28
+ //! and implements priority eviction among all users of the pool.
29
+ class BufferPool {
30
+ friend class BlockHandle;
31
+ friend class BlockManager;
32
+ friend class BufferManager;
33
+
34
+ public:
35
+ explicit BufferPool(idx_t maximum_memory);
36
+ virtual ~BufferPool();
37
+
38
+ //! Set a new memory limit to the buffer pool, throws an exception if the new limit is too low and not enough
39
+ //! blocks can be evicted
40
+ void SetLimit(idx_t limit, const char *exception_postscript);
41
+
42
+ idx_t GetUsedMemory();
43
+
44
+ idx_t GetMaxMemory();
45
+
46
+ protected:
47
+ //! Evict blocks until the currently used memory + extra_memory fit, returns false if this was not possible
48
+ //! (i.e. not enough blocks could be evicted)
49
+ //! If the "buffer" argument is specified AND the system can find a buffer to re-use for the given allocation size
50
+ //! "buffer" will be made to point to the re-usable memory. Note that this is not guaranteed.
51
+ //! Returns a pair. result.first indicates if eviction was successful. result.second contains the
52
+ //! reservation handle, which can be moved to the BlockHandle that will own the reservation.
53
+ struct EvictionResult {
54
+ bool success;
55
+ TempBufferPoolReservation reservation;
56
+ };
57
+ virtual EvictionResult EvictBlocks(idx_t extra_memory, idx_t memory_limit,
58
+ unique_ptr<FileBuffer> *buffer = nullptr);
59
+
60
+ //! Garbage collect eviction queue
61
+ void PurgeQueue();
62
+ void AddToEvictionQueue(shared_ptr<BlockHandle> &handle);
63
+
64
+ private:
65
+ //! The lock for changing the memory limit
66
+ mutex limit_lock;
67
+ //! The current amount of memory that is occupied by the buffer manager (in bytes)
68
+ atomic<idx_t> current_memory;
69
+ //! The maximum amount of memory that the buffer manager can keep (in bytes)
70
+ atomic<idx_t> maximum_memory;
71
+ //! Eviction queue
72
+ unique_ptr<EvictionQueue> queue;
73
+ //! Total number of insertions into the eviction queue. This guides the schedule for calling PurgeQueue.
74
+ atomic<uint32_t> queue_insertions;
75
+ };
76
+
77
+ } // namespace duckdb
@@ -0,0 +1,12 @@
1
+ #pragma once
2
+
3
+ #include "duckdb/common/common.hpp"
4
+
5
+ namespace duckdb {
6
+
7
+ struct TemporaryFileInformation {
8
+ string path;
9
+ idx_t size;
10
+ };
11
+
12
+ } // namespace duckdb
@@ -16,6 +16,8 @@
16
16
  #include "duckdb/storage/block_manager.hpp"
17
17
  #include "duckdb/storage/buffer/block_handle.hpp"
18
18
  #include "duckdb/storage/buffer/buffer_handle.hpp"
19
+ #include "duckdb/storage/buffer/buffer_pool.hpp"
20
+ #include "duckdb/storage/buffer/temporary_file_information.hpp"
19
21
 
20
22
  namespace duckdb {
21
23
  class BlockManager;
@@ -23,65 +25,7 @@ class DatabaseInstance;
23
25
  class TemporaryDirectoryHandle;
24
26
  struct EvictionQueue;
25
27
 
26
- //! The BufferPool is in charge of handling memory management for one or more databases. It defines memory limits
27
- //! and implements priority eviction among all users of the pool.
28
- class BufferPool {
29
- friend class BlockHandle;
30
- friend class BlockManager;
31
- friend class BufferManager;
32
-
33
- public:
34
- explicit BufferPool(idx_t maximum_memory);
35
- virtual ~BufferPool();
36
-
37
- //! Set a new memory limit to the buffer pool, throws an exception if the new limit is too low and not enough
38
- //! blocks can be evicted
39
- void SetLimit(idx_t limit, const char *exception_postscript);
40
-
41
- idx_t GetUsedMemory() {
42
- return current_memory;
43
- }
44
- idx_t GetMaxMemory() {
45
- return maximum_memory;
46
- }
47
-
48
- protected:
49
- //! Evict blocks until the currently used memory + extra_memory fit, returns false if this was not possible
50
- //! (i.e. not enough blocks could be evicted)
51
- //! If the "buffer" argument is specified AND the system can find a buffer to re-use for the given allocation size
52
- //! "buffer" will be made to point to the re-usable memory. Note that this is not guaranteed.
53
- //! Returns a pair. result.first indicates if eviction was successful. result.second contains the
54
- //! reservation handle, which can be moved to the BlockHandle that will own the reservation.
55
- struct EvictionResult {
56
- bool success;
57
- TempBufferPoolReservation reservation;
58
- };
59
- virtual EvictionResult EvictBlocks(idx_t extra_memory, idx_t memory_limit,
60
- unique_ptr<FileBuffer> *buffer = nullptr);
61
-
62
- //! Garbage collect eviction queue
63
- void PurgeQueue();
64
- void AddToEvictionQueue(shared_ptr<BlockHandle> &handle);
65
-
66
- private:
67
- //! The lock for changing the memory limit
68
- mutex limit_lock;
69
- //! The current amount of memory that is occupied by the buffer manager (in bytes)
70
- atomic<idx_t> current_memory;
71
- //! The maximum amount of memory that the buffer manager can keep (in bytes)
72
- atomic<idx_t> maximum_memory;
73
- //! Eviction queue
74
- unique_ptr<EvictionQueue> queue;
75
- //! Total number of insertions into the eviction queue. This guides the schedule for calling PurgeQueue.
76
- atomic<uint32_t> queue_insertions;
77
- };
78
-
79
- struct TemporaryFileInformation {
80
- string path;
81
- idx_t size;
82
- };
83
-
84
- //! The BufferManager is in charge of handling memory management for a singke database. It cooperatively shares a
28
+ //! The BufferManager is in charge of handling memory management for a single database. It cooperatively shares a
85
29
  //! BufferPool with other BufferManagers, belonging to different databases. It hands out memory buffers that can
86
30
  //! be used by the database internally, and offers configuration options specific to a database, which need not be
87
31
  //! shared by the BufferPool, including whether to support swapping temp buffers to disk, and where to swap them to.
@@ -39,6 +39,9 @@ const vector<string> ExtensionHelper::PathComponents() {
39
39
  }
40
40
 
41
41
  string ExtensionHelper::ExtensionDirectory(DBConfig &config, FileSystem &fs, FileOpener *opener) {
42
+ #ifdef WASM_LOADABLE_EXTENSIONS
43
+ static_assertion(0, "ExtensionDirectory functionality is not supported in duckdb-wasm");
44
+ #endif
42
45
  string extension_directory;
43
46
  if (!config.options.extension_directory.empty()) { // create the extension directory if not present
44
47
  extension_directory = config.options.extension_directory;
@@ -111,11 +114,19 @@ bool ExtensionHelper::CreateSuggestions(const string &extension_name, string &me
111
114
  }
112
115
 
113
116
  void ExtensionHelper::InstallExtension(DBConfig &config, FileSystem &fs, const string &extension, bool force_install) {
117
+ #ifdef WASM_LOADABLE_EXTENSIONS
118
+ // Install is currently a no-op
119
+ return;
120
+ #endif
114
121
  string local_path = ExtensionDirectory(config, fs, nullptr);
115
122
  InstallExtensionInternal(config, nullptr, fs, local_path, extension, force_install);
116
123
  }
117
124
 
118
125
  void ExtensionHelper::InstallExtension(ClientContext &context, const string &extension, bool force_install) {
126
+ #ifdef WASM_LOADABLE_EXTENSIONS
127
+ // Install is currently a no-op
128
+ return;
129
+ #endif
119
130
  auto &config = DBConfig::GetConfig(context);
120
131
  auto &fs = FileSystem::GetFileSystem(context);
121
132
  string local_path = ExtensionDirectory(context);
@@ -4,6 +4,10 @@
4
4
  #include "duckdb/main/error_manager.hpp"
5
5
  #include "mbedtls_wrapper.hpp"
6
6
 
7
+ #ifdef WASM_LOADABLE_EXTENSIONS
8
+ #include <emscripten.h>
9
+ #endif
10
+
7
11
  namespace duckdb {
8
12
 
9
13
  //===--------------------------------------------------------------------===//
@@ -86,13 +90,35 @@ bool ExtensionHelper::TryInitialLoad(DBConfig &config, FileOpener *opener, const
86
90
  throw IOException(config.error_manager->FormatException(ErrorType::UNSIGNED_EXTENSION, filename));
87
91
  }
88
92
  }
89
- auto lib_hdl = dlopen(filename.c_str(), RTLD_NOW | RTLD_LOCAL);
93
+ auto basename = fs.ExtractBaseName(filename);
94
+
95
+ #ifdef WASM_LOADABLE_EXTENSIONS
96
+ EM_ASM(
97
+ {
98
+ // Next few lines should argubly in separate JavaScript-land function call
99
+ // TODO: move them out / have them configurable
100
+ const xhr = new XMLHttpRequest();
101
+ xhr.open("GET", UTF8ToString($0), false);
102
+ xhr.responseType = "arraybuffer";
103
+ xhr.send(null);
104
+ var uInt8Array = xhr.response;
105
+ WebAssembly.validate(uInt8Array);
106
+ console.log('Loading extension ', UTF8ToString($1));
107
+
108
+ // Here we add the uInt8Array to Emscripten's filesystem, for it to be found by dlopen
109
+ FS.writeFile(UTF8ToString($1), new Uint8Array(uInt8Array));
110
+ },
111
+ filename.c_str(), basename.c_str());
112
+ auto dopen_from = basename;
113
+ #else
114
+ auto dopen_from = filename;
115
+ #endif
116
+
117
+ auto lib_hdl = dlopen(dopen_from.c_str(), RTLD_NOW | RTLD_LOCAL);
90
118
  if (!lib_hdl) {
91
119
  throw IOException("Extension \"%s\" could not be loaded: %s", filename, GetDLError());
92
120
  }
93
121
 
94
- auto basename = fs.ExtractBaseName(filename);
95
-
96
122
  ext_version_fun_t version_fun;
97
123
  auto version_fun_name = basename + "_version";
98
124
 
@@ -0,0 +1,128 @@
1
+ #include "duckdb/storage/buffer/block_handle.hpp"
2
+ #include "duckdb/storage/block.hpp"
3
+ #include "duckdb/storage/block_manager.hpp"
4
+ #include "duckdb/storage/buffer/buffer_handle.hpp"
5
+ #include "duckdb/storage/buffer_manager.hpp"
6
+ #include "duckdb/common/file_buffer.hpp"
7
+
8
+ namespace duckdb {
9
+
10
+ BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p)
11
+ : block_manager(block_manager), readers(0), block_id(block_id_p), buffer(nullptr), eviction_timestamp(0),
12
+ can_destroy(false), unswizzled(nullptr) {
13
+ eviction_timestamp = 0;
14
+ state = BlockState::BLOCK_UNLOADED;
15
+ memory_usage = Storage::BLOCK_ALLOC_SIZE;
16
+ }
17
+
18
+ BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, unique_ptr<FileBuffer> buffer_p,
19
+ bool can_destroy_p, idx_t block_size, BufferPoolReservation &&reservation)
20
+ : block_manager(block_manager), readers(0), block_id(block_id_p), eviction_timestamp(0), can_destroy(can_destroy_p),
21
+ unswizzled(nullptr) {
22
+ buffer = std::move(buffer_p);
23
+ state = BlockState::BLOCK_LOADED;
24
+ memory_usage = block_size;
25
+ memory_charge = std::move(reservation);
26
+ }
27
+
28
+ BlockHandle::~BlockHandle() { // NOLINT: allow internal exceptions
29
+ // being destroyed, so any unswizzled pointers are just binary junk now.
30
+ unswizzled = nullptr;
31
+ auto &buffer_manager = block_manager.buffer_manager;
32
+ // no references remain to this block: erase
33
+ if (buffer && state == BlockState::BLOCK_LOADED) {
34
+ D_ASSERT(memory_charge.size > 0);
35
+ // the block is still loaded in memory: erase it
36
+ buffer.reset();
37
+ memory_charge.Resize(buffer_manager.buffer_pool.current_memory, 0);
38
+ } else {
39
+ D_ASSERT(memory_charge.size == 0);
40
+ }
41
+ buffer_manager.buffer_pool.PurgeQueue();
42
+ block_manager.UnregisterBlock(block_id, can_destroy);
43
+ }
44
+
45
+ unique_ptr<Block> AllocateBlock(BlockManager &block_manager, unique_ptr<FileBuffer> reusable_buffer,
46
+ block_id_t block_id) {
47
+ if (reusable_buffer) {
48
+ // re-usable buffer: re-use it
49
+ if (reusable_buffer->type == FileBufferType::BLOCK) {
50
+ // we can reuse the buffer entirely
51
+ auto &block = (Block &)*reusable_buffer;
52
+ block.id = block_id;
53
+ return unique_ptr_cast<FileBuffer, Block>(std::move(reusable_buffer));
54
+ }
55
+ auto block = block_manager.CreateBlock(block_id, reusable_buffer.get());
56
+ reusable_buffer.reset();
57
+ return block;
58
+ } else {
59
+ // no re-usable buffer: allocate a new block
60
+ return block_manager.CreateBlock(block_id, nullptr);
61
+ }
62
+ }
63
+
64
+ BufferHandle BlockHandle::Load(shared_ptr<BlockHandle> &handle, unique_ptr<FileBuffer> reusable_buffer) {
65
+ if (handle->state == BlockState::BLOCK_LOADED) {
66
+ // already loaded
67
+ D_ASSERT(handle->buffer);
68
+ return BufferHandle(handle, handle->buffer.get());
69
+ }
70
+
71
+ auto &block_manager = handle->block_manager;
72
+ if (handle->block_id < MAXIMUM_BLOCK) {
73
+ auto block = AllocateBlock(block_manager, std::move(reusable_buffer), handle->block_id);
74
+ block_manager.Read(*block);
75
+ handle->buffer = std::move(block);
76
+ } else {
77
+ if (handle->can_destroy) {
78
+ return BufferHandle();
79
+ } else {
80
+ handle->buffer =
81
+ block_manager.buffer_manager.ReadTemporaryBuffer(handle->block_id, std::move(reusable_buffer));
82
+ }
83
+ }
84
+ handle->state = BlockState::BLOCK_LOADED;
85
+ return BufferHandle(handle, handle->buffer.get());
86
+ }
87
+
88
+ unique_ptr<FileBuffer> BlockHandle::UnloadAndTakeBlock() {
89
+ if (state == BlockState::BLOCK_UNLOADED) {
90
+ // already unloaded: nothing to do
91
+ return nullptr;
92
+ }
93
+ D_ASSERT(!unswizzled);
94
+ D_ASSERT(CanUnload());
95
+
96
+ if (block_id >= MAXIMUM_BLOCK && !can_destroy) {
97
+ // temporary block that cannot be destroyed: write to temporary file
98
+ block_manager.buffer_manager.WriteTemporaryBuffer(block_id, *buffer);
99
+ }
100
+ memory_charge.Resize(block_manager.buffer_manager.buffer_pool.current_memory, 0);
101
+ state = BlockState::BLOCK_UNLOADED;
102
+ return std::move(buffer);
103
+ }
104
+
105
+ void BlockHandle::Unload() {
106
+ auto block = UnloadAndTakeBlock();
107
+ block.reset();
108
+ }
109
+
110
+ bool BlockHandle::CanUnload() {
111
+ if (state == BlockState::BLOCK_UNLOADED) {
112
+ // already unloaded
113
+ return false;
114
+ }
115
+ if (readers > 0) {
116
+ // there are active readers
117
+ return false;
118
+ }
119
+ if (block_id >= MAXIMUM_BLOCK && !can_destroy && block_manager.buffer_manager.temp_directory.empty()) {
120
+ // in order to unload this block we need to write it to a temporary buffer
121
+ // however, no temporary directory is specified!
122
+ // hence we cannot unload the block
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+
128
+ } // namespace duckdb