ProtectedconnectionProtected ReadonlydreamProtected ReadonlydreamProtecteddreamProtected Readonly InternalinnerStatic InternalsupportsWhether this driver implements the transaction-scoped advisory lock seam
(acquireAdvisoryTransactionLocks). Adapters that can hold a
key-addressed lock for the life of a transaction override this to true.
It defaults to false so that a driver without the primitive fails loud
for features that depend on it (today, the Sortable decorator) rather
than silently racing on position values.
Static InternalsupportsWhether this driver implements the per-worker test-database claim seam
(openTestDatabaseLockSession). Adapters that can hold a
process-lifetime, auto-releasing lock to reserve one database from the
test pool override this to true. It defaults to false so that a new
adapter fails loud under vitest rather than silently sharing a single
database across overlapping workers — see src/db/testDatabasePool.ts.
InternalAdapter seam for exclusive row locking. Given the select statement about to
be executed, return a statement that additionally acquires an exclusive
lock on each row it returns, held until the enclosing transaction commits
or rolls back (Postgres: SELECT ... FOR UPDATE OF <table>).
This is only ever reached when a caller opts into a locked read (today,
Query#destroy({ lock: true })), and only from inside a transaction. The
base implementation throws so that an adapter which has not implemented row
locking fails loud rather than silently performing an unlocked read and
quietly dropping the compare-and-set guarantee the caller asked for.
the select statement to lock
the alias of the Query's base table; only rows of that table are locked, so a joined query does not lock unrelated rows and does not fail on the nullable side of an outer join
ProtectedassertInternalPrevents mutation paths that discard an association query's ownership constraint.
InternalThe order statements that the declared order of the associations the
Query is built on or joins contributes to its ORDER BY, in the order they
sort, each column namespaced by the alias it is read from. They are read
off the same join code that emits them, by building the select.
An array of namespaced order statements
Retrieves the average value of the specified column for this Query
await Game.query().avg('score')
// 1
a column name on the model
the average of the values of the specified column for this Query
Retrieves the average of the specified column within each group, keyed by the value of the provided group column.
await CompositionAsset.query().avgBy('name', 'score')
// Map(2) { 'primary' => 5, 'secondary' => 4 }
the column to group by (base or joined-association-namespaced)
the column to average within each group
A Map from each present group value to the average of the aggregated column in that group
Retrieves the number of records in the database
await User.query().count()
The number of records in the database
Retrieves the number of records in each group, keyed by the value of the provided group column.
await User.query().countBy('name')
// Map(2) { 'fred' => 2, 'zed' => 1 }
the column to group by (base or joined-association-namespaced)
A Map from each present group value to the number of records in that group
InternalThis method is used internally by a Query driver to take the result of a single row in a database, and turn that row into the provided dream instance.
If needed, the return type can be overriden to explicitly define the resulting dream instance, in cases where a proper type for the dream class cannot be inferred, i.e.
this.dbResultToDreamInstance<typeof Dream, DreamInstance>(result, this.dreamClass)
executes provided query instance as a deletion query.
the number of deleted rows
InternalUsed to hydrate dreams with the provided associations
InternalUsed by loadBuider
InternalThe primary key of each table the Query reaches through a HasOne or HasMany association (one it joins, one an association it joins goes through, or one its association query is built on), namespaced by the alias it is read from, in join order. They are read off the same join code that emits the joins, by building the select.
An array of namespaced primary key columns
Retrieves the max value of the specified column for this Query
await User.query().max('id')
// 99
a column name on the model
the max value of the specified column for this Query
Retrieves the max value of the specified column within each group, keyed by the value of the provided group column.
await CompositionAsset.query().maxBy('name', 'score')
// Map(2) { 'primary' => 9, 'secondary' => 4 }
the column to group by (base or joined-association-namespaced)
the column to take the max of within each group
A Map from each present group value to the max of the aggregated column in that group
Retrieves the min value of the specified column for this Query
await User.query().min('id')
// 1
a column name on the model
the min value of the specified column for this Query
Retrieves the min value of the specified column within each group, keyed by the value of the provided group column.
await CompositionAsset.query().minBy('name', 'score')
// Map(2) { 'primary' => 1, 'secondary' => 4 }
the column to group by (base or joined-association-namespaced)
the column to take the min of within each group
A Map from each present group value to the min of the aggregated column in that group
Returns a new Kysely SelectQueryBuilder instance to be used in a sub Query
const records = await User.where({
id: Post.query().nestedSelect('userId'),
}).all()
// [User{id: 1}, ...]
the column to use for your nested Query
A Kysely SelectQueryBuilder instance
InternalRuns the query and extracts plucked values
An array of plucked values
InternalA page of a select that carries DISTINCT ON (as a Query that calls
distinct, or an association that declares distinct, produces), or null
for any other select.
Such a select keeps the row of each group that sorts first in its ORDER
BY, so narrowing it to the rows after a cursor would let it keep a row it
does not return (a group's later row, once the cursor has passed the
group's first). It is read as a derived table instead, and narrowed to
rowsAfter, sorted and limited outside it. The Query's order statements
follow the select's own ORDER BY, if it has one, so that the select keeps
the same row of each group every time it runs, and each of their columns
is read out of it under a short alias (as pluck reads them), since a
joined table's column cannot be named outside it.
where statements, any of which a row of the page matches
The page's rows, each an array of its order statement columns' values, or null
InternalFor a select that carries DISTINCT ON (as a Query that calls distinct,
or an association that declares distinct, produces), the values of
rowKeyColumns for every row it returns, sorted by those columns,
ascending; null for any other select.
The select runs as all() runs it, with its own ORDER BY choosing each
group's row, and is read as a derived table, sorted outside it. Each of
rowKeyColumns is read out of it under a short alias (as pluck reads
them), since a joined table's column cannot be named outside it.
the namespaced columns that tell the select's rows apart
The rows' values of rowKeyColumns, or null
Returns the sql that would be executed by this Query
User.where({ email: 'how@yadoin' }).sql()
// {
// query: {
// kind: 'SelectQueryNode',
// from: { kind: 'FromNode', froms: [Array] },
// selections: [ [Object] ],
// distinctOn: undefined,
// joins: undefined,
// groupBy: undefined,
// orderBy: undefined,
// where: { kind: 'WhereNode', where: [Object] },
// frontModifiers: undefined,
// endModifiers: undefined,
// limit: undefined,
// offset: undefined,
// with: undefined,
// having: undefined,
// explain: undefined,
// setOperations: undefined
// },
// sql: 'select "users".* from "users" where ("users"."email" = $1 and "users"."deleted_at" is null)',
// parameters: [ 'how@yadoin' ]
//}
An object representing the underlying sql statement
Retrieves the sum value of the specified column for this Query
await Game.query().sum('score')
// 1
a column name on the model
the sum of the values of the specified column for this Query
Retrieves the sum of the specified column within each group, keyed by the value of the provided group column.
await CompositionAsset.query().sumBy('name', 'score')
// Map(2) { 'primary' => 10, 'secondary' => 4 }
the column to group by (base or joined-association-namespaced)
the column to sum within each group
A Map from each present group value to the sum of the aggregated column in that group
InternalRetrieves an array containing all records matching the Query.
Be careful using this, since it will attempt to pull every
record into memory at once. When querying might return a large
number of records, consider using .findEach, which will pull
the records in batches.
await User.query().all()
an array of dreams
InternalUsed for applying first and last queries
A dream instance or null
Converts the given dream class into a Kysely query, enabling you to build custom queries using the Kysely API
await User.query().toKysely('select').where('email', '=', 'how@yadoin').execute()
the type of Kysely query builder instance you would like to obtain
A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
executes provided query instance as an update query.
the number of updated rows
StaticacquireInternalAcquire exclusive, transaction-scoped locks on arbitrary keys, waiting
until any other transaction holding one of them finishes. The locks are
released when the enclosing transaction commits or rolls back — never by an
explicit unlock — so callers only ever have to acquire (Postgres:
pg_advisory_xact_lock(bigint)).
Unlike applyRowLock, this does not depend on rows existing: it is
the only way to serialize writers that are about to insert into the same
logical group. The Sortable decorator uses it to make each sort scope's
position computation exclusive, keyed on the physical database, table,
position field and scope values.
The seam takes the whole key set rather than one key at a time so that a driver able to express it can take them in a single round trip — the batched, row-locking query APIs can need one key per candidate record per sortable field. Keys must be acquired in the order given: callers sort them, and that shared order is what keeps two operations over the same scopes from deadlocking. A driver that has no bulk form implements this by looping over the keys in order.
The wait must be bounded. A caller that finds a key held waits for the
holder's transaction, and an implementation that waits forever turns an
ordinary mistake — a sortable operation issued without the enclosing
transaction, so that it waits on a key that transaction itself holds — into
a permanently wedged connection that no deadlock detector can see, because
only one side is waiting. So an implementation applies the
sortableScopeLockTimeout DreamApp option (milliseconds) to the
acquisition and raises SortableScopeLockWaitTimedOut when it elapses. A
configured 0 means no bound, and is applied just as literally: the
acquisition waits without bound even where the connection carries a
wait bound of its own. The bound belongs to
the acquisition alone: whatever lock-wait behavior the connection was
configured with has to be in force again by the time this returns, so that
the row locks applyRowLock takes are unaffected.
Only ever called when supportsAdvisoryTransactionLocks is true;
the base default throws.
the transaction the locks are scoped to
the 64-bit signed lock keys, in acquisition order
Staticdbcreate the database. Must respond to the NODE_ENV value.
Staticdbdelete the database. Must respond to the NODE_ENV value.
StaticdbOptionaldreamTransaction: DreamTransaction<I, I["DB"]> | nullStaticdestroydestroys a dream, possibly implementing soft delete if reallyDestroy is false
and the record being deleted implements soft delete. Does nothing at all when a
beforeDestroy hook called preventDeletion.
the dream instance you wish to destroy
a transaction to encapsulate, consistently provided by underlying dream mechanisms
whether or not to reallyDestroy. If false, soft delete will be attempted when relevant
the number of rows removed — zero when a preventDeletion veto
stopped the write, and zero when the row was already gone, which on the
soft delete path includes a row that is already soft deleted
StaticdialectStaticduplicateStaticensureStaticforeignInternalreturns the foreign key type based on the primary key received. gives the driver the opportunity to switch i.e. bigserial to bigint.
StaticgenerateThis should build a new migration file in the migrations folder
of your application. This will then need to be read and run
whenever the migrate method is called. The filename should
contain a timestamp at the front of the filename, so that it
is sorted by date in the file tree, and, more importantly, so
they can be run in order by your migration runner.
StaticintrospectReads the tables, columns and enums of the connection's database, from
which sync writes the connection's types/db.ts and types/dream.ts.
It returns every table the connection can read, sorted by schema and
name; sync applies tableIncludePattern and tableExcludePattern
itself.
the connection to read
StaticisInternalWhether error is the database aborting a transaction to break a deadlock
with another. withConcurrentWriterRetry reruns a transaction of Dream's
own that was aborted this way. A driver that does not recognize one reports
false, and nothing is retried.
StaticisInternalWhether error is the database refusing a write for violating a unique
constraint. withConcurrentWriterRetry asks this of an error raised at
COMMIT, which is where a DEFERRABLE INITIALLY DEFERRED constraint — the
one DreamMigrationHelpers.addDeferrableUniqueConstraint declares — reports
a collision. A driver that does not recognize one reports false, and nothing
is retried.
Staticmigratemigrate the database. Must respond to the NODE_ENV value.
StaticopenInternalOpen a dedicated, process-lifetime lock session used to claim one database
from the per-worker test pool. Only ever called when
supportsParallelTestDatabases is true; the base default throws.
The returned session wraps a single dedicated connection: the orchestrator
(src/db/testDatabasePool.ts) probes many pool indexes on the one session
via tryAcquire and keeps whichever it acquires, holding it for the
worker's lifetime.
StaticprimaryInternalused to return the computed primary key type based on the primaryKeyType set in the DreamApp class.
Staticrollbackrollback the database. Must respond to the NODE_ENV value.
Staticsavepersists any unsaved changes to the database. If a transaction is provided as a second argument, it will use that transaction to encapsulate the persisting of the dream, as well as any subsequent model hooks that are fired.
RETURNING * (rather than enumerating the compiled column list) keeps
writes working under schema/image skew: during a rolling deploy, a
container built before a drop-column migration would otherwise name the
dropped column in RETURNING and fail with 42703 column does not exist on every write, even writes that never touch that column. The
SET/VALUES half still names only dirty attributes, so a write that
actually sets a dropped column still fails loudly. The returned row is
filtered to the compiled column list before hydration (see
internal/saveDream.ts), so a column the image doesn't know about never
reaches setAttributes.
KNOWN CONSTRAINT: star-selects are only safe because nothing in this
stack uses named prepared statements — node-postgres prepares a
statement only when given an explicit name, and Kysely never names
them, so every query is re-planned. With named prepared statements, a
concurrent ADD COLUMN changes a cached plan's result shape and
Postgres raises cached plan must not change result type (the reason
Rails added enumerate_columns_in_select_statements). If a future
driver or pooling layer enables named prepared statements, revisit
every RETURNING * / select * in this driver.
StaticsetInternalthis method is called when dream is initializing, and is used to configure the database to utilize custom type parsers for a variety of data types.
the name of the connection you are doing this for
void
Staticsyncdefines the syncing behavior for dream and psychic,
which is run whenever the sync command is called.
StatictransactionBuilds a new DreamTransaction instance, provides the instance to the provided callback.
await ApplicationModel.transaction(async txn => {
const user = await User.txn(txn).create({ email: 'how@yadoin' })
await Pet.txn(txn).create({ user })
})
A callback function to call. The transaction provided to the callback can be passed to subsequent database calls within the transaction callback
void
stores the Dream models joined in this Query instance