blob: 0029ff92952c7cf4aa24bc208988ae521e9ffa84 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sql/recover_module/cursor.h"
#include "base/containers/span.h"
#include "sql/recover_module/table.h"
namespace sql {
namespace recover {
VirtualCursor::VirtualCursor(VirtualTable* table)
: table_(table),
db_reader_(table),
payload_reader_(&db_reader_),
record_reader_(&payload_reader_, table->column_specs().size()) {
DCHECK(table_ != nullptr);
}
VirtualCursor::~VirtualCursor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
table_->WillDeleteCursor(this);
}
int VirtualCursor::First() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
inner_decoders_.clear();
leaf_decoder_ = nullptr;
AppendPageDecoder(table_->root_page_id());
return Next();
}
int VirtualCursor::Next() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
record_reader_.Reset();
while (!inner_decoders_.empty() || leaf_decoder_.get()) {
if (leaf_decoder_.get()) {
if (!leaf_decoder_->CanAdvance()) {
// The leaf has been exhausted. Remove it from the DFS stack.
leaf_decoder_ = nullptr;
continue;
}
if (!leaf_decoder_->TryAdvance())
continue;
if (!payload_reader_.Initialize(leaf_decoder_->last_record_size(),
leaf_decoder_->last_record_offset())) {
continue;
}
if (!record_reader_.Initialize())
continue;
// Found a healthy record.
if (!IsAcceptableRecord()) {
record_reader_.Reset();
continue;
}
return SQLITE_OK;
}
// Try advancing the bottom-most inner node.
DCHECK(!inner_decoders_.empty());
InnerPageDecoder* inner_decoder = inner_decoders_.back().get();
if (!inner_decoder->CanAdvance()) {
// The inner node's sub-tree has been visited. Remove from the DFS stack.
inner_decoders_.pop_back();
continue;
}
int next_page_id = inner_decoder->TryAdvance();
if (next_page_id == DatabasePageReader::kInvalidPageId)
continue;
AppendPageDecoder(next_page_id);
}
// The cursor reached the end of the table.
return SQLITE_OK;
}
int VirtualCursor::ReadColumn(int column_index,
sqlite3_context* result_context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(column_index, 0);
DCHECK_LT(column_index, static_cast<int>(table_->column_specs().size()));
DCHECK(record_reader_.IsInitialized());
if (table_->column_specs()[column_index].type == ModuleColumnType::kRowId) {
sqlite3_result_int64(result_context, RowId());
return SQLITE_OK;
}
if (record_reader_.ReadValue(column_index, result_context))
return SQLITE_OK;
return SQLITE_ERROR;
}
int64_t VirtualCursor::RowId() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(record_reader_.IsInitialized());
DCHECK(leaf_decoder_.get());
return leaf_decoder_->last_record_rowid();
}
void VirtualCursor::AppendPageDecoder(int page_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(leaf_decoder_.get() == nullptr)
<< __func__
<< " must only be called when the current path has no leaf decoder";
if (db_reader_.ReadPage(page_id) != SQLITE_OK)
return;
if (LeafPageDecoder::IsOnValidPage(&db_reader_)) {
leaf_decoder_ = std::make_unique<LeafPageDecoder>(&db_reader_);
return;
}
if (InnerPageDecoder::IsOnValidPage(&db_reader_)) {
// Detect cycles.
for (const auto& decoder : inner_decoders_) {
if (decoder->page_id() == page_id)
return;
}
// Give up on overly deep tree branches.
//
// SQLite supports up to 2^31 pages. SQLite ensures that inner nodes can
// hold at least 4 child pointers, even in the presence of very large keys.
// So, even poorly balanced trees should not exceed 100 nodes in depth.
// InnerPageDecoder instances take up 32 bytes on 64-bit platforms.
//
// The depth limit below balances recovering broken trees with avoiding
// excessive memory consumption.
constexpr int kMaxTreeDepth = 10000;
if (inner_decoders_.size() == kMaxTreeDepth)
return;
inner_decoders_.emplace_back(
std::make_unique<InnerPageDecoder>(&db_reader_));
return;
}
}
bool VirtualCursor::IsAcceptableRecord() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(record_reader_.IsInitialized());
const std::vector<RecoveredColumnSpec>& column_specs = table_->column_specs();
const int column_count = static_cast<int>(column_specs.size());
for (int column_index = 0; column_index < column_count; ++column_index) {
ValueType value_type = record_reader_.GetValueType(column_index);
if (!column_specs[column_index].IsAcceptableValue(value_type))
return false;
}
return true;
}
} // namespace recover
} // namespace sql