diff --git a/src/lib/PostgresMetaTables.ts b/src/lib/PostgresMetaTables.ts index d4304645..2c886009 100644 --- a/src/lib/PostgresMetaTables.ts +++ b/src/lib/PostgresMetaTables.ts @@ -171,7 +171,13 @@ export default class PostgresMetaTables { if (replica_identity === undefined) { // skip } else if (replica_identity === 'INDEX') { - replicaSql = `${alter} REPLICA IDENTITY USING INDEX ${replica_identity_index};` + if (replica_identity_index === undefined) { + return { + data: null, + error: { message: 'replica_identity_index is required when replica_identity is INDEX' }, + } + } + replicaSql = `${alter} REPLICA IDENTITY USING INDEX ${ident(replica_identity_index)};` } else { replicaSql = `${alter} REPLICA IDENTITY ${replica_identity};` } diff --git a/test/lib/tables.ts b/test/lib/tables.ts index 752b9e96..d47693bd 100644 --- a/test/lib/tables.ts +++ b/test/lib/tables.ts @@ -454,6 +454,50 @@ test("allow ' in comments", async () => { await pgMeta.tables.remove(res.data!.id) }) +test('replica identity index', async () => { + const { data: table } = await pgMeta.tables.create({ name: 't_replica_idx' }) + await pgMeta.columns.create({ + table_id: table!.id, + name: 'c', + type: 'int8', + is_nullable: false, + }) + await pgMeta.query('CREATE UNIQUE INDEX "t_replica ""idx""" ON t_replica_idx (c)') + + let res = await pgMeta.tables.update(table!.id, { + replica_identity: 'INDEX', + replica_identity_index: 't_replica "idx"', + }) + expect(res).toMatchObject({ + data: { replica_identity: 'INDEX' }, + error: null, + }) + + res = await pgMeta.tables.update(table!.id, { replica_identity: 'INDEX' }) + expect(res).toMatchObject({ + data: null, + error: { message: 'replica_identity_index is required when replica_identity is INDEX' }, + }) + + res = await pgMeta.tables.update(table!.id, { + replica_identity: 'INDEX', + replica_identity_index: 'nonexistent_idx; DROP TABLE t_replica_idx; --', + }) + expect(res.data).toBeNull() + expect(res.error).toMatchObject({ + message: expect.stringContaining('nonexistent_idx; DROP TABLE t_replica_idx; --'), + }) + + // The injection payload above must not have dropped the table. + res = await pgMeta.tables.retrieve({ id: table!.id }) + expect(res).toMatchObject({ + data: { name: 't_replica_idx', replica_identity: 'INDEX' }, + error: null, + }) + + await pgMeta.tables.remove(table!.id) +}) + test('primary keys', async () => { let res = await pgMeta.tables.create({ name: 't' }) await pgMeta.columns.create({ table_id: res.data!.id, name: 'c', type: 'int8' })