import * as Context from "../Context.ts"; import * as Effect from "../Effect.ts"; import type * as Queue from "../Queue.ts"; import { type KeysInput, Reactivity } from "../reactivity/Reactivity.ts"; import type { ReadonlyRecord } from "../Record.ts"; import * as Scope from "../Scope.ts"; import * as Semaphore from "../Semaphore.ts"; import * as Stream from "../Stream.ts"; import type { NoInfer } from "../Types.ts"; import type * as Connection from "./SqlConnection.ts"; import { SqlError } from "./SqlError.ts"; import type { Compiler, Constructor } from "./Statement.ts"; declare const TypeId = "~effect/sql/SqlClient"; /** * SQL client service interface, combining the statement constructor API with * connection reservation, transaction handling, and reactive query helpers. * * @stability unstable * @category models * @since 4.0.0 */ export interface SqlClient extends Constructor { readonly [TypeId]: typeof TypeId; /** * Copy of the client for safeql etc. */ readonly safe: this; /** * Copy of the client without transformations. */ readonly withoutTransforms: () => this; readonly reserve: Effect.Effect; /** * Identifies the driver that created this client. * * **Details** * * Effect drivers use their package name without the `@effect/sql-` prefix, * such as `"pg"`, `"pglite"` or `"sqlite-node"`. Unlike the compiler * dialect, it distinguishes drivers that share a SQL dialect but manage * connections differently. */ readonly driver: string; /** * With the given effect, ensure all sql queries are run in a transaction. * * **Details** * * Inside the transaction, `Reactivity.invalidate` and `Reactivity.mutation` * invalidate once the outermost transaction commits, and not at all if it * rolls back. * * To start a top-level transaction with a statement other than the * client's, such as one that sets its isolation level, provide * `BeginTransaction`. * * **Gotchas** * * Deferred invalidations run individually after the commit and don't join an * enclosing `Reactivity.withBatch`. Only the `Reactivity` service read from * the transaction's context defers, and a fiber that outlives the * transaction can't defer through it, as with `afterCommit`. Transactions * that don't go through `makeWithTransaction`, such as those of * `@effect/sql-sqlite-do`, invalidate immediately. */ readonly withTransaction: (self: Effect.Effect) => Effect.Effect; /** * The transaction service for this client. */ readonly transactionService: Context.Service; /** * Runs `effect` once the outermost transaction of this client commits, or * immediately, and interruptibly, when no transaction is open. * * **When to use** * * Use to signal committed changes, such as waking workers, invalidating * caches or notifying subscribers. * * **Details** * * Callbacks are discarded if the transaction rolls back or its `COMMIT` * fails, and callbacks registered in a savepoint that rolls back are * discarded even if the outer transaction commits. After a successful * `COMMIT`, callbacks run once releasing the connection has been attempted, * before `withTransaction` returns. * * A callback runs with the services that were in scope when it was * registered, minus this client's transaction, so a callback that queries * uses a new connection. A callback that dies is logged at * `References.UnhandledLogLevel` and the rest still run. * * Callbacks run in registration order within a transaction or savepoint, * and a committed savepoint's callbacks follow those already registered in * the enclosing level. Callbacks registered concurrently from several * fibers are not guaranteed to run in registration order. * * **Gotchas** * * Callbacks are lost if the process exits before they run or the outcome of * `COMMIT` is unknown, so work that must happen belongs in a table written * in the same transaction. After `COMMIT`, callbacks run uninterruptibly, * even if the fiber was interrupted or releasing the connection failed, and * `withTransaction` waits for them, so keep them short and fork * long-running work. * Registering after the transaction has ended is a defect. * * Transactions that don't go through `makeWithTransaction`, such as those * of `@effect/sql-sqlite-do`, run callbacks immediately. */ readonly afterCommit: (effect: Effect.Effect) => Effect.Effect; /** * Use the Reactivity service to create a reactive query. */ readonly reactive: (keys: KeysInput, effect: Effect.Effect) => Stream.Stream; /** * Use the Reactivity service to create a reactive * query. */ readonly reactiveMailbox: (keys: KeysInput, effect: Effect.Effect) => Effect.Effect, never, R | Scope.Scope>; } /** * Service tag for the active SQL client service. * * **When to use** * * Use to access or provide the SQL client used to build statements, stream * rows, reserve connections, and run transactions. * * @stability unstable * @category services * @since 4.0.0 */ export declare const SqlClient: Context.Service; /** * Namespace containing types associated with the `SqlClient` service. * * @stability unstable * @since 4.0.0 */ export declare namespace SqlClient { /** * Options used to construct a `SqlClient`, including connection acquirers, * the SQL compiler, transaction SQL, row transformation, tracing attributes, * and optional reactive query integration. * * @stability unstable * @category options * @since 4.0.0 */ interface MakeOptions { readonly acquirer: Connection.Acquirer; /** * Lends a connection for one statement instead of leasing one into a * scope. A client that can do this saves the scope and finalizer per * statement; `stream`, transactions, and `reserve` keep the `acquirer`, * whose lease outlives the effect that starts it. */ readonly borrower?: Connection.Borrower | undefined; readonly compiler: Compiler; readonly transactionAcquirer?: Connection.Acquirer; /** * Identifies the driver creating the client, exposed as `SqlClient.driver`. */ readonly driver: string; readonly spanAttributes: ReadonlyArray; readonly transactionService?: Context.Service; /** * Whether the transaction control statements - `BEGIN`, `COMMIT`, * `ROLLBACK`, and the savepoint pair - can be prepared like any other * statement. * * **Details** * * They run on every transaction and never change, so a database that can * prepare them stops parsing them again each time. Off by default, * because several databases refuse to prepare transaction control at all; * a driver has to say that its own does not. */ readonly prepareTransactionControls?: boolean | undefined; readonly beginTransaction?: string | undefined; readonly rollback?: string | undefined; /** Commit SQL, or an effect that commits on the connection and checks the result. */ readonly commit?: string | ((conn: Connection.Connection) => Effect.Effect) | undefined; /** Cleanup on the same connection when COMMIT fails. Omit when the driver already ends the transaction. */ readonly onCommitFailure?: ((conn: Connection.Connection) => Effect.Effect) | undefined; /** * Reports whether a transaction is open. If false, rollback and savepoint * cleanup are skipped. */ readonly isTransaction?: ((conn: Connection.Connection) => boolean) | undefined; readonly savepoint?: ((name: string) => string) | undefined; /** * SQL to release a nested savepoint after success or a successful rollback. * Omit to leave savepoints unreleased, including for dialects such as MSSQL * that do not support releasing savepoints. */ readonly releaseSavepoint?: ((name: string) => string) | undefined; readonly rollbackSavepoint?: ((name: string) => string) | undefined; readonly transformRows?: ((row: ReadonlyArray) => ReadonlyArray) | undefined; readonly reactiveQueue?: (keys: ReadonlyArray | ReadonlyRecord>, effect: Effect.Effect) => Effect.Effect, never, R | Scope.Scope>; } } /** * Constructs a `SqlClient` from connection acquirers, a compiler, transaction * commands, tracing attributes, optional row transforms, and reactive query * integration. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const make: (options: SqlClient.MakeOptions) => Effect.Effect; /** * Builds the acquirers, borrower and failed-COMMIT cleanup for a SQLite client that * shares one connection through a semaphore. * * **Details** * * Both acquirers lease the connection into the current scope and hold the * semaphore until that scope closes. The borrower holds the same semaphore * until the statement finishes without creating a scope. Another fiber cannot * begin a transaction underneath either. Effects that use the connection directly, * such as an export, have to run inside `Effect.scoped`. * * SQLite can keep a transaction open after COMMIT fails, for example on a * deferred foreign key violation. `onCommitFailure` rolls it back before the * connection is reused. If that ROLLBACK fails, the connection is rejected, and * each acquisition or borrow retries the ROLLBACK once while holding the semaphore * until it succeeds. The database is never reopened, and transactions started with a * plain `BEGIN` are only touched after a failed cleanup. * * Pass `isTransaction` when the driver can report whether a transaction is * open. Without it, ROLLBACK always runs and SQLite's "no transaction is * active" error counts as clean. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const makeSqliteAcquirers: (options: { readonly connection: Effect.Effect; readonly semaphore: Semaphore.Semaphore; readonly isTransaction?: ((conn: C) => boolean) | undefined; }) => { readonly acquirer: Effect.Effect; readonly borrower: Connection.Borrower; readonly transactionAcquirer: Effect.Effect; readonly onCommitFailure: (conn: Connection.Connection) => Effect.Effect; readonly isTransaction: ((conn: Connection.Connection) => boolean) | undefined; }; /** * Builds a transaction wrapper that begins top-level transactions, uses * savepoints for nested transactions, commits on success, and rolls back on * failure or interruption. Releases nested savepoints when `releaseSavepoint` * is provided. * * @stability unstable * @category transactions * @since 4.0.0 */ export declare const makeWithTransaction: (options: { readonly transactionService: Context.Key; readonly spanAttributes: ReadonlyArray; /** * Acquires the connection for a top-level transaction. Runs under the * transaction's uninterruptible mask; wrap the wait for a connection in * `restore` so a queued transaction can be interrupted, and register * releases on the provided scope. The scope closes with the transaction, or * immediately when acquisition fails or is interrupted. */ readonly acquireConnection: (restore: (effect: Effect.Effect) => Effect.Effect) => Effect.Effect; /** * Starts a top-level transaction. `statement` is the `BeginTransaction` * provided to the transaction, if any; a driver that doesn't start * transactions with a SQL statement ignores it. */ readonly begin: (conn: NoInfer, statement: string | undefined) => Effect.Effect; readonly savepoint: (conn: NoInfer, id: number) => Effect.Effect; /** * Releases a nested savepoint after success or a successful rollback. * Omit to leave savepoints unreleased, including for dialects such as MSSQL * that do not support releasing savepoints. */ readonly releaseSavepoint?: ((conn: NoInfer, id: number) => Effect.Effect) | undefined; readonly commit: (conn: NoInfer) => Effect.Effect; /** Driver-specific recovery before the connection is released after a failed COMMIT. */ readonly onCommitFailure?: ((conn: NoInfer) => Effect.Effect) | undefined; readonly rollback: (conn: NoInfer) => Effect.Effect; readonly rollbackSavepoint: (conn: NoInfer, id: number) => Effect.Effect; }) => (effect: Effect.Effect) => Effect.Effect; /** * Phantom identifier for the scoped transaction connection service associated * with a SQL client. * * @stability unstable * @category models * @since 4.0.0 */ export interface TransactionConnection { readonly _: unique symbol; } /** * Namespace containing types associated with transaction connection services. * * @stability unstable * @since 4.0.0 */ export declare namespace TransactionConnection { /** * Service payload stored during a transaction, containing the active * connection and nested transaction depth. * * @stability unstable * @category services * @since 4.0.0 */ type Service = readonly [conn: Connection.Connection, depth: number]; } /** * Creates a unique context service tag for the active transaction connection of * a specific SQL client. * * @stability unstable * @category services * @since 4.0.0 */ export declare const TransactionConnection: (clientId: number) => Context.Service; /** * Context reference used by SQL integrations to opt in to safe integer * handling; defaults to `false`. * * @stability unstable * @category services * @since 4.0.0 */ export declare const SafeIntegers: Context.Reference; /** * Context reference holding the statement that starts a top-level * transaction in place of the client's `BEGIN`; defaults to `undefined`, which * keeps the client's. * * **When to use** * * Use when one transaction needs its own isolation level or access mode, as * in `BEGIN ISOLATION LEVEL SERIALIZABLE` on PostgreSQL, rather than following * the client's `BEGIN` with a `SET TRANSACTION`, which costs another round * trip. * * **Details** * * The statement runs unprepared. Nested transactions ignore it and keep their * savepoints. * * **Gotchas** * * It applies to every top-level transaction started while it is provided, * including in forked fibers, so provide it around the single * `withTransaction` it is meant for. `@effect/sql-libsql`, `@effect/sql-mssql` * and `@effect/sql-sqlite-do` don't start transactions with a SQL statement * and ignore it. The statement is sent as raw SQL, so never build it from * untrusted input. * * **Example** (Starting a serializable transaction) * * ```ts * import { Effect } from "effect" * import { SqlClient } from "effect/sql" * * const program = Effect.gen(function*() { * const sql = yield* SqlClient.SqlClient * return yield* sql.withTransaction(sql`SELECT 1`).pipe( * Effect.provideService(SqlClient.BeginTransaction, "BEGIN ISOLATION LEVEL SERIALIZABLE") * ) * }) * ``` * * @stability unstable * @category services * @since 4.0.6 */ export declare const BeginTransaction: Context.Reference; export {}; //# sourceMappingURL=SqlClient.d.ts.map