oguimbal / pg-mem

An in memory postgres DB instance for your unit tests

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Default values are evaluated at column creation time rather than insert time

zookatron opened this issue Β· comments

Describe the bug

The Postgres documentation specifically mentions that default values should be evaluated when a record is inserted, not when the table is created: https://www.postgresql.org/docs/current/ddl-default.html The current implementation seems to evaluate default values at table creation time, breaking many common default value use cases.

QueryError: insert into "devices" ("value") values ($1), ($2) - ERROR: insert into "devices" (value, id) values ($1, $2) returning "id" - duplicate key value violates unique constraint "devices_pkey"
DETAIL: Key (id)=(cfeea46a-be4d-45f4-ba47-21fe2316eeb9) already exists.

🐜 This seems to be an execution error, which means that your request syntax seems okay,
    but the resulting statement cannot be executed β†’ Probably not a pg-mem error.

*️⃣ Failed SQL statement: insert into "devices" ("value") values ('1'), ('2');

πŸ‘‰ You can file an issue at https://github.com/oguimbal/pg-mem along with a way to reproduce this error (if you can), and  the stacktrace:

    at BIndex.add (/node_modules/pg-mem/index.js:9506:19)
    at MemoryTable.indexElt (/node_modules/pg-mem/index.js:8276:21)
    at MemoryTable.doInsert (/node_modules/pg-mem/index.js:8155:14)
    at Insert.performMutation (/node_modules/pg-mem/index.js:9239:33)
    at Insert._doExecuteOnce (/node_modules/pg-mem/index.js:4335:29)
    at Insert.enumerate (/node_modules/pg-mem/index.js:4343:31)
    at enumerate.next (<anonymous>)
    at SelectExec.execute (/node_modules/pg-mem/index.js:3451:75)
    at /node_modules/pg-mem/index.js:5975:42
    at pushExecutionCtx (/node_modules/pg-mem/index.js:588:16) {
  data: {
    error: 'insert into "devices" (value, id) values ($1, $2) returning "id" - duplicate key value violates unique constraint "devices_pkey"',
    details: 'Key (id)=(cfeea46a-be4d-45f4-ba47-21fe2316eeb9) already exists.',
    code: '23505'
  },
  code: '23505',
  location: { start: 0, end: 0 },
  [Symbol(errorDetailsIncluded)]: true
}

To Reproduce

package.json:

{
  "type": "module",
  "dependencies": {
    "knex": "2.5.1",
    "pg-mem": "2.6.13"
  }
}

index.js:

import { newDb, DataType } from 'pg-mem';
import { randomUUID } from 'node:crypto';

const database = newDb();
const knex = database.adapters.createKnex();

database.public.registerFunction({
  name: 'uuid',
  args: [],
  returns: DataType.uuid,
  implementation: () => randomUUID(),
});

await knex.schema.createTable('devices', (table) => {
  table.uuid('id').primary().notNullable().defaultsTo(knex.raw('UUID()'));
  table.string('value');
});
await knex.table('devices').insert([{value: '1'}, {value: '2'}]);

knex.destroy();

pg-mem version

2.6.13

Same issue using :

  • typeorm: 9.0.1
  • pg-mem: 2.7.1

@oguimbal Any hints to where we can start if we want to fix this? I just started using pg-mem and am open to contributing to this issue.

Found a workaround. Since it's evaluating only once, a peek through the code suggested that it's simply checking for the type of function and evaluating in case it's a function. So, double wrapping the evaluator works. For the above example, this should work,

database.public.registerFunction({
  name: 'uuid',
  args: [],
  returns: DataType.uuid,
  implementation: () => () => randomUUID(),
});

Or when using the uuid package,

    schema.registerFunction({
      name: 'uuid_generate_v4',
      returns: DataType.uuid,
      implementation: () => v4
    });