# frozen_string_literal: false module PGTrunk::Operations::Domains # @private # Definition for the domain's constraint class Constraint include ActiveModel::Model include ActiveModel::Attributes include ActiveModel::Validations def self.build(data) data.is_a?(self) ? data : new(**data) end attribute :check, :pg_trunk_multiline_text attribute :drop, :boolean attribute :force, :pg_trunk_symbol attribute :if_exists, :boolean attribute :name, :string attribute :new_name, :string attribute :valid, :boolean validates :name, presence: true validates :new_name, "PGTrunk/difference": { from: :name }, allow_nil: true validates :force, inclusion: { in: %i[force restrict] }, allow_nil: true def to_h @to_h ||= attributes.compact.symbolize_keys end def opts to_h.slice(:name) end def invert @invert ||= {}.tap do |i| i[:name] = new_name.presence || name i[:new_name] = name if new_name.present? i[:drop] = !drop if new_name.blank? i[:check] = check if drop end end def to_sql rename_sql || drop_sql || add_sql || validate_sql end def inversion_error return <<~MSG.squish if if_exists with `if_exists: true` option cannot be inverted due to uncertainty of the previous state of the database. MSG return <<~MSG.squish if force == :cascade with `force: :cascade` option cannot be inverted due to uncertainty of the previous state of the database. MSG return if check.present? "the constraint `#{name}` is dropped without `check` option." end private def rename_sql <<~SQL.squish if new_name.present? RENAME CONSTRAINT #{name.inspect} TO #{new_name.inspect} SQL end def drop_sql return unless drop sql = "DROP CONSTRAINT" sql << " IF EXISTS" if if_exists sql << " #{name.inspect}" sql << " CASCADE" if force == :cascade sql << ";" end def add_sql <<~SQL.squish if check.present? ADD CONSTRAINT #{name.inspect} CHECK (#{check})#{' NOT VALID' unless valid} SQL end def validate_sql "VALIDATE CONSTRAINT #{name.inspect}" if valid end end end