Skip to content

Commit dadf93e

Browse files
authored
rework optimization for db header reads (#5)
The old strategy suffered reliability issues for reasons not fully determined.
1 parent 8a64b1a commit dadf93e

File tree

1 file changed

+39
-23
lines changed

1 file changed

+39
-23
lines changed

src/SQLiteNestedVFS.h

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ class InnerDatabaseFile : public SQLiteVFS::File {
115115
// integrity check: page indices are sequential [1..page_count_]
116116
sqlite3_int64 pagenosum =
117117
outer_db_->execAndGet("SELECT SUM(pageno) FROM " + inner_db_pages_table_).getInt64();
118-
return pagenosum == page_count_ * (page_count_ + 1) / 2;
118+
return pagenosum == page_count_ * (page_count_ + 1) / 2 ||
119+
(page1plain_ && pagenosum == page_count_ * (page_count_ + 1) / 2 - 100);
119120
}
120121

121122
int FileSize(sqlite3_int64 *pSize) override {
@@ -453,24 +454,29 @@ class InnerDatabaseFile : public SQLiteVFS::File {
453454
}
454455
}
455456

456-
void ReadPlainPage1(void *zBuf, int iAmt, int iOfst) {
457-
assert(page1plain_ && iOfst + iAmt <= page_size_);
458-
sqlite3_blob *pBlob = nullptr;
459-
int rc = sqlite3_blob_open(outer_db_->getHandle(), "main", inner_db_pages_table_.c_str(),
460-
"data", 1, 0, &pBlob);
461-
if (rc != SQLITE_OK) {
462-
throw SQLite::Exception("couldn't open page 1 blob", rc);
463-
}
464-
assert(sqlite3_blob_bytes(pBlob) == page_size_);
465-
rc = sqlite3_blob_read(pBlob, zBuf, iAmt, iOfst);
466-
if (rc != SQLITE_OK) {
467-
sqlite3_blob_close(pBlob);
468-
throw SQLite::Exception("couldn't read page 1 blob", rc);
469-
}
470-
rc = sqlite3_blob_close(pBlob);
471-
if (rc != SQLITE_OK) {
472-
throw SQLite::Exception("couldn't close page 1 blob", rc);
457+
std::unique_ptr<SQLite::Statement> read_header_;
458+
bool ReadPlainPage1(void *zBuf, int iAmt, int iOfst) {
459+
// Optimization for frequent reads of database header fields (within the first 100 bytes of
460+
// page 1). To avoid frequent reading/copying of the up-to-64KiB page, we keep an
461+
// up-to-date copy of its first 100 bytes in a special row of the pages table with
462+
// pageno = -100. Then we can fetch it with less overhead here. This applies only if page 1
463+
// is stored "plaintext" (page1plain_ which may be set false by a subclass). The special
464+
// row is kept up-to-date in ExecuteUpsert, below.
465+
if (!page1plain_ || iOfst + iAmt > 100)
466+
return false;
467+
PrefetchBarrier();
468+
if (!read_header_) {
469+
read_header_.reset(new SQLite::Statement(
470+
*outer_db_, "SELECT data FROM " + inner_db_pages_table_ + " WHERE pageno = -100"));
473471
}
472+
StatementResetter resetter(*read_header_);
473+
if (!read_header_->executeStep())
474+
return false;
475+
SQLite::Column data = read_header_->getColumn(0);
476+
if (!data.isBlob() || iOfst + iAmt > data.getBytes())
477+
return false;
478+
memcpy(zBuf, ((char *)data.getBlob()) + iOfst, iAmt);
479+
return true;
474480
}
475481

476482
int Read(void *zBuf, int iAmt, sqlite3_int64 iOfst) override {
@@ -500,11 +506,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
500506
for (sqlite3_int64 pageno = first_page; pageno <= last_page; ++pageno) {
501507
int page_ofs = sofar == 0 ? iOfst % page_size_ : 0;
502508
int desired = std::min(iAmt - sofar, (int)page_size_ - page_ofs);
503-
if (pageno == 1 && page1plain_) {
504-
// optimized read of "plaintext" page 1, possibly including non-aligned
505-
// reads (SQLite reading just the header or version number)
506-
ReadPlainPage1(zBuf, desired, page_ofs);
507-
} else {
509+
if (pageno != 1 || !ReadPlainPage1(zBuf, desired, page_ofs)) {
508510
bool partial = page_ofs != 0 || desired != page_size_;
509511
void *dest = (char *)zBuf + sofar;
510512
Read1Page((partial ? (pagebuf.resize(page_size_), pagebuf.data()) : dest),
@@ -692,6 +694,20 @@ class InnerDatabaseFile : public SQLiteVFS::File {
692694
throw std::runtime_error("unexpected result from page upsert");
693695
}
694696

697+
if (page1plain_ && job->pageno == 1) {
698+
// see ReadPlainPage1() above. When we write page 1, cc its first 100 bytes into a
699+
// special row with pageno = -100.
700+
upsert->reset();
701+
upsert->bindNoCopy(1, job->encoded_page,
702+
std::min(job->encoded_page_size, size_t(100)));
703+
upsert->bind(2, job->meta1);
704+
upsert->bind(3, job->meta2);
705+
upsert->bind(4, (sqlite_int64)-100);
706+
if (upsert->exec() != 1) {
707+
throw std::runtime_error("unexpected result from header upsert");
708+
}
709+
}
710+
695711
{
696712
// return to buffer pool
697713
std::lock_guard<std::mutex> lock(encode_job_pool_mutex_);

0 commit comments

Comments
 (0)