Postgres - cannot use subquery in DEFAULT expression - postgresql

A new unique column is being added to a table. This column's value comes from a C# random code generator method, but for existing rows I need to rely on Postgres.
I have the following EF Core migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
var randomCodeSql = #"
SELECT string_agg (substr('ABCDEFGHJKLMNPQRSTUVWXYZ123456789', ceil (random() * 33)::integer, 1), '')
FROM generate_series(1, 6)";
migrationBuilder.AddColumn<string>(
name: "reference_code",
table: "invitations",
type: "character varying(12)",
maxLength: 12,
nullable: false,
defaultValueSql: randomCodeSql);
migrationBuilder.CreateIndex(
name: "ix_invitations_reference_code",
table: "invitations",
column: "reference_code",
unique: true);
}
The raw SQL is from this answer.
I get this error:
0A000: cannot use subquery in DEFAULT expression
Is there a code-first solution to this problem?

Related

EF Core 5 migrations - update composite primary and foreign keys

I am using EF Core code first migrations. I have two tables (models), TableA has 4 columns which are set as composite primary key and TableB has the same 4 columns set as primary key and foreign key to TableA.
On of the four columns is of type int and I need to change it to long. I changed the type to long in both models and added a new migration. However, I am getting the following error when trying to update the database to apply the migration:
The object 'PK_TableA' is dependent on column 'Number'.
The object 'FK_TableB_TableA_Column1_Column2_Column3_Column4' is dependent on column 'Number'.
ALTER TABLE ALTER COLUMN Number failed because one or more objects access this column.
The column Number (column #3) in the error above is the one which has the type of int.
The generated migration is as follows:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableA",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableB",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
}
I checked a number of topics here but none of them includes a solution for a "composite primary and foreign keys".
I found this article about manually changing the generated migration code. But I am not sure if it s going to work with a composite primary and foreign key. Or, if it is the right way to fix this issue.
Any advise will be appreciated.
Just in case someone was looking for an answer to this question, this is how I solved the issue.
I edited the generated migration code, as follows, to manually remove the foreign key first in TableB and then the two composite primary keys in TableA and TableB and then added them after changing the column type to bigint (according to the generated migration code):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_TableB",
table: "TableB");
migrationBuilder.DropPrimaryKey(
name: "PK_TableA",
table: "TableA");
migrationBuilder.DropPrimaryKey(
name: "PK_TableB",
table: "TableB");
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableA",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableB",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AddPrimaryKey(
name: "PK_TableA",
table: "TableA",
columns: new[] { "Column1", "Column2", "Column3", "Column4" });
migrationBuilder.AddPrimaryKey(
name: "PK_TableB",
table: "TableB",
columns: new[] { "Column1", "Column2", "Column3", "Column4" });
migrationBuilder.AddForeignKey(
name: "FK_TableB",
table: "TableB",
columns: new[] { "Column1", "Column2", "Column3", "Column4" },
principalTable: "TableA",
principalColumns: new[] { "Column1", "Column2", "Column3", "Column4" },
onDelete: ReferentialAction.Cascade);
}
The same code was added for the Down method.

typeorm relation in embedded column

I'm working with NestJs, Typeorm and Postgresql.
I'm trying to use ManyToOne relation in embedded entity. I need to load foreign key column in node environment, so added one additional column(createdById column below). This makes problem.
Here is my code.
A.ts
#Entity()
export class A {
#PrimaryGeneratedColumn()
id!: number;
#Column(() => Embed, { prefix: false })
embed!: Embed;
#CreateDateColumn({ name: 'created_at' })
createdAt!: Date;
}
Embed.ts
export class Embed {
#Column()
x!: number;
#Column()
y!: number;
#ManyToOne(() => B)
#JoinColumn({ name: 'created_by_id' })
createdBy?: B;
#Column({ name: 'created_by_id' })
createdById!: number;
}
B.ts
#Entity()
export class B {
#PrimaryGeneratedColumn()
id!: number;
#CreateDateColumn({ name: 'created_at' })
createdAt!: Date;
}
When I run the app with option TYPEORM_SYNCHRONIZE=true and TYPEORM_LOGGING=true, I get error messages like query failed: CREATE TABLE "a" ("id" SERIAL NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "created_by_id" integer NOT NULL, "created_by_id" integer NOT NULL, "x" integer NOT NULL, "y" integer NOT NULL, CONSTRAINT "PK_684f21444e543375e4c2e6f27fe" PRIMARY KEY ("id")), Message: column \"created_by_id\" specified more than once.. Typeorm trying to create created_by_id column twice. (I applied custom NamingStrategy so that column of embedded entity's name to be snake_case)
If I place createdBy and createdById column to A directly, then it makes no error. Is it problem of typeorm version? Or any other solutions?
package version:
"dependencies": {
"#nestjs/typeorm": "7.1.0,
"typeorm": "0.2.31",
}
run with docker container,
node image: 16.14.2-alpine3.15,
postgres image: mdillon/postgis:11-alpine
TypeORM docs say you don't need to decorate embed class with #Entity().
See Name class in https://orkhan.gitbook.io/typeorm/docs/embedded-entities

EF Core migration taking old migrations changes along with new columns

I have added 2 more columns in aspnetusers table and added migrations. But it's failing and showing error stating FK_Projects_AspNetUsers_UserId' is not a constraint.
Could not drop constraint. See previous errors.
public string NavbarBackGroundImagePath { get; set; }
public string NavbarBackGroundColorCode { get; set; }
Added these 2 columns.
migrationBuilder.DropForeignKey(
name: "FK_Projects_AspNetUsers_UserId",
table: "Projects");
migrationBuilder.DropIndex(
name: "IX_Projects_UserId",
table: "Projects");
migrationBuilder.DropColumn(
name: "UserId",
table: "Projects");
migrationBuilder.AddColumn<int>(
name: "WorkspaceId",
table: "Projects",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<string>(
name: "NavbarBackGroundColorCode",
table: "AspNetUsers",
maxLength: 10,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "NavbarBackGroundImagePath",
table: "AspNetUsers",
maxLength: 200,
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Projects_WorkspaceId",
table: "Projects",
column: "WorkspaceId");
migrationBuilder.AddForeignKey(
name: "FK_Projects_Workspaces_WorkspaceId",
table: "Projects",
column: "WorkspaceId",
principalTable: "Workspaces",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
But in migration file it's taking all these things.
According to the error message, it seems that this error happens when you want to drop the foreign key constraint in the Projects table, if the Projects table doesn't contain the "FK_Projects_AspNetUsers_UserId" constraint or doesn't contain the "UserId" property, it will show this error.
So, please check the Projects table in the database, whether you have removed the UerId column. And whether the Projects table have the foreign key constraint with the AspNetUsers table. If the Projects table doesn't contain this constraint, try to remove the DropForeignKey related code:
migrationBuilder.DropForeignKey(
name: "FK_Projects_AspNetUsers_UserId",
table: "Projects");

How to avoid DROP DEFAULT statements with Doctrine 2 Migrations diff on first run?

I had an existing PostgreSQL database with a table created like this:
CREATE TABLE product (id SERIAL PRIMARY KEY, name VARCHAR(100) DEFAULT NULL)
This table is described in a YML Doctrine2 file within a Symfony2 project:
Acme\DemoBundle\Entity\Product:
type: entity
table: product
fields:
id:
id: true
type: integer
nullable: false
generator:
strategy: SEQUENCE
name:
type: string
length: 100
nullable: true
When I run for the first time the Doctrine Migrations diff task, I should get a versioning file with no data in the up and down methods. But what I get instead is this :
// ...
class Version20120807125808 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is autogenerated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "postgresql");
$this->addSql("ALTER TABLE product ALTER id DROP DEFAULT");
}
public function down(Schema $schema)
{
// this down() migration is autogenerated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "postgresql");
$this->addSql("CREATE SEQUENCE product_id_seq");
$this->addSql("SELECT setval('product_id_seq', (SELECT MAX(id) FROM product))");
$this->addSql("ALTER TABLE product ALTER id SET DEFAULT nextval('product_id_seq')");
}
}
Why are differences detected? How can I avoid this? I tried several sequence strategies with no success.
A little update on this question.
Using Doctrine 2.4, the solution is to use the IDENTITY generator strategy :
Acme\DemoBundle\Entity\Product:
type: entity
table: product
id:
type: integer
generator:
strategy: IDENTITY
fields:
name:
type: string
length: 100
nullable: true
To avoid DROP DEFAULT on fields that have a default value in the database, the default option on the field is the way to go. Of course this can be done with lifecycle callbacks, but it's necessary to keep the default value in the database if this database is used by other apps.
For a "DEFAULT NOW()" like default value, the solution is the following one:
Acme\DemoBundle\Entity\Product:
type: entity
table: product
id:
type: integer
generator:
strategy: IDENTITY
fields:
creation_date:
type: datetime
nullable: false
options:
default: CURRENT_TIMESTAMP
Doctrine 2.0 does not support the SQL DEFAULT keyword, and will always try to drop a postgres default value.
I have found no solution to this problem, I just let doctrine handle the sequences itself.
This is a opened bug registered here :
http://www.doctrine-project.org/jira/browse/DBAL-903

Entity Framework Migrations: convert decimal? to decimal with default value for nulls

I get SqlCeException ... The column cannot contain null values. [ Column name = Subtotal
when I run the following Entity Framework migration.
public override void Up()
{
AlterColumn("BuildingOrders", "Subtotal",
c => c.Decimal(nullable: false, precision: 18, scale: 2));
}
Is this the right way to set the default value? It seems too easy :)
public override void Up()
{
AlterColumn("BuildingOrders", "Subtotal",
c => c.Decimal(nullable: false, precision: 18, scale: 2, defaultValue: 0));
}
I tried this after seeing a similar question for ruby on rails.
It doesn't work this way. You set default value in database but it is not used. You cannot define your property as nullable in class when mapped column is not nullable. Default value in database is applied only if you don't insert any value into that column but EF always inserts value to all columns which are not database generated (in turn values for database generated columns cannot be defined in the application) so in this case you defined your property as nullable and EF inserts null explicitly => exception.