Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: inline primary key creation for postgres flavours #5233

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 33 additions & 6 deletions lib/dialects/postgres/schema/pg-tablecompiler.js
Expand Up @@ -44,7 +44,10 @@ class TableCompiler_PG extends TableCompiler {
const createStatement = ifNot
? 'create table if not exists '
: 'create table ';
const columnsSql = ' (' + columns.sql.join(', ') + this._addChecks() + ')';
const columnsSql = ` (${columns.sql.join(', ')}${
this.primaryKeys() || ''
}${this._addChecks()})`;

let sql =
createStatement +
this.tableName() +
Expand All @@ -65,6 +68,28 @@ class TableCompiler_PG extends TableCompiler {
if (hasComment) this.comment(this.single.comment);
}

primaryKeys() {
const pks = (this.grouped.alterTable || []).filter(
(k) => k.method === 'primary'
);
if (pks.length > 0 && pks[0].args.length > 0) {
const columns = pks[0].args[0];
let constraintName = pks[0].args[1] || '';
let deferrable;
if (isObject(constraintName)) {
({ constraintName, deferrable } = constraintName);
}
deferrable = deferrable ? ` deferrable initially ${deferrable}` : '';
constraintName = constraintName
? this.formatter.wrap(constraintName)
: this.formatter.wrap(`${this.tableNameRaw}_pkey`);

return `, constraint ${constraintName} primary key (${this.formatter.columnize(
columns
)})${deferrable}`;
}
}

addColumns(columns, prefix, colCompilers) {
if (prefix === this.alterColumnsPrefix) {
// alter columns
Expand Down Expand Up @@ -153,11 +178,13 @@ class TableCompiler_PG extends TableCompiler {
constraintName = constraintName
? this.formatter.wrap(constraintName)
: this.formatter.wrap(`${this.tableNameRaw}_pkey`);
this.pushQuery(
`alter table ${this.tableName()} add constraint ${constraintName} primary key (${this.formatter.columnize(
columns
)})${deferrable}`
);
if (this.method !== 'create' && this.method !== 'createIfNot') {
this.pushQuery(
`alter table ${this.tableName()} add constraint ${constraintName} primary key (${this.formatter.columnize(
columns
)})${deferrable}`
);
}
}

unique(columns, indexName) {
Expand Down
46 changes: 37 additions & 9 deletions test/integration2/schema/misc.spec.js
Expand Up @@ -483,25 +483,54 @@ describe('Schema (misc)', () => {
});

describe('uuid types - postgres', () => {
before(async () => {
await knex.schema.createTable('uuid_column_test', (table) => {
table.uuid('id', { primaryKey: true });
});
after(async () => {
if (isPgBased(knex)) {
await knex.schema.dropTable('uuid_column_test');
}
});

after(async () => {
await knex.schema.dropTable('uuid_column_test');
it('creates a uuid column as primary using fluid syntax', async function () {
if (!isPgBased(knex)) {
return this.skip();
}
const table_name = 'uuid_column_test';
const expected_column = 'id';
const expected_type = 'uuid';

await knex.schema.dropTableIfExists(table_name);
await knex.schema.createTable(table_name, (table) => {
table.uuid('id').primary();
});

const cols = await knex.raw(
`select c.column_name, c.data_type
from INFORMATION_SCHEMA.COLUMNS c
join INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu
on (c.table_name = cu.table_name and c.column_name = cu.column_name)
where c.table_name = ?
and (cu.constraint_name like '%_pkey' or cu.constraint_name = 'primary')`,
table_name
);
const column_name = cols.rows[0].column_name;
const column_type = cols.rows[0].data_type;

expect(column_name).to.equal(expected_column);
expect(column_type).to.equal(expected_type);
});

it('#5211 - creates an uuid column as primary key', async function () {
if (!isPgBased(knex)) {
return this.skip();
}

const table_name = 'uuid_column_test';
const expected_column = 'id';
const expected_type = 'uuid';

await knex.schema.dropTableIfExists(table_name);
await knex.schema.createTable(table_name, (table) => {
table.uuid('id', { primaryKey: true });
});

const cols = await knex.raw(
`select c.column_name, c.data_type
from INFORMATION_SCHEMA.COLUMNS c
Expand Down Expand Up @@ -862,8 +891,7 @@ describe('Schema (misc)', () => {
tester(
['pg', 'cockroachdb'],
[
'create table "test_table_three" ("main" integer not null, "paragraph" text default \'Lorem ipsum Qui quis qui in.\', "metadata" json default \'{"a":10}\', "details" jsonb default \'{"b":{"d":20}}\')',
'alter table "test_table_three" add constraint "test_table_three_pkey" primary key ("main")',
'create table "test_table_three" ("main" integer not null, "paragraph" text default \'Lorem ipsum Qui quis qui in.\', "metadata" json default \'{"a":10}\', "details" jsonb default \'{"b":{"d":20}}\', constraint "test_table_three_pkey" primary key ("main"))',
]
);
tester('pg-redshift', [
Expand Down
99 changes: 87 additions & 12 deletions test/unit/schema-builder/postgres.js
Expand Up @@ -150,6 +150,20 @@ describe('PostgreSQL SchemaBuilder', function () {
);
});

it('should create a table with inline uuid primary key creation', function () {
tableSql = client
.schemaBuilder()
.createTable('uuids', function (table) {
table.uuid('id').primary();
})
.toSQL();

equal(1, tableSql.length);
expect(tableSql[0].sql).to.equal(
'create table "uuids" ("id" uuid, constraint "uuids_pkey" primary key ("id"))'
);
});

it('basic alter table', function () {
tableSql = client
.schemaBuilder()
Expand Down Expand Up @@ -547,6 +561,19 @@ describe('PostgreSQL SchemaBuilder', function () {
);
});

it('drop primary takes constraint name', function () {
tableSql = client
.schemaBuilder()
.table('users', function (table) {
table.dropPrimary('testconstraintname');
})
.toSQL();
equal(1, tableSql.length);
expect(tableSql[0].sql).to.equal(
'alter table "users" drop constraint "testconstraintname"'
);
});

it('drop unique', function () {
tableSql = client
.schemaBuilder()
Expand Down Expand Up @@ -688,9 +715,9 @@ describe('PostgreSQL SchemaBuilder', function () {
});
})
.toSQL();
equal(2, tableSql.length);
expect(tableSql[1].sql).to.equal(
'alter table "person" add constraint "user_id_primary" primary key ("user_id") deferrable initially immediate'
equal(1, tableSql.length);
expect(tableSql[0].sql).to.equal(
'create table "person" ("user_id" integer, constraint "user_id_primary" primary key ("user_id") deferrable initially immediate)'
);
});

Expand Down Expand Up @@ -733,12 +760,9 @@ describe('PostgreSQL SchemaBuilder', function () {
table.string('name').primary();
})
.toSQL();
equal(2, tableSql.length);
equal(1, tableSql.length);
expect(tableSql[0].sql).to.equal(
'create table "users" ("name" varchar(255))'
);
expect(tableSql[1].sql).to.equal(
'alter table "users" add constraint "users_pkey" primary key ("name")'
'create table "users" ("name" varchar(255), constraint "users_pkey" primary key ("name"))'
);
});

Expand Down Expand Up @@ -1992,12 +2016,15 @@ describe('PostgreSQL SchemaBuilder', function () {
it('#1430 - .primary & .dropPrimary takes columns and constraintName', function () {
tableSql = client
.schemaBuilder()
.table('users', function (t) {
.createTable('users', function (t) {
t.string('test1');
t.string('test2');
t.primary(['test1', 'test2'], 'testconstraintname');
})
.toSQL();
equal(1, tableSql.length);
expect(tableSql[0].sql).to.equal(
'alter table "users" add constraint "testconstraintname" primary key ("test1", "test2")'
'create table "users" ("test1" varchar(255), "test2" varchar(255), constraint "testconstraintname" primary key ("test1", "test2"))'
);

tableSql = client
Expand All @@ -2007,11 +2034,59 @@ describe('PostgreSQL SchemaBuilder', function () {
})
.toSQL();

expect(tableSql[1].sql).to.equal(
'alter table "users" add constraint "testconstraintname" primary key ("test")'
expect(tableSql[0].sql).to.equal(
'create table "users" ("test" varchar(255), constraint "testconstraintname" primary key ("test"))'
);
});

describe('alter with primary', function () {
it('liquid argument', function () {
tableSql = client
.schemaBuilder()
.alterTable('users', function (t) {
t.string('test').primary();
})
.toSQL();

equal(2, tableSql.length);
expect(tableSql[0].sql).to.equal(
'alter table "users" add column "test" varchar(255)'
);
expect(tableSql[1].sql).to.equal(
'alter table "users" add constraint "users_pkey" primary key ("test")'
);
});
it('liquid argument with name', function () {
tableSql = client
.schemaBuilder()
.alterTable('users', function (t) {
t.string('test').primary('testname');
})
.toSQL();

equal(2, tableSql.length);
expect(tableSql[0].sql).to.equal(
'alter table "users" add column "test" varchar(255)'
);
expect(tableSql[1].sql).to.equal(
'alter table "users" add constraint "testname" primary key ("test")'
);
});
it('call on table with columns and name', function () {
tableSql = client
.schemaBuilder()
.alterTable('users', function (t) {
t.primary(['test1', 'test2'], 'testconstraintname');
})
.toSQL();

equal(1, tableSql.length);
expect(tableSql[0].sql).to.equal(
'alter table "users" add constraint "testconstraintname" primary key ("test1", "test2")'
);
});
});

describe('queryContext', function () {
let spy;
let originalWrapIdentifier;
Expand Down