Create a column with primary key using migrations - yii

I am using migrations in Yii to create a new column with. My code works fine however, I am unsure how to set a primary key?
This is a part from my migration file:
public function up()
{
$this->addColumn(
'competition_prizes',
'prize_id',
'INT(11) UNSIGNED NOT NULL FIRST',
);
$this->addPrimaryKey('PK1', 'competition_prizes', 'prize_id');
}
I don't know, how to make competition_prizes column the primary key.

After your addColumn function add this line
$this->addPrimaryKey('PK1', 'competition_prizes', 'prize_id')
Make sure there is no primary key column in your table.

This is working now - did the following:
$this->addColumn(
'competition_prizes',
'prize_id',
'INT(11) UNSIGNED NOT NULL AUTO_INCREMENT primary key FIRST'
);

For Yii2 version 2.0.6 - Take a look at http://www.yiiframework.com/doc-2.0/yii-db-schemabuildertrait.html#primaryKey%28%29-detail
$this->createTable('sometable', [
'sometable_id' => $this->primaryKey(), // does not depend on Your DBMS
'sometable_number' => 'SMALLINT(6) UNSIGNED NULL DEFAULT NULL'// will be depend on your DBMS.
]);

you can also do this
$this->addColumn('competition_prizes', 'prize_id', 'pk');
'pk' will translate to 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY' Yii2 QueryBuilder Column Type

Use constructer. At the begining of file add
use yii\db\Schema;
And then add:
$this->addColumn(
'competition_prizes'=> Schema::TYPE_PK,
'prize_id'=> Schema::TYPE:INTEGER,
);

Related

Why is Sequelize upsert not working with composite unique key?

I use this table in a PostgreSQL database:
create table if not exists "Service" (
_id uuid not null primary key,
service text not null,
"count" integer not null,
"date" timestamp with time zone,
team uuid,
organisation uuid,
"createdAt" timestamp with time zone not null,
"updatedAt" timestamp with time zone not null,
unique (service, "date", organisation),
foreign key ("team") references "Team"("_id"),
foreign key ("organisation") references "Organisation"("_id")
);
When I try an upsert with Sequelize with the following code, it throws an error:
Service.upsert({ team, date, service, organisation, count }, { returning: true })
Error is:
error: duplicate key value violates unique constraint "Service_service_date_organisation_key"
Key (service, date, organisation)= (xxx, 2022-12-30 01:00:00+01, 12345678-5f63-1bc6-3924-517713f97cc3) already exists.
But according to Sequelize documentation it should work: https://sequelize.org/docs/v6/other-topics/upgrade/#modelupsert
Note for Postgres users: If upsert payload contains PK field, then PK will be used as the conflict target. Otherwise first unique constraint will be selected as the conflict key.
How can I find this duplicate key error and get it work with the composite unique key: unique (service, "date", organisation)?
It looks like your problem is related to issue #13240.
If you're on Sequelize 6.12 or above, you should be able to use an explicit list of conflictFields:
Service.upsert(
{ team, date, service, organisation, count },
{ conflictFields: ["service", "date", "organisation"] },
{ returning: true }
)
References
Similar questions were asked on GitHub, see:
https://github.com/sequelize/sequelize/issues/13240
https://github.com/sequelize/sequelize/issues/13412
and they were not solved so far, so, as the time of this writing, this issue seems to be unresolved, so you will need to work-around it. Below I will provide a few ideas to solve this, but since I have never worked with Sequelize, it is possible that I have some syntax error or some misunderstanding. If so, please point it out and I'll fix it.
Approach 1: Querying by your unique key and inserting/updating by it
Post.findAll({
where: {
service: yourservice,
date: yourdate,
organization: yourorganization
}
});
And then insert if the result is empty, update otherwise.
Approach 2: Modifying your schema
Since your composite unique key is a candidate key, an option would be to remove your _id field and make your (service, "date", organization) unique.
Approach 3: Implement an insert trigger on your table
You could simply call insert from Sequelize and let a PostgreSQL trigger handle the upserting, see: How to write an upsert trigger in PostgreSQL?
Example trigger:
CREATE OR REPLACE FUNCTION on_before_insert_versions() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
IF pg_trigger_depth() = 1 THEN
INSERT INTO versions (key, version) VALUES (NEW.key, NEW.version)
ON CONFLICT (key)
DO UPDATE SET version = NEW.version;
RETURN NULL;
ELSE
RETURN NEW;
END IF;
END;$$;
You of course will need to change table and field names accordingly to your schema and command.

jOOQ: Is there a restriction in the DDLDatabase generator for deferrable constraints?

I'm using the DDLDatabase generator from Gradle to generate jOOQ sources. I seem to have hit a snag when it comes to deferrable constraints.
Generating from a live database for the same DDL works...
Here's my configuration for DDLDatabase:
jooq {
version = "3.11.11"
edition = "OSS"
reference(sourceSets.main) {
generator {
database {
name = "org.jooq.meta.extensions.ddl.DDLDatabase"
properties {
property {
key = "scripts"
value = "schema-gen/schema.sql"
}
property {
key = "sort"
value = "semantic"
}
}
inputSchema = "REFERENCE"
}
generate {
relations = true
deprecated = false
records = true
immutablePojos = false
fluentSetters = true
}
target {
packageName = "com.octeris.aml.reference.persistence.jooq"
directory = "jooq-gen"
}
}
}
}
and here's the part of the DDL the generator stumbles at:
create table reference.a (
id varchar(100) not null
,foo_id varchar(100)
,constraint a$c$p primary key (id)
,constraint a$f$1 foreign key (foo_id) references reference.b(id) on delete restrict on update restrict deferrable initially deferred
);
the error reported is the following:
Token ')' expected: [8:106] ...rence.b(id) on delete restrict on update restrict [*]deferrable initially deferred
Does anyone know of an alternative syntax that would be accepted? Or is this potentially a bug in the generator?
The jOOQ parser doesn't recognise this syntax yet. I've created a feature request for this:
https://github.com/jOOQ/jOOQ/issues/8799
jOOQ 3.11 workaround
Right now, the workaround is to preprocess the SQL files before you pass them to the DDLDatabase. You should search for the deferrable initially deferred syntax and replace it by an empty string.
jOOQ 3.12 workaround
Note that starting from the upcoming jOOQ 3.12, there will be a new jOOQ-specific comment syntax where you can comment out such syntax elements only for the jOOQ parser, while they keep being executed by your database. The feature request is this one:
https://github.com/jOOQ/jOOQ/issues/8325
The feature will work like this (this is what will be executed in the database):
create table reference.a (
id varchar(100) not null
,foo_id varchar(100)
,constraint a$c$p primary key (id)
,constraint a$f$1
foreign key (foo_id)
references reference.b(id)
on delete restrict
on update restrict
-- [jooq ignore start]
deferrable initially deferred
-- [jooq ignore stop]
);
jOOQ will ignore everything between those two markers, so this will be what jOOQ parses
create table reference.a (
id varchar(100) not null
,foo_id varchar(100)
,constraint a$c$p primary key (id)
,constraint a$f$1
foreign key (foo_id)
references reference.b(id)
on delete restrict
on update restrict
-- [jooq ignore start]
-- [ ... ignored ... ]
-- [jooq ignore stop]
);
The SQL doesn't need to be formatted this way. I just did this for this question here. You could also use /* [jooq ignore start] */ on a one liner SQL statement.
The exact token that delimits the parts to be ignored by jOOQ can be specified by:
Settings.parseIgnoreCommentStart
Settings.parseIgnoreCommentStop

Adding an one-out-of-two not null constraint in postgresql

If I have a table in Postgresql:
create table Education (
id integer references Profiles(id),
finished YearValue not null,
started YearValue,
qualification text,
schoolName text,
studiedAt integer references Organizations(id),
primary key (id)
);
I need to make a constraint so that either schoolName or studiedAt needs to not be null (one of them has to have information in it).
How do I do this?
You can use a check constraint e.g.
constraint chk_education check (schoolName is not null or studiedAt is not null)
From the manual:
A check constraint is the most generic constraint type. It allows you to specify that the value in a certain column must satisfy a Boolean (truth-value) expression.
Edit: Alternative to comply with Pithyless' interpretation:
constraint chk_education check ((schoolName is not null and studiedAt is null) or (schoolName is null and studiedAt is not null))
You can also use a trigger on update and insert to check that a rule is followed before allowing the data into the table. You would normally use this type of approach when the check constraint needs more complicated logic.
This is my solution for sequelize migration file in "up" function
queryInterface.addConstraint('Education', {
fields: ['schoolName', 'studiedAt'],
type: 'check',
name: 'schoolName_or_studiedAt_is_null',
where: { [Sequelize.Op.or]: [{ password: null }, { googleId: null }] },
}),

How to do multiple column unique-constraint in ormlite ( SQLite )

I'm using ormlite for Android and I'm trying to get a multiple column unique-constraint. As of now i'm only able to get a unique constraint on indiviudal columns like this:
CREATE TABLE `store_group_item` (`store_group_id` INTEGER NOT NULL UNIQUE ,
`store_item_id` INTEGER NOT NULL UNIQUE ,
`_id` INTEGER PRIMARY KEY AUTOINCREMENT );
and what I want is
CREATE TABLE `store_group_item` (`store_group_id` INTEGER NOT NULL ,
`store_item_id` INTEGER NOT NULL ,
`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
UNIQUE( `store_group_id`, `store_item_id` );
In my model I've been using the following annotations for the unique columns:
#DatabaseField( unique = true )
Is there a way to get this to work?
How about using
#DatabaseField (uniqueCombo = true)
String myField;
annotation instead - is it a matter of the uniqueIndexName being faster when accessing items in the table?
Edit:
As #Ready4Android pointed out, we've since added in version 4.20 support for uniqueCombo annotation field. Here are the docs:
http://ormlite.com/docs/unique-combo
There should be no performance differences between using this mechanism versus the uniqueIndexName mentioned below.
Yes. You can't do this with the unique=true tag but you can with a unique index.
#DatabaseField(uniqueIndexName = "unique_store_group_and_item_ids")
int store_group_id;
#DatabaseField(uniqueIndexName = "unique_store_group_and_item_ids")
int store_item_id;
This will create an index to accomplish the unique-ness but I suspect that the unique=true has a hidden index anyway. See the docs:
http://ormlite.com/docs/unique-index
I will look into allowing multiple unique fields. May not be supported by all database types.

PostgreSQL - Error: SQL state: XX000

I have a table in Postgres that looks like this:
CREATE TABLE "Population"
(
"Id" bigint NOT NULL DEFAULT nextval('"population_Id_seq"'::regclass),
"Name" character varying(255) NOT NULL,
"Description" character varying(1024),
"IsVisible" boolean NOT NULL
CONSTRAINT "pk_Population" PRIMARY KEY ("Id")
)
WITH (
OIDS=FALSE
);
And a select function that looks like this:
CREATE OR REPLACE FUNCTION "Population_SelectAll"()
RETURNS SETOF "Population" AS
$BODY$select
"Id",
"Name",
"Description",
"IsVisible"
from "Population";
$BODY$
LANGUAGE 'sql' STABLE
COST 100
Calling the select function returns all the rows in the table as expected.
I have a need to add a couple of columns to the table (both of which are foreign keys to other tables in the database). This gives me a new table def as follows:
CREATE TABLE "Population"
(
"Id" bigint NOT NULL DEFAULT nextval('"population_Id_seq"'::regclass),
"Name" character varying(255) NOT NULL,
"Description" character varying(1024),
"IsVisible" boolean NOT NULL,
"DefaultSpeciesId" bigint NOT NULL,
"DefaultEcotypeId" bigint NOT NULL,
CONSTRAINT "pk_Population" PRIMARY KEY ("Id"),
CONSTRAINT "fk_Population_DefaultEcotypeId" FOREIGN KEY ("DefaultEcotypeId")
REFERENCES "Ecotype" ("Id") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT "fk_Population_DefaultSpeciesId" FOREIGN KEY ("DefaultSpeciesId")
REFERENCES "Species" ("Id") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
and function:
CREATE OR REPLACE FUNCTION "Population_SelectAll"()
RETURNS SETOF "Population" AS
$BODY$select
"Id",
"Name",
"Description",
"IsVisible",
"DefaultSpeciesId",
"DefaultEcotypeId"
from "Population";
$BODY$
LANGUAGE 'sql' STABLE
COST 100
ROWS 1000;
Calling the function after these changes results in the following error message:
ERROR: could not find attribute 11 in subquery targetlist
SQL state: XX000
What is causing this error and how do I fix it? I have tried to drop and recreate the columns and function - but the same error occurs.
Platform is PostgreSQL 8.4 running on Windows Server. Thanks.
Did you dropping and recreating the function?
By the way, you gotta love how user friendly Postgres is. What other database would you hugs and kisses(XXOOO) as an error state?
When I've seen something similar in the past, it was because the database connection cached certain function attributes. So if I was using pgAdmin, I had to close the SQL editor window and establish a new connection in order to get the function to work correctly. If you haven't already, be sure you are testing the function on new db connections.
I thought the issue was fixed a few versions ago in PostgreSQL, but it's worth a try.
Found a bit easier for me solution: created a backup of the database and restored it from this backup.