Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/errors/SchemaValidationError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const ormSvCodeMap = {
message: 'Column type for one-to-one relation (owned side) must be bigint',
explanation: 'The column type for a one-to-one relation (owned side) must be defined as a bigint. Resolve by changing the column type to bigint.',
},
// 3103 Missing index on owned side does not make sense
'ORM-SV-3104': {
message: 'No unique constraint or unique index was defined for one-to-one relation',
explanation: 'Without the database forcing uniqueness, the one-to-one property might be violated and turn into a many-to-one. Putting a unique constraint or unique index on the column is an effective way to prevent this problem.',
},

// One-to-one inverse relations
'ORM-SV-3110': {
Expand Down
10 changes: 10 additions & 0 deletions src/tools/validateSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ export async function validateSchema(entityDefinitions: Record<string, BaseEntit
if (table[columnName]['udt_name'] != 'int8') {
errors.push(new SchemaValidationError('ORM-SV-3102', { entity: entityName, field: fieldName, table: tableName, column: columnName }));
}

// Make sure there is a unique constraint / index on the column in question
const hasUniqueIndex = indexes.some(idx => {
return idx.tablename == tableName &&
idx.indexdef.split('USING')[1].includes('(' + columnName + ')') &&
idx.indexdef.includes('CREATE UNIQUE INDEX');
});
if (!hasUniqueIndex) {
warnings.push(new SchemaValidationError('ORM-SV-3104', { entity: entityName, field: fieldName, table: tableName, column: columnName }));
}
}

// One-to-one inverse relations
Expand Down
34 changes: 34 additions & 0 deletions tests/pg-backend/validateSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,39 @@ describe('Postgres: validateSchema', () => {
expect(result.valid).toBe(true);
expect(result.warnings.map(w => w.code)).not.toContain('ORM-SV-3113');
});

it('give a warning when one-to-one does not have unique constraint or unique index', async () => {
const result = await runValidationAgainstSchema(`
create table "user" (id bigserial primary key, full_name character varying not null, username character varying not null);
create table "todo_item" (id bigserial primary key, created_at timestamptz not null, description character varying not null, author_id bigint not null references "user");
alter table "user" add column favorite_todo_id bigint references "todo_item";
create index on "user" (favorite_todo_id);
`, `drop table if exists "user", "todo_item" cascade;`, entitySchema);
expect(result.valid).toBe(true);
expect(result.warnings.map(w => w.code)).toContain('ORM-SV-3104');
});

it('not give a warning when one-to-one does have a unique constraint', async () => {
const result = await runValidationAgainstSchema(`
create table "user" (id bigserial primary key, full_name character varying not null, username character varying not null);
create table "todo_item" (id bigserial primary key, created_at timestamptz not null, description character varying not null, author_id bigint not null references "user");
alter table "user" add column favorite_todo_id bigint references "todo_item";
create index on "user" (favorite_todo_id);
alter table "user" add constraint uniq_user_favorite_todo unique (favorite_todo_id);
`, `drop table if exists "user", "todo_item" cascade;`, entitySchema);
expect(result.valid).toBe(true);
expect(result.warnings.map(w => w.code)).not.toContain('ORM-SV-3104');
});

it('not give a warning when one-to-one does have a unique index', async () => {
const result = await runValidationAgainstSchema(`
create table "user" (id bigserial primary key, full_name character varying not null, username character varying not null);
create table "todo_item" (id bigserial primary key, created_at timestamptz not null, description character varying not null, author_id bigint not null references "user");
alter table "user" add column favorite_todo_id bigint references "todo_item";
create unique index on "user" (favorite_todo_id);
`, `drop table if exists "user", "todo_item" cascade;`, entitySchema);
expect(result.valid).toBe(true);
expect(result.warnings.map(w => w.code)).not.toContain('ORM-SV-3104');
});

});
Loading