Skip to content

DatasourceTransaction ^v3.2.2 ​

An open database transaction, passed to the callback of DatasourceClient's transaction().

Passed to DatasourceClient.transaction
js
const client = datasource.client("Internal Datastore");

client.transaction(tx => {
    tx.execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", [100, "A-1"]);
    tx.execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", [100, "A-2"]);
});

Overview ​

  • All or nothing: every statement run through the transaction uses the same connection, and either all of them are saved or none are.
  • Ending the transaction: it commits when the callback returns and rolls back when the callback throws. commit() and rollback() end it early. After that, and after the callback returns, any call on it throws.
  • Errors always throw: suppressErrors() doesn't apply inside a transaction. If a statement fails, or anything else in the callback throws, the transaction is rolled back and the error is thrown from transaction().
  • Use tx, not client: a client.select(...) or client.execute(...) call inside the callback runs on a different connection, outside the transaction. It doesn't see the transaction's uncommitted changes, it can wait on rows the transaction has locked (and so wait on itself), and it can hang if the datasource has no free connections. Don't keep tx after the callback returns.
  • Only database changes are rolled back: cube writes, files, emails and HTTP calls made inside the callback are not undone.
  • The callback must be synchronous: an async callback, or one that returns a Promise, is rejected and the transaction rolls back.
  • No nested transactions: calling client.transaction(...) inside the callback starts a separate, independent transaction on another connection, with the same caveats as any other client call there.
  • JDBC only: a BigQuery client throws.

Statements that end the transaction for you

Some databases (MySQL, Oracle) commit automatically when a CREATE, ALTER or DROP runs, which also commits everything before it in the transaction. Don't send BEGIN, COMMIT, ROLLBACK or SET TRANSACTION ISOLATION LEVEL through execute(); use the callback, commit(), rollback() and the isolation option instead.

Methods ​

select select(query, params, rowLimit) iterable ​

Runs a SELECT query inside the transaction and returns its rows, read with for...of. Unlike the client's select(), every row is read into memory before the call returns, so other statements can run on the same connection while you iterate them. Pass a rowLimit for large reads.

ParameterTypeDescription
querystringThe SQL query. Use ? placeholders for values.
paramsarrayOptional. Values for the ? placeholders, in order.
rowLimitnumberOptional. The most rows to return. 0 or omitted means no limit.

selectAll selectAll(query, params) array ​

Runs a SELECT query inside the transaction and returns every row in an array.

ParameterTypeDescription
querystringThe SQL query. Use ? placeholders for values.
paramsarrayOptional. Values for the ? placeholders, in order.

first first(query, params) object | null ​

Runs a SELECT query inside the transaction and returns the first row, or null when there are no rows.

ParameterTypeDescription
querystringThe SQL query. Use ? placeholders for values.
paramsarrayOptional. Values for the ? placeholders, in order.

execute execute(statement, params) object ​

Runs an INSERT, UPDATE, DELETE or DDL statement inside the transaction. The change is only saved when the transaction commits. Returns an object with:

  • affected: the number of rows changed.
  • generatedKey: the key generated by an insert, or null when there isn't one.
ParameterTypeDescription
statementstringThe SQL statement. Use ? placeholders for values.
paramsarrayOptional. Values for the ? placeholders, in order.

commit commit() optional ​

Commits the transaction immediately, saving its changes. You don't normally need it, because the transaction commits on its own when the callback returns. The transaction can't be used after this.

rollback rollback() ​

Rolls the transaction back immediately, discarding every change made through it. Throwing from the callback does the same; call rollback() when you want to discard the changes and carry on without an error. The transaction can't be used after this.

Examples ​

Insert a header and its lines together ​

js
const client = datasource.client("Internal Datastore");

client.transaction(tx => {
    const header = tx.execute("INSERT INTO invoices (customer) VALUES (?)", ["C-12"]);
    tx.execute("INSERT INTO invoice_lines (invoice_id, sku, qty) VALUES (?, ?, ?)",
        [header.generatedKey, "W-100", 3]);
});

Discard the changes when a check fails ​

js
const client = datasource.client("Internal Datastore");

const applied = client.transaction(tx => {
    tx.execute("UPDATE prices SET amount = amount * 1.1 WHERE category = ?", ["tools"]);
    const bad = tx.first("SELECT COUNT(*) AS n FROM prices WHERE amount > ?", [10000]);
    if (bad.n > 0) {
        tx.rollback();
        return false;
    }
    return true;
});