aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/schema_cache.rb
blob: 9c56d35bcc95394a33993030e1e15a57465bfe88 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
require 'active_support/deprecation/reporting'

module ActiveRecord
  module ConnectionAdapters
    class SchemaCache
      attr_reader :version
      attr_accessor :connection

      def initialize(conn)
        @connection = conn

        @columns      = {}
        @columns_hash = {}
        @primary_keys = {}
        @tables       = {}
        prepare_default_proc
      end

      def primary_keys(table_name = nil)
        if table_name
          @primary_keys[table_name]
        else
          ActiveSupport::Deprecation.warn('call primary_keys with a table name!')
          @primary_keys.dup
        end
      end

      # A cached lookup for table existence.
      def table_exists?(name)
        return @tables[name] if @tables.key? name

        @tables[name] = connection.table_exists?(name)
      end

      # Add internal cache for table with +table_name+.
      def add(table_name)
        if table_exists?(table_name)
          @primary_keys[table_name]
          @columns[table_name]
          @columns_hash[table_name]
        end
      end

      def tables(name = nil)
        if name
          @tables[name]
        else
          ActiveSupport::Deprecation.warn('call tables with a name!')
          @tables.dup
        end
      end

      # Get the columns for a table
      def columns(table = nil)
        if table
          @columns[table]
        else
          ActiveSupport::Deprecation.warn('call columns with a table name!')
          @columns.dup
        end
      end

      # Get the columns for a table as a hash, key is the column name
      # value is the column object.
      def columns_hash(table = nil)
        if table
          @columns_hash[table]
        else
          ActiveSupport::Deprecation.warn('call columns_hash with a table name!')
          @columns_hash.dup
        end
      end

      # Clears out internal caches
      def clear!
        @columns.clear
        @columns_hash.clear
        @primary_keys.clear
        @tables.clear
        @version = nil
      end

      def size
        [@columns, @columns_hash, @primary_keys, @tables].map { |x|
          x.size
        }.inject :+
      end

      # Clear out internal caches for table with +table_name+.
      def clear_table_cache!(table_name)
        @columns.delete table_name
        @columns_hash.delete table_name
        @primary_keys.delete table_name
        @tables.delete table_name
      end

      def marshal_dump
        # if we get current version during initialization, it happens stack over flow.
        @version = ActiveRecord::Migrator.current_version
        [@version] + [:@columns, :@columns_hash, :@primary_keys, :@tables].map do |val|
          instance_variable_get(val).dup.tap { |h|
            h.default_proc = nil
          }
        end
      end

      def marshal_load(array)
        @version, @columns, @columns_hash, @primary_keys, @tables = array
        prepare_default_proc
      end

      private

      def prepare_default_proc
        @columns.default_proc = Proc.new do |h, table_name|
          h[table_name] = connection.columns(table_name)
        end

        @columns_hash.default_proc = Proc.new do |h, table_name|
          h[table_name] = Hash[columns(table_name).map { |col|
            [col.name, col]
          }]
        end

        @primary_keys.default_proc = Proc.new do |h, table_name|
          h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
        end
      end
    end
  end
end