forked from UWQuickstep/quickstep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSplitRowStoreTupleStorageSubBlock.hpp
430 lines (355 loc) · 16 KB
/
SplitRowStoreTupleStorageSubBlock.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
**/
#ifndef QUICKSTEP_STORAGE_SPLIT_ROW_STORE_TUPLE_STORAGE_SUB_BLOCK_HPP_
#define QUICKSTEP_STORAGE_SPLIT_ROW_STORE_TUPLE_STORAGE_SUB_BLOCK_HPP_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "expressions/predicate/PredicateCost.hpp"
#include "storage/SubBlockTypeRegistryMacros.hpp"
#include "storage/TupleIdSequence.hpp"
#include "storage/TupleStorageSubBlock.hpp"
#include "types/TypedValue.hpp"
#include "utility/BitVector.hpp"
#include "utility/Macros.hpp"
#include "gtest/gtest_prod.h"
namespace quickstep {
class ComparisonPredicate;
class Tuple;
class TupleStorageSubBlockDescription;
class ValueAccessor;
QUICKSTEP_DECLARE_SUB_BLOCK_TYPE_REGISTERED(SplitRowStoreTupleStorageSubBlock);
namespace splitrow_internal {
// A CopyGroup contains information about one run of attributes in the source
// ValueAccessor that can be copied into the output block. The
// getCopyGroupsForAttributeMap function below takes an attribute map for a source
// and converts it into a sequence of runs. The goal is to minimize the number
// of memcpy calls and address calculations that occur during bulk insertion.
// Contiguous attributes from a rowstore source can be merged into a single copy group.
//
// A single ContiguousAttrs CopyGroup consists of contiguous attributes, nullable
// or not. "Contiguous" here means that their attribute IDs are successive in both
// the source and destination relations.
//
// A NullAttr refers to exactly one nullable attribute. Nullable columns are
// represented using fixed length inline data as well as a null bitmap.
// In a particular tuple, if the attribute has a null value, the inline data
// has no meaning. So it is safe to copy it or not. We use this fact to merge
// runs together aggressively, i.e., a ContiguousAttrs group may include a
// nullable attribute. However, we also create a NullableAttr in that case in
// order to check the null bitmap.
//
// A gap is a run of destination (output) attributes that don't come from a
// particular source. This occurs during bulkInsertPartialTuples. They must be
// skipped during the insert (not copied over). They are indicated by a
// kInvalidCatalogId in the attribute map. For efficiency, the gap size
// is merged into the bytes_to_advance of previous ContiguousAttrs copy group.
// For gaps at the start of the attribute map, we just create a ContiguousAttrs
// copy group with 0 bytes to copy and dummy (0) source attribute id.
//
// eg. For 4B integer attrs, from a row store source,
// if the input attribute_map is {-1,0,5,6,7,-1,2,4,9,10,-1}
// with input/output attributes 4 and 7 being nullable,
// we will create the following ContiguousAttrs copy groups
//
// ----------------------------------------------------
// |src_id |bytes_to_advance |bytes_to_copy |
// |-------------|-----------------|------------------|
// | 0| 4| 4|
// | 5| 4| 12|
// | 2| 16| 4|
// | 4| 4| 4|
// | 9| 4| 8|
// ----------------------------------------------------
// and two NullableAttrs with src_attr_id set to 4 and 7.
//
// In this example, we do 6 memcpy calls and 6 address calculations
// as well as 2 bitvector lookups for each tuple. A naive copy algorithm
// would do 11 memcpy calls and address calculations, along with the
// bitvector lookups, not to mention the schema lookups,
// all interspersed in a complex loop with lots of branches.
//
// If the source was a column store, then we can't merge contiguous
// attributes (or gaps). So we would have 11 ContigousAttrs copy groups with
// three of them having bytes_to_copy = 0 (corresponding to the gaps) and
// the rest having bytes_to_copy = 4.
//
// SplitRowStore supports variable length attributes. Since the layout of the
// tuple is like: [null bitmap][fixed length attributes][variable length offsets]
// we do all the variable length copies after the fixed length copies.
//
struct CopyGroup {
explicit CopyGroup(const attribute_id source_attr_id)
: src_attr_id(source_attr_id) {}
// The id of starting input attribute for run.
const attribute_id src_attr_id;
};
struct ContiguousAttrs : public CopyGroup {
ContiguousAttrs(const attribute_id source_attr_id,
const std::size_t bytes_to_copy_in,
const std::size_t bytes_to_advance_in)
: CopyGroup(source_attr_id),
bytes_to_advance(bytes_to_advance_in),
bytes_to_copy(bytes_to_copy_in) {}
// Number of bytes to advance destination ptr to get to the location where we
// copy THIS attribute.
std::size_t bytes_to_advance;
// Number of bytes to copy from source.
std::size_t bytes_to_copy;
};
struct VarLenAttr : public CopyGroup {
VarLenAttr(const attribute_id source_attr_id,
const attribute_id destination_attr_id,
const std::size_t bytes_to_advance_in)
: CopyGroup(source_attr_id),
dst_attr_id(destination_attr_id),
bytes_to_advance(bytes_to_advance_in) {}
const attribute_id dst_attr_id;
std::size_t bytes_to_advance;
};
struct NullableAttr : public CopyGroup {
NullableAttr(const attribute_id source_attr_id,
const std::size_t nullable_attr_idx_in)
: CopyGroup(source_attr_id),
nullable_attr_idx(nullable_attr_idx_in) {}
// Index into null bitmap.
const std::size_t nullable_attr_idx;
};
struct CopyGroupList {
CopyGroupList() {}
/**
* @brief Attributes which are exactly sequential are merged to a single copy.
*/
void mergeContiguous() {
if (contiguous_attrs.size() < 2) {
return;
}
int add_to_advance = 0;
std::vector<ContiguousAttrs> merged_attrs;
merged_attrs.emplace_back(contiguous_attrs.front());
for (std::size_t idx = 1; idx < contiguous_attrs.size(); ++idx) {
const ContiguousAttrs ¤t_attr = contiguous_attrs[idx];
const ContiguousAttrs &previous_attr = contiguous_attrs[idx - 1];
if (previous_attr.src_attr_id + 1 == current_attr.src_attr_id &&
previous_attr.bytes_to_copy == current_attr.bytes_to_advance) {
merged_attrs.back().bytes_to_copy += current_attr.bytes_to_copy;
add_to_advance += current_attr.bytes_to_advance;
} else {
merged_attrs.emplace_back(current_attr.src_attr_id,
current_attr.bytes_to_copy,
current_attr.bytes_to_advance + add_to_advance);
add_to_advance = 0;
}
}
contiguous_attrs = std::move(merged_attrs);
if (varlen_attrs.size() > 0) {
varlen_attrs[0].bytes_to_advance += add_to_advance;
}
}
std::vector<ContiguousAttrs> contiguous_attrs;
std::vector<NullableAttr> nullable_attrs;
std::vector<VarLenAttr> varlen_attrs;
};
} // namespace splitrow_internal
/** \addtogroup Storage
* @{
*/
/**
* @brief An implementation of TupleStorageSubBlock as a slotted row-store that
* splits storage between an array of slots for fixed-length attributes
* at the front of the block and a region of storage for variable-length
* attributes at the back of the block that both grow towards each
* other.
* @note Deletions and updates can cause both the slot array and the
* variable-length storage region to become fragmented. Otherwise wasted
* storage can be reclaimed by calling rebuild().
**/
class SplitRowStoreTupleStorageSubBlock: public TupleStorageSubBlock {
static const std::size_t kVarLenSlotSize;
public:
SplitRowStoreTupleStorageSubBlock(const CatalogRelationSchema &relation,
const TupleStorageSubBlockDescription &description,
const bool new_block,
void *sub_block_memory,
const std::size_t sub_block_memory_size);
~SplitRowStoreTupleStorageSubBlock() override {
}
/**
* @brief Determine whether a TupleStorageSubBlockDescription is valid for
* this type of TupleStorageSubBlock.
*
* @param relation The relation a tuple store described by description would
* belong to.
* @param description A description of the parameters for this type of
* TupleStorageSubBlock, which will be checked for validity.
* @return Whether description is well-formed and valid for this type of
* TupleStorageSubBlock belonging to relation (i.e. whether a
* TupleStorageSubBlock of this type, belonging to relation, can be
* constructed according to description).
**/
static bool DescriptionIsValid(const CatalogRelationSchema &relation,
const TupleStorageSubBlockDescription &description);
/**
* @brief Estimate the average number of bytes (including any applicable
* overhead) used to store a single tuple in this type of
* TupleStorageSubBlock. Used by StorageBlockLayout::finalize() to
* divide block memory amongst sub-blocks.
* @warning description must be valid. DescriptionIsValid() should be called
* first if necessary.
*
* @param relation The relation tuples belong to.
* @param description A description of the parameters for this type of
* TupleStorageSubBlock.
* @return The average/ammortized number of bytes used to store a single
* tuple of relation in a TupleStorageSubBlock of this type described
* by description.
**/
static std::size_t EstimateBytesPerTuple(const CatalogRelationSchema &relation,
const TupleStorageSubBlockDescription &description);
bool supportsUntypedGetAttributeValue(const attribute_id attr) const override {
return true;
}
bool supportsAdHocInsert() const override {
return true;
}
bool adHocInsertIsEfficient() const override {
return true;
}
TupleStorageSubBlockType getTupleStorageSubBlockType() const override {
return kSplitRowStore;
}
bool isEmpty() const override {
return header_->num_tuples == 0;
}
bool isPacked() const override {
return header_->num_tuples == header_->max_tid + 1;
}
tuple_id getMaxTupleID() const override {
return header_->max_tid;
}
tuple_id numTuples() const override {
return header_->num_tuples;
}
bool hasTupleWithID(const tuple_id tuple) const override {
return (tuple >= 0)
&& (static_cast<std::size_t>(tuple) < occupancy_bitmap_->size())
&& occupancy_bitmap_->getBit(tuple);
}
InsertResult insertTuple(const Tuple &tuple) override;
inline bool insertTupleInBatch(const Tuple &tuple) override {
const InsertResult result = insertTuple(tuple);
return (result.inserted_id >= 0);
}
tuple_id bulkInsertTuples(ValueAccessor *accessor) override;
tuple_id bulkInsertTuplesWithRemappedAttributes(
const std::vector<attribute_id> &attribute_map,
ValueAccessor *accessor) override;
tuple_id bulkInsertPartialTuples(
const std::vector<attribute_id> &attribute_map,
ValueAccessor *accessor,
const tuple_id max_num_tuples_to_insert) override;
void bulkInsertPartialTuplesFinalize(const tuple_id num_tuples_inserted) override;
const void* getAttributeValue(const tuple_id tuple,
const attribute_id attr) const override;
TypedValue getAttributeValueTyped(const tuple_id tuple,
const attribute_id attr) const override;
ValueAccessor* createValueAccessor(
const TupleIdSequence *sequence = nullptr) const override;
bool canSetAttributeValuesInPlaceTyped(
const tuple_id tuple,
const std::unordered_map<attribute_id, TypedValue> &new_values) const override;
void setAttributeValueInPlaceTyped(const tuple_id tuple,
const attribute_id attr,
const TypedValue &value) override;
bool deleteTuple(const tuple_id tuple) override;
bool bulkDeleteTuples(TupleIdSequence *tuples) override;
TupleIdSequence* getExistenceMap() const override {
return new TupleIdSequence(header_->max_tid + 1, *occupancy_bitmap_);
}
OrderedTupleIdSequence* getExistenceList() const override;
predicate_cost_t estimatePredicateEvaluationCost(
const ComparisonPredicate &predicate) const override {
return predicate_cost::kRowScan;
}
void rebuild() override;
bool isInsertOrderPreserving() const override {
return true;
}
private:
struct Header {
tuple_id num_tuples;
tuple_id max_tid;
std::uint32_t variable_length_bytes_allocated;
bool variable_length_storage_compact;
};
inline bool spaceToInsert(const tuple_id position,
const std::size_t variable_length_bytes) const {
return (header_->max_tid + 1) * tuple_slot_bytes_ // Already allocated slots
+ (position == header_->max_tid + 1) * tuple_slot_bytes_ // An extra slot if needed
+ header_->variable_length_bytes_allocated // Already allocated variable storage.
+ variable_length_bytes // Extra needed variable storage.
<= tuple_storage_bytes_;
}
template <bool nullable_attrs, bool variable_length_attrs>
InsertResult insertTupleImpl(const Tuple &tuple);
template<bool copy_nulls, bool copy_varlen, bool fill_to_capacity>
tuple_id bulkInsertPartialTuplesImpl(
const splitrow_internal::CopyGroupList ©_groups,
ValueAccessor *accessor,
std::size_t max_num_tuples_to_insert);
tuple_id bulkInsertDispatcher(
const std::vector<attribute_id> &attribute_map,
ValueAccessor *accessor,
tuple_id max_num_tuples_to_insert,
bool finalize);
void getCopyGroupsForAttributeMap(
const std::vector<attribute_id> &attribute_map,
splitrow_internal::CopyGroupList *copy_groups);
std::size_t getInsertLowerBound() const;
// When varlen attributes are bulk inserted, the difference between the maximum
// possible size and the actual size of the tuples will cause an underestimate of
// the number of tuples we can insert. This threshold puts a limit on the number
// of tuples to attempt to insert. A smaller number will give more rounds of insertion
// and a more-packed block, but at the cost of insertion speed.
std::size_t getInsertLowerBoundThreshold() const {
return 10;
}
Header *header_;
std::unique_ptr<BitVector<false>> occupancy_bitmap_;
std::size_t occupancy_bitmap_bytes_;
void *tuple_storage_;
std::size_t tuple_storage_bytes_;
std::size_t tuple_slot_bytes_;
std::vector<std::size_t> fixed_len_attr_sizes_;
std::size_t num_null_attrs_;
std::size_t num_fixed_attrs_;
std::size_t num_var_attrs_;
std::size_t per_tuple_null_bitmap_bytes_;
friend class SplitRowStoreTupleStorageSubBlockTest;
friend class SplitRowStoreValueAccessor;
FRIEND_TEST(SplitRowStoreTupleStorageSubBlockTest, InitializeTest);
FRIEND_TEST(SplitRowStoreTupleStorageSubBlockTest, GetCopyGroupsForAttributeMapTest);
DISALLOW_COPY_AND_ASSIGN(SplitRowStoreTupleStorageSubBlock);
};
/** @} */
} // namespace quickstep
#endif // QUICKSTEP_STORAGE_SPLIT_ROW_STORE_TUPLE_STORAGE_SUB_BLOCK_HPP_