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

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')
});
}

Related

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

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')
})
}

Migration on two schemas

I have 2 schemas on my db:schema1 and public and I want to create some tables there. I tried code like this in my migration:
return knex.schema
.raw('CREATE SCHEMA IF NOT EXISTS schema1;')
.createTableIfNotExists('table1', table => {
table.increments('id')
})
.withSchema('public')
.createTableIfNotExists('table2', table => {
table.increments('id')
})
And I hoped to have: schema1.table1 and public.table2 but I don't. Any ideas how to do that?
Belayer is quite correct in the comments, but I'll expand into an answer to demonstrate the syntax. You'll also need to avoid doing things like:
createTableIfNotExists('schema1.table1', table => {
per Identifier Syntax, this won't work: just get into the habit of always using withSchema.
Further, I don't recommend issuing subsequent createTables without waiting for the previous one to finish. While it might work, I think you'll run into trouble with relations etc. That makes your original code more like:
exports.up = knex =>
knex
.raw("CREATE SCHEMA IF NOT EXISTS schema1")
.then(() =>
knex.withSchema("schema1").createTableIfNotExists("table1", table => {
table.increments("id");
})
)
.then(() =>
knex.withSchema("public").createTableIfNotExists("table2", table => {
table.increments("id");
})
);

How to declare cascade delete in EF when child table isn't mapped as separate entity

I have got a database first approach with EF5 and here is a fragment of mappings:
internal class xxxMapping : EntityTypeConfiguration<Order>
{
public xxxMapping ()
{
ToTable("my_table");
//......
HasMany(it => it.Documents)
.WithMany()
.Map(
m =>
{
m.ToTable("dependent_table");
m.MapLeftKey("left_key_id");
m.MapRightKey("right_key_id");
});
}
What is the best way to declare, using fluent API, that when some row is deleted from my_table, then dependent rows from dependent_table will be deleted too (Cascade delete option in FK)
UPD It seems to be working without any additional code (Of course - if foreign key in table is configured properly). But i'm not sure it's a good practice to do so

EntityFramework - many-to-many reference in the DB without a backreference in the model

In my application users can define Parameters, and then create SlideSets based on a grouping of parameters.
I am using code-first Entity Framework 5.0 and I have the following model:
class SlideSet {
public ICollection<Parameter> Parameter
}
class Parameter {}
A parameter might be used by many slidesets or none at all. However, in my domain a parameter has no need to reference a SlideSet, they are in separate bounded contexts (both SlideSet and Parameter are Aggregate Roots). As such, I don't want to put a reference from Parameter to SlideSet.
The table model (I don't care about table/column names) that I want is
Table SlideSet
Table Param
Table SlideSetParam
FK_SlideSet
FK_Param
I know I could model this by introducing a ParameterGroup entity or a Param.SlideSets collection, but it would exist solely for ORM mapping purposes (and cause serialization issues). Is there any other way to tell EF to generate this table model from my entities?
This should make you a Parameter w/o a navigation property:
modelBuilder.Entity<SlideSet>()
.HasMany(x => x.Parameters)
.WithRequired();
EDIT:
Based on the comment - that should be all together similar. This seems to work nicely what you're trying to do....
modelBuilder.Entity<SlideSet>()
.HasMany(x => x.Parameters)
.WithMany();
...and you can use it either way:
var slideset = new SlideSet { Parameters = new []
{
new Parameter{},
new Parameter{},
new Parameter{},
new Parameter{},
}
};
var slideset2 = new SlideSet { };
db.SlideSets.Add(slideset);
db.SaveChanges();
var slidesets = db.SlideSets.ToList();
var parameters = db.Parameters.ToList();
Console.WriteLine("");
db.SlideSets.Add(slideset2);
db.SaveChanges();
slidesets = db.SlideSets.ToList();
parameters = db.Parameters.ToList();
Console.WriteLine("");
...and the SQL:
CREATE TABLE [dbo].[Parameters] (
[ParameterID] [int] NOT NULL IDENTITY,
CONSTRAINT [PK_dbo.Parameters] PRIMARY KEY ([ParameterID])
)
CREATE TABLE [dbo].[SlideSets] (
[SlideSetID] [int] NOT NULL IDENTITY,
CONSTRAINT [PK_dbo.SlideSets] PRIMARY KEY ([SlideSetID])
)
CREATE TABLE [dbo].[SlideSetParameters] (
[SlideSet_SlideSetID] [int] NOT NULL,
[Parameter_ParameterID] [int] NOT NULL,
CONSTRAINT [PK_dbo.SlideSetParameters] PRIMARY KEY ([SlideSet_SlideSetID], [Parameter_ParameterID])
)
CREATE INDEX [IX_SlideSet_SlideSetID] ON [dbo].[SlideSetParameters]([SlideSet_SlideSetID])
CREATE INDEX [IX_Parameter_ParameterID] ON [dbo].[SlideSetParameters]([Parameter_ParameterID])
ALTER TABLE [dbo].[SlideSetParameters] ADD CONSTRAINT [FK_dbo.SlideSetParameters_dbo.SlideSets_SlideSet_SlideSetID] FOREIGN KEY ([SlideSet_SlideSetID]) REFERENCES [dbo].[SlideSets] ([SlideSetID]) ON DELETE CASCADE
ALTER TABLE [dbo].[SlideSetParameters] ADD CONSTRAINT [FK_dbo.SlideSetParameters_dbo.Parameters_Parameter_ParameterID] FOREIGN KEY ([Parameter_ParameterID]) REFERENCES [dbo].[Parameters] ([ParameterID]) ON DELETE CASCADE
...this makes the original tables practically 'agnostic' of the relationships (many-to-many) - while index table is automatically generated in the background.
You can also further customize that and make your own SlideSetParam (e.g. if you'd want to add additional fields there) with pretty much the same layout - just Parameters would have to point to that instead.

Entity Frmwk pb: search for 'added' entities in current context

I'm using guids as PK for my entities.
As EF doesn't support the newid() SQL statement, I force a Guid.NewGuid() in my creation methods.
Here is my problem :
I've a table with a clustered unique constraint (2 strings, not PK).
I'm running some code in the same EF context which perfoms operations and adds/links entities, etc.
Is it possible to search for an entity in 'Added' state in my context ? ; that is to say which is in my context, but not yet inserted in my DB.
To avoid the raising of the SQL unique constraint, I have to know if the entity is already 'queued' in the context, and to re-use it instead of creating a new Guid (... and a different entity! :( )
this post saved me :) :
Link
var stateEntries = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EtityState.Modified | EntityState.Unchanged);
var roleEntityEntries = stateEntries.Select(s => s.Entity).OfType<Role>();
roleEntity = roleEntityEntries.FirstOrDefault(r => r.RoleName.Trim().ToLower() == roleName.Trim().ToLower());
if (roleEntity == null)
{
return new Role { RoleName = roleName };
}