Ошибка записи в Postgres
Такая ситуация. После создания таблиц в БД, сидами добавляю нового пользователя (в конфигурации id = 1). Все норм отрабатывает, пользователь записан в БД. Но если я добавляю нового пользователя через приложение, вылетает ошибка "Validation error". Если пытаюсь еще раз - та же ошибка. При третьей попытке аккаунт создается, но уже с id = 3.
Поле id инкрементное. В дебаге "спотыкается" о метод create (sequelize).
Что это может быть, куда смотреть?
DDL ::
CREATE TABLE IF NOT EXISTS public.users
(
id integer NOT NULL DEFAULT nextval('users_id_seq'::regclass),
login character varying(255) COLLATE pg_catalog."default" NOT NULL,
email character varying(255) COLLATE pg_catalog."default" NOT NULL,
password character varying(255) COLLATE pg_catalog."default" NOT NULL,
"roleId" integer,
banned boolean DEFAULT false,
CONSTRAINT users_pkey PRIMARY KEY (id),
CONSTRAINT users_email_key UNIQUE (email),
CONSTRAINT users_login_key UNIQUE (login),
CONSTRAINT "users_roleId_fkey" FOREIGN KEY ("roleId")
REFERENCES public.roles (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
)
Ответы (2 шт):
Основная проблема заключается в "рассинхронизации первичных ключей в Postgres":
"Postgres обрабатывает автоматическое увеличение немного иначе, чем MySQL. В Postgres при создании serial поля вы также создаете поле последовательности, которое отслеживает используемый идентификатор. Это поле последовательности будет начинаться со значения 1.
Если вы вставляете в таблицу новую запись и не указываете поле id, она будет использовать значение последовательности, а затем увеличивать последовательность.
Однако если вы укажете id поле, последовательность не будет использоваться и не будет обновляться".
(взято отсюда: https://stackoverflow.com/questions/37970743/postgresql-unique-violation-7-error-duplicate-key-value-violates-unique-const)
Используя следующий скрипт можно пофиксить данную проблему:
SELECT setval(pg_get_serial_sequence('users', 'id'), max(id)) FROM users;
После этого, последовательность используется в обычном порядке, т.е. инкремент работает как обычно.
(https://stackoverflow.com/questions/244243/how-to-reset-postgres-primary-key-sequence-when-it-falls-out-of-sync)
Вот как реализовал в seed:
async up (queryInterface) {
await queryInterface.bulkInsert('users', [userdata])
await queryInterface.sequelize.query(
`SELECT setval(pg_get_serial_sequence('users', 'id'), max(id)) FROM users;`,
{type: queryInterface.sequelize.QueryTypes.SELECT}
);
}
Будет интересно почитать комментарии более лучшего решения
Переделал так:
// utils.js
const updateSequence = async (table, column, { queryInterface, transaction }) => {
const [[{ max }]] = await queryInterface.sequelize.query(`SELECT MAX(${column}) FROM ${table}`, {
transaction,
});
if (!max) return;
await queryInterface.sequelize.query(
`SELECT setval(pg_get_serial_sequence('${table}', '${column}'), max(${column})) FROM ${table};`,
{ transaction }
);
};
module.exports = { updateSequence };
Seed:
const { updateSequence } = require("../utils");
//...
async up (queryInterface) {
await queryInterface.bulkInsert('users', [userdata])
await updateSequence('users', 'id', { queryInterface });
},
updateSequence, в принципе, можно использозвать в миграции, после описания таблицы:
await queryInterface.createTable('users', {
...
});
await updateSequence('users', 'id', { queryInterface });