@@ -115,7 +115,8 @@ class InnerDatabaseFile : public SQLiteVFS::File {
115
115
// integrity check: page indices are sequential [1..page_count_]
116
116
sqlite3_int64 pagenosum =
117
117
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 );
119
120
}
120
121
121
122
int FileSize (sqlite3_int64 *pSize) override {
@@ -453,24 +454,29 @@ class InnerDatabaseFile : public SQLiteVFS::File {
453
454
}
454
455
}
455
456
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" ));
473
471
}
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 ;
474
480
}
475
481
476
482
int Read (void *zBuf, int iAmt, sqlite3_int64 iOfst) override {
@@ -500,11 +506,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
500
506
for (sqlite3_int64 pageno = first_page; pageno <= last_page; ++pageno) {
501
507
int page_ofs = sofar == 0 ? iOfst % page_size_ : 0 ;
502
508
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)) {
508
510
bool partial = page_ofs != 0 || desired != page_size_;
509
511
void *dest = (char *)zBuf + sofar;
510
512
Read1Page ((partial ? (pagebuf.resize (page_size_), pagebuf.data ()) : dest),
@@ -692,6 +694,20 @@ class InnerDatabaseFile : public SQLiteVFS::File {
692
694
throw std::runtime_error (" unexpected result from page upsert" );
693
695
}
694
696
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
+
695
711
{
696
712
// return to buffer pool
697
713
std::lock_guard<std::mutex> lock (encode_job_pool_mutex_);
0 commit comments