Repository: tursodatabase/turso (Rust)

Issue:
Committed transaction is not visible to another connection
The following test fails with commit from connection 1 not being visible in connection2. Note that if I use explicit BEGIN in connection 2 instead of autocommit, it passes.

```
#[test]
fn test_transaction_visibility() {
    let tmp_db = TempDatabase::new("test_transaction_visibility.db", true);
    let conn1 = tmp_db.connect_limbo();
    let conn2 = tmp_db.connect_limbo();

    conn1
        .execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
        .unwrap();

    // Insert initial data
    conn1
        .execute("INSERT INTO test (id, value) VALUES (1, 'initial')")
        .unwrap();

    // Connection 2 reads from the database and is succesful because deferred transaction did not upgrade to write transaction
    let mut stmt = conn2.query("SELECT COUNT(*) FROM test").unwrap().unwrap();
    if let StepResult::Row = stmt.step().unwrap() {
        let row = stmt.row().unwrap();
        assert_eq!(*row.get::<&Value>(0).unwrap(), Value::Integer(1));
    }
}
```


Discussion:
This is just about us eagerly cloning `schema` for Connection but we don't check its freshness when you call `conn.query()`



Patch:
diff --git a/core/lib.rs b/core/lib.rs
index cb31846255..38008a4e2a 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -1106,6 +1106,7 @@ impl Connection {
                 "The supplied SQL string contains no statements".to_string(),
             ));
         }
+        self.maybe_update_schema()?;
         let sql = sql.as_ref();
         tracing::trace!("Preparing and executing batch: {}", sql);
         let mut parser = Parser::new(sql.as_bytes());
@@ -1143,6 +1144,7 @@ impl Connection {
             return Err(LimboError::InternalError("Connection closed".to_string()));
         }
         let sql = sql.as_ref();
+        self.maybe_update_schema()?;
         tracing::trace!("Querying: {}", sql);
         let mut parser = Parser::new(sql.as_bytes());
         let cmd = parser.next_cmd()?;
@@ -1216,6 +1218,7 @@ impl Connection {
             return Err(LimboError::InternalError("Connection closed".to_string()));
         }
         let sql = sql.as_ref();
+        self.maybe_update_schema()?;
         let mut parser = Parser::new(sql.as_bytes());
         while let Some(cmd) = parser.next_cmd()? {
             let syms = self.syms.borrow();
@@ -1224,7 +1227,6 @@ impl Connection {
             let input = str::from_utf8(&sql.as_bytes()[..byte_offset_end])
                 .unwrap()
                 .trim();
-            self.maybe_update_schema()?;
             match cmd {
                 Cmd::Explain(stmt) => {
                     let program = translate::translate(


Test patch:
diff --git a/tests/integration/query_processing/test_transactions.rs b/tests/integration/query_processing/test_transactions.rs
index bb1f2e77b4..3a9779e87c 100644
--- a/tests/integration/query_processing/test_transactions.rs
+++ b/tests/integration/query_processing/test_transactions.rs
@@ -123,3 +123,52 @@ fn test_txn_error_doesnt_rollback_txn() -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+/// Connection 2 should see the initial data (table 'test' in schema + 2 rows). Regression test for #2997
+/// It should then see another created table 'test2' in schema, as well.
+fn test_transaction_visibility() {
+    let tmp_db = TempDatabase::new("test_transaction_visibility.db", true);
+    let conn1 = tmp_db.connect_limbo();
+    let conn2 = tmp_db.connect_limbo();
+
+    conn1
+        .execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
+        .unwrap();
+
+    conn1
+        .execute("INSERT INTO test (id, value) VALUES (1, 'initial')")
+        .unwrap();
+
+    let mut stmt = conn2.query("SELECT COUNT(*) FROM test").unwrap().unwrap();
+    loop {
+        match stmt.step().unwrap() {
+            StepResult::Row => {
+                let row = stmt.row().unwrap();
+                assert_eq!(*row.get::<&Value>(0).unwrap(), Value::Integer(1));
+            }
+            StepResult::IO => stmt.run_once().unwrap(),
+            StepResult::Done => break,
+            StepResult::Busy => panic!("database is busy"),
+            StepResult::Interrupt => panic!("interrupted"),
+        }
+    }
+
+    conn1
+        .execute("CREATE TABLE test2 (id INTEGER PRIMARY KEY, value TEXT)")
+        .unwrap();
+
+    let mut stmt = conn2.query("SELECT COUNT(*) FROM test2").unwrap().unwrap();
+    loop {
+        match stmt.step().unwrap() {
+            StepResult::Row => {
+                let row = stmt.row().unwrap();
+                assert_eq!(*row.get::<&Value>(0).unwrap(), Value::Integer(0));
+            }
+            StepResult::IO => stmt.run_once().unwrap(),
+            StepResult::Done => break,
+            StepResult::Busy => panic!("database is busy"),
+            StepResult::Interrupt => panic!("interrupted"),
+        }
+    }
+}