Executing Queries
Both SqbClient and SqbConnection expose an execute() method with the same signature:
execute(query: string | Query, options?: QueryExecuteOptions): Promise<QueryResult>;
query is either a raw SQL string or a Query object built with
@sqb/builder (Select(), Insert(), Update(),
Delete(), ...).
client.execute() vs connection.execute()
// SqbClient.execute(): acquires a connection, runs the query, releases the connection.
const result = await client.execute('select * from customers where id = $1', {
params: [1],
});
// SqbConnection.execute(): runs on a connection you already hold.
await client.acquire(async connection => {
const result = await connection.execute('select * from customers where id = $1', {
params: [1],
});
});
client.execute() is a convenience wrapper: it calls client.acquire() internally, runs your
query on the freshly acquired connection, and releases that connection in a finally block —
unless the result carries an open Cursor, in which case the
connection is instead retain()-ed and only released once the cursor is closed. Use
connection.execute() directly when you need multiple statements to share one connection (e.g.
inside a transaction).
Queries on a given connection are also serialized through an internal task queue, so calling
execute() multiple times concurrently on the same SqbConnection runs them one after another
rather than interleaved.
QueryExecuteOptions
interface QueryExecuteOptions {
params?: Record<string, any> | any[];
autoCommit?: boolean;
cursor?: boolean;
transform?: ValueTransformFunction;
fetchRows?: number;
ignoreNulls?: boolean;
namingStrategy?: FieldNaming;
objectRows?: boolean;
showSql?: boolean;
prettyPrint?: boolean;
action?: string;
fetchAsString?: DataType[];
}
Any option you don't pass falls back to the matching field on
ClientDefaults (config.defaults from
SqbClient's constructor), and if that's also unset, to a hard-coded runtime default. The table
below documents the actual runtime defaults, verified directly against the
_prepareQueryRequest() method in sqb-connection.ts — several of these differ from what the
inline // Default = ... comments in the source say, so trust this table over the JSDoc:
| Option | Resolved via | Runtime default | Notes |
|---|---|---|---|
autoCommit | options.autoCommit → connection's ConnectionOptions.autoCommit → defaults.autoCommit → true | true | Only applies when the connection isn't already inside a transaction — if connection.inTransaction is true, autoCommit is forced to false regardless of what you pass. The JSDoc on ConnectionOptions.autoCommit / QueryExecuteOptions.autoCommit says "Default = false" — that is incorrect; outside a transaction, a query auto-commits unless you explicitly pass autoCommit: false. |
cursor | options.cursor → defaults.cursor → false | false | See Cursors & Streaming. |
objectRows | options.objectRows → defaults.objectRows → true | true | Rows are returned as plain objects (keyed by field name) unless you opt into array rows. The JSDoc says "Default = driver default" — in the actual code it's hard-coded to true, independent of the adapter. |
ignoreNulls | options.ignoreNulls → defaults.ignoreNulls → false | false | Also forced back to false whenever the effective objectRows is false — ignoreNulls only ever applies to object rows. |
fetchRows | options.fetchRows → defaults.fetchRows → 100 | 100 | In regular (non-cursor) mode, the maximum number of rows fetched. In cursor mode, the prefetch batch size (see Cursors & Streaming). Both ClientDefaults.fetchRows and QueryExecuteOptions.fetchRows carry a stale // Default = 10 comment in the source — the actual fallback coded in _prepareQueryRequest() is 100. |
namingStrategy | options.namingStrategy → defaults.fieldNaming → undefined | none (original names) | See FieldNaming. |
transform | options.transform → defaults.transform → undefined | none | See below. |
showSql | options.showSql → defaults.showSql → false | false | When true, QueryResult.query is populated with the executed QueryRequest (SQL + params). |
prettyPrint | options.prettyPrint → defaults.prettyPrint → false | false | Passed through to the SQL generator when the query is a Query builder object. |
params | — | none | Positional array or named-parameter object, forwarded to the query generator (for Query objects) or the driver (for raw SQL). |
action | — | '' | Free-form label attached to the request, mainly useful for logging/hooks. |
fetchAsString | — | none | List of DataTypes the adapter should coerce to string instead of native type. |
objectRows and row shape
With the default objectRows: true, rows come back as Record<string, any> keyed by field name
(after namingStrategy is applied). With objectRows: false, rows come back as arrays, positioned
by QueryResult.fields' index.
Field naming
namingStrategy accepts a FieldNaming value —
'original' | 'lowercase' | 'uppercase' | 'camelcase' | 'pascalcase', or a
(fieldName: string) => string | undefined function. It renames both the keys on object rows and
the name field on each FieldInfo in QueryResult.fields. Returning undefined from a custom
function drops that field from object rows entirely.
transform
type ValueTransformFunction = (value: any, fieldInfo?: FieldInfo) => any;
Runs over every field value as rows are normalized, letting you convert/mask driver values before
they reach your code (e.g. converting a driver-specific date type to a plain Date).
Reading the result: QueryResult
interface QueryResult {
executeTime: number;
fields?: FieldInfoMap;
rows?: any;
rowType?: 'array' | 'object';
query?: QueryRequest; // only populated when showSql: true
returns?: any;
rowsAffected?: number;
cursor?: Cursor; // only populated when cursor: true
}
executeTime— milliseconds the query took, measured around the adapter call.fields/rowType/rows— populated for statements that return rows (accessingresult.fields.get('columnName')gives you aFieldInfo).rowsAffected— populated for statements that report an affected-row count (e.g.UPDATE,DELETE).cursor— populated instead ofrowswhencursor: truewas requested — see Cursors & Streaming.
Example: raw SQL with parameters
const result = await client.execute(
'select id, name from customers where active = $1',
{ params: [true], fetchRows: 500 },
);
console.log(result.rows);