duckdb 0.8.2-dev4203.0 → 0.8.2-dev4314.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/src/common/sort/partition_state.cpp +107 -29
- package/src/duckdb/src/execution/index/art/art.cpp +5 -1
- package/src/duckdb/src/execution/index/art/leaf.cpp +13 -10
- package/src/duckdb/src/execution/index/art/node48.cpp +0 -2
- package/src/duckdb/src/execution/index/fixed_size_allocator.cpp +38 -73
- package/src/duckdb/src/execution/index/fixed_size_buffer.cpp +245 -27
- package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +2 -3
- package/src/duckdb/src/execution/operator/join/physical_asof_join.cpp +35 -20
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/common/sort/partition_state.hpp +14 -4
- package/src/duckdb/src/include/duckdb/execution/index/art/leaf.hpp +2 -0
- package/src/duckdb/src/include/duckdb/execution/index/fixed_size_allocator.hpp +1 -7
- package/src/duckdb/src/include/duckdb/execution/index/fixed_size_buffer.hpp +38 -8
- package/src/duckdb/src/include/duckdb/main/relation.hpp +9 -2
- package/src/duckdb/src/include/duckdb/storage/partial_block_manager.hpp +35 -19
- package/src/duckdb/src/include/duckdb/storage/table/column_checkpoint_state.hpp +4 -19
- package/src/duckdb/src/main/relation.cpp +15 -2
- package/src/duckdb/src/storage/partial_block_manager.cpp +42 -15
- package/src/duckdb/src/storage/table/column_checkpoint_state.cpp +26 -32
@@ -8,36 +8,61 @@
|
|
8
8
|
|
9
9
|
#pragma once
|
10
10
|
|
11
|
+
#include "duckdb/storage/partial_block_manager.hpp"
|
11
12
|
#include "duckdb/common/typedefs.hpp"
|
12
13
|
#include "duckdb/storage/buffer/block_handle.hpp"
|
13
14
|
#include "duckdb/storage/buffer/buffer_handle.hpp"
|
15
|
+
#include "duckdb/storage/block_manager.hpp"
|
14
16
|
|
15
17
|
namespace duckdb {
|
16
18
|
|
17
19
|
class FixedSizeAllocator;
|
18
20
|
class MetadataWriter;
|
19
21
|
|
22
|
+
struct PartialBlockForIndex : public PartialBlock {
|
23
|
+
public:
|
24
|
+
PartialBlockForIndex(PartialBlockState state, BlockManager &block_manager,
|
25
|
+
const shared_ptr<BlockHandle> &block_handle);
|
26
|
+
~PartialBlockForIndex() override {};
|
27
|
+
|
28
|
+
public:
|
29
|
+
void Flush(const idx_t free_space_left) override;
|
30
|
+
void Clear() override;
|
31
|
+
void Merge(PartialBlock &other, idx_t offset, idx_t other_size) override;
|
32
|
+
};
|
33
|
+
|
20
34
|
//! A fixed-size buffer holds fixed-size segments of data. It lazily deserializes a buffer, if on-disk and not
|
21
35
|
//! yet in memory, and it only serializes dirty and non-written buffers to disk during
|
22
36
|
//! serialization.
|
23
37
|
class FixedSizeBuffer {
|
38
|
+
public:
|
39
|
+
//! Constants for fast offset calculations in the bitmask
|
40
|
+
static constexpr idx_t BASE[] = {0x00000000FFFFFFFF, 0x0000FFFF, 0x00FF, 0x0F, 0x3, 0x1};
|
41
|
+
static constexpr uint8_t SHIFT[] = {32, 16, 8, 4, 2, 1};
|
42
|
+
|
24
43
|
public:
|
25
44
|
//! Constructor for a new in-memory buffer
|
26
45
|
explicit FixedSizeBuffer(BlockManager &block_manager);
|
27
46
|
//! Constructor for deserializing buffer metadata from disk
|
28
|
-
FixedSizeBuffer(BlockManager &block_manager, const idx_t segment_count, const
|
47
|
+
FixedSizeBuffer(BlockManager &block_manager, const idx_t segment_count, const idx_t allocation_size,
|
48
|
+
const BlockPointer &block_pointer);
|
29
49
|
|
30
50
|
//! Block manager of the database instance
|
31
51
|
BlockManager &block_manager;
|
32
52
|
|
33
53
|
//! The number of allocated segments
|
34
54
|
idx_t segment_count;
|
55
|
+
//! The size of allocated memory in this buffer (necessary for copying while pinning)
|
56
|
+
idx_t allocation_size;
|
35
57
|
|
36
58
|
//! True: the in-memory buffer is no longer consistent with a (possibly existing) copy on disk
|
37
59
|
bool dirty;
|
38
60
|
//! True: can be vacuumed after the vacuum operation
|
39
61
|
bool vacuum;
|
40
62
|
|
63
|
+
//! Partial block id and offset
|
64
|
+
BlockPointer block_pointer;
|
65
|
+
|
41
66
|
public:
|
42
67
|
//! Returns true, if the buffer is in-memory
|
43
68
|
inline bool InMemory() const {
|
@@ -45,12 +70,7 @@ public:
|
|
45
70
|
}
|
46
71
|
//! Returns true, if the block is on-disk
|
47
72
|
inline bool OnDisk() const {
|
48
|
-
return (
|
49
|
-
}
|
50
|
-
//! Returns the block ID
|
51
|
-
inline block_id_t BlockId() const {
|
52
|
-
D_ASSERT(OnDisk());
|
53
|
-
return block_handle->BlockId();
|
73
|
+
return block_pointer.IsValid();
|
54
74
|
}
|
55
75
|
//! Returns a pointer to the buffer in memory, and calls Deserialize, if the buffer is not in memory
|
56
76
|
inline data_ptr_t Get(const bool dirty_p = true) {
|
@@ -65,15 +85,25 @@ public:
|
|
65
85
|
//! Destroys the in-memory buffer and the on-disk block
|
66
86
|
void Destroy();
|
67
87
|
//! Serializes a buffer (if dirty or not on disk)
|
68
|
-
void Serialize(
|
88
|
+
void Serialize(PartialBlockManager &partial_block_manager, const idx_t available_segments, const idx_t segment_size,
|
89
|
+
const idx_t bitmask_offset);
|
69
90
|
//! Pin a buffer (if not in-memory)
|
70
91
|
void Pin();
|
92
|
+
//! Returns the first free offset in a bitmask
|
93
|
+
uint32_t GetOffset(const idx_t bitmask_count);
|
71
94
|
|
72
95
|
private:
|
73
96
|
//! The buffer handle of the in-memory buffer
|
74
97
|
BufferHandle buffer_handle;
|
75
98
|
//! The block handle of the on-disk buffer
|
76
99
|
shared_ptr<BlockHandle> block_handle;
|
100
|
+
|
101
|
+
private:
|
102
|
+
//! Returns the maximum non-free offset in a bitmask
|
103
|
+
uint32_t GetMaxOffset(const idx_t available_segments_per_buffer);
|
104
|
+
//! Sets all uninitialized regions of a buffer in the respective partial block allocation
|
105
|
+
void SetUninitializedRegions(PartialBlockForIndex &p_block_for_index, const idx_t segment_size, const idx_t offset,
|
106
|
+
const idx_t bitmask_offset);
|
77
107
|
};
|
78
108
|
|
79
109
|
} // namespace duckdb
|
@@ -19,8 +19,11 @@
|
|
19
19
|
#include "duckdb/main/client_context.hpp"
|
20
20
|
#include "duckdb/main/external_dependencies.hpp"
|
21
21
|
#include "duckdb/parser/statement/explain_statement.hpp"
|
22
|
-
|
23
|
-
#include
|
22
|
+
#include "duckdb/parser/parsed_expression.hpp"
|
23
|
+
#include "duckdb/parser/result_modifier.hpp"
|
24
|
+
#include "duckdb/common/unique_ptr.hpp"
|
25
|
+
#include "duckdb/common/vector.hpp"
|
26
|
+
#include "duckdb/common/helper.hpp"
|
24
27
|
|
25
28
|
namespace duckdb {
|
26
29
|
struct BoundStatement;
|
@@ -81,9 +84,12 @@ public:
|
|
81
84
|
DUCKDB_API shared_ptr<Relation> Project(const string &select_list, const vector<string> &aliases);
|
82
85
|
DUCKDB_API shared_ptr<Relation> Project(const vector<string> &expressions);
|
83
86
|
DUCKDB_API shared_ptr<Relation> Project(const vector<string> &expressions, const vector<string> &aliases);
|
87
|
+
DUCKDB_API shared_ptr<Relation> Project(vector<unique_ptr<ParsedExpression>> expressions,
|
88
|
+
const vector<string> &aliases);
|
84
89
|
|
85
90
|
// FILTER
|
86
91
|
DUCKDB_API shared_ptr<Relation> Filter(const string &expression);
|
92
|
+
DUCKDB_API shared_ptr<Relation> Filter(unique_ptr<ParsedExpression> expression);
|
87
93
|
DUCKDB_API shared_ptr<Relation> Filter(const vector<string> &expressions);
|
88
94
|
|
89
95
|
// LIMIT
|
@@ -92,6 +98,7 @@ public:
|
|
92
98
|
// ORDER
|
93
99
|
DUCKDB_API shared_ptr<Relation> Order(const string &expression);
|
94
100
|
DUCKDB_API shared_ptr<Relation> Order(const vector<string> &expressions);
|
101
|
+
DUCKDB_API shared_ptr<Relation> Order(vector<OrderByNode> expressions);
|
95
102
|
|
96
103
|
// JOIN operation
|
97
104
|
DUCKDB_API shared_ptr<Relation> Join(const shared_ptr<Relation> &other, const string &condition,
|
@@ -25,30 +25,46 @@ class TableCatalogEntry;
|
|
25
25
|
class ViewCatalogEntry;
|
26
26
|
class TypeCatalogEntry;
|
27
27
|
|
28
|
+
//! Regions that require zero-initialization to avoid leaking memory
|
29
|
+
struct UninitializedRegion {
|
30
|
+
idx_t start;
|
31
|
+
idx_t end;
|
32
|
+
};
|
33
|
+
|
34
|
+
//! The current state of a partial block
|
28
35
|
struct PartialBlockState {
|
36
|
+
//! The block id of the partial block
|
29
37
|
block_id_t block_id;
|
30
|
-
//!
|
38
|
+
//! The total bytes that we can assign to this block
|
31
39
|
uint32_t block_size;
|
32
|
-
//!
|
33
|
-
uint32_t
|
34
|
-
//!
|
40
|
+
//! Next allocation offset, and also the current allocation size
|
41
|
+
uint32_t offset;
|
42
|
+
//! The number of times that this block has been used for partial allocations
|
35
43
|
uint32_t block_use_count;
|
36
44
|
};
|
37
45
|
|
38
46
|
struct PartialBlock {
|
39
|
-
|
40
|
-
}
|
47
|
+
PartialBlock(PartialBlockState state, BlockManager &block_manager, const shared_ptr<BlockHandle> &block_handle);
|
41
48
|
virtual ~PartialBlock() {
|
42
49
|
}
|
43
50
|
|
51
|
+
//! The current state of a partial block
|
44
52
|
PartialBlockState state;
|
53
|
+
//! All uninitialized regions on this block, we need to zero-initialize them when flushing
|
54
|
+
vector<UninitializedRegion> uninitialized_regions;
|
55
|
+
//! The block manager of the partial block manager
|
56
|
+
BlockManager &block_manager;
|
57
|
+
//! The block handle of the underlying block that this partial block writes to
|
58
|
+
shared_ptr<BlockHandle> block_handle;
|
45
59
|
|
46
60
|
public:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
61
|
+
//! Add regions that need zero-initialization to avoid leaking memory
|
62
|
+
void AddUninitializedRegion(const idx_t start, const idx_t end);
|
63
|
+
//! Flush the block to disk and zero-initialize any free space and uninitialized regions
|
64
|
+
virtual void Flush(const idx_t free_space_left) = 0;
|
65
|
+
void FlushInternal(const idx_t free_space_left);
|
66
|
+
virtual void Merge(PartialBlock &other, idx_t offset, idx_t other_size) = 0;
|
67
|
+
virtual void Clear() = 0;
|
52
68
|
|
53
69
|
public:
|
54
70
|
template <class TARGET>
|
@@ -59,13 +75,13 @@ public:
|
|
59
75
|
};
|
60
76
|
|
61
77
|
struct PartialBlockAllocation {
|
62
|
-
|
78
|
+
//! The BlockManager owning the block_id
|
63
79
|
BlockManager *block_manager {nullptr};
|
64
|
-
//!
|
80
|
+
//! The number of assigned bytes to the caller
|
65
81
|
uint32_t allocation_size;
|
66
|
-
//!
|
82
|
+
//! The current state of the partial block
|
67
83
|
PartialBlockState state;
|
68
|
-
//! Arbitrary state related to partial block storage
|
84
|
+
//! Arbitrary state related to the partial block storage
|
69
85
|
unique_ptr<PartialBlock> partial_block;
|
70
86
|
};
|
71
87
|
|
@@ -76,12 +92,12 @@ enum class CheckpointType { FULL_CHECKPOINT, APPEND_TO_TABLE };
|
|
76
92
|
//! In any case, they must share a block manager.
|
77
93
|
class PartialBlockManager {
|
78
94
|
public:
|
79
|
-
|
95
|
+
//! 20% free / 80% utilization
|
80
96
|
static constexpr const idx_t DEFAULT_MAX_PARTIAL_BLOCK_SIZE = Storage::BLOCK_SIZE / 5 * 4;
|
81
|
-
|
97
|
+
//! Max number of shared references to a block. No effective limit by default.
|
82
98
|
static constexpr const idx_t DEFAULT_MAX_USE_COUNT = 1u << 20;
|
83
|
-
|
84
|
-
|
99
|
+
//! No point letting map size grow unbounded. We'll drop blocks with the
|
100
|
+
//! least free space first.
|
85
101
|
static constexpr const idx_t MAX_BLOCK_MAP_SIZE = 1u << 31;
|
86
102
|
|
87
103
|
public:
|
@@ -68,37 +68,22 @@ struct PartialBlockForCheckpoint : public PartialBlock {
|
|
68
68
|
};
|
69
69
|
|
70
70
|
public:
|
71
|
-
PartialBlockForCheckpoint(ColumnData &data, ColumnSegment &segment,
|
72
|
-
|
71
|
+
PartialBlockForCheckpoint(ColumnData &data, ColumnSegment &segment, PartialBlockState state,
|
72
|
+
BlockManager &block_manager);
|
73
73
|
~PartialBlockForCheckpoint() override;
|
74
74
|
|
75
75
|
// We will copy all segment data into the memory of the shared block.
|
76
76
|
// Once the block is full (or checkpoint is complete) we'll invoke Flush().
|
77
77
|
// This will cause the block to get written to storage (via BlockManger::ConvertToPersistent),
|
78
78
|
// and all segments to have their references updated (via ColumnSegment::ConvertToPersistent)
|
79
|
-
BlockManager &block_manager;
|
80
|
-
shared_ptr<BlockHandle> block;
|
81
79
|
vector<PartialColumnSegment> segments;
|
82
80
|
|
83
|
-
private:
|
84
|
-
struct UninitializedRegion {
|
85
|
-
idx_t start;
|
86
|
-
idx_t end;
|
87
|
-
};
|
88
|
-
vector<UninitializedRegion> uninitialized_regions;
|
89
|
-
|
90
81
|
public:
|
91
82
|
bool IsFlushed();
|
92
|
-
|
93
|
-
void AddUninitializedRegion(idx_t start, idx_t end) override;
|
94
|
-
|
95
|
-
void Flush(idx_t free_space_left) override;
|
96
|
-
|
97
|
-
void Clear() override;
|
98
|
-
|
83
|
+
void Flush(const idx_t free_space_left) override;
|
99
84
|
void Merge(PartialBlock &other, idx_t offset, idx_t other_size) override;
|
100
|
-
|
101
85
|
void AddSegmentToTail(ColumnData &data, ColumnSegment &segment, uint32_t offset_in_block);
|
86
|
+
void Clear() override;
|
102
87
|
};
|
103
88
|
|
104
89
|
} // namespace duckdb
|
@@ -47,6 +47,11 @@ shared_ptr<Relation> Relation::Project(const vector<string> &expressions) {
|
|
47
47
|
return Project(expressions, aliases);
|
48
48
|
}
|
49
49
|
|
50
|
+
shared_ptr<Relation> Relation::Project(vector<unique_ptr<ParsedExpression>> expressions,
|
51
|
+
const vector<string> &aliases) {
|
52
|
+
return make_shared<ProjectionRelation>(shared_from_this(), std::move(expressions), aliases);
|
53
|
+
}
|
54
|
+
|
50
55
|
static vector<unique_ptr<ParsedExpression>> StringListToExpressionList(ClientContext &context,
|
51
56
|
const vector<string> &expressions) {
|
52
57
|
if (expressions.empty()) {
|
@@ -73,7 +78,11 @@ shared_ptr<Relation> Relation::Filter(const string &expression) {
|
|
73
78
|
if (expression_list.size() != 1) {
|
74
79
|
throw ParserException("Expected a single expression as filter condition");
|
75
80
|
}
|
76
|
-
return
|
81
|
+
return Filter(std::move(expression_list[0]));
|
82
|
+
}
|
83
|
+
|
84
|
+
shared_ptr<Relation> Relation::Filter(unique_ptr<ParsedExpression> expression) {
|
85
|
+
return make_shared<FilterRelation>(shared_from_this(), std::move(expression));
|
77
86
|
}
|
78
87
|
|
79
88
|
shared_ptr<Relation> Relation::Filter(const vector<string> &expressions) {
|
@@ -95,6 +104,10 @@ shared_ptr<Relation> Relation::Limit(int64_t limit, int64_t offset) {
|
|
95
104
|
|
96
105
|
shared_ptr<Relation> Relation::Order(const string &expression) {
|
97
106
|
auto order_list = Parser::ParseOrderList(expression, context.GetContext()->GetParserOptions());
|
107
|
+
return Order(std::move(order_list));
|
108
|
+
}
|
109
|
+
|
110
|
+
shared_ptr<Relation> Relation::Order(vector<OrderByNode> order_list) {
|
98
111
|
return make_shared<OrderRelation>(shared_from_this(), std::move(order_list));
|
99
112
|
}
|
100
113
|
|
@@ -110,7 +123,7 @@ shared_ptr<Relation> Relation::Order(const vector<string> &expressions) {
|
|
110
123
|
}
|
111
124
|
order_list.push_back(std::move(inner_list[0]));
|
112
125
|
}
|
113
|
-
return
|
126
|
+
return Order(std::move(order_list));
|
114
127
|
}
|
115
128
|
|
116
129
|
shared_ptr<Relation> Relation::Join(const shared_ptr<Relation> &other, const string &condition, JoinType type,
|
@@ -2,6 +2,38 @@
|
|
2
2
|
|
3
3
|
namespace duckdb {
|
4
4
|
|
5
|
+
//===--------------------------------------------------------------------===//
|
6
|
+
// PartialBlock
|
7
|
+
//===--------------------------------------------------------------------===//
|
8
|
+
|
9
|
+
PartialBlock::PartialBlock(PartialBlockState state, BlockManager &block_manager,
|
10
|
+
const shared_ptr<BlockHandle> &block_handle)
|
11
|
+
: state(state), block_manager(block_manager), block_handle(block_handle) {
|
12
|
+
}
|
13
|
+
|
14
|
+
void PartialBlock::AddUninitializedRegion(idx_t start, idx_t end) {
|
15
|
+
uninitialized_regions.push_back({start, end});
|
16
|
+
}
|
17
|
+
|
18
|
+
void PartialBlock::FlushInternal(const idx_t free_space_left) {
|
19
|
+
|
20
|
+
// ensure that we do not leak any data
|
21
|
+
if (free_space_left > 0 || !uninitialized_regions.empty()) {
|
22
|
+
auto buffer_handle = block_manager.buffer_manager.Pin(block_handle);
|
23
|
+
|
24
|
+
// memset any uninitialized regions
|
25
|
+
for (auto &uninitialized : uninitialized_regions) {
|
26
|
+
memset(buffer_handle.Ptr() + uninitialized.start, 0, uninitialized.end - uninitialized.start);
|
27
|
+
}
|
28
|
+
// memset any free space at the end of the block to 0 prior to writing to disk
|
29
|
+
memset(buffer_handle.Ptr() + Storage::BLOCK_SIZE - free_space_left, 0, free_space_left);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
//===--------------------------------------------------------------------===//
|
34
|
+
// PartialBlockManager
|
35
|
+
//===--------------------------------------------------------------------===//
|
36
|
+
|
5
37
|
PartialBlockManager::PartialBlockManager(BlockManager &block_manager, CheckpointType checkpoint_type,
|
6
38
|
uint32_t max_partial_block_size, uint32_t max_use_count)
|
7
39
|
: block_manager(block_manager), checkpoint_type(checkpoint_type), max_partial_block_size(max_partial_block_size),
|
@@ -9,9 +41,7 @@ PartialBlockManager::PartialBlockManager(BlockManager &block_manager, Checkpoint
|
|
9
41
|
}
|
10
42
|
PartialBlockManager::~PartialBlockManager() {
|
11
43
|
}
|
12
|
-
|
13
|
-
// Partial Blocks
|
14
|
-
//===--------------------------------------------------------------------===//
|
44
|
+
|
15
45
|
PartialBlockAllocation PartialBlockManager::GetBlockAllocation(uint32_t segment_size) {
|
16
46
|
PartialBlockAllocation allocation;
|
17
47
|
allocation.block_manager = &block_manager;
|
@@ -47,7 +77,7 @@ void PartialBlockManager::AllocateBlock(PartialBlockState &state, uint32_t segme
|
|
47
77
|
state.block_id = INVALID_BLOCK;
|
48
78
|
}
|
49
79
|
state.block_size = Storage::BLOCK_SIZE;
|
50
|
-
state.
|
80
|
+
state.offset = 0;
|
51
81
|
state.block_use_count = 1;
|
52
82
|
}
|
53
83
|
|
@@ -60,21 +90,22 @@ bool PartialBlockManager::GetPartialBlock(idx_t segment_size, unique_ptr<Partial
|
|
60
90
|
partial_block = std::move(entry->second);
|
61
91
|
partially_filled_blocks.erase(entry);
|
62
92
|
|
63
|
-
D_ASSERT(partial_block->state.
|
64
|
-
D_ASSERT(ValueIsAligned(partial_block->state.
|
93
|
+
D_ASSERT(partial_block->state.offset > 0);
|
94
|
+
D_ASSERT(ValueIsAligned(partial_block->state.offset));
|
65
95
|
return true;
|
66
96
|
}
|
67
97
|
|
68
98
|
void PartialBlockManager::RegisterPartialBlock(PartialBlockAllocation &&allocation) {
|
69
99
|
auto &state = allocation.partial_block->state;
|
100
|
+
D_ASSERT(checkpoint_type != CheckpointType::FULL_CHECKPOINT || state.block_id >= 0);
|
70
101
|
if (state.block_use_count < max_use_count) {
|
71
|
-
auto unaligned_size = allocation.allocation_size + state.
|
102
|
+
auto unaligned_size = allocation.allocation_size + state.offset;
|
72
103
|
auto new_size = AlignValue(unaligned_size);
|
73
104
|
if (new_size != unaligned_size) {
|
74
105
|
// register the uninitialized region so we can correctly initialize it before writing to disk
|
75
106
|
allocation.partial_block->AddUninitializedRegion(unaligned_size, new_size);
|
76
107
|
}
|
77
|
-
state.
|
108
|
+
state.offset = new_size;
|
78
109
|
auto new_space_left = state.block_size - new_size;
|
79
110
|
// check if the block is STILL partially filled after adding the segment_size
|
80
111
|
if (new_space_left >= Storage::BLOCK_SIZE - max_partial_block_size) {
|
@@ -82,7 +113,7 @@ void PartialBlockManager::RegisterPartialBlock(PartialBlockAllocation &&allocati
|
|
82
113
|
partially_filled_blocks.insert(make_pair(new_space_left, std::move(allocation.partial_block)));
|
83
114
|
}
|
84
115
|
}
|
85
|
-
idx_t free_space = state.block_size - state.
|
116
|
+
idx_t free_space = state.block_size - state.offset;
|
86
117
|
auto block_to_free = std::move(allocation.partial_block);
|
87
118
|
if (!block_to_free && partially_filled_blocks.size() > MAX_BLOCK_MAP_SIZE) {
|
88
119
|
// Free the page with the least space free.
|
@@ -98,10 +129,6 @@ void PartialBlockManager::RegisterPartialBlock(PartialBlockAllocation &&allocati
|
|
98
129
|
}
|
99
130
|
}
|
100
131
|
|
101
|
-
void PartialBlock::Merge(PartialBlock &other, idx_t offset, idx_t other_size) {
|
102
|
-
throw InternalException("PartialBlock::Merge not implemented for this block type");
|
103
|
-
}
|
104
|
-
|
105
132
|
void PartialBlockManager::Merge(PartialBlockManager &other) {
|
106
133
|
if (&other == this) {
|
107
134
|
throw InternalException("Cannot merge into itself");
|
@@ -117,10 +144,10 @@ void PartialBlockManager::Merge(PartialBlockManager &other) {
|
|
117
144
|
// we can merge this block into an existing block - merge them
|
118
145
|
// merge blocks
|
119
146
|
auto allocation = GetBlockAllocation(used_space);
|
120
|
-
allocation.partial_block->Merge(*e.second, allocation.state.
|
147
|
+
allocation.partial_block->Merge(*e.second, allocation.state.offset, used_space);
|
121
148
|
|
122
149
|
// re-register the partial block
|
123
|
-
allocation.state.
|
150
|
+
allocation.state.offset += used_space;
|
124
151
|
RegisterPartialBlock(std::move(allocation));
|
125
152
|
} else {
|
126
153
|
// we cannot merge this block - append it directly to the current block manager
|
@@ -22,9 +22,9 @@ unique_ptr<BaseStatistics> ColumnCheckpointState::GetStatistics() {
|
|
22
22
|
return std::move(global_stats);
|
23
23
|
}
|
24
24
|
|
25
|
-
PartialBlockForCheckpoint::PartialBlockForCheckpoint(ColumnData &data, ColumnSegment &segment,
|
26
|
-
BlockManager &block_manager
|
27
|
-
: PartialBlock(state
|
25
|
+
PartialBlockForCheckpoint::PartialBlockForCheckpoint(ColumnData &data, ColumnSegment &segment, PartialBlockState state,
|
26
|
+
BlockManager &block_manager)
|
27
|
+
: PartialBlock(state, block_manager, segment.block) {
|
28
28
|
AddSegmentToTail(data, segment, 0);
|
29
29
|
}
|
30
30
|
|
@@ -37,24 +37,15 @@ bool PartialBlockForCheckpoint::IsFlushed() {
|
|
37
37
|
return segments.empty();
|
38
38
|
}
|
39
39
|
|
40
|
-
void PartialBlockForCheckpoint::
|
41
|
-
uninitialized_regions.push_back({start, end});
|
42
|
-
}
|
40
|
+
void PartialBlockForCheckpoint::Flush(const idx_t free_space_left) {
|
43
41
|
|
44
|
-
void PartialBlockForCheckpoint::Flush(idx_t free_space_left) {
|
45
42
|
if (IsFlushed()) {
|
46
43
|
throw InternalException("Flush called on partial block that was already flushed");
|
47
44
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
for (auto &uninitialized : uninitialized_regions) {
|
53
|
-
memset(handle.Ptr() + uninitialized.start, 0, uninitialized.end - uninitialized.start);
|
54
|
-
}
|
55
|
-
// memset any free space at the end of the block to 0 prior to writing to disk
|
56
|
-
memset(handle.Ptr() + Storage::BLOCK_SIZE - free_space_left, 0, free_space_left);
|
57
|
-
}
|
45
|
+
|
46
|
+
// zero-initialize unused memory
|
47
|
+
FlushInternal(free_space_left);
|
48
|
+
|
58
49
|
// At this point, we've already copied all data from tail_segments
|
59
50
|
// into the page owned by first_segment. We flush all segment data to
|
60
51
|
// disk with the following call.
|
@@ -63,6 +54,7 @@ void PartialBlockForCheckpoint::Flush(idx_t free_space_left) {
|
|
63
54
|
if (fetch_new_block) {
|
64
55
|
state.block_id = block_manager.GetFreeBlockId();
|
65
56
|
}
|
57
|
+
|
66
58
|
for (idx_t i = 0; i < segments.size(); i++) {
|
67
59
|
auto &segment = segments[i];
|
68
60
|
segment.data.IncrementVersion();
|
@@ -71,23 +63,18 @@ void PartialBlockForCheckpoint::Flush(idx_t free_space_left) {
|
|
71
63
|
D_ASSERT(segment.offset_in_block == 0);
|
72
64
|
segment.segment.ConvertToPersistent(&block_manager, state.block_id);
|
73
65
|
// update the block after it has been converted to a persistent segment
|
74
|
-
|
66
|
+
block_handle = segment.segment.block;
|
75
67
|
} else {
|
76
68
|
// subsequent segments are MARKED as persistent - they don't need to be rewritten
|
77
|
-
segment.segment.MarkAsPersistent(
|
69
|
+
segment.segment.MarkAsPersistent(block_handle, segment.offset_in_block);
|
78
70
|
if (fetch_new_block) {
|
79
71
|
// if we fetched a new block we need to increase the reference count to the block
|
80
72
|
block_manager.IncreaseBlockReferenceCount(state.block_id);
|
81
73
|
}
|
82
74
|
}
|
83
75
|
}
|
84
|
-
Clear();
|
85
|
-
}
|
86
76
|
|
87
|
-
|
88
|
-
uninitialized_regions.clear();
|
89
|
-
block.reset();
|
90
|
-
segments.clear();
|
77
|
+
Clear();
|
91
78
|
}
|
92
79
|
|
93
80
|
void PartialBlockForCheckpoint::Merge(PartialBlock &other_p, idx_t offset, idx_t other_size) {
|
@@ -95,13 +82,13 @@ void PartialBlockForCheckpoint::Merge(PartialBlock &other_p, idx_t offset, idx_t
|
|
95
82
|
|
96
83
|
auto &buffer_manager = block_manager.buffer_manager;
|
97
84
|
// pin the source block
|
98
|
-
auto old_handle = buffer_manager.Pin(other.
|
85
|
+
auto old_handle = buffer_manager.Pin(other.block_handle);
|
99
86
|
// pin the target block
|
100
|
-
auto new_handle = buffer_manager.Pin(
|
87
|
+
auto new_handle = buffer_manager.Pin(block_handle);
|
101
88
|
// memcpy the contents of the old block to the new block
|
102
89
|
memcpy(new_handle.Ptr() + offset, old_handle.Ptr(), other_size);
|
103
90
|
|
104
|
-
// now copy over all
|
91
|
+
// now copy over all segments to the new block
|
105
92
|
// move over the uninitialized regions
|
106
93
|
for (auto ®ion : other.uninitialized_regions) {
|
107
94
|
region.start += offset;
|
@@ -113,6 +100,7 @@ void PartialBlockForCheckpoint::Merge(PartialBlock &other_p, idx_t offset, idx_t
|
|
113
100
|
for (auto &segment : other.segments) {
|
114
101
|
AddSegmentToTail(segment.data, segment.segment, segment.offset_in_block + offset);
|
115
102
|
}
|
103
|
+
|
116
104
|
other.Clear();
|
117
105
|
}
|
118
106
|
|
@@ -120,6 +108,12 @@ void PartialBlockForCheckpoint::AddSegmentToTail(ColumnData &data, ColumnSegment
|
|
120
108
|
segments.emplace_back(data, segment, offset_in_block);
|
121
109
|
}
|
122
110
|
|
111
|
+
void PartialBlockForCheckpoint::Clear() {
|
112
|
+
uninitialized_regions.clear();
|
113
|
+
block_handle.reset();
|
114
|
+
segments.clear();
|
115
|
+
}
|
116
|
+
|
123
117
|
void ColumnCheckpointState::FlushSegment(unique_ptr<ColumnSegment> segment, idx_t segment_size) {
|
124
118
|
D_ASSERT(segment_size <= Storage::BLOCK_SIZE);
|
125
119
|
auto tuple_count = segment->count.load();
|
@@ -140,7 +134,7 @@ void ColumnCheckpointState::FlushSegment(unique_ptr<ColumnSegment> segment, idx_
|
|
140
134
|
// non-constant block
|
141
135
|
PartialBlockAllocation allocation = partial_block_manager.GetBlockAllocation(segment_size);
|
142
136
|
block_id = allocation.state.block_id;
|
143
|
-
offset_in_block = allocation.state.
|
137
|
+
offset_in_block = allocation.state.offset;
|
144
138
|
|
145
139
|
if (allocation.partial_block) {
|
146
140
|
// Use an existing block.
|
@@ -149,7 +143,7 @@ void ColumnCheckpointState::FlushSegment(unique_ptr<ColumnSegment> segment, idx_
|
|
149
143
|
// pin the source block
|
150
144
|
auto old_handle = buffer_manager.Pin(segment->block);
|
151
145
|
// pin the target block
|
152
|
-
auto new_handle = buffer_manager.Pin(pstate.
|
146
|
+
auto new_handle = buffer_manager.Pin(pstate.block_handle);
|
153
147
|
// memcpy the contents of the old block to the new block
|
154
148
|
memcpy(new_handle.Ptr() + offset_in_block, old_handle.Ptr(), segment_size);
|
155
149
|
pstate.AddSegmentToTail(column_data, *segment, offset_in_block);
|
@@ -162,8 +156,8 @@ void ColumnCheckpointState::FlushSegment(unique_ptr<ColumnSegment> segment, idx_
|
|
162
156
|
segment->Resize(Storage::BLOCK_SIZE);
|
163
157
|
}
|
164
158
|
D_ASSERT(offset_in_block == 0);
|
165
|
-
allocation.partial_block = make_uniq<PartialBlockForCheckpoint>(
|
166
|
-
|
159
|
+
allocation.partial_block = make_uniq<PartialBlockForCheckpoint>(column_data, *segment, allocation.state,
|
160
|
+
*allocation.block_manager);
|
167
161
|
}
|
168
162
|
// Writer will decide whether to reuse this block.
|
169
163
|
partial_block_manager.RegisterPartialBlock(std::move(allocation));
|