| 1 | #include "duckdb/storage/segment/uncompressed.hpp" |
| 2 | #include "duckdb/storage/buffer_manager.hpp" |
| 3 | #include "duckdb/common/types/vector.hpp" |
| 4 | #include "duckdb/storage/table/append_state.hpp" |
| 5 | |
| 6 | #include "duckdb/common/types/null_value.hpp" |
| 7 | #include "duckdb/storage/table/column_segment.hpp" |
| 8 | #include "duckdb/function/compression_function.hpp" |
| 9 | #include "duckdb/storage/table/scan_state.hpp" |
| 10 | |
| 11 | namespace duckdb { |
| 12 | |
| 13 | //===--------------------------------------------------------------------===// |
| 14 | // Mask constants |
| 15 | //===--------------------------------------------------------------------===// |
| 16 | // LOWER_MASKS contains masks with all the lower bits set until a specific value |
| 17 | // LOWER_MASKS[0] has the 0 lowest bits set, i.e.: |
| 18 | // 0b0000000000000000000000000000000000000000000000000000000000000000, |
| 19 | // LOWER_MASKS[10] has the 10 lowest bits set, i.e.: |
| 20 | // 0b0000000000000000000000000000000000000000000000000000000111111111, |
| 21 | // etc... |
| 22 | // 0b0000000000000000000000000000000000000001111111111111111111111111, |
| 23 | // ... |
| 24 | // 0b0000000000000000000001111111111111111111111111111111111111111111, |
| 25 | // until LOWER_MASKS[64], which has all bits set: |
| 26 | // 0b1111111111111111111111111111111111111111111111111111111111111111 |
| 27 | // generated with this python snippet: |
| 28 | // for i in range(65): |
| 29 | // print(hex(int((64 - i) * '0' + i * '1', 2)) + ",") |
| 30 | const validity_t ValidityUncompressed::LOWER_MASKS[] = {0x0, |
| 31 | 0x1, |
| 32 | 0x3, |
| 33 | 0x7, |
| 34 | 0xf, |
| 35 | 0x1f, |
| 36 | 0x3f, |
| 37 | 0x7f, |
| 38 | 0xff, |
| 39 | 0x1ff, |
| 40 | 0x3ff, |
| 41 | 0x7ff, |
| 42 | 0xfff, |
| 43 | 0x1fff, |
| 44 | 0x3fff, |
| 45 | 0x7fff, |
| 46 | 0xffff, |
| 47 | 0x1ffff, |
| 48 | 0x3ffff, |
| 49 | 0x7ffff, |
| 50 | 0xfffff, |
| 51 | 0x1fffff, |
| 52 | 0x3fffff, |
| 53 | 0x7fffff, |
| 54 | 0xffffff, |
| 55 | 0x1ffffff, |
| 56 | 0x3ffffff, |
| 57 | 0x7ffffff, |
| 58 | 0xfffffff, |
| 59 | 0x1fffffff, |
| 60 | 0x3fffffff, |
| 61 | 0x7fffffff, |
| 62 | 0xffffffff, |
| 63 | 0x1ffffffff, |
| 64 | 0x3ffffffff, |
| 65 | 0x7ffffffff, |
| 66 | 0xfffffffff, |
| 67 | 0x1fffffffff, |
| 68 | 0x3fffffffff, |
| 69 | 0x7fffffffff, |
| 70 | 0xffffffffff, |
| 71 | 0x1ffffffffff, |
| 72 | 0x3ffffffffff, |
| 73 | 0x7ffffffffff, |
| 74 | 0xfffffffffff, |
| 75 | 0x1fffffffffff, |
| 76 | 0x3fffffffffff, |
| 77 | 0x7fffffffffff, |
| 78 | 0xffffffffffff, |
| 79 | 0x1ffffffffffff, |
| 80 | 0x3ffffffffffff, |
| 81 | 0x7ffffffffffff, |
| 82 | 0xfffffffffffff, |
| 83 | 0x1fffffffffffff, |
| 84 | 0x3fffffffffffff, |
| 85 | 0x7fffffffffffff, |
| 86 | 0xffffffffffffff, |
| 87 | 0x1ffffffffffffff, |
| 88 | 0x3ffffffffffffff, |
| 89 | 0x7ffffffffffffff, |
| 90 | 0xfffffffffffffff, |
| 91 | 0x1fffffffffffffff, |
| 92 | 0x3fffffffffffffff, |
| 93 | 0x7fffffffffffffff, |
| 94 | 0xffffffffffffffff}; |
| 95 | |
| 96 | // UPPER_MASKS contains masks with all the highest bits set until a specific value |
| 97 | // UPPER_MASKS[0] has the 0 highest bits set, i.e.: |
| 98 | // 0b0000000000000000000000000000000000000000000000000000000000000000, |
| 99 | // UPPER_MASKS[10] has the 10 highest bits set, i.e.: |
| 100 | // 0b1111111111110000000000000000000000000000000000000000000000000000, |
| 101 | // etc... |
| 102 | // 0b1111111111111111111111110000000000000000000000000000000000000000, |
| 103 | // ... |
| 104 | // 0b1111111111111111111111111111111111111110000000000000000000000000, |
| 105 | // until UPPER_MASKS[64], which has all bits set: |
| 106 | // 0b1111111111111111111111111111111111111111111111111111111111111111 |
| 107 | // generated with this python snippet: |
| 108 | // for i in range(65): |
| 109 | // print(hex(int(i * '1' + (64 - i) * '0', 2)) + ",") |
| 110 | const validity_t ValidityUncompressed::UPPER_MASKS[] = {0x0, |
| 111 | 0x8000000000000000, |
| 112 | 0xc000000000000000, |
| 113 | 0xe000000000000000, |
| 114 | 0xf000000000000000, |
| 115 | 0xf800000000000000, |
| 116 | 0xfc00000000000000, |
| 117 | 0xfe00000000000000, |
| 118 | 0xff00000000000000, |
| 119 | 0xff80000000000000, |
| 120 | 0xffc0000000000000, |
| 121 | 0xffe0000000000000, |
| 122 | 0xfff0000000000000, |
| 123 | 0xfff8000000000000, |
| 124 | 0xfffc000000000000, |
| 125 | 0xfffe000000000000, |
| 126 | 0xffff000000000000, |
| 127 | 0xffff800000000000, |
| 128 | 0xffffc00000000000, |
| 129 | 0xffffe00000000000, |
| 130 | 0xfffff00000000000, |
| 131 | 0xfffff80000000000, |
| 132 | 0xfffffc0000000000, |
| 133 | 0xfffffe0000000000, |
| 134 | 0xffffff0000000000, |
| 135 | 0xffffff8000000000, |
| 136 | 0xffffffc000000000, |
| 137 | 0xffffffe000000000, |
| 138 | 0xfffffff000000000, |
| 139 | 0xfffffff800000000, |
| 140 | 0xfffffffc00000000, |
| 141 | 0xfffffffe00000000, |
| 142 | 0xffffffff00000000, |
| 143 | 0xffffffff80000000, |
| 144 | 0xffffffffc0000000, |
| 145 | 0xffffffffe0000000, |
| 146 | 0xfffffffff0000000, |
| 147 | 0xfffffffff8000000, |
| 148 | 0xfffffffffc000000, |
| 149 | 0xfffffffffe000000, |
| 150 | 0xffffffffff000000, |
| 151 | 0xffffffffff800000, |
| 152 | 0xffffffffffc00000, |
| 153 | 0xffffffffffe00000, |
| 154 | 0xfffffffffff00000, |
| 155 | 0xfffffffffff80000, |
| 156 | 0xfffffffffffc0000, |
| 157 | 0xfffffffffffe0000, |
| 158 | 0xffffffffffff0000, |
| 159 | 0xffffffffffff8000, |
| 160 | 0xffffffffffffc000, |
| 161 | 0xffffffffffffe000, |
| 162 | 0xfffffffffffff000, |
| 163 | 0xfffffffffffff800, |
| 164 | 0xfffffffffffffc00, |
| 165 | 0xfffffffffffffe00, |
| 166 | 0xffffffffffffff00, |
| 167 | 0xffffffffffffff80, |
| 168 | 0xffffffffffffffc0, |
| 169 | 0xffffffffffffffe0, |
| 170 | 0xfffffffffffffff0, |
| 171 | 0xfffffffffffffff8, |
| 172 | 0xfffffffffffffffc, |
| 173 | 0xfffffffffffffffe, |
| 174 | 0xffffffffffffffff}; |
| 175 | |
| 176 | //===--------------------------------------------------------------------===// |
| 177 | // Analyze |
| 178 | //===--------------------------------------------------------------------===// |
| 179 | struct ValidityAnalyzeState : public AnalyzeState { |
| 180 | ValidityAnalyzeState() : count(0) { |
| 181 | } |
| 182 | |
| 183 | idx_t count; |
| 184 | }; |
| 185 | |
| 186 | unique_ptr<AnalyzeState> ValidityInitAnalyze(ColumnData &col_data, PhysicalType type) { |
| 187 | return make_uniq<ValidityAnalyzeState>(); |
| 188 | } |
| 189 | |
| 190 | bool ValidityAnalyze(AnalyzeState &state_p, Vector &input, idx_t count) { |
| 191 | auto &state = state_p.Cast<ValidityAnalyzeState>(); |
| 192 | state.count += count; |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | idx_t ValidityFinalAnalyze(AnalyzeState &state_p) { |
| 197 | auto &state = state_p.Cast<ValidityAnalyzeState>(); |
| 198 | return (state.count + 7) / 8; |
| 199 | } |
| 200 | |
| 201 | //===--------------------------------------------------------------------===// |
| 202 | // Scan |
| 203 | //===--------------------------------------------------------------------===// |
| 204 | struct ValidityScanState : public SegmentScanState { |
| 205 | BufferHandle handle; |
| 206 | block_id_t block_id; |
| 207 | }; |
| 208 | |
| 209 | unique_ptr<SegmentScanState> ValidityInitScan(ColumnSegment &segment) { |
| 210 | auto result = make_uniq<ValidityScanState>(); |
| 211 | auto &buffer_manager = BufferManager::GetBufferManager(db&: segment.db); |
| 212 | result->handle = buffer_manager.Pin(handle&: segment.block); |
| 213 | result->block_id = segment.block->BlockId(); |
| 214 | return std::move(result); |
| 215 | } |
| 216 | |
| 217 | //===--------------------------------------------------------------------===// |
| 218 | // Scan base data |
| 219 | //===--------------------------------------------------------------------===// |
| 220 | void ValidityScanPartial(ColumnSegment &segment, ColumnScanState &state, idx_t scan_count, Vector &result, |
| 221 | idx_t result_offset) { |
| 222 | auto start = segment.GetRelativeIndex(row_index: state.row_index); |
| 223 | |
| 224 | static_assert(sizeof(validity_t) == sizeof(uint64_t), "validity_t should be 64-bit" ); |
| 225 | auto &scan_state = state.scan_state->Cast<ValidityScanState>(); |
| 226 | |
| 227 | auto &result_mask = FlatVector::Validity(vector&: result); |
| 228 | auto buffer_ptr = scan_state.handle.Ptr() + segment.GetBlockOffset(); |
| 229 | D_ASSERT(scan_state.block_id == segment.block->BlockId()); |
| 230 | auto input_data = reinterpret_cast<validity_t *>(buffer_ptr); |
| 231 | |
| 232 | #ifdef DEBUG |
| 233 | // this method relies on all the bits we are going to write to being set to valid |
| 234 | for (idx_t i = 0; i < scan_count; i++) { |
| 235 | D_ASSERT(result_mask.RowIsValid(result_offset + i)); |
| 236 | } |
| 237 | #endif |
| 238 | #if STANDARD_VECTOR_SIZE < 128 |
| 239 | // fallback for tiny vector sizes |
| 240 | // the bitwise ops we use below don't work if the vector size is too small |
| 241 | ValidityMask source_mask(input_data); |
| 242 | for (idx_t i = 0; i < scan_count; i++) { |
| 243 | if (!source_mask.RowIsValid(start + i)) { |
| 244 | if (result_mask.AllValid()) { |
| 245 | result_mask.Initialize(MaxValue<idx_t>(STANDARD_VECTOR_SIZE, result_offset + scan_count)); |
| 246 | } |
| 247 | result_mask.SetInvalid(result_offset + i); |
| 248 | } |
| 249 | } |
| 250 | #else |
| 251 | // the code below does what the fallback code above states, but using bitwise ops: |
| 252 | auto result_data = (validity_t *)result_mask.GetData(); |
| 253 | |
| 254 | // set up the initial positions |
| 255 | // we need to find the validity_entry to modify, together with the bit-index WITHIN the validity entry |
| 256 | idx_t result_entry = result_offset / ValidityMask::BITS_PER_VALUE; |
| 257 | idx_t result_idx = result_offset - result_entry * ValidityMask::BITS_PER_VALUE; |
| 258 | |
| 259 | // same for the input: find the validity_entry we are pulling from, together with the bit-index WITHIN that entry |
| 260 | idx_t input_entry = start / ValidityMask::BITS_PER_VALUE; |
| 261 | idx_t input_idx = start - input_entry * ValidityMask::BITS_PER_VALUE; |
| 262 | |
| 263 | // now start the bit games |
| 264 | idx_t pos = 0; |
| 265 | while (pos < scan_count) { |
| 266 | // these are the current validity entries we are dealing with |
| 267 | idx_t current_result_idx = result_entry; |
| 268 | idx_t offset; |
| 269 | validity_t input_mask = input_data[input_entry]; |
| 270 | |
| 271 | // construct the mask to AND together with the result |
| 272 | if (result_idx < input_idx) { |
| 273 | // we have to shift the input RIGHT if the result_idx is smaller than the input_idx |
| 274 | auto shift_amount = input_idx - result_idx; |
| 275 | D_ASSERT(shift_amount > 0 && shift_amount <= ValidityMask::BITS_PER_VALUE); |
| 276 | |
| 277 | input_mask = input_mask >> shift_amount; |
| 278 | |
| 279 | // now the upper "shift_amount" bits are set to 0 |
| 280 | // we need them to be set to 1 |
| 281 | // otherwise the subsequent bitwise & will modify values outside of the range of values we want to alter |
| 282 | input_mask |= ValidityUncompressed::UPPER_MASKS[shift_amount]; |
| 283 | |
| 284 | // after this, we move to the next input_entry |
| 285 | offset = ValidityMask::BITS_PER_VALUE - input_idx; |
| 286 | input_entry++; |
| 287 | input_idx = 0; |
| 288 | result_idx += offset; |
| 289 | } else if (result_idx > input_idx) { |
| 290 | // we have to shift the input LEFT if the result_idx is bigger than the input_idx |
| 291 | auto shift_amount = result_idx - input_idx; |
| 292 | D_ASSERT(shift_amount > 0 && shift_amount <= ValidityMask::BITS_PER_VALUE); |
| 293 | |
| 294 | // to avoid overflows, we set the upper "shift_amount" values to 0 first |
| 295 | input_mask = (input_mask & ~ValidityUncompressed::UPPER_MASKS[shift_amount]) << shift_amount; |
| 296 | |
| 297 | // now the lower "shift_amount" bits are set to 0 |
| 298 | // we need them to be set to 1 |
| 299 | // otherwise the subsequent bitwise & will modify values outside of the range of values we want to alter |
| 300 | input_mask |= ValidityUncompressed::LOWER_MASKS[shift_amount]; |
| 301 | |
| 302 | // after this, we move to the next result_entry |
| 303 | offset = ValidityMask::BITS_PER_VALUE - result_idx; |
| 304 | result_entry++; |
| 305 | result_idx = 0; |
| 306 | input_idx += offset; |
| 307 | } else { |
| 308 | // if the input_idx is equal to result_idx they are already aligned |
| 309 | // we just move to the next entry for both after this |
| 310 | offset = ValidityMask::BITS_PER_VALUE - result_idx; |
| 311 | input_entry++; |
| 312 | result_entry++; |
| 313 | result_idx = input_idx = 0; |
| 314 | } |
| 315 | // now we need to check if we should include the ENTIRE mask |
| 316 | // OR if we need to mask from the right side |
| 317 | pos += offset; |
| 318 | if (pos > scan_count) { |
| 319 | // we need to set any bits that are past the scan_count on the right-side to 1 |
| 320 | // this is required so we don't influence any bits that are not part of the scan |
| 321 | input_mask |= ValidityUncompressed::UPPER_MASKS[pos - scan_count]; |
| 322 | } |
| 323 | // now finally we can merge the input mask with the result mask |
| 324 | if (input_mask != ValidityMask::ValidityBuffer::MAX_ENTRY) { |
| 325 | if (!result_data) { |
| 326 | result_mask.Initialize(count: MaxValue<idx_t>(STANDARD_VECTOR_SIZE, b: result_offset + scan_count)); |
| 327 | result_data = (validity_t *)result_mask.GetData(); |
| 328 | } |
| 329 | result_data[current_result_idx] &= input_mask; |
| 330 | } |
| 331 | } |
| 332 | #endif |
| 333 | |
| 334 | #ifdef DEBUG |
| 335 | // verify that we actually accomplished the bitwise ops equivalent that we wanted to do |
| 336 | ValidityMask input_mask(input_data); |
| 337 | for (idx_t i = 0; i < scan_count; i++) { |
| 338 | D_ASSERT(result_mask.RowIsValid(result_offset + i) == input_mask.RowIsValid(start + i)); |
| 339 | } |
| 340 | #endif |
| 341 | } |
| 342 | |
| 343 | void ValidityScan(ColumnSegment &segment, ColumnScanState &state, idx_t scan_count, Vector &result) { |
| 344 | result.Flatten(count: scan_count); |
| 345 | |
| 346 | auto start = segment.GetRelativeIndex(row_index: state.row_index); |
| 347 | if (start % ValidityMask::BITS_PER_VALUE == 0) { |
| 348 | auto &scan_state = state.scan_state->Cast<ValidityScanState>(); |
| 349 | |
| 350 | // aligned scan: no need to do anything fancy |
| 351 | // note: this is only an optimization which avoids having to do messy bitshifting in the common case |
| 352 | // it is not required for correctness |
| 353 | auto &result_mask = FlatVector::Validity(vector&: result); |
| 354 | auto buffer_ptr = scan_state.handle.Ptr() + segment.GetBlockOffset(); |
| 355 | D_ASSERT(scan_state.block_id == segment.block->BlockId()); |
| 356 | auto input_data = reinterpret_cast<validity_t *>(buffer_ptr); |
| 357 | auto result_data = result_mask.GetData(); |
| 358 | idx_t start_offset = start / ValidityMask::BITS_PER_VALUE; |
| 359 | idx_t entry_scan_count = (scan_count + ValidityMask::BITS_PER_VALUE - 1) / ValidityMask::BITS_PER_VALUE; |
| 360 | for (idx_t i = 0; i < entry_scan_count; i++) { |
| 361 | auto input_entry = input_data[start_offset + i]; |
| 362 | if (!result_data && input_entry == ValidityMask::ValidityBuffer::MAX_ENTRY) { |
| 363 | continue; |
| 364 | } |
| 365 | if (!result_data) { |
| 366 | result_mask.Initialize(count: MaxValue<idx_t>(STANDARD_VECTOR_SIZE, b: scan_count)); |
| 367 | result_data = result_mask.GetData(); |
| 368 | } |
| 369 | result_data[i] = input_entry; |
| 370 | } |
| 371 | } else { |
| 372 | // unaligned scan: fall back to scan_partial which does bitshift tricks |
| 373 | ValidityScanPartial(segment, state, scan_count, result, result_offset: 0); |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | //===--------------------------------------------------------------------===// |
| 378 | // Fetch |
| 379 | //===--------------------------------------------------------------------===// |
| 380 | void ValidityFetchRow(ColumnSegment &segment, ColumnFetchState &state, row_t row_id, Vector &result, idx_t result_idx) { |
| 381 | D_ASSERT(row_id >= 0 && row_id < row_t(segment.count)); |
| 382 | auto &buffer_manager = BufferManager::GetBufferManager(db&: segment.db); |
| 383 | auto handle = buffer_manager.Pin(handle&: segment.block); |
| 384 | auto dataptr = handle.Ptr() + segment.GetBlockOffset(); |
| 385 | ValidityMask mask(reinterpret_cast<validity_t *>(dataptr)); |
| 386 | auto &result_mask = FlatVector::Validity(vector&: result); |
| 387 | if (!mask.RowIsValidUnsafe(row_idx: row_id)) { |
| 388 | result_mask.SetInvalid(result_idx); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | //===--------------------------------------------------------------------===// |
| 393 | // Append |
| 394 | //===--------------------------------------------------------------------===// |
| 395 | static unique_ptr<CompressionAppendState> ValidityInitAppend(ColumnSegment &segment) { |
| 396 | auto &buffer_manager = BufferManager::GetBufferManager(db&: segment.db); |
| 397 | auto handle = buffer_manager.Pin(handle&: segment.block); |
| 398 | return make_uniq<CompressionAppendState>(args: std::move(handle)); |
| 399 | } |
| 400 | |
| 401 | unique_ptr<CompressedSegmentState> ValidityInitSegment(ColumnSegment &segment, block_id_t block_id) { |
| 402 | auto &buffer_manager = BufferManager::GetBufferManager(db&: segment.db); |
| 403 | if (block_id == INVALID_BLOCK) { |
| 404 | auto handle = buffer_manager.Pin(handle&: segment.block); |
| 405 | memset(s: handle.Ptr(), c: 0xFF, n: segment.SegmentSize()); |
| 406 | } |
| 407 | return nullptr; |
| 408 | } |
| 409 | |
| 410 | idx_t ValidityAppend(CompressionAppendState &append_state, ColumnSegment &segment, SegmentStatistics &stats, |
| 411 | UnifiedVectorFormat &data, idx_t offset, idx_t vcount) { |
| 412 | D_ASSERT(segment.GetBlockOffset() == 0); |
| 413 | auto &validity_stats = stats.statistics; |
| 414 | |
| 415 | auto max_tuples = segment.SegmentSize() / ValidityMask::STANDARD_MASK_SIZE * STANDARD_VECTOR_SIZE; |
| 416 | idx_t append_count = MinValue<idx_t>(a: vcount, b: max_tuples - segment.count); |
| 417 | if (data.validity.AllValid()) { |
| 418 | // no null values: skip append |
| 419 | segment.count += append_count; |
| 420 | validity_stats.SetHasNoNull(); |
| 421 | return append_count; |
| 422 | } |
| 423 | |
| 424 | ValidityMask mask(reinterpret_cast<validity_t *>(append_state.handle.Ptr())); |
| 425 | for (idx_t i = 0; i < append_count; i++) { |
| 426 | auto idx = data.sel->get_index(idx: offset + i); |
| 427 | if (!data.validity.RowIsValidUnsafe(row_idx: idx)) { |
| 428 | mask.SetInvalidUnsafe(segment.count + i); |
| 429 | validity_stats.SetHasNull(); |
| 430 | } else { |
| 431 | validity_stats.SetHasNoNull(); |
| 432 | } |
| 433 | } |
| 434 | segment.count += append_count; |
| 435 | return append_count; |
| 436 | } |
| 437 | |
| 438 | idx_t ValidityFinalizeAppend(ColumnSegment &segment, SegmentStatistics &stats) { |
| 439 | return ((segment.count + STANDARD_VECTOR_SIZE - 1) / STANDARD_VECTOR_SIZE) * ValidityMask::STANDARD_MASK_SIZE; |
| 440 | } |
| 441 | |
| 442 | void ValidityRevertAppend(ColumnSegment &segment, idx_t start_row) { |
| 443 | idx_t start_bit = start_row - segment.start; |
| 444 | |
| 445 | auto &buffer_manager = BufferManager::GetBufferManager(db&: segment.db); |
| 446 | auto handle = buffer_manager.Pin(handle&: segment.block); |
| 447 | idx_t revert_start; |
| 448 | if (start_bit % 8 != 0) { |
| 449 | // handle sub-bit stuff (yay) |
| 450 | idx_t byte_pos = start_bit / 8; |
| 451 | idx_t bit_start = byte_pos * 8; |
| 452 | idx_t bit_end = (byte_pos + 1) * 8; |
| 453 | ValidityMask mask(reinterpret_cast<validity_t *>(handle.Ptr() + byte_pos)); |
| 454 | for (idx_t i = start_bit; i < bit_end; i++) { |
| 455 | mask.SetValid(i - bit_start); |
| 456 | } |
| 457 | revert_start = bit_end / 8; |
| 458 | } else { |
| 459 | revert_start = start_bit / 8; |
| 460 | } |
| 461 | // for the rest, we just memset |
| 462 | memset(s: handle.Ptr() + revert_start, c: 0xFF, n: segment.SegmentSize() - revert_start); |
| 463 | } |
| 464 | |
| 465 | //===--------------------------------------------------------------------===// |
| 466 | // Get Function |
| 467 | //===--------------------------------------------------------------------===// |
| 468 | CompressionFunction ValidityUncompressed::GetFunction(PhysicalType data_type) { |
| 469 | D_ASSERT(data_type == PhysicalType::BIT); |
| 470 | return CompressionFunction(CompressionType::COMPRESSION_UNCOMPRESSED, data_type, ValidityInitAnalyze, |
| 471 | ValidityAnalyze, ValidityFinalAnalyze, UncompressedFunctions::InitCompression, |
| 472 | UncompressedFunctions::Compress, UncompressedFunctions::FinalizeCompress, |
| 473 | ValidityInitScan, ValidityScan, ValidityScanPartial, ValidityFetchRow, |
| 474 | UncompressedFunctions::EmptySkip, ValidityInitSegment, ValidityInitAppend, |
| 475 | ValidityAppend, ValidityFinalizeAppend, ValidityRevertAppend); |
| 476 | } |
| 477 | |
| 478 | } // namespace duckdb |
| 479 | |