Change primary key and its (foreign) references in existing table - Knex.js / Postgres - postgresql

My Postgres DB has 2 tables, with thousands of rows each, that were initially created with the following migration:
exports.up = async function(knex, Promise) {
// users
await knex.schema.createTable('users', table => {
table.increments('id');
table.timestamps(false, true);
table.text('uid').notNullable().unique(),
table.text('email').notNullable();
table.text('password');
table.text('first_name').notNullable();
table.text('last_name').notNullable();
table.text('subscription_id');
table.boolean('is_active').notNullable().defaultTo(true);
table.boolean('is_blocked').notNullable().defaultTo(false);
table.enum('role', ['member', 'admin', 'test_user']).notNullable().defaultTo('member');
});
await knex.schema.raw('create unique index users_lower_email_index on users (lower(email))');
// projects
await knex.schema.createTable('projects', table => {
table.increments('id');
table.timestamps(false, true);
table.text('name').notNullable();
table.integer('user_id').notNullable().references('users.id').onDelete('cascade');
table.text('data');
});
};
I need to change the foreign key on the projects table so that it references the uid column instead from the users table.
The constraints on the users table are:
I tried the following migration but I get the error:
migration failed with error: alter table "users" add column "uid" text - column "uid" of relation "users" already exists
My code:
exports.up = async function(knex, Promise) {
await knex.schema.alterTable('users', table => {
table.text('uid').primary('users_pkey');
})
await knex.schema.alterTable('projects', table => {
table.text('user_id').notNullable().references('users.uid').onDelete('cascade').alter();
});
};
I also tried table.text('uid').primary('users_pkey').alter(); but then I get:
migration failed with error: alter table "users" add constraint "users_pkey" primary key ("uid") - multiple primary keys for table "users" are not allowed
I will transfer all users in auth0 and I though its better if I use a UUID primary key for the users table.

Before you can change the primary key of users, you need to remove the existing one, then you should be able to drop and recreate the foreign key in projects:
exports.up = async (knex) => {
await knex.schema.alterTable('users', (table) => {
table.dropPrimary()
table.primary('uid')
})
await knex.schema.alterTable('projects', (table) => {
table.dropForeign('user_id')
table.foreign('user_id').references('users.uid').onDelete('cascade')
})
}

Related

SequelizeJS - Drop old pkey column

I created a new int8 column (id_int8) for a table and copied all of the ids into the column. I'd now like to write a migration that will rename the new column to id and delete the old id column. But I've gotten the error ERROR: Unknown constraint error.
import { QueryInterface } from 'sequelize';
export default {
up: async (queryInterface: QueryInterface): Promise<void> => {
await queryInterface.sequelize.query(
`
BEGIN;
LOCK TABLE table IN EXCLUSIVE MODE;
ALTER TABLE table DROP CONSTRAINT table_pkey, ADD CONSTRAINT table_pkey PRIMARY KEY USING INDEX id_int8_unique;
ALTER SEQUENCE table_id_seq OWNED BY table.id_int8;
ALTER TABLE table ALTER COLUMN id_int8 SET DEFAULT nextval('table_id_seq');
ALTER TABLE table DROP COLUMN id;
ALTER TABLE table RENAME COLUMN id_int8 TO id;
COMMIT;
`,
);
},
down: async (): Promise<void> => {
// no-op transaction
},
};
I can see that I have the index "table_pkey" PRIMARY KEY, btree (id)

knex.js migrations create tables with relationship between each other fails

I've looked all over the place, there are quite a few examples, but nothing complete or working.
Use case:
Simple database structure, a few tables and some relationships. Set up node, knex and pg - in a docker container.
All works well.
Created the migration file for the first table (table A) - ok.
Added the second table (table B) and a 1:n relationship from table B to A. All good.
Added a 1:n relationship from table A to table B. And the script errored out.
table info:
exports.up = function(knex) {
return knex.schema
.createTable('user', t => {
t.uuid('user_id').primary()
t.string('name', 100).notNullable()
t.string('surname', 100)
t.string('email').notNullable().unique()
t.string('password')
t
.boolean('email_confirmed')
.notNullable()
.defaultTo(false)
t
.datetime('last_login', { precision: 6 })
.defaultTo(knex.fn.now(6))
t.string('language')
t.string('newsletter')
t.timestamps(true, true)
t
.uuid('company_id')
.references('company_id')
.inTable('company')
})
.createTable('company', t => {
t.uuid('company_id').primary()
t.string('address_id')
t.string('name', 100).notNullable()
t.string('phone')
t.timestamps(true, true)
t
.uuid('owner_user_id')
.references('user_id')
.inTable('user')
})
}
error:
migration failed with error: alter table "user" add constraint
"user_company_uuid_foreign" foreign key ("company_uuid") references
"company" ("company_id") - relation "company" does not exist
I'd say it tries to create a table and add the foreign key before creating the second table (which the FK references).
Any idea on how to solve this.
it is not a m:n relationship really. A company can and should only have 1 owner. A user has to belong to a company, otherwise it can't exist. If this is not solvable I can have the user.company_id field as a simple string w/o any relationship.
Having a different table for user_companies would be overkill.
Thanks!
You are correct that it is trying to create the reference before the table it is referencing exists. The easiest way would probably be to simply delay the creation of the foreign key until after the companies table has been created. i.e.
exports.up = async function(knex) {
await knex.schema.createTable('user', t => {
t.uuid('user_id').primary()
t.string('name', 100).notNullable()
t.string('surname', 100)
t.string('email').notNullable().unique()
t.string('password')
t.boolean('email_confirmed')
.notNullable()
.defaultTo(false)
t.datetime('last_login', { precision: 6 })
.defaultTo(knex.fn.now(6))
t.string('language')
t.string('newsletter')
t.timestamps(true, true)
t.uuid('company_id')
});
await knex.schema.createTable('company', t => {
t.uuid('company_id').primary()
t.string('address_id')
t.string('name', 100).notNullable()
t.string('phone')
t.timestamps(true, true)
t.uuid('owner_user_id')
.references('user_id')
.inTable('user')
});
await knex.schema.table('user', t => {
t.foreign('company_id')
.references('company_id')
.inTable('company')
});
}

No unique constraint for composite foreign key, PostgreSQL

I am trying to relate an assosciative table with another table.
...
knex.schema.createTable("test", (table) => {
table.integer("subject_id").unsigned().references("id").inTable("subject").notNullable();
table.integer("duration_id").unsigned().references("id").inTable("duration").notNullable();
table.unique(["subject_id", "duration_id"]);
}),
knex.schema.createTable("exam", (table) => {
table.increments().notNullable();
table.integer("subject_id").unsigned();
table.integer("duration_id").unsigned();
...
...
table.foreign(["subject_id", "duration_id"]).references(["subject_id", "duration_id"]).inTable("test");
}),
...
throws the following error:
migration failed with error: alter table "exam" add constraint "exam_subject_id_duration_id_foreign" foreign key ("subject_id", "duration_id") references "test" ("subject_id", "duration_id")
- there is no unique constraint matching given keys for referenced table "test"
Having unique or primary key constraint for the two columns results in the same error. Is this a knex related error?
Moving the foreign key constraint after the unique constraint fixed the problem. Though I don't know the exact reason.
...
knex.schema.createTable("test", (table) => {
table.integer("subject_id").unsigned().notNullable();
table.integer("duration_id").unsigned().notNullable();
table.unique(["subject_id", "duration_id"]);
table.foreign("subject_id").references("id").inTable("subject");
table.foreign("duration_id").references("id").inTable("duration");
}),
knex.schema.createTable("exam", (table) => {
table.increments().notNullable();
table.integer("subject_id").unsigned();
table.integer("duration_id").unsigned();
...
...
table.foreign(["subject_id", "duration_id"]).references(["subject_id", "duration_id"]).inTable("test");
}),
...

knex / postgresql updating a foreign key to null

I am using knex.js with postgresql
So I have a table with a nullable foreign key.
shortened version:
exports.up = function (knex, Promise) {
return knex.schema.createTable('Note', function (table) {
table.string('id').primary()
table
.string('sourceId')
.references('id')
.inTable('Source')
.onDelete('SET NULL')
.index()
})
}
exports.down = function (knex, Promise) {
return knex.schema.dropTable('Note')
}
I am able to create a Note with or without a sourceId. However, if I create a Note with a sourceId and then update it to set the sourceId to NULL, the update does not work. I do not get an error message, but the foreign key is not removed.
For example if I create a Note with:
{
id: '123',
sourceId: '456'
}
and then try to update it:
const result = await Note.query().updateAndFetchById(id, {
id: '123',
sourceId: null
})
The result I get is :
Note {
id: '123',
sourceId: '456'
}
I have no problem if I try to update other nullable values to null (as long as they are not foreign keys) and I can update the sourceId to a different source's id.
If I try to update a not nullable foreign key to null, I get an error. But in the above case, I get no error. It just doesn't update.
Any idea what might be going on here?

Knex : Altering Primary Key Id column Error

The database is built on Knex / PostgreSQL.
I would like to alter a table to add 'unique()' type to primary key Id column.
But Migration failed with an error message below.
alter table "users" alter column "id" drop not null - column "id" is
in a primary key
exports.up = knex =>
knex.schema
.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
.createTable('users', table => {
table
.uuid('id')
.primary()
.notNullable()
.defaultTo(knex.raw('uuid_generate_v4()'));
table
.string('companyName')
.unique()
.notNullable();
table
.string('username')
.unique()
.notNullable();
table.string('password').notNullable();
table.string('contactNo').notNullable();
table.string('email').unique();
table.string('address');
table
.boolean('isAdmin')
.notNullable()
.defaultTo(false);
table
.enum('businessType', ['catering', 'restaurant'])
.defaultTo('catering');
table.integer('lunchQty').defaultTo(null);
table.integer('dinnerQty').defaultTo(null);
table
.uuid('bankAccountId')
.references('id')
.inTable('bank_account')
.onDelete('SET NULL')
.onUpdate('RESTRICT')
.index();
table.string('resetPasswordToken');
table.timestamps(true, true);
});
exports.down = knex => knex.schema.dropTable('users');
exports.up = knex =>
knex.schema.alterTable('users', table => {
table
.uuid('id')
.unique()
.primary()
.notNullable()
.defaultTo(knex.raw('uuid_generate_v4()'))
.alter();
});
exports.down = knex =>
knex.schema.table('users', table => {
table.dropColumn('id').alter();
});
PostgreSQL version : 11.1
Knex version : 0.19.2
I have searched here and there but couldn't find an answer for this issue.
Thanks for taking your time to help me out !
------------------------------ EDITION -----------------------------------
Qustion ) when I created delivery table like below. The error below accurred. I thought this was caused because I didn't set primary key unique.
migration failed with error: alter table "delivery" add constraint
"delivery_userid_foreign" foreign key ("userId") references "users"
("id") on update RESTRICT on delete CASCADE - there is no unique
constraint matching given keys for referenced table "users"
exports.up = knex =>
knex.schema
.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
.createTable('delivery', table => {
table
.uuid('id')
.primary()
.notNullable()
.defaultTo(knex.raw('uuid_generate_v4()'));
table
.uuid('routeId')
.references('id')
.inTable('routes')
.onDelete('CASCADE')
.onUpdate('RESTRICT')
.index();
table
.uuid('userId')
.references('id')
.inTable('users')
.onDelete('CASCADE')
.onUpdate('RESTRICT')
.index();
table.timestamps(true, true);
});
exports.down = knex => knex.schema.dropTable('delivery');
```
Primary keys are already unique and not null: there is no need for your alteration. See documentation. Knex is trying to do what you ask, but in order to alter the table it would have to drop id which is in a PRIMARY KEY constraint.
resolved the issue by removing alter users file !!
the 'unique key()' was the one that was causing the problem.