module ForeignKeyChecker module Utils class Result attr_reader :from_table, :to_table, :from_column, :to_column def initialize(args) args.each { |k, v| instance_variable_set("@#{k}", v) } end end class UnsupportedConnectionAdapter < StandardError; end def self.get_foreign_keys(model = ActiveRecord::Base) adapter = model.connection_config[:adapter] raise(UnsupportedConnectionAdapter, adapter) unless %w[postgresql mysql2].include?(adapter) connection = model.connection send("get_#{adapter}_foreign_keys", connection) end def self.get_foreign_keys_hash(model = ActiveRecord::Base) get_foreign_keys(model).to_a.each_with_object({}) do |datum, obj| obj[datum.to_table] ||= [] obj[datum.to_table].push(datum) end end def self.get_mysql2_foreign_keys(connection) res = connection.select_all <<-SQL SELECT fks.TABLE_NAME AS from_table, fks.COLUMN_NAME AS from_column, fks.REFERENCED_TABLE_NAME AS to_table, fks.REFERENCED_COLUMN_NAME AS to_column FROM information_schema.KEY_COLUMN_USAGE AS fks INNER JOIN information_schema.REFERENTIAL_CONSTRAINTS rules ON rules.CONSTRAINT_NAME = fks.CONSTRAINT_NAME WHERE fks.CONSTRAINT_SCHEMA = DATABASE() AND rules.CONSTRAINT_SCHEMA = DATABASE(); SQL res.to_a.map{|i| Result.new(i) } end def self.get_postgresql_foreign_keys(connection) res = connection.select_all <<-SQL SELECT tc.table_name AS from_table, kcu.column_name AS from_column, ccu.table_name AS to_table, ccu.column_name AS to_column FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema WHERE tc.constraint_type = 'FOREIGN KEY'; SQL res.to_a.map{ |i| Result.new(i) } end end end