Skip to content

Conversation

@elcritch
Copy link
Contributor

@elcritch elcritch commented Oct 7, 2025

Adds support for multiple statements in a query.

@Araq
Copy link
Owner

Araq commented Oct 7, 2025

Is this in preparation of transactions? Cause if not, I fail to see the benefit, it's complex and non-intuitive and you might as well write query twice instead...

@elcritch
Copy link
Contributor Author

elcritch commented Oct 7, 2025

Is this in preparation of transactions?

Yes, it is.

Cause if not, I fail to see the benefit, it's complex and non-intuitive and you might as well write query twice instead...

That works well for sqlite3. However for Postgres and others that'll can cause significant performance overhead and latency increases right? Unless I'm missing something with how db connectors work.

@elcritch elcritch marked this pull request as draft October 7, 2025 06:54
@elcritch
Copy link
Contributor Author

elcritch commented Oct 7, 2025

That works well for sqlite3. However for Postgres and others that'll can cause significant performance overhead and latency increases right? Unless I'm missing something with how db connectors work.

Scratch that. I haven't used SQL for quite a while. Claude says multiple prepared statements tend to be preferred.

Though this PR does execute individual prepare statements after all.

I've got a branch with transactions as part of the DSL. However, it seems somewhat over-complicated. It might be easier to do transactions as a separate helper function?

@elcritch
Copy link
Contributor Author

elcritch commented Oct 7, 2025

Here's an example Python w/ Sqlite3 Claude gives me:

import sqlite3

def transfer_money(conn, from_account, to_account, amount):
    try:
        cursor = conn.cursor()
        cursor.execute("BEGIN TRANSACTION")
        cursor.execute(
            "UPDATE accounts SET balance = balance - ? WHERE account_id = ?",
            (amount, from_account)
        )
        cursor.execute(
            "UPDATE accounts SET balance = balance + ? WHERE account_id = ?",
            (amount, to_account)
        )
        cursor.execute("COMMIT")
        return "Transfer successful"
    except sqlite3.IntegrityError as e:
        cursor.execute("ROLLBACK")
        return f"Transfer failed: {e}"

# Usage
conn = sqlite3.connect('bank.db')
result = transfer_money(conn, 1, 2, 100)
print(result)

Option 1

Translating to Ormin in a literal fashion:

proc transfer_money(from_account, to_account: int, amount: Decimal): string =
    try:
        query:
           begin transaction
        
        let _ = query:
            UPDATE accounts(balance = balance - ?amount)
            WHERE account_id = ?from_account
        
        let _ = query:
            UPDATE accounts(balance = balance + ?amount)
            WHERE account_id = ?to_account

        query:
          commit
        return "Transfer successful"
    except CatchableError as e:
        query:
          rollback
        return fmt"Transfer failed: {e}"

Option 2

I have this format working on another branch. It builds on the current PR:

    test "transaction example":
      # Insert and then select within a single transaction block.
      let id = 21
      let (newId, selected) = query:
        transaction:
          insert person(id = ?id, name = "txuser", password = "fake", email = "tx@example.com", salt = "salt21", status = "ok")
          returning id
          select person(id)
          where id == ?id
      check newId == id
      # Verify the inserted row is visible after the transaction completes.
      let outside = query:
        select person(id)
        where id == ?id
      check outside == [id]

However, this starts getting complicated if you add multiple transaction: blocks.

Option 3

Maybe something like this:

proc transfer_money(db: DbConn, from_account, to_account: int, amount: Decimal): string =
    transaction:
        let _ = query:
          UPDATE accounts(balance = balance - ?amount)
          WHERE account_id = ?from_account
        
        let _ = query:
          UPDATE accounts(balance = balance + ?amount)
          WHERE account_id = ?to_account

        return "Transfer successful"
    except CatchableError as e:
        rollback()
        return fmt"Transfer failed: {e}"

@elcritch
Copy link
Contributor Author

@Araq thoughts on this?

@elcritch elcritch closed this Nov 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants