diff --git a/core/mvcc/database/mod.rs b/core/mvcc/database/mod.rs index 31bc2c199..984e90de9 100644 --- a/core/mvcc/database/mod.rs +++ b/core/mvcc/database/mod.rs @@ -1930,13 +1930,13 @@ impl MvStore { loop { match reader.next_record(&pager.io).unwrap() { StreamingResult::InsertRow { row, rowid } => { - tracing::trace!("read {row:?} with rowid {rowid:?}"); if rowid.table_id == SQLITE_SCHEMA_MVCC_TABLE_ID { // Sqlite schema row version inserts let row_data = row.data.clone(); let record = ImmutableRecord::from_bin_record(row_data); let mut record_cursor = RecordCursor::new(); let record_values = record_cursor.get_values(&record).unwrap(); + println!("record_values: {:?}", record_values); let RefValue::Integer(root_page) = record_values[3] else { panic!( "Expected integer value for root page, got {:?}", diff --git a/core/mvcc/database/tests.rs b/core/mvcc/database/tests.rs index af8093bbe..20df86ac0 100644 --- a/core/mvcc/database/tests.rs +++ b/core/mvcc/database/tests.rs @@ -123,14 +123,14 @@ fn test_insert_read() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -148,7 +148,7 @@ fn test_insert_read() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -167,7 +167,7 @@ fn test_read_nonexistent() { let row = db.mvcc_store.read( tx, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ); @@ -182,14 +182,14 @@ fn test_delete() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -200,7 +200,7 @@ fn test_delete() { .delete( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -210,7 +210,7 @@ fn test_delete() { .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -227,7 +227,7 @@ fn test_delete() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -247,7 +247,7 @@ fn test_delete_nonexistent() { .delete( tx, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1 }, ) @@ -261,28 +261,28 @@ fn test_commit() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) .unwrap() .unwrap(); assert_eq!(tx1_row, row); - let tx1_updated_row = generate_simple_string_row((-1).into(), 1, "World"); + let tx1_updated_row = generate_simple_string_row((-2).into(), 1, "World"); db.mvcc_store.update(tx1, tx1_updated_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -300,7 +300,7 @@ fn test_commit() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -318,28 +318,28 @@ fn test_rollback() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let row1 = generate_simple_string_row((-1).into(), 1, "Hello"); + let row1 = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, row1.clone()).unwrap(); let row2 = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) .unwrap() .unwrap(); assert_eq!(row1, row2); - let row3 = generate_simple_string_row((-1).into(), 1, "World"); + let row3 = generate_simple_string_row((-2).into(), 1, "World"); db.mvcc_store.update(tx1, row3.clone()).unwrap(); let row4 = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -358,7 +358,7 @@ fn test_rollback() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -375,14 +375,14 @@ fn test_dirty_write() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -393,7 +393,7 @@ fn test_dirty_write() { let conn2 = db.db.connect().unwrap(); // T2 attempts to delete row with ID 1, but fails because T1 has not committed. let tx2 = db.mvcc_store.begin_tx(conn2.pager.read().clone()).unwrap(); - let tx2_row = generate_simple_string_row((-1).into(), 1, "World"); + let tx2_row = generate_simple_string_row((-2).into(), 1, "World"); assert!(!db.mvcc_store.update(tx2, tx2_row).unwrap()); let row = db @@ -401,7 +401,7 @@ fn test_dirty_write() { .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -419,7 +419,7 @@ fn test_dirty_read() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let row1 = generate_simple_string_row((-1).into(), 1, "Hello"); + let row1 = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, row1).unwrap(); // T2 attempts to read row with ID 1, but doesn't see one because T1 has not committed. @@ -430,7 +430,7 @@ fn test_dirty_read() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -447,7 +447,7 @@ fn test_dirty_read_deleted() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); commit_tx(db.mvcc_store.clone(), &db.conn, tx1).unwrap(); @@ -459,7 +459,7 @@ fn test_dirty_read_deleted() { .delete( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1 }, ) @@ -473,7 +473,7 @@ fn test_dirty_read_deleted() { .read( tx3, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -491,14 +491,14 @@ fn test_fuzzy_read() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "First"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "First"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -515,7 +515,7 @@ fn test_fuzzy_read() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -526,7 +526,7 @@ fn test_fuzzy_read() { // T3 updates the row and commits. let conn3 = db.db.connect().unwrap(); let tx3 = db.mvcc_store.begin_tx(conn3.pager.read().clone()).unwrap(); - let tx3_row = generate_simple_string_row((-1).into(), 1, "Second"); + let tx3_row = generate_simple_string_row((-2).into(), 1, "Second"); db.mvcc_store.update(tx3, tx3_row).unwrap(); commit_tx(db.mvcc_store.clone(), &conn3, tx3).unwrap(); @@ -536,7 +536,7 @@ fn test_fuzzy_read() { .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -546,7 +546,7 @@ fn test_fuzzy_read() { // T2 tries to update the row, but fails because T3 has already committed an update to the row, // so T2 trying to write would violate snapshot isolation if it succeeded. - let tx2_newrow = generate_simple_string_row((-1).into(), 1, "Third"); + let tx2_newrow = generate_simple_string_row((-2).into(), 1, "Third"); let update_result = db.mvcc_store.update(tx2, tx2_newrow); assert!(matches!(update_result, Err(LimboError::WriteWriteConflict))); } @@ -560,14 +560,14 @@ fn test_lost_update() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); let row = db .mvcc_store .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -579,13 +579,13 @@ fn test_lost_update() { // T2 attempts to update row ID 1 within an active transaction. let conn2 = db.db.connect().unwrap(); let tx2 = db.mvcc_store.begin_tx(conn2.pager.read().clone()).unwrap(); - let tx2_row = generate_simple_string_row((-1).into(), 1, "World"); + let tx2_row = generate_simple_string_row((-2).into(), 1, "World"); assert!(db.mvcc_store.update(tx2, tx2_row.clone()).unwrap()); // T3 also attempts to update row ID 1 within an active transaction. let conn3 = db.db.connect().unwrap(); let tx3 = db.mvcc_store.begin_tx(conn3.pager.read().clone()).unwrap(); - let tx3_row = generate_simple_string_row((-1).into(), 1, "Hello, world!"); + let tx3_row = generate_simple_string_row((-2).into(), 1, "Hello, world!"); assert!(matches!( db.mvcc_store.update(tx3, tx3_row), Err(LimboError::WriteWriteConflict) @@ -608,7 +608,7 @@ fn test_lost_update() { .read( tx4, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -628,21 +628,21 @@ fn test_committed_visibility() { .mvcc_store .begin_tx(db.conn.pager.read().clone()) .unwrap(); - let tx1_row = generate_simple_string_row((-1).into(), 1, "10"); + let tx1_row = generate_simple_string_row((-2).into(), 1, "10"); db.mvcc_store.insert(tx1, tx1_row.clone()).unwrap(); commit_tx(db.mvcc_store.clone(), &db.conn, tx1).unwrap(); // but I like more money, so let me try adding $10 more let conn2 = db.db.connect().unwrap(); let tx2 = db.mvcc_store.begin_tx(conn2.pager.read().clone()).unwrap(); - let tx2_row = generate_simple_string_row((-1).into(), 1, "20"); + let tx2_row = generate_simple_string_row((-2).into(), 1, "20"); assert!(db.mvcc_store.update(tx2, tx2_row.clone()).unwrap()); let row = db .mvcc_store .read( tx2, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -658,7 +658,7 @@ fn test_committed_visibility() { .read( tx3, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -679,7 +679,7 @@ fn test_future_row() { let conn2 = db.db.connect().unwrap(); let tx2 = db.mvcc_store.begin_tx(conn2.pager.read().clone()).unwrap(); - let tx2_row = generate_simple_string_row((-1).into(), 1, "Hello"); + let tx2_row = generate_simple_string_row((-2).into(), 1, "Hello"); db.mvcc_store.insert(tx2, tx2_row).unwrap(); // transaction in progress, so tx1 shouldn't be able to see the value @@ -688,7 +688,7 @@ fn test_future_row() { .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -702,7 +702,7 @@ fn test_future_row() { .read( tx1, RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: 1, }, ) @@ -1080,7 +1080,7 @@ fn test_snapshot_isolation_tx_visible1() { let row_version = RowVersion { begin, end, - row: generate_simple_string_row((-1).into(), 1, "testme"), + row: generate_simple_string_row((-2).into(), 1, "testme"), }; tracing::debug!("Testing visibility of {row_version:?}"); row_version.is_visible_to(¤t_tx, &txs) @@ -1162,8 +1162,34 @@ fn test_restart() { let conn = db.connect(); let mvcc_store = db.get_mvcc_store(); let tx_id = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); - let row = generate_simple_string_row((-1).into(), 1, "foo"); - + // insert table id -2 into sqlite_schema table (table_id -1) + let data = ImmutableRecord::from_values( + &[ + Value::Text(Text::new("table")), // type + Value::Text(Text::new("test")), // name + Value::Text(Text::new("test")), // tbl_name + Value::Integer(-2), // rootpage + Value::Text(Text::new( + "CREATE TABLE test(id INTEGER PRIMARY KEY, data TEXT)", + )), // sql + ], + 5, + ); + mvcc_store + .insert( + tx_id, + Row { + id: RowID { + table_id: (-1).into(), + row_id: 1, + }, + data: data.as_blob().to_vec(), + column_count: 5, + }, + ) + .unwrap(); + // now insert a row into table -2 + let row = generate_simple_string_row((-2).into(), 1, "foo"); mvcc_store.insert(tx_id, row).unwrap(); commit_tx(mvcc_store.clone(), &conn, tx_id).unwrap(); conn.close().unwrap(); @@ -1174,14 +1200,14 @@ fn test_restart() { let conn = db.connect(); let mvcc_store = db.get_mvcc_store(); let tx_id = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); - let row = generate_simple_string_row((-1).into(), 2, "bar"); + let row = generate_simple_string_row((-2).into(), 2, "bar"); mvcc_store.insert(tx_id, row).unwrap(); commit_tx(mvcc_store.clone(), &conn, tx_id).unwrap(); let tx_id = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); let row = mvcc_store - .read(tx_id, RowID::new((-1).into(), 2)) + .read(tx_id, RowID::new((-2).into(), 2)) .unwrap() .unwrap(); let record = get_record_value(&row); @@ -1461,8 +1487,8 @@ fn transaction_display() { let begin_ts = 20250914; let write_set = SkipSet::new(); - write_set.insert(RowID::new((-1).into(), 11)); - write_set.insert(RowID::new((-1).into(), 13)); + write_set.insert(RowID::new((-2).into(), 11)); + write_set.insert(RowID::new((-2).into(), 13)); let read_set = SkipSet::new(); read_set.insert(RowID::new((-2).into(), 17)); diff --git a/core/mvcc/mod.rs b/core/mvcc/mod.rs index c7899ba97..ba96b4c3e 100644 --- a/core/mvcc/mod.rs +++ b/core/mvcc/mod.rs @@ -68,10 +68,10 @@ mod tests { let tx = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); let id = IDS.fetch_add(1, Ordering::SeqCst); let id = RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: id, }; - let row = generate_simple_string_row((-1).into(), id.row_id, "Hello"); + let row = generate_simple_string_row((-2).into(), id.row_id, "Hello"); mvcc_store.insert(tx, row.clone()).unwrap(); commit_tx_no_conn(&db, tx, &conn).unwrap(); let tx = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); @@ -89,10 +89,10 @@ mod tests { let tx = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); let id = IDS.fetch_add(1, Ordering::SeqCst); let id = RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: id, }; - let row = generate_simple_string_row((-1).into(), id.row_id, "World"); + let row = generate_simple_string_row((-2).into(), id.row_id, "World"); mvcc_store.insert(tx, row.clone()).unwrap(); commit_tx_no_conn(&db, tx, &conn).unwrap(); let tx = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); @@ -130,11 +130,11 @@ mod tests { let tx = mvcc_store.begin_tx(conn.pager.read().clone()).unwrap(); let id = i % 16; let id = RowID { - table_id: (-1).into(), + table_id: (-2).into(), row_id: id, }; let row = generate_simple_string_row( - (-1).into(), + (-2).into(), id.row_id, &format!("{prefix} @{tx}"), ); diff --git a/core/mvcc/persistent_storage/logical_log.rs b/core/mvcc/persistent_storage/logical_log.rs index 5cb63cf95..2d0208438 100644 --- a/core/mvcc/persistent_storage/logical_log.rs +++ b/core/mvcc/persistent_storage/logical_log.rs @@ -452,13 +452,13 @@ mod tests { mvcc::{ database::{ tests::{commit_tx, generate_simple_string_row, MvccTestDbNoConn}, - RowID, + Row, RowID, }, persistent_storage::Storage, LocalClock, MvStore, }, - types::ImmutableRecord, - OpenFlags, RefValue, + types::{ImmutableRecord, Text}, + OpenFlags, RefValue, Value, }; use super::LogRecordType; @@ -473,8 +473,34 @@ mod tests { let pager = conn.pager.read().clone(); let mvcc_store = db.get_mvcc_store(); let tx_id = mvcc_store.begin_tx(pager.clone()).unwrap(); - - let row = generate_simple_string_row((-1).into(), 1, "foo"); + // insert table id -2 into sqlite_schema table (table_id -1) + let data = ImmutableRecord::from_values( + &[ + Value::Text(Text::new("table")), // type + Value::Text(Text::new("test")), // name + Value::Text(Text::new("test")), // tbl_name + Value::Integer(-2), // rootpage + Value::Text(Text::new( + "CREATE TABLE test(id INTEGER PRIMARY KEY, data TEXT)", + )), // sql + ], + 5, + ); + mvcc_store + .insert( + tx_id, + Row { + id: RowID { + table_id: (-1).into(), + row_id: 1, + }, + data: data.as_blob().to_vec(), + column_count: 5, + }, + ) + .unwrap(); + // now insert a row into table -2 + let row = generate_simple_string_row((-2).into(), 1, "foo"); mvcc_store.insert(tx_id, row).unwrap(); commit_tx(mvcc_store.clone(), &conn, tx_id).unwrap(); conn.close().unwrap(); @@ -490,7 +516,7 @@ mod tests { mvcc_store.maybe_recover_logical_log(pager.clone()).unwrap(); let tx = mvcc_store.begin_tx(pager.clone()).unwrap(); let row = mvcc_store - .read(tx, RowID::new((-1).into(), 1)) + .read(tx, RowID::new((-2).into(), 1)) .unwrap() .unwrap(); let record = ImmutableRecord::from_bin_record(row.data.clone()); @@ -505,7 +531,7 @@ mod tests { #[test] fn test_logical_log_read_multiple_transactions() { let values = (0..100) - .map(|i| (RowID::new((-1).into(), i), format!("foo_{i}"))) + .map(|i| (RowID::new((-2).into(), i), format!("foo_{i}"))) .collect::>(); // let's not drop db as we don't want files to be removed let db = MvccTestDbNoConn::new_with_random_db(); @@ -514,6 +540,35 @@ mod tests { let pager = conn.pager.read().clone(); let mvcc_store = db.get_mvcc_store(); + let tx_id = mvcc_store.begin_tx(pager.clone()).unwrap(); + // insert table id -2 into sqlite_schema table (table_id -1) + let data = ImmutableRecord::from_values( + &[ + Value::Text(Text::new("table")), // type + Value::Text(Text::new("test")), // name + Value::Text(Text::new("test")), // tbl_name + Value::Integer(-2), // rootpage + Value::Text(Text::new( + "CREATE TABLE test(id INTEGER PRIMARY KEY, data TEXT)", + )), // sql + ], + 5, + ); + mvcc_store + .insert( + tx_id, + Row { + id: RowID { + table_id: (-1).into(), + row_id: 1, + }, + data: data.as_blob().to_vec(), + column_count: 5, + }, + ) + .unwrap(); + commit_tx(mvcc_store.clone(), &conn, tx_id).unwrap(); + // now insert a row into table -2 // generate insert per transaction for (rowid, value) in &values { let tx_id = mvcc_store.begin_tx(pager.clone()).unwrap(); @@ -562,7 +617,7 @@ mod tests { match op_type { 0 => { let row_id = rng.next_u64(); - let rowid = RowID::new((-1).into(), row_id as i64); + let rowid = RowID::new((-2).into(), row_id as i64); let row = generate_simple_string_row( rowid.table_id, rowid.row_id,