From 8ca4540e2de3fbf670c4f30dfb6107a6ec8f17dd Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 22 May 2026 15:34:38 -0700 Subject: [PATCH] Optimize variable-size single row replacements --- src/prolly_mutate.c | 14 +++++++++----- test/invariant_test.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/prolly_mutate.c b/src/prolly_mutate.c index d1df20c837..4cf6b81830 100644 --- a/src/prolly_mutate.c +++ b/src/prolly_mutate.c @@ -663,13 +663,11 @@ static int tryInsertOrReplaceSingleNoRechunk(ProllyMutator *pMut){ const u8 *pOldVal; int nOldVal; ProllyNodeBuilder b; + int sameSize; int i; prollyNodeValue(pLeaf, idx, &pOldVal, &nOldVal); - if( nOldVal!=pEdit->nVal ){ - prollyCursorClose(&cur); - return SQLITE_NOTFOUND; - } + sameSize = (nOldVal==pEdit->nVal); prollyNodeBuilderInit(&b, 0, pMut->flags); for(i=0; i<(int)pLeaf->nItems; i++){ @@ -685,7 +683,13 @@ static int tryInsertOrReplaceSingleNoRechunk(ProllyMutator *pMut){ return rc; } } - rc = writeBuilderNode(pMut->pStore, &b, &childHash); + if( sameSize ){ + rc = writeBuilderNode(pMut->pStore, &b, &childHash); + }else{ + rc = finishAndWriteBuilderNode(pMut->pStore, &b, + !cursorLeafIsRightmost(&cur), + &childHash); + } prollyNodeBuilderFree(&b); if( rc!=SQLITE_OK ){ prollyCursorClose(&cur); diff --git a/test/invariant_test.c b/test/invariant_test.c index f8159609c0..75a98d58a5 100644 --- a/test/invariant_test.c +++ b/test/invariant_test.c @@ -959,6 +959,35 @@ static void test_single_row_mutation_fast_path(void){ check("single_mutation_integrity", strcmp(queryScalarText(db, "PRAGMA integrity_check"), "ok")==0); + execSql(db, "CREATE TABLE tblob(id BLOB PRIMARY KEY, val TEXT, pad TEXT)"); + execSql(db, "BEGIN"); + for( i=1; i<=5000; i++ ){ + char sql[256]; + snprintf(sql, sizeof(sql), + "INSERT INTO tblob VALUES(x'%032x', 'row_%d', 'pad')", i, i); + execSql(db, sql); + } + execSql(db, "COMMIT"); + + execSql(db, + "UPDATE tblob SET val='short' WHERE id=x'000000000000000000000000000009c4'"); + execSql(db, + "UPDATE tblob SET val='this replacement is intentionally longer' " + "WHERE id=x'00000000000000000000000000000d05'"); + + check("single_mutation_blob_count", + queryScalarInt(db, "SELECT count(*) FROM tblob")==5000); + check("single_mutation_blob_short", + strcmp(queryScalarText(db, + "SELECT val FROM tblob WHERE id=x'000000000000000000000000000009c4'"), + "short")==0); + check("single_mutation_blob_long", + strcmp(queryScalarText(db, + "SELECT val FROM tblob WHERE id=x'00000000000000000000000000000d05'"), + "this replacement is intentionally longer")==0); + check("single_mutation_blob_integrity", + strcmp(queryScalarText(db, "PRAGMA integrity_check"), "ok")==0); + sqlite3_close(db); removeDb(dbpath); }