drizzle-team / drizzle-orm

Headless TypeScript ORM with a head. Runs on Node, Bun and Deno. Lives on the Edge and yes, it's a JavaScript ORM too 😅
https://orm.drizzle.team
Apache License 2.0
24.34k stars 633 forks source link

[FEATURE]: Allow passing empty arrays to `.values()` during insert #1828

Open Hansenq opened 9 months ago

Hansenq commented 9 months ago

Describe what you want

When inserting values into the database, sometimes I end up needing to add an empty array. Currently this throws an error:

Error: values() must be called with at least one value
    at PgInsertBuilder.values (/Users/hansenq/Documents/CS/lightski/node_modules/src/pg-core/query-builders/insert.ts:50:10)
    at db.transaction.isolationLevel (/Users/hansenq/Documents/CS/lightski/packages/database/bin/data-migrations/2024-01-23-backfill-embeds-from-assistants.ts:50:33)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async NodePgSession.transaction (/Users/hansenq/Documents/CS/lightski/node_modules/src/node-postgres/session.ts:138:19)
    at async backfillEmbedsFromAssistants (/Users/hansenq/Documents/CS/lightski/packages/database/bin/data-migrations/2024-01-23-backfill-embeds-from-assistants.ts:38:3)
    at async /Users/hansenq/Documents/CS/lightski/packages/database/bin/data-migrations/2024-01-23-backfill-embeds-from-assistants.ts:84:5
    at async withDb (/Users/hansenq/Documents/CS/lightski/packages/database/bin/data-migrations/2024-01-23-backfill-embeds-from-assistants.ts:31:5)
    at async main (/Users/hansenq/Documents/CS/lightski/packages/database/bin/data-migrations/2024-01-23-backfill-embeds-from-assistants.ts:83:3)

Supporting [] inside db.insert(myTable).values([]) would be more ergonomically friendly and allow me to skip pulling the array into a variable and checking the length of that array before calling db.insert.

This is similar in spirit to #1295. Edit: Looks like this is the same as a previous issue https://github.com/drizzle-team/drizzle-orm/issues/1078

Angelelz commented 9 months ago

This is already supported in a different way. You can do:

// To insert a single value:
db.insert(myTable).values({});

// To insert 3 values:
db.insert(myTable).values([{}, {}, {}]);
Hansenq commented 9 months ago

I'm aware that exists, but that doesn't solve my issue. Basically, sometimes I'll copy some data from one table into another table (in order to backfill it). Sometimes a local dev DB doesn't have data in the first table, so the table will be empty:

let oldModels = await db.query.oldModels.findMany({}) // has length 0
await db
  .insert(newModels)
  .values(oldModels.map((om) => ({
    name: om.name,
    description: om.description,
  })))

But this code fails when oldModels is empty, forcing me to change the code to the following, which is unergonomic.

let oldModels = await db.query.oldModels.findMany({}) // has length 0
if (oldModels.length !== 0) {
  await db
    .insert(newModels)
    .values(oldModels.map((om) => ({
      name: om.name,
      description: om.description,
    })))
}

The most annoying thing is, because this happens to different local/test DBs that don't have prod data, this bug doesn't surface until later during runtime when another engineer is running the code or tests are being run, so it's super easy to forget.

elijaholmos commented 2 months ago

I have run into the same issue with MySQL. Although this is more of a QoL change, I was disappointed to find that the existing DX is lacking. Small FRs like these are still valuable and can set one library apart from another.