question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Partial Indexes not working with unique index where column value is null

See original GitHub issue

I saw in this issue that support for Partial Indexes was added: https://github.com/oguimbal/pg-mem/issues/89

However, if you insert null as a value for a column with a unique index, in actual postgres it works since null values are not considered in unique value validation, but pg-mem does appear to include them when validating uniques and prevents the 2nd row from inserting with a value of null.

Example SQL for postgres (works as expected against an actual postgres engine)

create table "test_table" (
  "id" character varying(36) NOT NULL, 
  "unique_data" character varying(36), 
  "deleted_at" timestamp without time zone,
  CONSTRAINT "PK_test_table_id"
  PRIMARY KEY ("id")  
);

CREATE UNIQUE INDEX "UXtest_table_unique_data" ON "public"."test_table" ("unique_data") WHERE deleted_at IS NULL;

insert into test_table ("id", "unique_data", "deleted_at") VALUES('1', default, default );
insert into test_table ("id", "unique_data", "deleted_at") VALUES('2', default, default );

Example jest / nestjs / typeorm / pg-mem test (NOT WORKING but I’d expect it to)

// "@nestjs/testing": "^8.0.0",
import { Test, TestingModule } from '@nestjs/testing';
// "@nestjs/typeorm": "^8.0.2",
import { getRepositoryToken } from '@nestjs/typeorm';
// "typeorm": "^0.2.37",
import { Repository, Connection, Entity,
  Column,
  PrimaryColumn,
  Index,
  DeleteDateColumn, 
} from 'typeorm';
// "pg-mem": "^2.0.2",
import { newDb } from 'pg-mem';

@Index('UXtest_table_unique_data', ['uniqueData'], {
  unique: true,
  where: 'deleted_at IS NULL'
})
@Entity()
export class TestEntity {
  @PrimaryColumn({ length: 36 })
  id: string;

  @Column({ length: 36, name: 'unique_data', nullable: true })
  uniqueData?: string;

  @DeleteDateColumn({
    name: 'deleted_at',
    type: 'timestamp without time zone',
    nullable: true,
  })
  deletedAt: Date;
}

describe('ExamplePartialUniqueProblemWithPgMem', () => {
  let testRepo: Repository<TestEntity>, connection: Connection;

  beforeEach(async () => {
    // create a postgres memory db
    const db = newDb({});

    // define current_database
    // work around typeorm calling 'current_database' function
    db.public.registerFunction({
      implementation: () => 'test',
      name: 'current_database',
    });

    // create a Typeorm connection
    connection = await db.adapters.createTypeormConnection({
      type: 'postgres',
      entities: [TestEntity],
    });

    // create tables
    await connection.synchronize();
    testRepo = connection.getRepository(TestEntity);
  });

  afterEach(async () => {
    await connection.close();
  });

  it('create 2 test entries will null for a unique column', async () => {

    await testRepo.save({
      id: '1',
    });

    await testRepo.save({
      id: '2',
    });

    // get id 2
    const record2 = await testRepo.findOne('2');

    expect(record2.id).toEqual('2');
  });
});

Running this test produces this output for me

npm run test -- test.spec

> test@0.0.1 test
> jest --testResultsProcessor=jest-junit "test.spec"

 FAIL  src/talent/test.spec.ts (8.483 s)
  ExamplePartialUniqueProblemWithPgMem
    ✕ create 2 test entries will null for a unique column (241 ms)

  ● ExamplePartialUniqueProblemWithPgMem › create 2 test entries will null for a unique column

    QueryFailedError: ERROR: insert into "test_entity" (id, unique_data, deleted_at) values ($1, $2, $3) returning "unique_data" - duplicate key value violates unique constraint "test_entity_pkey"
    DETAIL: Key (unique_data)=() 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 "test_entity"("id", "unique_data", "deleted_at") VALUES ('2', DEFAULT, DEFAULT) RETURNING "deleted_at";

    👉 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 QueryFailedError.TypeORMError [as constructor] (error/TypeORMError.ts:7:9)
      at new QueryFailedError (error/QueryFailedError.ts:9:9)
      at PostgresQueryRunner.<anonymous> (driver/postgres/PostgresQueryRunner.ts:258:19)
      at step (../node_modules/tslib/tslib.js:143:27)
      at Object.throw (../node_modules/tslib/tslib.js:124:57)
      at rejected (../node_modules/tslib/tslib.js:115:69)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        8.542 s, estimated 9 s
Ran all test suites matching /test.spec/i.
npm ERR! code ELIFECYCLE
npm ERR! errno 1

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
jshanmancommented, Oct 5, 2021

Confirmed fixed!

0reactions
jshanmancommented, Oct 5, 2021

I’ll test it out, thanks for the fast followup!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Postgres partial index on IS NULL not working
As mentioned above, I am trying to drop the state column so I want to be able to fetch the same rows using...
Read more >
Can't create a unique index that ignores nulls in MongoDB
To use the partial index, a query must contain the filter expression (or a modified filter expression that specifies a subset of the...
Read more >
Documentation: 15: 11.8. Partial Indexes - PostgreSQL
A partial index is an index built over a subset of a table; the subset is defined by a conditional expression (called the...
Read more >
Unique index in mongoDB 3.2 ignoring null values
The problem is Sparse indexes can't be used with the Partial index. Also, adding unique indexes, adds the null value to the index...
Read more >
Partial Indexes - SQLite
A partial index is an index over a subset of the rows of a table. In ordinary indexes, there is exactly one entry...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found