Skip to content

migrate() blocks forever if passed db.$qb.adapter #652

@IlyaSemenov

Description

@IlyaSemenov

In my schema/database management helper functions, I prefer to pass around instances of Db not OrchidORM<any> to reduce coupling.

However, when I call migrate() passing db.adapter, it blocks forever and never resolves. When I re-construct the ORM interface as { $qb: db, $adapter: db.adapter }, it works fine. Please check the reproduction below. Note that the bug only repeats inside a true (non-test) transaction.

At the very least, this needs to be fixed (either runtime or type-wise). Currently, db.adapter is an AdapterBase, which is what migrate() is supposed to accept.

Broader question is, why do we even need this type?

export interface OrmParam {
$qb: Query;
$adapter: AdapterBase;
}
export type DbParam = OrmParam | AdapterBase;

I propose to get rid of this 'pseudo ORM' interface object unless there is a clear reason to have it. I believe it's not used anywhere outside rake-db?


Reproduction (orchid-orm@1.62.3):

import type { Db } from "orchid-orm"
import { createBaseTable } from "orchid-orm"
import { makeRakeDbConfig, migrate } from "orchid-orm/migrations"
import { orchidORM } from "orchid-orm/postgres-js"

export const BaseTable = createBaseTable()

const db = orchidORM({ log: true }, {})

const rakeDbConfig = makeRakeDbConfig({
  baseTable: BaseTable,
  basePath: import.meta.url,
  migrations: {
    "0001_initial": async () => [],
  },
  migrationsTable: "rake_migration",
})


// I don't need full ORM in these functions so prefer to pass Db, not OrchidORM<any>
async function myMigrate(db: Db) {
  await db.query`CREATE TABLE IF NOT EXISTS ${db.ref(rakeDbConfig.migrationsTable)} (version text NOT NULL, name text NOT NULL)`
  console.log("calling migrate()")
  // Below, migrate() will block forever.
  // Replacing db.adapter with { $qb: db, $adapter: db.adapter } fixes blocking.
  await migrate(db.adapter, { ...rakeDbConfig }).then(
    () => console.log("migrate() completed"),
    () => console.log("migrate() failed"),
  )
}

async function main() {
  // The issue only surfaces when using $transaction (and not testTransaction!).
  await db.$transaction(async () => {
    await myMigrate(db.$qb).then(
      () => console.log("myMigrate() completed"),
      () => console.log("myMigrate() failed"),
    )
  })
}

main()

Result:

% dropdb is;createdb is; bun 1.ts
(34.1ms) BEGIN
(5.8ms) CREATE TABLE IF NOT EXISTS "rake_migration" (version text NOT NULL, name text NOT NULL)
calling migrate()
--- blocks forever until ^C

with db.adapter replaced with { $qb: db, $adapter: db.adapter }, it works correctly:

% dropdb is;createdb is; bun 1.ts
(31.5ms) BEGIN
(6.0ms) CREATE TABLE IF NOT EXISTS "rake_migration" (version text NOT NULL, name text NOT NULL)
calling migrate()
Migrating database undefined

Migrated file:///Users/is/test/0001_initial

migrate() completed
myMigrate() completed
(10.1ms) COMMIT

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions