# This migration comes from spree (originally 20150515211137) class FixAdjustmentOrderId < ActiveRecord::Migration def change say 'Populate order_id from adjustable_id where appropriate' # 3 separate execute calls to workaround MySQL limitation execute(<<-'SQL'.squish) UPDATE spree_adjustments SET order_id = adjustable_id WHERE adjustable_type = 'Spree::Order' AND order_id IS NULL SQL execute(<<-'SQL'.squish) UPDATE spree_adjustments SET order_id = (SELECT order_id FROM spree_line_items WHERE spree_line_items.id = spree_adjustments.adjustable_id) WHERE adjustable_type = 'Spree::LineItem' AND order_id IS NULL ; SQL execute(<<-'SQL'.squish) UPDATE spree_adjustments SET order_id = (SELECT order_id FROM spree_shipments WHERE spree_shipments.id = spree_adjustments.adjustable_id) WHERE adjustable_type = 'Spree::Shipment' AND order_id IS NULL SQL say 'Fix schema for spree_adjustments order_id column' change_table :spree_adjustments do |t| t.change :order_id, :integer, null: false t.change :adjustable_id, :integer, null: false add_foreign_key :spree_adjustments, :spree_orders, name: 'fk_spree_adjustments_order_id', # default is indeterministic column: :order_id, on_delete: :restrict, # handled by models on_update: :restrict # handled by models end if connection.adapter_name.eql?('PostgreSQL') # Negated Logical implication. # # When adjustable_type is 'Spree::Order' (p) the adjustable_id must be order_id (q). # # When adjustable_type is NOT 'Spree::Order' the adjustable id allowed to be any value (including of order_id in # case foreign keys match). XOR does not work here. # # Postgresql does not have an operator for logical implication. So we need to build the following truth table # via AND with OR: # # p q | CHECK = !(p -> q) # ----------- # t t | t # t f | f # f t | t # f f | t # # According to de-morgans law the logical implication q -> p is equivalent to !p || q # execute(<<-SQL.squish) ALTER TABLE ONLY spree_adjustments ADD CONSTRAINT check_spree_adjustments_order_id CHECK (adjustable_type <> 'Spree::Order' OR order_id = adjustable_id); SQL end end end