How to add not null constraint in seqeulize if records with null already exist? - postgresql

I had a table.
I added a new column.
Even though I had set default value in sequelize model, those columns still ended up empty.
So I get error Unhandled rejection SequelizeDatabaseError: column "col_name" contains null values
How do you populate new column with default values upon creation so not null constraint is not broken.

You can update the values first:
update t
set col_name = ?
where col_name is null;
Then add the not null constraint.

Even though I had set default value in sequelize model
I suspect there is a discrepancy between the migration and model. To insert the column with a migration containing a default value use defaultValue in your migration.
The following is a working example:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('tests', 'new_column', {
defaultValue: 'test',
type: Sequelize.STRING
})
}
}
Before running sequelize db:migrate
After sequelize db:migrate:
The documentation for the options object in addColumn is hard to find, it's listed for a different method

Related

Sequelize upsert throws unique violation for schema with nullable unique column

I'm facing an unique constraint violation issue when doing an upsert, because the UPDATE query built by sequelize ignores the partial index constraint defined by the model (unless it doesn't matter). I'm new to node+sequelize so I might be missing something obvious, but I went through all the potential places for finding the appropriate answers, inclusive of the sequelize code, but I'm not able to find the answer I'm looking for. Really appreciate your help!
My current versions:
"pg": "7.9.0",
"sequelize": "5.21.3"
I have a model that consists of a primary key: id and two other unique indexes of which one of them is a nullable field.
module.exports.Entities = sequelize.define('entities', {
id: {type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, allowNull: false, primaryKey: true},
cId: {type: Sequelize.STRING, allowNull: false},
pId: {type: Sequelize.UUID, allowNull: false},
eKey: {type: Sequelize.INTEGER, allowNull: true}
}, {
indexes: [
{
name: 'unique_c_id_p_id',
fields: ['c_id', 'p_id'],
unique: true
},
{
name: 'unique_e_key',
fields: ['e_key'],
unique: true,
where: {
eKey: {
[Op.not]: null
}
}
}
]
})
and the table itself looks like below:
CREATE TABLE public.entities (
id UUID DEFAULT uuid_generate_v4 (),
c_id UUID NOT NULL,
p_id UUID NOT NULL,
e_key INTEGER DEFAULT NULL,
CONSTRAINT ENTITY_SERVICE_PKEY PRIMARY KEY (id),
CONSTRAINT unique_c_id_p_id UNIQUE (c_id, p_id)
);
CREATE UNIQUE INDEX unique_e_key ON public.entities (e_key) WHERE e_key IS NOT NULL;
The upsert method call looks like:
module.exports.upsert = async (Model, values) => Model.upsert(values, {returning: true})
I pass the above Entities model, and the below value as arguments to this function.
{
"id"="3169d4e2-8e2d-451e-8be0-40c0b28e2aa9",
"c_id"="00000000-0000-0000-0000-000000000000",
"p_id"="78bce392-4a15-4a8a-986b-c9398787345f",
"e_key"= null
}
Issue: SequelizeUniqueConstraintError
Sequelize tries to do an insert followed by an update query when we attempt to update an existing record using the upsert method.
The insert query shows a conflict, since the record exists already, and sequelize upsert call proceeds on to invoke the update query.
However, the query that it builds to UPDATE looks something like below:
"SQL statement UPDATE entities SET id='3169d4e2-8e2d-451e-8be0-40c0b28e2aa9',c_id='00000000-0000-0000-0000-000000000000',p_id='78bce392-4a15-4a8a-986b-c9398787345f',e_key=NULL
WHERE (id = '3169d4e2-8e2d-451e-8be0-40c0b28e2aa9'
OR e_key IS NULL
OR (c_id = '00000000-0000-0000-0000-000000000000' AND p_id = '78bce392-4a15-4a8a-986b-c9398787345f'))
RETURNING id\nPL/pgSQL function pg_temp_5.sequelize_upsert() line 1 at SQL statement"
Now, I do understand the reason why it's throwing the unique constraint violation, since in the above query's WHERE clause sequelize calls OR e_key IS NULL since e_key = null and that could potentially return more than 1 record, and the SET is trying to update the same value for all those records that were returned thereby violating the primaryKey constraints, unique constraints etc.
What I would like to understand is that:
Why does sequelize not exclude the e_key unique constraint based on the partial index defined given that it picks the WHERE clause attributes based on the constraints defined in the Model & it's indexes?
Is there anything that I could do to get past this issue?
Or, am I missing something obvious that I could fix and try?
Really appreciate you taking your time to read and respond. Thanks!

How do I make a column DEFAULT(NULL) in SQLite.swift

I'm trying to define my database structure, but I can't figure out how to set the default value of a column to NULL. This is what I want it to produce:
CREATE TABLE test (
content text DEFAULT(NULL),
);
I have the following code
let content = Expression<String?>("content")
try self.db.run(test.create { t in
t.column(content, defaultValue: nil)
})
But defaultValue: nil obviously does not work. What do I set instead?
Unless you create the column in the table with the NOT NULL qualifier, the column's default value is NULL. You don't need to do anything special to get the behavior you want.
CREATE TABLE test {
content TEXT
};
Create the table that way. When you insert rows into that table, if you don't provide a specific value for the content column, the row will be added with no value (NULL) in that column.

How to insert empty array into jsonb column (pgsql) by Yii2?

Created a migration with a new field of jsonb type, not null and default value = []. (example of stored data: ["235", "214"]) and add a rule to model [['unique_users'], 'safe']
public function up()
{
$connection = Yii::$app->getDb();
$sql = 'ALTER TABLE offer ADD unique_users jsonb not null default \'[]\'';
$command = $connection->createCommand($sql, []);
$command->queryAll();
}
Result: Added a unique_users field with a default value [] to each row. jsonb_typeof(unique_users) returns an array type.
Created needed query for test
select jsonb_array_length(unique_users) from test where unique_users #> '"19"'::jsonb
Result from PgAdmin:
It seemed that everything was ready. But after saving a new record with Yii2, I received a query error:
ERROR: you can not get the length of a scalar
And I saw that another value was recorded in the field - ""
I was tryed to add the validation rule to Model: ['unique_users', 'default', 'value' => '[]'],.
Result:
...with the same problem of query - value is not an array. jsonb_typeof(unique_users) returns an string type.
How to insert empty array into jsonb column?
I think you're accidentally sending an empty string as the value for your unique_users field. If the value would be completely empty it should take the default DB value for the column. Please make sure the unique_users field is completely empty (null) when saving.
You can however also do this with a default value rule. This should do the trick:
['unique_users', 'default', 'value' => json_encode([])],
['unique_users', 'default', 'value' => []],

How to change what sequence will be used by postgresql when it gets next value for id?

I have 2 table with field id which automatic created by sequelize.
table1
id integer not null default nextval('table1_d_seq'::regclass)
table2
id integer not null default nextval('table2_d_seq'::regclass)
Could I tune sequelize to create id field in table2 with modifier nextval('table1_d_seq'::regclass) ?
You can create a migration file which will change specified column and set it's default value via the sequelize.query() method available through queryInterface
module.exports = {
up: function (queryInterface, Sequelize){
return queryInterface.sequelize.query(
"ALTER TABLE table2 ALTER COLUMN id SET DEFAULT nextval('table1_d_seq'::regclass)"
);
},
down: function (queryInterface, Sequelize) {
}
}

Eloquent default attribute values: $attributes or DB column default value?

What's the proper way to implement default values for Eloquent models?
I've configured my database tables using Laravel's migrations. Some columns have default values specified. When using these tables in conjunction with Eloquent models, different things happen depending on the selected database driver:
In MySQL, when creating a new model and saving it, a DB row is inserted having the column's default value for every attribute that was not explicitly specified. This is what I would like to happen.
In Postgres and SQLite however, this is not the case. A PDOException is thrown:
[PDOException]
SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column
"some_column" violates not-null constraint
DETAIL: Failing row contains (1, 2, 3, 4, 5, 6, null, null, null, null, null, null, 7, 8, null, 9, null, null, 10, 11, 12, null).
It is clear to me that the column is not nullable and that null values are not accepted. I would expect however that the default value was inserted instead of an error being raised.
I would suggest that you create your own parent model that extends Eloquent directly, and have all of your models extend this custom parent.
In the custom parent, override the performInsert() method to remove null values just before inserting. Be sure to copy the entire method from the Eloquent source code so you don't lose any important steps in the process:
class MyModelParent extends Illuminate\Database\Eloquent\Model
{
/**
* Perform a model insert operation.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #return bool
*/
protected function performInsert(Builder $query)
{
if ($this->fireModelEvent('creating') === false) {
return false;
}
... // Be sure to copy all of it!
// This is the change you'll make. Before, it was just:
// $attributes = $this->attributes;
$attributes = array_filter($this->attributes, function($val){
return $val !== null;
});
... // Be sure to copy all of it!
return true;
}
}
performUpdate() should handle this issue fine, since it uses getDirty() to get the list of fields instead of accessing the property directly.
And while you're at it, you should consider submitting a patch to Laravel that would make the core Postgres-safe.
SQL NOT NULL Constraint
The NOT NULL constraint enforces a column to NOT accept NULL values.
you are adding a null value on NOT NULL column
it seems
http://www.w3schools.com/sql/sql_notnull.asp