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.
Related
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)
I have the following nodejs code (simplified):
// tables
CREATE TABLE admins (
admin_id bigint DEFAULT nextval('admins_seq') NOT NULL,
username character varying(25) NOT NULL,
password character varying(150) NOT NULL
);
CREATE TABLE admin_notifications (
admin_notification_id bigint DEFAULT nextval('admin_notifications_seq') NOT NULL,
admin_id bigint NOT NULL,
type character varying(150) NOT NULL
);
ALTER TABLE admin_notifications ADD CONSTRAINT admin_notifications_to_admins_fk FOREIGN KEY (admin_id) REFERENCES admins(admin_id) ON DELETE CASCADE;
await client.query('BEGIN');
// create a new array in the database and return the newly created admin id
const query = 'INSERT INTO admins (username, password) VALUES($1,$2) RETURNING admin_id';
const values = [];
const result = await client.query(query, values);
// insert a welcome notification for the newly created admin
const query2 = 'INSERT INTO admin_notifications (admin_id, type) VALUES ($1,$2)';
const values2 = [result.rows[0].admin_id, 'welcome'];
const result2 = await client.query(query2, values2);
await client.query('COMMIT');
A little explanation: i create a new admin in the table admins. When a new admin is created, I want to insert a new welcome notification in the table admin_notifications. The column admin_id in the table admin_notifications is a foreign key to the table admins (admin_id).
I get the following error:
Error: insert or update on table "admin_notifications" violates foreign key constraint "admin_notifications_to_admins_fk"
When I leave out the transactions it works?!
Does it has something to do with deferring constraints? And how to handle this in my code?
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");
}),
...
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')
})
}
Am working on bigger project where we have multiple schemas in one Postgres DB. We have created foreign keys between schemas. Here is an example >
We have company schema and user schema. Company schema has company_users table which have foreign key restriction on user.users table
CREATE TABLE company.company_user
(
id serial NOT NULL,
company_id integer NOT NULL,
user_id integer NOT NULL,
created_at timestamp(0) without time zone,
updated_at timestamp(0) without time zone,
deleted_at timestamp(0) without time zone,
CONSTRAINT company_user_pkey PRIMARY KEY (id),
CONSTRAINT company_user_company_id_foreign FOREIGN KEY (company_id)
REFERENCES company.companies (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT company_user_user_id_foreign FOREIGN KEY (user_id)
REFERENCES "user".users (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Following queries run in Postgres without issue
BEGIN;
insert into "db"."user"."users" (id,"surname", "firstname", "email", "position", "language_id", "is_super_admin", "updated_at", "created_at") values (156,'Mueller', 'Julianne', 'julianne.mueller1#example.org', 'Nuclear Power Reactor Operator', 41, false, '2017-01-13 12:35:10', '2017-01-13 12:35:10') returning "id";
insert into "db"."company"."company_user" ("company_id", "user_id", "updated_at", "created_at") values (4445, 156, '2017-01-13 12:35:10', '2017-01-13 12:35:10') returning "id";
COMMIT;
However if i perform same queries via Eloquent in Laravel
\DB::beginTransaction();
$user = new User(["surname" => 'Mueller',
"firstname" => 'Julianne',
"email" => 'julianne.mueller1#example.org',
"position" => 'Nuclear Power Reactor Operator',
"language_id" => 41,
"is_super_admin" => false]
);
if (!$user->save()) {
\DB::rollBack();
return false;
}
\Log::error($user->id);
$company_user = new CompanyUser([
"company_id" => 4445,
"user_id" => $user->id
]);
if (!$company_user->save()) {
\DB::rollBack();
return false;
}
\DB::commit();
is throwing folloing error (it seems that it cannot find id of user in the table)
PDOException: SQLSTATE[23503]: Foreign key violation: 7 ERROR: insert or update on table "company_user" violates foreign key constraint "company_user_user_id_foreign"
Would anyone can say why this is not working? \Log::error($user->id) is printing id of inserted user. I tried to print out queries from Laravel with DB listener, all queries are executed in correct order, but am still getting this error.
Ok so we found a solution. It seems that we need to start transaction for each of schemas separately + each foreign key that are referencing different schema than their own should be created as deferred.
Make sure CompanyUser has $fillable:
$fillable = ['user_id', 'company_id'];
Also, make sure there is a user with this ID is already in users table. Maybe you'll need to get rid of transaction.