| 594 | |
| 595 | |
| 596 | static void appendBlock(LoggerPtr log, const Block & from, Block & to) |
| 597 | { |
| 598 | size_t rows = from.rows(); |
| 599 | size_t old_bytes = to.bytes(); |
| 600 | |
| 601 | if (to.empty()) |
| 602 | to = from.cloneEmpty(); |
| 603 | |
| 604 | assertBlocksHaveEqualStructure(from, to, "Buffer"); |
| 605 | |
| 606 | from.checkNumberOfRows(); |
| 607 | to.checkNumberOfRows(); |
| 608 | |
| 609 | /// Take checkpoints of all destination columns before any modifications |
| 610 | /// to be able to rollback in case of an exception in the middle of insertion. |
| 611 | ColumnCheckpoints checkpoints; |
| 612 | checkpoints.reserve(to.columns()); |
| 613 | for (size_t column_no = 0; column_no < to.columns(); ++column_no) |
| 614 | checkpoints.push_back(to.getByPosition(column_no).column->getCheckpoint()); |
| 615 | |
| 616 | MutableColumnPtr last_col; |
| 617 | size_t mutated_columns = 0; |
| 618 | try |
| 619 | { |
| 620 | MemoryTrackerBlockerInThread temporarily_disable_memory_tracker; |
| 621 | |
| 622 | for (size_t column_no = 0, columns = to.columns(); column_no < columns; ++column_no) |
| 623 | { |
| 624 | const IColumn & col_from = *from.getByPosition(column_no).column.get(); |
| 625 | { |
| 626 | /// Usually IColumn::mutate() here will simply move pointers, |
| 627 | /// however in case of parallel reading from it via SELECT, it |
| 628 | /// is possible for the full IColumn::clone() here, and in this |
| 629 | /// case it may fail due to MEMORY_LIMIT_EXCEEDED, and this |
| 630 | /// breaks the rollback, since the column got lost, it is |
| 631 | /// neither in last_col nor in "to" block. |
| 632 | /// |
| 633 | /// The safest option here, is to do a full clone every time, |
| 634 | /// however, it is overhead. And it looks like the only |
| 635 | /// exception that is possible here is MEMORY_LIMIT_EXCEEDED, |
| 636 | /// and it is better to simply suppress it, to avoid overhead |
| 637 | /// for every INSERT into Buffer (Anyway we have a |
| 638 | /// LOGICAL_ERROR in rollback that will bail if something else |
| 639 | /// will happens here). |
| 640 | LockMemoryExceptionInThread temporarily_ignore_any_memory_limits(VariableContext::Global); |
| 641 | last_col = IColumn::mutate(std::move(to.getByPosition(column_no).column)); |
| 642 | } |
| 643 | ++mutated_columns; |
| 644 | |
| 645 | /// In case of ColumnAggregateFunction aggregate states will |
| 646 | /// be allocated from the query context but can be destroyed from the |
| 647 | /// server context (in case of background flush), and thus memory |
| 648 | /// will be leaked from the query, but only tracked memory, not |
| 649 | /// memory itself. |
| 650 | /// |
| 651 | /// To avoid this, prohibit sharing the aggregate states. |
| 652 | last_col->ensureOwnership(); |
| 653 | last_col->insertRangeFrom(col_from, 0, rows); |
no test coverage detected