Repository: tursodatabase/limbo (rust)

Issue:
Deferred transactions
Add support for `BEGIN DEFERRED`.

Pull request description:
Implement `BEGIN DEFERRED`
As explained in [docs](https://sqlite.org/lang_transaction.html):

> "DEFERRED means that the transaction does not actually start until the database is first accessed. Internally, the BEGIN DEFERRED statement merely sets a flag on the database connection that turns off the automatic commit that would normally occur when the last statement finishes. This causes the transaction that is automatically started to persist until an explicit COMMIT or ROLLBACK or until a rollback is provoked by an error or an ON CONFLICT ROLLBACK clause. If the first statement after BEGIN DEFERRED is a SELECT, then a read transaction is started. Subsequent write statements will upgrade the transaction to a write transaction if possible, or return SQLITE_BUSY. If the first statement after BEGIN DEFERRED is a write statement, then a write transaction is started. "

The transaction upgrade `read -> write` is already handled by the VDBE in `Transaction`.

closes #1001

Patch:
diff --git a/core/translate/transaction.rs b/core/translate/transaction.rs
index 01994dfd..60e00e73 100644
--- a/core/translate/transaction.rs
+++ b/core/translate/transaction.rs
@@ -1,6 +1,6 @@
 use crate::translate::{ProgramBuilder, ProgramBuilderOpts};
 use crate::vdbe::insn::Insn;
-use crate::{bail_parse_error, QueryMode, Result};
+use crate::{QueryMode, Result};
 use limbo_sqlite3_parser::ast::{Name, TransactionType};
 
 pub fn translate_tx_begin(
@@ -18,7 +18,10 @@ pub fn translate_tx_begin(
     let tx_type = tx_type.unwrap_or(TransactionType::Deferred);
     match tx_type {
         TransactionType::Deferred => {
-            bail_parse_error!("BEGIN DEFERRED not supported yet");
+            program.emit_insn(Insn::AutoCommit {
+                auto_commit: false,
+                rollback: false,
+            });
         }
         TransactionType::Immediate | TransactionType::Exclusive => {
             program.emit_insn(Insn::Transaction { write: true });
diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs
index 0917729c..613cd66e 100644
--- a/core/vdbe/explain.rs
+++ b/core/vdbe/explain.rs
@@ -599,7 +599,7 @@ pub fn insn_to_str(
                 0,
                 OwnedValue::build_text(""),
                 0,
-                "".to_string(),
+                format!("write={}", write),
             ),
             Insn::Goto { target_pc } => (
                 "Goto",
diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs
index af3349a5..95e115ad 100644
--- a/core/vdbe/mod.rs
+++ b/core/vdbe/mod.rs
@@ -1263,9 +1263,7 @@ impl Program {
                             }
                         }
                         if updated {
-                            connection
-                                .transaction_state
-                                .replace(new_transaction_state.clone());
+                            connection.transaction_state.replace(new_transaction_state);
                         }
                     }
                     state.pc += 1;


Test patch:
diff --git a/testing/transactions.test b/testing/transactions.test
index 3a14d812..142c721f 100755
--- a/testing/transactions.test
+++ b/testing/transactions.test
@@ -10,3 +10,7 @@ do_execsql_test basic-tx-1 {
 do_execsql_test basic-tx-2 {
   BEGIN EXCLUSIVE; END
 } {}
+
+do_execsql_test basic-tx-3 {
+  BEGIN DEFERRED; END
+  } {}
\ No newline at end of file