diff --git a/db/migrate/20250522201031_stronger_unique_index_on_security.rb b/db/migrate/20250522201031_stronger_unique_index_on_security.rb new file mode 100644 index 00000000..43672f81 --- /dev/null +++ b/db/migrate/20250522201031_stronger_unique_index_on_security.rb @@ -0,0 +1,13 @@ +class StrongerUniqueIndexOnSecurity < ActiveRecord::Migration[7.2] + def change + remove_index :securities, [ :ticker, :exchange_operating_mic ], unique: true + + # Matches our ActiveRecord validation: + # - uppercase ticker + # - either exchange_operating_mic or empty string (unique index doesn't work with NULL values) + add_index :securities, + "UPPER(ticker), COALESCE(UPPER(exchange_operating_mic), '')", + unique: true, + name: "index_securities_on_ticker_and_exchange_operating_mic_unique" + end +end diff --git a/db/schema.rb b/db/schema.rb index 9f079956..11612da4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_05_21_112347) do +ActiveRecord::Schema[7.2].define(version: 2025_05_22_201031) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -517,9 +517,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_05_21_112347) do t.datetime "failed_fetch_at" t.integer "failed_fetch_count", default: 0, null: false t.datetime "last_health_check_at" + t.index "upper((ticker)::text), COALESCE(upper((exchange_operating_mic)::text), ''::text)", name: "index_securities_on_ticker_and_exchange_operating_mic_unique", unique: true t.index ["country_code"], name: "index_securities_on_country_code" t.index ["exchange_operating_mic"], name: "index_securities_on_exchange_operating_mic" - t.index ["ticker", "exchange_operating_mic"], name: "index_securities_on_ticker_and_exchange_operating_mic", unique: true end create_table "security_prices", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|