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.
- package/package.json +1 -1
- package/src/duckdb/extension/icu/icu-datefunc.cpp +20 -8
- package/src/duckdb/extension/icu/icu-strptime.cpp +117 -29
- package/src/duckdb/extension/icu/include/icu-datefunc.hpp +2 -0
- package/src/duckdb/src/common/local_file_system.cpp +13 -2
- package/src/duckdb/src/common/sort/partition_state.cpp +644 -0
- package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +77 -849
- package/src/duckdb/src/function/table/system/duckdb_extensions.cpp +2 -2
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/common/sort/partition_state.hpp +247 -0
- package/src/duckdb/src/include/duckdb/storage/buffer/block_handle.hpp +1 -2
- package/src/duckdb/src/include/duckdb/storage/buffer/buffer_pool.hpp +77 -0
- package/src/duckdb/src/include/duckdb/storage/buffer/temporary_file_information.hpp +12 -0
- package/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +3 -59
- package/src/duckdb/src/main/extension/extension_install.cpp +11 -0
- package/src/duckdb/src/main/extension/extension_load.cpp +29 -3
- package/src/duckdb/src/storage/buffer/block_handle.cpp +128 -0
- package/src/duckdb/src/storage/buffer/block_manager.cpp +81 -0
- package/src/duckdb/src/storage/buffer/buffer_pool.cpp +132 -0
- package/src/duckdb/src/storage/buffer/buffer_pool_reservation.cpp +32 -0
- package/src/duckdb/src/storage/buffer_manager.cpp +0 -351
- package/src/duckdb/third_party/libpg_query/postgres_parser.cpp +3 -5
- package/src/duckdb/ub_src_common_sort.cpp +2 -0
- package/src/duckdb/ub_src_storage_buffer.cpp +8 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
#include "duckdb/storage/block_manager.hpp"
|
2
|
+
#include "duckdb/storage/buffer_manager.hpp"
|
3
|
+
|
4
|
+
namespace duckdb {
|
5
|
+
|
6
|
+
shared_ptr<BlockHandle> BlockManager::RegisterBlock(block_id_t block_id, bool is_meta_block) {
|
7
|
+
lock_guard<mutex> lock(blocks_lock);
|
8
|
+
// check if the block already exists
|
9
|
+
auto entry = blocks.find(block_id);
|
10
|
+
if (entry != blocks.end()) {
|
11
|
+
// already exists: check if it hasn't expired yet
|
12
|
+
auto existing_ptr = entry->second.lock();
|
13
|
+
if (existing_ptr) {
|
14
|
+
//! it hasn't! return it
|
15
|
+
return existing_ptr;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
// create a new block pointer for this block
|
19
|
+
auto result = make_shared<BlockHandle>(*this, block_id);
|
20
|
+
// for meta block, cache the handle in meta_blocks
|
21
|
+
if (is_meta_block) {
|
22
|
+
meta_blocks[block_id] = result;
|
23
|
+
}
|
24
|
+
// register the block pointer in the set of blocks as a weak pointer
|
25
|
+
blocks[block_id] = weak_ptr<BlockHandle>(result);
|
26
|
+
return result;
|
27
|
+
}
|
28
|
+
|
29
|
+
void BlockManager::ClearMetaBlockHandles() {
|
30
|
+
meta_blocks.clear();
|
31
|
+
}
|
32
|
+
|
33
|
+
shared_ptr<BlockHandle> BlockManager::ConvertToPersistent(block_id_t block_id, shared_ptr<BlockHandle> old_block) {
|
34
|
+
|
35
|
+
// pin the old block to ensure we have it loaded in memory
|
36
|
+
auto old_handle = buffer_manager.Pin(old_block);
|
37
|
+
D_ASSERT(old_block->state == BlockState::BLOCK_LOADED);
|
38
|
+
D_ASSERT(old_block->buffer);
|
39
|
+
|
40
|
+
// Temp buffers can be larger than the storage block size. But persistent buffers
|
41
|
+
// cannot.
|
42
|
+
D_ASSERT(old_block->buffer->AllocSize() <= Storage::BLOCK_ALLOC_SIZE);
|
43
|
+
|
44
|
+
// register a block with the new block id
|
45
|
+
auto new_block = RegisterBlock(block_id);
|
46
|
+
D_ASSERT(new_block->state == BlockState::BLOCK_UNLOADED);
|
47
|
+
D_ASSERT(new_block->readers == 0);
|
48
|
+
|
49
|
+
// move the data from the old block into data for the new block
|
50
|
+
new_block->state = BlockState::BLOCK_LOADED;
|
51
|
+
new_block->buffer = CreateBlock(block_id, old_block->buffer.get());
|
52
|
+
new_block->memory_usage = old_block->memory_usage;
|
53
|
+
new_block->memory_charge = std::move(old_block->memory_charge);
|
54
|
+
|
55
|
+
// clear the old buffer and unload it
|
56
|
+
old_block->buffer.reset();
|
57
|
+
old_block->state = BlockState::BLOCK_UNLOADED;
|
58
|
+
old_block->memory_usage = 0;
|
59
|
+
old_handle.Destroy();
|
60
|
+
old_block.reset();
|
61
|
+
|
62
|
+
// persist the new block to disk
|
63
|
+
Write(*new_block->buffer, block_id);
|
64
|
+
|
65
|
+
buffer_manager.buffer_pool.AddToEvictionQueue(new_block);
|
66
|
+
|
67
|
+
return new_block;
|
68
|
+
}
|
69
|
+
|
70
|
+
void BlockManager::UnregisterBlock(block_id_t block_id, bool can_destroy) {
|
71
|
+
if (block_id >= MAXIMUM_BLOCK) {
|
72
|
+
// in-memory buffer: buffer could have been offloaded to disk: remove the file
|
73
|
+
buffer_manager.DeleteTemporaryFile(block_id);
|
74
|
+
} else {
|
75
|
+
lock_guard<mutex> lock(blocks_lock);
|
76
|
+
// on-disk block: erase from list of blocks in manager
|
77
|
+
blocks.erase(block_id);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
} // namespace duckdb
|
@@ -0,0 +1,132 @@
|
|
1
|
+
#include "duckdb/storage/buffer/buffer_pool.hpp"
|
2
|
+
#include "duckdb/parallel/concurrentqueue.hpp"
|
3
|
+
#include "duckdb/common/exception.hpp"
|
4
|
+
|
5
|
+
namespace duckdb {
|
6
|
+
|
7
|
+
typedef duckdb_moodycamel::ConcurrentQueue<BufferEvictionNode> eviction_queue_t;
|
8
|
+
|
9
|
+
struct EvictionQueue {
|
10
|
+
eviction_queue_t q;
|
11
|
+
};
|
12
|
+
|
13
|
+
bool BufferEvictionNode::CanUnload(BlockHandle &handle_p) {
|
14
|
+
if (timestamp != handle_p.eviction_timestamp) {
|
15
|
+
// handle was used in between
|
16
|
+
return false;
|
17
|
+
}
|
18
|
+
return handle_p.CanUnload();
|
19
|
+
}
|
20
|
+
|
21
|
+
shared_ptr<BlockHandle> BufferEvictionNode::TryGetBlockHandle() {
|
22
|
+
auto handle_p = handle.lock();
|
23
|
+
if (!handle_p) {
|
24
|
+
// BlockHandle has been destroyed
|
25
|
+
return nullptr;
|
26
|
+
}
|
27
|
+
if (!CanUnload(*handle_p)) {
|
28
|
+
// handle was used in between
|
29
|
+
return nullptr;
|
30
|
+
}
|
31
|
+
// this is the latest node in the queue with this handle
|
32
|
+
return handle_p;
|
33
|
+
}
|
34
|
+
|
35
|
+
BufferPool::BufferPool(idx_t maximum_memory)
|
36
|
+
: current_memory(0), maximum_memory(maximum_memory), queue(make_uniq<EvictionQueue>()), queue_insertions(0) {
|
37
|
+
}
|
38
|
+
BufferPool::~BufferPool() {
|
39
|
+
}
|
40
|
+
|
41
|
+
void BufferPool::AddToEvictionQueue(shared_ptr<BlockHandle> &handle) {
|
42
|
+
constexpr int INSERT_INTERVAL = 1024;
|
43
|
+
|
44
|
+
D_ASSERT(handle->readers == 0);
|
45
|
+
handle->eviction_timestamp++;
|
46
|
+
// After each 1024 insertions, run through the queue and purge.
|
47
|
+
if ((++queue_insertions % INSERT_INTERVAL) == 0) {
|
48
|
+
PurgeQueue();
|
49
|
+
}
|
50
|
+
queue->q.enqueue(BufferEvictionNode(weak_ptr<BlockHandle>(handle), handle->eviction_timestamp));
|
51
|
+
}
|
52
|
+
|
53
|
+
idx_t BufferPool::GetUsedMemory() {
|
54
|
+
return current_memory;
|
55
|
+
}
|
56
|
+
idx_t BufferPool::GetMaxMemory() {
|
57
|
+
return maximum_memory;
|
58
|
+
}
|
59
|
+
|
60
|
+
BufferPool::EvictionResult BufferPool::EvictBlocks(idx_t extra_memory, idx_t memory_limit,
|
61
|
+
unique_ptr<FileBuffer> *buffer) {
|
62
|
+
BufferEvictionNode node;
|
63
|
+
TempBufferPoolReservation r(current_memory, extra_memory);
|
64
|
+
while (current_memory > memory_limit) {
|
65
|
+
// get a block to unpin from the queue
|
66
|
+
if (!queue->q.try_dequeue(node)) {
|
67
|
+
// Failed to reserve. Adjust size of temp reservation to 0.
|
68
|
+
r.Resize(current_memory, 0);
|
69
|
+
return {false, std::move(r)};
|
70
|
+
}
|
71
|
+
// get a reference to the underlying block pointer
|
72
|
+
auto handle = node.TryGetBlockHandle();
|
73
|
+
if (!handle) {
|
74
|
+
continue;
|
75
|
+
}
|
76
|
+
// we might be able to free this block: grab the mutex and check if we can free it
|
77
|
+
lock_guard<mutex> lock(handle->lock);
|
78
|
+
if (!node.CanUnload(*handle)) {
|
79
|
+
// something changed in the mean-time, bail out
|
80
|
+
continue;
|
81
|
+
}
|
82
|
+
// hooray, we can unload the block
|
83
|
+
if (buffer && handle->buffer->AllocSize() == extra_memory) {
|
84
|
+
// we can actually re-use the memory directly!
|
85
|
+
*buffer = handle->UnloadAndTakeBlock();
|
86
|
+
return {true, std::move(r)};
|
87
|
+
} else {
|
88
|
+
// release the memory and mark the block as unloaded
|
89
|
+
handle->Unload();
|
90
|
+
}
|
91
|
+
}
|
92
|
+
return {true, std::move(r)};
|
93
|
+
}
|
94
|
+
|
95
|
+
void BufferPool::PurgeQueue() {
|
96
|
+
BufferEvictionNode node;
|
97
|
+
while (true) {
|
98
|
+
if (!queue->q.try_dequeue(node)) {
|
99
|
+
break;
|
100
|
+
}
|
101
|
+
auto handle = node.TryGetBlockHandle();
|
102
|
+
if (!handle) {
|
103
|
+
continue;
|
104
|
+
} else {
|
105
|
+
queue->q.enqueue(std::move(node));
|
106
|
+
break;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
void BufferPool::SetLimit(idx_t limit, const char *exception_postscript) {
|
112
|
+
lock_guard<mutex> l_lock(limit_lock);
|
113
|
+
// try to evict until the limit is reached
|
114
|
+
if (!EvictBlocks(0, limit).success) {
|
115
|
+
throw OutOfMemoryException(
|
116
|
+
"Failed to change memory limit to %lld: could not free up enough memory for the new limit%s", limit,
|
117
|
+
exception_postscript);
|
118
|
+
}
|
119
|
+
idx_t old_limit = maximum_memory;
|
120
|
+
// set the global maximum memory to the new limit if successful
|
121
|
+
maximum_memory = limit;
|
122
|
+
// evict again
|
123
|
+
if (!EvictBlocks(0, limit).success) {
|
124
|
+
// failed: go back to old limit
|
125
|
+
maximum_memory = old_limit;
|
126
|
+
throw OutOfMemoryException(
|
127
|
+
"Failed to change memory limit to %lld: could not free up enough memory for the new limit%s", limit,
|
128
|
+
exception_postscript);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
} // namespace duckdb
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#include "duckdb/storage/buffer/block_handle.hpp"
|
2
|
+
|
3
|
+
namespace duckdb {
|
4
|
+
|
5
|
+
BufferPoolReservation::BufferPoolReservation(BufferPoolReservation &&src) noexcept {
|
6
|
+
size = src.size;
|
7
|
+
src.size = 0;
|
8
|
+
}
|
9
|
+
|
10
|
+
BufferPoolReservation &BufferPoolReservation::operator=(BufferPoolReservation &&src) noexcept {
|
11
|
+
size = src.size;
|
12
|
+
src.size = 0;
|
13
|
+
return *this;
|
14
|
+
}
|
15
|
+
|
16
|
+
BufferPoolReservation::~BufferPoolReservation() {
|
17
|
+
D_ASSERT(size == 0);
|
18
|
+
}
|
19
|
+
|
20
|
+
void BufferPoolReservation::Resize(atomic<idx_t> &counter, idx_t new_size) {
|
21
|
+
int64_t delta = (int64_t)new_size - size;
|
22
|
+
D_ASSERT(delta > 0 || (int64_t)counter >= -delta);
|
23
|
+
counter += delta;
|
24
|
+
size = new_size;
|
25
|
+
}
|
26
|
+
|
27
|
+
void BufferPoolReservation::Merge(BufferPoolReservation &&src) {
|
28
|
+
size += src.size;
|
29
|
+
src.size = 0;
|
30
|
+
}
|
31
|
+
|
32
|
+
} // namespace duckdb
|
@@ -3,7 +3,6 @@
|
|
3
3
|
#include "duckdb/common/allocator.hpp"
|
4
4
|
#include "duckdb/common/exception.hpp"
|
5
5
|
#include "duckdb/common/set.hpp"
|
6
|
-
#include "duckdb/parallel/concurrentqueue.hpp"
|
7
6
|
#include "duckdb/storage/in_memory_block_manager.hpp"
|
8
7
|
#include "duckdb/storage/storage_manager.hpp"
|
9
8
|
#include "duckdb/main/attached_database.hpp"
|
@@ -11,33 +10,6 @@
|
|
11
10
|
|
12
11
|
namespace duckdb {
|
13
12
|
|
14
|
-
BufferPoolReservation::BufferPoolReservation(BufferPoolReservation &&src) noexcept {
|
15
|
-
size = src.size;
|
16
|
-
src.size = 0;
|
17
|
-
}
|
18
|
-
|
19
|
-
BufferPoolReservation &BufferPoolReservation::operator=(BufferPoolReservation &&src) noexcept {
|
20
|
-
size = src.size;
|
21
|
-
src.size = 0;
|
22
|
-
return *this;
|
23
|
-
}
|
24
|
-
|
25
|
-
BufferPoolReservation::~BufferPoolReservation() {
|
26
|
-
D_ASSERT(size == 0);
|
27
|
-
}
|
28
|
-
|
29
|
-
void BufferPoolReservation::Resize(atomic<idx_t> &counter, idx_t new_size) {
|
30
|
-
int64_t delta = (int64_t)new_size - size;
|
31
|
-
D_ASSERT(delta > 0 || (int64_t)counter >= -delta);
|
32
|
-
counter += delta;
|
33
|
-
size = new_size;
|
34
|
-
}
|
35
|
-
|
36
|
-
void BufferPoolReservation::Merge(BufferPoolReservation &&src) {
|
37
|
-
size += src.size;
|
38
|
-
src.size = 0;
|
39
|
-
}
|
40
|
-
|
41
13
|
struct BufferAllocatorData : PrivateAllocatorData {
|
42
14
|
explicit BufferAllocatorData(BufferManager &manager) : manager(manager) {
|
43
15
|
}
|
@@ -45,60 +17,6 @@ struct BufferAllocatorData : PrivateAllocatorData {
|
|
45
17
|
BufferManager &manager;
|
46
18
|
};
|
47
19
|
|
48
|
-
BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p)
|
49
|
-
: block_manager(block_manager), readers(0), block_id(block_id_p), buffer(nullptr), eviction_timestamp(0),
|
50
|
-
can_destroy(false), unswizzled(nullptr) {
|
51
|
-
eviction_timestamp = 0;
|
52
|
-
state = BlockState::BLOCK_UNLOADED;
|
53
|
-
memory_usage = Storage::BLOCK_ALLOC_SIZE;
|
54
|
-
}
|
55
|
-
|
56
|
-
BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, unique_ptr<FileBuffer> buffer_p,
|
57
|
-
bool can_destroy_p, idx_t block_size, BufferPoolReservation &&reservation)
|
58
|
-
: block_manager(block_manager), readers(0), block_id(block_id_p), eviction_timestamp(0), can_destroy(can_destroy_p),
|
59
|
-
unswizzled(nullptr) {
|
60
|
-
buffer = std::move(buffer_p);
|
61
|
-
state = BlockState::BLOCK_LOADED;
|
62
|
-
memory_usage = block_size;
|
63
|
-
memory_charge = std::move(reservation);
|
64
|
-
}
|
65
|
-
|
66
|
-
BlockHandle::~BlockHandle() { // NOLINT: allow internal exceptions
|
67
|
-
// being destroyed, so any unswizzled pointers are just binary junk now.
|
68
|
-
unswizzled = nullptr;
|
69
|
-
auto &buffer_manager = block_manager.buffer_manager;
|
70
|
-
// no references remain to this block: erase
|
71
|
-
if (buffer && state == BlockState::BLOCK_LOADED) {
|
72
|
-
D_ASSERT(memory_charge.size > 0);
|
73
|
-
// the block is still loaded in memory: erase it
|
74
|
-
buffer.reset();
|
75
|
-
memory_charge.Resize(buffer_manager.buffer_pool.current_memory, 0);
|
76
|
-
} else {
|
77
|
-
D_ASSERT(memory_charge.size == 0);
|
78
|
-
}
|
79
|
-
buffer_manager.buffer_pool.PurgeQueue();
|
80
|
-
block_manager.UnregisterBlock(block_id, can_destroy);
|
81
|
-
}
|
82
|
-
|
83
|
-
unique_ptr<Block> AllocateBlock(BlockManager &block_manager, unique_ptr<FileBuffer> reusable_buffer,
|
84
|
-
block_id_t block_id) {
|
85
|
-
if (reusable_buffer) {
|
86
|
-
// re-usable buffer: re-use it
|
87
|
-
if (reusable_buffer->type == FileBufferType::BLOCK) {
|
88
|
-
// we can reuse the buffer entirely
|
89
|
-
auto &block = (Block &)*reusable_buffer;
|
90
|
-
block.id = block_id;
|
91
|
-
return unique_ptr_cast<FileBuffer, Block>(std::move(reusable_buffer));
|
92
|
-
}
|
93
|
-
auto block = block_manager.CreateBlock(block_id, reusable_buffer.get());
|
94
|
-
reusable_buffer.reset();
|
95
|
-
return block;
|
96
|
-
} else {
|
97
|
-
// no re-usable buffer: allocate a new block
|
98
|
-
return block_manager.CreateBlock(block_id, nullptr);
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
20
|
idx_t GetAllocSize(idx_t size) {
|
103
21
|
return AlignValue<idx_t, Storage::SECTOR_SIZE>(size + Storage::BLOCK_HEADER_SIZE);
|
104
22
|
}
|
@@ -115,110 +33,6 @@ unique_ptr<FileBuffer> BufferManager::ConstructManagedBuffer(idx_t size, unique_
|
|
115
33
|
}
|
116
34
|
}
|
117
35
|
|
118
|
-
BufferHandle BlockHandle::Load(shared_ptr<BlockHandle> &handle, unique_ptr<FileBuffer> reusable_buffer) {
|
119
|
-
if (handle->state == BlockState::BLOCK_LOADED) {
|
120
|
-
// already loaded
|
121
|
-
D_ASSERT(handle->buffer);
|
122
|
-
return BufferHandle(handle, handle->buffer.get());
|
123
|
-
}
|
124
|
-
|
125
|
-
auto &block_manager = handle->block_manager;
|
126
|
-
if (handle->block_id < MAXIMUM_BLOCK) {
|
127
|
-
auto block = AllocateBlock(block_manager, std::move(reusable_buffer), handle->block_id);
|
128
|
-
block_manager.Read(*block);
|
129
|
-
handle->buffer = std::move(block);
|
130
|
-
} else {
|
131
|
-
if (handle->can_destroy) {
|
132
|
-
return BufferHandle();
|
133
|
-
} else {
|
134
|
-
handle->buffer =
|
135
|
-
block_manager.buffer_manager.ReadTemporaryBuffer(handle->block_id, std::move(reusable_buffer));
|
136
|
-
}
|
137
|
-
}
|
138
|
-
handle->state = BlockState::BLOCK_LOADED;
|
139
|
-
return BufferHandle(handle, handle->buffer.get());
|
140
|
-
}
|
141
|
-
|
142
|
-
unique_ptr<FileBuffer> BlockHandle::UnloadAndTakeBlock() {
|
143
|
-
if (state == BlockState::BLOCK_UNLOADED) {
|
144
|
-
// already unloaded: nothing to do
|
145
|
-
return nullptr;
|
146
|
-
}
|
147
|
-
D_ASSERT(!unswizzled);
|
148
|
-
D_ASSERT(CanUnload());
|
149
|
-
|
150
|
-
if (block_id >= MAXIMUM_BLOCK && !can_destroy) {
|
151
|
-
// temporary block that cannot be destroyed: write to temporary file
|
152
|
-
block_manager.buffer_manager.WriteTemporaryBuffer(block_id, *buffer);
|
153
|
-
}
|
154
|
-
memory_charge.Resize(block_manager.buffer_manager.buffer_pool.current_memory, 0);
|
155
|
-
state = BlockState::BLOCK_UNLOADED;
|
156
|
-
return std::move(buffer);
|
157
|
-
}
|
158
|
-
|
159
|
-
void BlockHandle::Unload() {
|
160
|
-
auto block = UnloadAndTakeBlock();
|
161
|
-
block.reset();
|
162
|
-
}
|
163
|
-
|
164
|
-
bool BlockHandle::CanUnload() {
|
165
|
-
if (state == BlockState::BLOCK_UNLOADED) {
|
166
|
-
// already unloaded
|
167
|
-
return false;
|
168
|
-
}
|
169
|
-
if (readers > 0) {
|
170
|
-
// there are active readers
|
171
|
-
return false;
|
172
|
-
}
|
173
|
-
if (block_id >= MAXIMUM_BLOCK && !can_destroy && block_manager.buffer_manager.temp_directory.empty()) {
|
174
|
-
// in order to unload this block we need to write it to a temporary buffer
|
175
|
-
// however, no temporary directory is specified!
|
176
|
-
// hence we cannot unload the block
|
177
|
-
return false;
|
178
|
-
}
|
179
|
-
return true;
|
180
|
-
}
|
181
|
-
|
182
|
-
struct BufferEvictionNode {
|
183
|
-
BufferEvictionNode() {
|
184
|
-
}
|
185
|
-
BufferEvictionNode(weak_ptr<BlockHandle> handle_p, idx_t timestamp_p)
|
186
|
-
: handle(std::move(handle_p)), timestamp(timestamp_p) {
|
187
|
-
D_ASSERT(!handle.expired());
|
188
|
-
}
|
189
|
-
|
190
|
-
weak_ptr<BlockHandle> handle;
|
191
|
-
idx_t timestamp;
|
192
|
-
|
193
|
-
bool CanUnload(BlockHandle &handle_p) {
|
194
|
-
if (timestamp != handle_p.eviction_timestamp) {
|
195
|
-
// handle was used in between
|
196
|
-
return false;
|
197
|
-
}
|
198
|
-
return handle_p.CanUnload();
|
199
|
-
}
|
200
|
-
|
201
|
-
shared_ptr<BlockHandle> TryGetBlockHandle() {
|
202
|
-
auto handle_p = handle.lock();
|
203
|
-
if (!handle_p) {
|
204
|
-
// BlockHandle has been destroyed
|
205
|
-
return nullptr;
|
206
|
-
}
|
207
|
-
if (!CanUnload(*handle_p)) {
|
208
|
-
// handle was used in between
|
209
|
-
return nullptr;
|
210
|
-
}
|
211
|
-
// this is the latest node in the queue with this handle
|
212
|
-
return handle_p;
|
213
|
-
}
|
214
|
-
};
|
215
|
-
|
216
|
-
typedef duckdb_moodycamel::ConcurrentQueue<BufferEvictionNode> eviction_queue_t;
|
217
|
-
|
218
|
-
struct EvictionQueue {
|
219
|
-
eviction_queue_t q;
|
220
|
-
};
|
221
|
-
|
222
36
|
class TemporaryFileManager;
|
223
37
|
|
224
38
|
class TemporaryDirectoryHandle {
|
@@ -242,12 +56,6 @@ void BufferManager::SetTemporaryDirectory(string new_dir) {
|
|
242
56
|
this->temp_directory = std::move(new_dir);
|
243
57
|
}
|
244
58
|
|
245
|
-
BufferPool::BufferPool(idx_t maximum_memory)
|
246
|
-
: current_memory(0), maximum_memory(maximum_memory), queue(make_uniq<EvictionQueue>()), queue_insertions(0) {
|
247
|
-
}
|
248
|
-
BufferPool::~BufferPool() {
|
249
|
-
}
|
250
|
-
|
251
59
|
BufferManager::BufferManager(DatabaseInstance &db, string tmp)
|
252
60
|
: db(db), buffer_pool(db.GetBufferPool()), temp_directory(std::move(tmp)), temporary_id(MAXIMUM_BLOCK),
|
253
61
|
buffer_allocator(BufferAllocatorAllocate, BufferAllocatorFree, BufferAllocatorRealloc,
|
@@ -258,70 +66,6 @@ BufferManager::BufferManager(DatabaseInstance &db, string tmp)
|
|
258
66
|
BufferManager::~BufferManager() {
|
259
67
|
}
|
260
68
|
|
261
|
-
shared_ptr<BlockHandle> BlockManager::RegisterBlock(block_id_t block_id, bool is_meta_block) {
|
262
|
-
lock_guard<mutex> lock(blocks_lock);
|
263
|
-
// check if the block already exists
|
264
|
-
auto entry = blocks.find(block_id);
|
265
|
-
if (entry != blocks.end()) {
|
266
|
-
// already exists: check if it hasn't expired yet
|
267
|
-
auto existing_ptr = entry->second.lock();
|
268
|
-
if (existing_ptr) {
|
269
|
-
//! it hasn't! return it
|
270
|
-
return existing_ptr;
|
271
|
-
}
|
272
|
-
}
|
273
|
-
// create a new block pointer for this block
|
274
|
-
auto result = make_shared<BlockHandle>(*this, block_id);
|
275
|
-
// for meta block, cache the handle in meta_blocks
|
276
|
-
if (is_meta_block) {
|
277
|
-
meta_blocks[block_id] = result;
|
278
|
-
}
|
279
|
-
// register the block pointer in the set of blocks as a weak pointer
|
280
|
-
blocks[block_id] = weak_ptr<BlockHandle>(result);
|
281
|
-
return result;
|
282
|
-
}
|
283
|
-
|
284
|
-
void BlockManager::ClearMetaBlockHandles() {
|
285
|
-
meta_blocks.clear();
|
286
|
-
}
|
287
|
-
|
288
|
-
shared_ptr<BlockHandle> BlockManager::ConvertToPersistent(block_id_t block_id, shared_ptr<BlockHandle> old_block) {
|
289
|
-
|
290
|
-
// pin the old block to ensure we have it loaded in memory
|
291
|
-
auto old_handle = buffer_manager.Pin(old_block);
|
292
|
-
D_ASSERT(old_block->state == BlockState::BLOCK_LOADED);
|
293
|
-
D_ASSERT(old_block->buffer);
|
294
|
-
|
295
|
-
// Temp buffers can be larger than the storage block size. But persistent buffers
|
296
|
-
// cannot.
|
297
|
-
D_ASSERT(old_block->buffer->AllocSize() <= Storage::BLOCK_ALLOC_SIZE);
|
298
|
-
|
299
|
-
// register a block with the new block id
|
300
|
-
auto new_block = RegisterBlock(block_id);
|
301
|
-
D_ASSERT(new_block->state == BlockState::BLOCK_UNLOADED);
|
302
|
-
D_ASSERT(new_block->readers == 0);
|
303
|
-
|
304
|
-
// move the data from the old block into data for the new block
|
305
|
-
new_block->state = BlockState::BLOCK_LOADED;
|
306
|
-
new_block->buffer = CreateBlock(block_id, old_block->buffer.get());
|
307
|
-
new_block->memory_usage = old_block->memory_usage;
|
308
|
-
new_block->memory_charge = std::move(old_block->memory_charge);
|
309
|
-
|
310
|
-
// clear the old buffer and unload it
|
311
|
-
old_block->buffer.reset();
|
312
|
-
old_block->state = BlockState::BLOCK_UNLOADED;
|
313
|
-
old_block->memory_usage = 0;
|
314
|
-
old_handle.Destroy();
|
315
|
-
old_block.reset();
|
316
|
-
|
317
|
-
// persist the new block to disk
|
318
|
-
Write(*new_block->buffer, block_id);
|
319
|
-
|
320
|
-
buffer_manager.buffer_pool.AddToEvictionQueue(new_block);
|
321
|
-
|
322
|
-
return new_block;
|
323
|
-
}
|
324
|
-
|
325
69
|
template <typename... ARGS>
|
326
70
|
TempBufferPoolReservation BufferManager::EvictBlocksOrThrow(idx_t memory_delta, unique_ptr<FileBuffer> *buffer,
|
327
71
|
ARGS... args) {
|
@@ -438,18 +182,6 @@ BufferHandle BufferManager::Pin(shared_ptr<BlockHandle> &handle) {
|
|
438
182
|
return buf;
|
439
183
|
}
|
440
184
|
|
441
|
-
void BufferPool::AddToEvictionQueue(shared_ptr<BlockHandle> &handle) {
|
442
|
-
constexpr int INSERT_INTERVAL = 1024;
|
443
|
-
|
444
|
-
D_ASSERT(handle->readers == 0);
|
445
|
-
handle->eviction_timestamp++;
|
446
|
-
// After each 1024 insertions, run through the queue and purge.
|
447
|
-
if ((++queue_insertions % INSERT_INTERVAL) == 0) {
|
448
|
-
PurgeQueue();
|
449
|
-
}
|
450
|
-
queue->q.enqueue(BufferEvictionNode(weak_ptr<BlockHandle>(handle), handle->eviction_timestamp));
|
451
|
-
}
|
452
|
-
|
453
185
|
void BufferManager::VerifyZeroReaders(shared_ptr<BlockHandle> &handle) {
|
454
186
|
#ifdef DUCKDB_DEBUG_DESTROY_BLOCKS
|
455
187
|
auto replacement_buffer = make_uniq<FileBuffer>(Allocator::Get(db), handle->buffer->type,
|
@@ -473,89 +205,6 @@ void BufferManager::Unpin(shared_ptr<BlockHandle> &handle) {
|
|
473
205
|
}
|
474
206
|
}
|
475
207
|
|
476
|
-
BufferPool::EvictionResult BufferPool::EvictBlocks(idx_t extra_memory, idx_t memory_limit,
|
477
|
-
unique_ptr<FileBuffer> *buffer) {
|
478
|
-
BufferEvictionNode node;
|
479
|
-
TempBufferPoolReservation r(current_memory, extra_memory);
|
480
|
-
while (current_memory > memory_limit) {
|
481
|
-
// get a block to unpin from the queue
|
482
|
-
if (!queue->q.try_dequeue(node)) {
|
483
|
-
// Failed to reserve. Adjust size of temp reservation to 0.
|
484
|
-
r.Resize(current_memory, 0);
|
485
|
-
return {false, std::move(r)};
|
486
|
-
}
|
487
|
-
// get a reference to the underlying block pointer
|
488
|
-
auto handle = node.TryGetBlockHandle();
|
489
|
-
if (!handle) {
|
490
|
-
continue;
|
491
|
-
}
|
492
|
-
// we might be able to free this block: grab the mutex and check if we can free it
|
493
|
-
lock_guard<mutex> lock(handle->lock);
|
494
|
-
if (!node.CanUnload(*handle)) {
|
495
|
-
// something changed in the mean-time, bail out
|
496
|
-
continue;
|
497
|
-
}
|
498
|
-
// hooray, we can unload the block
|
499
|
-
if (buffer && handle->buffer->AllocSize() == extra_memory) {
|
500
|
-
// we can actually re-use the memory directly!
|
501
|
-
*buffer = handle->UnloadAndTakeBlock();
|
502
|
-
return {true, std::move(r)};
|
503
|
-
} else {
|
504
|
-
// release the memory and mark the block as unloaded
|
505
|
-
handle->Unload();
|
506
|
-
}
|
507
|
-
}
|
508
|
-
return {true, std::move(r)};
|
509
|
-
}
|
510
|
-
|
511
|
-
void BufferPool::PurgeQueue() {
|
512
|
-
BufferEvictionNode node;
|
513
|
-
while (true) {
|
514
|
-
if (!queue->q.try_dequeue(node)) {
|
515
|
-
break;
|
516
|
-
}
|
517
|
-
auto handle = node.TryGetBlockHandle();
|
518
|
-
if (!handle) {
|
519
|
-
continue;
|
520
|
-
} else {
|
521
|
-
queue->q.enqueue(std::move(node));
|
522
|
-
break;
|
523
|
-
}
|
524
|
-
}
|
525
|
-
}
|
526
|
-
|
527
|
-
void BlockManager::UnregisterBlock(block_id_t block_id, bool can_destroy) {
|
528
|
-
if (block_id >= MAXIMUM_BLOCK) {
|
529
|
-
// in-memory buffer: buffer could have been offloaded to disk: remove the file
|
530
|
-
buffer_manager.DeleteTemporaryFile(block_id);
|
531
|
-
} else {
|
532
|
-
lock_guard<mutex> lock(blocks_lock);
|
533
|
-
// on-disk block: erase from list of blocks in manager
|
534
|
-
blocks.erase(block_id);
|
535
|
-
}
|
536
|
-
}
|
537
|
-
|
538
|
-
void BufferPool::SetLimit(idx_t limit, const char *exception_postscript) {
|
539
|
-
lock_guard<mutex> l_lock(limit_lock);
|
540
|
-
// try to evict until the limit is reached
|
541
|
-
if (!EvictBlocks(0, limit).success) {
|
542
|
-
throw OutOfMemoryException(
|
543
|
-
"Failed to change memory limit to %lld: could not free up enough memory for the new limit%s", limit,
|
544
|
-
exception_postscript);
|
545
|
-
}
|
546
|
-
idx_t old_limit = maximum_memory;
|
547
|
-
// set the global maximum memory to the new limit if successful
|
548
|
-
maximum_memory = limit;
|
549
|
-
// evict again
|
550
|
-
if (!EvictBlocks(0, limit).success) {
|
551
|
-
// failed: go back to old limit
|
552
|
-
maximum_memory = old_limit;
|
553
|
-
throw OutOfMemoryException(
|
554
|
-
"Failed to change memory limit to %lld: could not free up enough memory for the new limit%s", limit,
|
555
|
-
exception_postscript);
|
556
|
-
}
|
557
|
-
}
|
558
|
-
|
559
208
|
void BufferManager::IncreaseUsedMemory(idx_t size) {
|
560
209
|
ReserveMemory(size);
|
561
210
|
}
|
@@ -5,13 +5,11 @@
|
|
5
5
|
#include "parser/scansup.hpp"
|
6
6
|
#include "common/keywords.hpp"
|
7
7
|
|
8
|
-
using namespace std;
|
9
|
-
|
10
8
|
namespace duckdb {
|
11
9
|
|
12
10
|
PostgresParser::PostgresParser() : success(false), parse_tree(nullptr), error_message(""), error_location(0) {}
|
13
11
|
|
14
|
-
void PostgresParser::Parse(const string &query) {
|
12
|
+
void PostgresParser::Parse(const std::string &query) {
|
15
13
|
duckdb_libpgquery::pg_parser_init();
|
16
14
|
duckdb_libpgquery::parse_result res;
|
17
15
|
pg_parser_parse(query.c_str(), &res);
|
@@ -20,7 +18,7 @@ void PostgresParser::Parse(const string &query) {
|
|
20
18
|
if (success) {
|
21
19
|
parse_tree = res.parse_tree;
|
22
20
|
} else {
|
23
|
-
error_message = string(res.error_message);
|
21
|
+
error_message = std::string(res.error_message);
|
24
22
|
error_location = res.error_location;
|
25
23
|
}
|
26
24
|
}
|
@@ -42,7 +40,7 @@ bool PostgresParser::IsKeyword(const std::string &text) {
|
|
42
40
|
|
43
41
|
vector<duckdb_libpgquery::PGKeyword> PostgresParser::KeywordList() {
|
44
42
|
// FIXME: because of this, we might need to change the libpg_query library to use duckdb::vector
|
45
|
-
return std::
|
43
|
+
return std::forward<vector<duckdb_libpgquery::PGKeyword> >(duckdb_libpgquery::keyword_list());
|
46
44
|
}
|
47
45
|
|
48
46
|
void PostgresParser::SetPreserveIdentifierCase(bool preserve) {
|
@@ -1,2 +1,10 @@
|
|
1
1
|
#include "src/storage/buffer/buffer_handle.cpp"
|
2
2
|
|
3
|
+
#include "src/storage/buffer/block_handle.cpp"
|
4
|
+
|
5
|
+
#include "src/storage/buffer/block_manager.cpp"
|
6
|
+
|
7
|
+
#include "src/storage/buffer/buffer_pool.cpp"
|
8
|
+
|
9
|
+
#include "src/storage/buffer/buffer_pool_reservation.cpp"
|
10
|
+
|