This is called for each query before it is executed. You can modify the query by transforming its OperationNode tree provided in args.node and returning the transformed tree. You'd usually want to use an OperationNodeTransformer for this.
If you need to pass some query-related data between this method and transformResult
you
can use a WeakMap
with args.queryId as the key:
import type {
KyselyPlugin,
QueryResult,
RootOperationNode,
UnknownRow
} from 'kysely'
interface MyData {
// ...
}
const data = new WeakMap<any, MyData>()
const plugin = {
transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
const something: MyData = {}
// ...
data.set(args.queryId, something)
// ...
return args.node
},
async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
// ...
const something = data.get(args.queryId)
// ...
return args.result
}
} satisfies KyselyPlugin
You should use a WeakMap
instead of a Map
or some other strong references because transformQuery
is not always matched by a call to transformResult
which would leave orphaned items in the map
and cause a memory leak.
This method is called for each query after it has been executed. The result of the query can be accessed through args.result. You can modify the result and return the modifier result.
A plugin that allows handling
in ()
andnot in ()
expressions.These expressions are invalid SQL syntax for many databases, and result in runtime database errors.
The workarounds used by other libraries always involve modifying the query under the hood, which is not aligned with Kysely's philosophy of WYSIWYG. We recommend manually checking for empty arrays before passing them as arguments to
in
andnot in
expressions instead, but understand that this can be cumbersome. Hence we're going with an opt-in approach where you can choose if and how to handle these cases. We do not want to make this the default behavior, as it can lead to unexpected behavior. Use it at your own risk. Test it. Make sure it works as expected for you.Using this plugin also allows you to throw an error (thus avoiding unnecessary requests to the database) or print a warning in these cases.
Examples
The following strategy replaces the
in
/not in
expression with a noncontingent expression. A contradiction (falsy)1 = 0
forin
, and a tautology (truthy)1 = 1
fornot in
), similarily to how Knex.js, PrismaORM, Laravel, SQLAlchemy handle this.The generated SQL (SQLite):
The following strategy does the following:
When
in
, pushes anull
value into the empty list resulting inin (null)
, similiarly to how TypeORM and Sequelize handlein ()
.in (null)
is logically the equivalent of= null
, which returnsnull
, which is a falsy expression in most SQL databases. We recommend NOT using this strategy if you plan to usein
inselect
,returning
, oroutput
clauses, as the return type differs from theSqlBool
default type for comparisons.When
not in
, casts the left operand aschar
and pushes a unique value into the empty list resulting incast({{lhs}} as char) not in ({{VALUE}})
. Casting is required to avoid database errors with non-string values.The generated SQL (SQLite):
The following custom strategy throws an error when an empty list is encountered to avoid unnecessary requests to the database: