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.
InternalPostgres implementation of the row-locking seam: appends
FOR UPDATE OF "<tableAlias>" to the select, so every row the statement
returns is exclusively locked until the enclosing transaction ends.
Scoping the lock to the Query's own base table (rather than a bare
FOR UPDATE) means a query that joins other tables neither locks rows the
caller never asked about nor raises Postgres's "FOR UPDATE cannot be
applied to the nullable side of an outer join".
This is a waiting lock: neither NOWAIT nor SKIP LOCKED is applied, so a
statement that collides with another transaction's lock blocks until that
transaction ends. Callers that cannot afford an unbounded wait should set a
lock_timeout on the connection; see the guarded-destroy notes on
Query#destroy.
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
StaticacquireInternalPostgres implementation of the transaction-scoped advisory lock seam.
Uses the single-bigint form pg_advisory_xact_lock(bigint), whose lock
space is distinct from the two-int4 form the test-database pool claims
with (see openTestDatabaseLockSession), so an application lock can
never collide with a pool claim.
Every key is taken in one statement, over unnest of the key array: a
function scan yields its rows in array order, so the caller's sorted
acquisition order — the thing that keeps two operations over the same
scopes from deadlocking — is preserved, while a batch that needs one key
per candidate record costs one round trip rather than N.
The locks are held until the enclosing transaction ends, and are waiting
(no NOWAIT): a second transaction asking for the same key blocks at that
key's row until the holder commits or rolls back. Because they are
transaction-scoped there is nothing to release, and re-acquiring a key
within one transaction is a no-op rather than an error.
The wait is bounded by lock_timeout, taken from
DreamApp.sortableScopeLockTimeout and applied to the acquisition
statement alone: the prior value is read in the same statement that sets
the bound and restored in the next one, so the transaction's row-lock waits
keep whatever the application configured. Postgres cancels the statement
with 55P03 when the bound elapses, and that is translated here into
SortableScopeLockWaitTimedOut so no Postgres error code escapes the
driver.
A timeout of 0 means "no bound", which is what lock_timeout itself
means by it, and is sent as the bound rather than skipped: an application
whose connection carries a lock_timeout of its own would otherwise have
its acquisition cancelled by that bound, which is neither the unbounded
wait 0 asks for nor a cancellation this driver could translate.
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, views, materialized views, and partitioned and foreign
tables of every schema the connection's user can use, except Postgres's
own schemas, tables that inherit from another (partitions included) and
Kysely's migration tables. A domain column reads as the type at the
bottom of its domains. An enum outside the public schema is named
<schema>.<name>.
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.
StaticopenInternalPostgres implementation of the per-worker test-database claim seam (see
TestDatabaseLockSession and src/db/testDatabasePool.ts).
Postgres advisory locks are global to the cluster (not scoped to a single
database), so one dedicated connection — to the postgres maintenance
database — can reserve a pool index for the whole cluster. We use the
two-int4 form pg_try_advisory_lock(key1, key2), whose lock space is
distinct from the single-bigint form pg_advisory_lock(bigint) that
application code typically uses, so a claim can never clash with an
app-level advisory lock. key1 folds a hash of the lock namespace (the
base database name) into a fixed dream-pool constant; key2 is the pool
index. The lock auto-releases when the connection drops on process exit.
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