Files
alphane.ca/api/src/db/utils/knex-query-to-sequelize-raw.ts
T
2026-06-19 22:20:43 -07:00

55 lines
1.5 KiB
TypeScript

import { Knex } from "knex"
import { QueryTypes, QueryOptionsWithType } from "@sequelize/core"
import db from "@/db/db-client"
type QueryOptions = Omit<QueryOptionsWithType<QueryTypes.SELECT>, "bind" | "type">
// TODO: fix types to show that it might return null
export async function knexQueryToSequelizeSelect<T extends object>(
knexQuery: Knex.QueryBuilder,
options: QueryOptions = {}
) {
const { sql: knexSql, bindings } = knexQuery.toSQL().toNative()
const { sql: sequelizeSql, bind } = knexSqlNativeToSequelizeQueryWithBind({
sql: knexSql,
bindings,
})
return db.query<T>(sequelizeSql, {
...options,
bind,
type: QueryTypes.SELECT,
})
}
/**
* Note siganture is chosen so you can pass knexQuery.toSQL().toNative() directly
*
* Currently only tested with MSSQL dialect
*
* @param sqlWithKnexBindings knexQuery.toSQL().toNative().sql
* @param bindings knexQuery.toSQL().toNative().bindings
* @returns { sql: string, bind: unknown[] } in Sequelize format
*/
export function knexSqlNativeToSequelizeQueryWithBind({
sql: sqlWithKnexBindings,
bindings,
}: {
sql: string
bindings: readonly unknown[]
}): { sql: string; bind: unknown[] } {
let sqlWithSequelizeBindings = sqlWithKnexBindings
// converts "@p0" to "$1", "@p1" to "$2", etc.
bindings.forEach((_, i) => {
const pattern = new RegExp(`@p${i}\\b`, "g")
sqlWithSequelizeBindings = sqlWithSequelizeBindings.replace(pattern, `$${i + 1}`)
})
const mutableBindings = [...bindings]
return {
sql: sqlWithSequelizeBindings,
bind: mutableBindings,
}
}