From ed688bec8c88d23111dc584ebe351578ac1578f4 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 30 Aug 2021 12:31:22 +0100 Subject: [PATCH] FIX: Ensure id sequences are not reset during db:migrate (#14184) The seed-fu gem resets the sequence on all the tables it touches. In some situations, this can cause primary keys to be re-used. This commit introduces a freedom patch which ensures seed-fu only touches the sequence when it is less than the id of one of the seeded records. --- lib/freedom_patches/seed_fu.rb | 19 +++++++++++++++++++ .../freedom_patches/seed_fu_spec.rb | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 lib/freedom_patches/seed_fu.rb create mode 100644 spec/components/freedom_patches/seed_fu_spec.rb diff --git a/lib/freedom_patches/seed_fu.rb b/lib/freedom_patches/seed_fu.rb new file mode 100644 index 00000000000..b24241b3d73 --- /dev/null +++ b/lib/freedom_patches/seed_fu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +SeedFu::Seeder.prepend(Module.new do + def update_id_sequence + max_seeded_id = @data.filter_map { |d| d["id"] }.max + seq = @model_class.connection.execute(<<~SQL) + SELECT last_value + FROM #{@model_class.sequence_name} + SQL + last_seq_value = seq.first["last_value"] + + if max_seeded_id && last_seq_value < max_seeded_id + # Update the sequence to start from the highest existing id + @model_class.connection.reset_pk_sequence!(@model_class.table_name) + else + # The sequence is already higher than any of our seeded ids - better not touch it + end + end +end) diff --git a/spec/components/freedom_patches/seed_fu_spec.rb b/spec/components/freedom_patches/seed_fu_spec.rb new file mode 100644 index 00000000000..3bb1876411a --- /dev/null +++ b/spec/components/freedom_patches/seed_fu_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe "seed-fu patch" do + it "does not modify a sequence on an existing table" do + u = User.create!(username: "test1", email: "test1@example.com") + uid1 = u.id + UserDestroyer.new(Discourse.system_user).destroy(u) + + SeedFu.quiet = true + SeedFu.seed + + # Ensure sequence was not reset. A new user should have + # id one greater than the last user + u2 = User.create!(username: "test1", email: "test1@example.com") + expect(u2.id).to eq(uid1 + 1) + end +end