Skip to content

Commit eadb758

Browse files
committed
pageno filter for btree interior index
1 parent 6a4f445 commit eadb758

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ include_directories(${sqlitecpp_SOURCE_DIR}/include)
2929
FetchContent_Declare(
3030
sqlite_web_vfs
3131
GIT_REPOSITORY https://github.com/mlin/sqlite_web_vfs.git
32-
GIT_TAG 45d5383
32+
GIT_TAG 892c5c6
3333
)
3434
FetchContent_MakeAvailable(sqlite_web_vfs)
3535
FetchContent_MakeAvailable(concurrentqueue)

src/SQLiteNestedVFS.h

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
177177
std::vector<char> decodebuf; // otherwise decode into this buffer (background fetch)
178178

179179
SQLite::Statement cursor; // outer db cursor
180-
std::unique_ptr<SQLite::Statement> btree_interior_cursor;
180+
std::unique_ptr<SQLite::Statement> btree_interior_cursor, btree_interior_pageno_cursor;
181181
sqlite3_int64 cursor_pageno = 0; // if >0 then cursor is currently on this page
182182
unsigned long long last_op = 0; // "time" of the cursor's last use
183183
bool was_sequential = false;
@@ -199,12 +199,16 @@ class InnerDatabaseFile : public SQLiteVFS::File {
199199
PutState(State::NEW);
200200
// prepare to read from btree interior index in web mode only. it can be
201201
// counterproductive locally due to its big index keys => low fan-out
202-
if (that.web_ && !that.btree_interior_index_.empty()) {
202+
if (!that.btree_interior_index_.empty()) {
203203
btree_interior_cursor.reset(new SQLite::Statement(
204204
*(that.outer_db_), "SELECT pageno, data, meta1, meta2 FROM " +
205205
that.inner_db_pages_table_ + " INDEXED BY " +
206206
that.btree_interior_index_ +
207207
" WHERE btree_interior AND pageno = ?"));
208+
btree_interior_pageno_cursor.reset(new SQLite::Statement(
209+
*(that.outer_db_), "SELECT 1 FROM " + that.inner_db_pages_table_ +
210+
" INDEXED BY " + that.btree_interior_index_ +
211+
"_pageno WHERE btree_interior AND pageno = ?"));
208212
}
209213
}
210214

@@ -251,10 +255,23 @@ class InnerDatabaseFile : public SQLiteVFS::File {
251255
non_sequential++;
252256
#endif
253257

254-
// first try the covering index of interior btree pages
255-
if (btree_interior_cursor &&
256-
(btree_interior_cursor->reset(), btree_interior_cursor->bind(1, pageno),
257-
btree_interior_cursor->executeStep())) {
258+
// First try the index of interior btree pages. The covering index is somewhat
259+
// costly to search due to its large btree keys (=> low fan-out). Therefore, we
260+
// first search the pageno-only index so that we can skip the costlier lookup
261+
// in the common case that it'd fail anyway (like a bloom filter, but exact).
262+
if (btree_interior_pageno_cursor &&
263+
(btree_interior_pageno_cursor->reset(),
264+
btree_interior_pageno_cursor->bind(1, pageno),
265+
btree_interior_pageno_cursor->executeStep())) {
266+
assert(btree_interior_cursor);
267+
btree_interior_cursor->reset();
268+
btree_interior_cursor->bind(1, pageno);
269+
if (!btree_interior_cursor->executeStep()) {
270+
// impossible
271+
throw SQLite::Exception("inconsistent btree interior index, page " +
272+
std::to_string(pageno),
273+
SQLITE_CORRUPT);
274+
}
258275
btree_interior = true;
259276
#ifndef NDEBUG
260277
interior_hits++;
@@ -1093,7 +1110,7 @@ class InnerDatabaseFile : public SQLiteVFS::File {
10931110
if (outer_db_
10941111
->execAndGet(
10951112
"SELECT count(1) FROM sqlite_master WHERE type = 'index' AND name = '" +
1096-
btree_interior_index_ + "'")
1113+
btree_interior_index_ + "_pageno'")
10971114
.getInt() != 1) {
10981115
btree_interior_index_.clear();
10991116
}
@@ -1160,6 +1177,15 @@ class VFS : public SQLiteVFS::Wrapper {
11601177
"pages_btree_interior ON " + inner_db_tablename_prefix_ +
11611178
"pages (pageno,data,meta1,meta2) WHERE btree_interior")
11621179
.executeStep();
1180+
// redundant second index with only the page numbers. the covering index is somewhat
1181+
// costly to search due to its large keys => low fan-out. therefore, we first search
1182+
// the pageno-only index so that we can skip the costlier lookup in the common case
1183+
// that it'd fail anyway (like a bloom filter, but exact).
1184+
SQLite::Statement(db, "CREATE INDEX " + inner_db_tablename_prefix_ +
1185+
"pages_btree_interior_pageno ON " +
1186+
inner_db_tablename_prefix_ +
1187+
"pages (pageno) WHERE btree_interior")
1188+
.executeStep();
11631189
}
11641190
}
11651191

test/test.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def test_web():
332332
# compressed db served from GitHub Releases. if we change the db schema then we'll need to
333333
# update accordingly (with a version generated locally by TPC-H.py)
334334
DB_URL = (
335-
"https://github.com/mlin/sqlite_zstd_vfs/releases/download/web-test-db-v2/TPC-H.zstd.db"
335+
"https://github.com/mlin/sqlite_zstd_vfs/releases/download/web-test-db-v3/TPC-H.zstd.db"
336336
)
337337
con = sqlite3.connect(f":memory:")
338338
con.enable_load_extension(True)
@@ -367,3 +367,5 @@ def test_web():
367367
)
368368
print(results)
369369
sys.stdout.flush()
370+
371+
# TODO: verify that btree interior index was used (currently, watch stderr for "interior: K")

0 commit comments

Comments
 (0)