New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(postgres): change returning option to only return model attributes #11526
feat(postgres): change returning option to only return model attributes #11526
Conversation
9dae6b1
to
fd956cd
Compare
Codecov Report
@@ Coverage Diff @@
## master #11526 +/- ##
==========================================
+ Coverage 95.86% 96.26% +0.39%
==========================================
Files 91 94 +3
Lines 8982 9186 +204
==========================================
+ Hits 8611 8843 +232
+ Misses 371 343 -28
Continue to review full report at Codecov.
|
d45e8cc
to
c3a8341
Compare
LGTM, This is a breaking change. We have been planing to start work related to next major release. I think I'll have some time during weekend of 19-20th Oct. I'll start next beta cycle then and will include this change. Thanks |
01 - IssueThis appears to unfortunately break upsert feature of postgres. const [ baseline ] = await db.session_users.findOrCreate({
defaults: {
start_at: moment.utc().toISOString(),
},
logging: console.log,
where: {
id_session: session.id_session,
id_user: user.id_user,
},
});
//=========================
// OUTPUT FROM SEQUELIZE
//=========================
Executing (95687979-1cf9-4c19-8d46-abe0ab0e706e): START TRANSACTION;
Executing (95687979-1cf9-4c19-8d46-abe0ab0e706e): SELECT
"id_session_user", "id_session", "id_user", "device_type", "start_at", "end_at", "state", "token", "active_time", "accept_recording", "created_at", "updated_at"
FROM "session_users" AS "session_users"
WHERE "session_users"."id_session" = 1 AND "session_users"."id_user" = 1 LIMIT 1;
Executing (95687979-1cf9-4c19-8d46-abe0ab0e706e): CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response "session_users", OUT sequelize_caught_exception text)
RETURNS RECORD AS $func_238212f3bd9f4e0c8b34e77774f9fd37$
BEGIN INSERT INTO "session_users" ("id_session_user","id_session","id_user","start_at","state","active_time","accept_recording","created_at","updated_at")
VALUES (DEFAULT,1,1,'2019-10-14 16:23:45.794 +00:00','pending',NULL,NULL,'2019-10-14 16:23:45.797 +00:00','2019-10-14 16:23:45.797 +00:00')
RETURNING "id_session_user","id_session","id_user","device_type","start_at","end_at","state","token","active_time","accept_recording","created_at","updated_at" INTO response;
EXCEPTION WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL; END $func_238212f3bd9f4e0c8b34e77774f9fd37$ LANGUAGE plpgsql;
SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();
Executing (95687979-1cf9-4c19-8d46-abe0ab0e706e): COMMIT;
//=========================
// OUTPUT FROM POSTGRES
//=========================
2019-10-14 16:46:02.141 UTC [2900] ERROR: invalid input syntax for integer: "2019-10-14 16:46:02.139+00"
2019-10-14 16:46:02.141 UTC [2900] CONTEXT: PL/pgSQL function pg_temp_6.testfunc() line 1 at SQL statement 02 - Troubleshooting / fixI haven't completely narrowed down why it does not work. BUT I've noticed that enforcing the return fragment to equal To do so, I've added //============================================
// OUTPUT FROM SEQUELIZE (with "RETURNING *")
//============================================
Executing (37209d5c-b8e2-42a1-af05-b718cde87431): START TRANSACTION;
Executing (37209d5c-b8e2-42a1-af05-b718cde87431): SELECT
"id_session_user", "id_session", "id_user", "device_type", "start_at", "end_at", "state", "token", "active_time", "accept_recording", "created_at", "updated_at"
FROM "session_users" AS "session_users"
WHERE "session_users"."id_session" = 1 AND "session_users"."id_user" = 1 LIMIT 1;
Executing (37209d5c-b8e2-42a1-af05-b718cde87431): CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response "session_users", OUT sequelize_caught_exception text)
RETURNS RECORD AS $func_ab6518d65b79441791ef8ccdbd4ec59d$
BEGIN INSERT INTO "session_users" ("id_session_user","id_session","id_user","start_at","state","active_time","accept_recording","created_at","updated_at")
VALUES (DEFAULT,1,1,'2019-10-14 16:49:29.275 +00:00','pending',NULL,NULL,'2019-10-14 16:49:29.277 +00:00','2019-10-14 16:49:29.277 +00:00')
RETURNING * INTO response;
EXCEPTION WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL; END $func_ab6518d65b79441791ef8ccdbd4ec59d$ LANGUAGE plpgsql;
SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();
Executing (37209d5c-b8e2-42a1-af05-b718cde87431): COMMIT; |
Here is the model for full disclosure const SessionUsers = sequelize.define('session_users', {
id_session_user: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
id_session: {
type: DataTypes.INTEGER,
allowNull: false,
},
id_user: {
type: DataTypes.INTEGER,
allowNull: false,
},
device_type: DataTypes.ENUM('web', 'ios'),
start_at: DataTypes.DATE,
end_at: DataTypes.DATE,
state: {
type: DataTypes.ENUM('declined', 'cancelled', 'joined', 'left', 'pending'),
defaultValue: 'pending',
},
token: {
type: DataTypes.TEXT,
allowNull: true,
},
active_time: {
allowNull: true,
defaultValue: null,
type: DataTypes.INTEGER,
},
accept_recording: {
allowNull: true,
defaultValue: null,
type: DataTypes.BOOLEAN,
},
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
}, {
underscored: true,
// WORKAROUND until https://github.com/sequelize/sequelize/issues/11225 gets fixed
createdAt: 'created_at',
updatedAt: 'updated_at',
}); |
@TheOptimisticFactory I'm going to need more information, because that query works for me. Postgres output shows no error, and the query returns the created or selected row as expected... Executing (40e92b5f-83d6-49c6-b43e-4833f68578f3): START TRANSACTION;
Executing (40e92b5f-83d6-49c6-b43e-4833f68578f3): SELECT
"id_session_user", "id_session", "id_user", "device_type", "start_at", "end_at", "state", "token", "active_time", "accept_recording", "created_at", "updated_at"
FROM "session_users" AS "session_users"
WHERE "session_users"."id_session" = 1 AND "session_users"."id_user" = 1
LIMIT 1;
Executing (40e92b5f-83d6-49c6-b43e-4833f68578f3): CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response "session_users", OUT sequelize_caught_exception text)
RETURNS RECORD AS $func_4cec91f65fc34cef9f32ca4f80638b76$
BEGIN INSERT INTO "session_users" ("id_session_user","id_session","id_user","start_at","state","active_time","accept_recording","created_at","updated_at")
VALUES (DEFAULT,1,1,'2019-10-15 08:27:29.247 +00:00','pending',NULL,NULL,'2019-10-15 08:27:29.268 +00:00','2019-10-15 08:27:29.268 +00:00')
RETURNING "id_session_user","id_session","id_user","device_type","start_at","end_at","state","token","active_time","accept_recording","created_at","updated_at" INTO response;
EXCEPTION WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL; END $func_4cec91f65fc34cef9f32ca4f80638b76$ LANGUAGE plpgsql;
SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();
Executing (40e92b5f-83d6-49c6-b43e-4833f68578f3): COMMIT; Instance created: session_users {
dataValues:
{ state: 'pending',
active_time: null,
accept_recording: null,
id_session_user: 1,
start_at: 2019-10-15T08:27:29.247Z,
id_session: 1,
id_user: 1,
updated_at: 2019-10-15T08:27:29.268Z,
created_at: 2019-10-15T08:27:29.268Z,
device_type: null,
end_at: null,
token: null },
_previousDataValues:
{ start_at: 2019-10-15T08:27:29.247Z,
id_session: 1,
id_user: 1,
id_session_user: 1,
device_type: null,
end_at: null,
state: 'pending',
token: null,
active_time: null,
accept_recording: null,
created_at: 2019-10-15T08:27:29.268Z,
updated_at: 2019-10-15T08:27:29.268Z },
_changed:
{ start_at: false,
id_session: false,
id_user: false,
id_session_user: false,
device_type: false,
end_at: false,
state: false,
token: false,
active_time: false,
accept_recording: false,
created_at: false,
updated_at: false },
_modelOptions:
{ timestamps: true,
validate: {},
freezeTableName: false,
underscored: true,
paranoid: false,
rejectOnEmpty: false,
whereCollection: { id_session: 1, id_user: 1 },
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: {},
indexes: [],
name: { plural: 'session_users', singular: 'session_user' },
omitNull: false,
createdAt: 'created_at',
updatedAt: 'updated_at',
sequelize:
Sequelize {
options: [Object],
config: [Object],
dialect: [PostgresDialect],
queryInterface: [QueryInterface],
models: [Object],
modelManager: [ModelManager],
connectionManager: [ConnectionManager],
importCache: {} },
hooks: {} },
_options:
{ isNewRecord: true,
_schema: null,
_schemaDelimiter: '',
attributes: undefined,
include: undefined,
raw: undefined,
silent: undefined },
isNewRecord: false } |
Sure thing. I would be happy to help. |
Postgres version, is it running native or not? Do you have a migration for the table? Or did you create it using |
Environment
Context
Snippet n°1'use strict';
const path = require('path');
const Umzug = require('umzug');
const { database: db } = require('~API_SERVICES');
async function runMigrations() {
await db.sequelize.drop({
cascade: true,
});
const umzug = new Umzug({
migrations: {
params: [
db.sequelize.getQueryInterface(),
db.Sequelize,
],
path: path.join(process.cwd(), './services/database/migrations/scripts'),
},
storage: 'none',
});
await umzug.up();
}
module.exports = runMigrations; Snippet n°2 |
Still nothing... Judging by the error context |
No trigger nor any sequelize hook. Discard the following (wrong assessment wtith stashed diffs)
|
I checked out to v5.19.0 and cherry-picked this commit on top of it. It had no issues running the git log:
|
Nevermind, I am dumb. I am facing the issue using both -- My stashed diffs included the workaround |
Removing The thing is another model with a similar |
Enforcing In the Is there a difference I am missing between this operation and EDITThe only diff I can see between the tables before/after running => as shown here: https://www.diffchecker.com/0e8MaKWO Here is my updated migration runner snippet: 'use strict';
const path = require('path');
const Umzug = require('umzug');
const { database: db } = require('~API_SERVICES');
async function runMigrations() {
await db.sequelize.drop({
cascade: true,
});
const umzug = new Umzug({
migrations: {
params: [
db.sequelize.getQueryInterface(),
db.Sequelize,
],
path: path.join(process.cwd(), './services/database/migrations/scripts'),
},
storage: 'none',
});
await umzug.up();
const debugQuery = `SELECT
"column_name",
"ordinal_position",
"udt_name",
"udt_schema",
"data_type",
"is_nullable",
"column_default"
from INFORMATION_SCHEMA.COLUMNS where table_name = 'session_users'
ORDER BY ordinal_position`;
const [ before ] = await db.sequelize.query(debugQuery);
console.log(JSON.stringify(before, null, 2));
await db.sequelize.sync({
force: true,
});
const [ after ] = await db.sequelize.query(debugQuery);
console.log(JSON.stringify(after, null, 2));
}
module.exports = runMigrations; |
Adding an additional field after the table creation through a migration, seems to be the root cause of the error. I suspect a mismatch between the order of returned columns and their respective value somehow |
Yes, if I order the table as the diff, I get that error, hurray 😄 |
This is a bigger bug related to the |
@TheOptimisticFactory I fixed the issue with the insert while using option The only issue now is that if that option is true, it will return all the table's columns, so I'll have to fix that as well now 😆 Can you confirm that this fixes your use case? It works for me. |
@Americas I am out of the office ATM. I will try your hotfix tomorrow first thing (CEST timezone) 😃 |
@Americas I can confirm your hotfix 44c2603 indeed solved the issue I was encountering! 🎉 |
Awesome! I'll update tests then, and fix this to return only model attributes as well. |
42b37b8
to
e92ac61
Compare
If an underlying table has columns that don't exist in the model, when using `returning: true` the instance will be created with these extra attributes. This change alters this behavior to only return the fields defined in the model, provided a model definition is passed. BREAKING CHANGE: setting `returning: true` will no longer create attributes on the instance that are not defined in the model. The old default behavior, if desired, can be used by changing the: `returning: true` to `returning: ['*']`
e92ac61
to
498a1f9
Compare
Pull Request check-list
Please make sure to review and check all of these items:
npm run test
ornpm run test-DIALECT
pass with this change (including linting)?Description of change
If an underlying table has columns that don't exist in the model, when using
returning: true
the instance will be created with these extra attributes. This change alters this behavior to only return the fields defined in the model, provided a model definition is passed.Note: this is a breaking change since the option
returning: true
will no longer createattributes on the instance that are not defined in the model.
The old default behavior can be used by: