aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
blob: 42ab6ec4bdf8e23226ddbc1409dfbf36250c9690 (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
module ActiveRecord
  module ConnectionAdapters
    module PostgreSQL
      module OID # :nodoc:
        # This class uses the data from PostgreSQL pg_type table to build
        # the OID -> Type mapping.
        #   - OID is an integer representing the type.
        #   - Type is an OID::Type object.
        # This class has side effects on the +store+ passed during initialization.
        class TypeMapInitializer # :nodoc:
          def initialize(store, run_complex_types = true)
            @store = store
            @run_complex_types = run_complex_types
          end

          def run(records)
            nodes = records.reject { |row| @store.key? row['oid'].to_i }
            mapped, nodes = nodes.partition { |row| @store.key? row['typname'] }
            ranges, nodes = nodes.partition { |row| row['typtype'] == 'r' }
            enums, nodes = nodes.partition { |row| row['typtype'] == 'e' }
            domains, nodes = nodes.partition { |row| row['typtype'] == 'd' }
            arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
            composites, nodes = nodes.partition { |row| row['typelem'] != '0' }

            mapped.each     { |row| register_mapped_type(row)    }
            enums.each      { |row| register_enum_type(row)      }
            domains.each    { |row| register_domain_type(row)    }
            arrays.each     { |row| register_array_type(row)     }
            ranges.each     { |row| register_range_type(row)     }
            composites.each { |row| register_composite_type(row) }
          end

          private
          def register_mapped_type(row)
            alias_type row['oid'], row['typname']
          end

          def register_enum_type(row)
            register row['oid'], OID::Enum.new
          end

          def register_array_type(row)
            register_with_subtype(row['oid'], row['typelem'].to_i) do |subtype|
              OID::Array.new(subtype, row['typdelim'])
            end
          end

          def register_range_type(row)
            register_with_subtype(row['oid'], row['rngsubtype'].to_i) do |subtype|
              OID::Range.new(subtype, row['typname'].to_sym)
            end
          end

          def register_domain_type(row)
            if base_type = @store.lookup(row["typbasetype"].to_i)
              register row['oid'], base_type
            else
              warn "unknown base type (OID: #{row["typbasetype"]}) for domain #{row["typname"]}."
            end
          end

          def register_composite_type(row)
            if subtype = @store.lookup(row['typelem'].to_i)
              register row['oid'], OID::Vector.new(row['typdelim'], subtype)
            end
          end

          def register(oid, oid_type = nil, &block)
            oid = assert_valid_registration(oid, oid_type || block)
            if block_given?
              @store.register_type(oid, &block)
            else
              @store.register_type(oid, oid_type)
            end
          end

          def alias_type(oid, target)
            oid = assert_valid_registration(oid, target)
            @store.alias_type(oid, target)
          end

          def register_with_subtype(oid, target_oid)
            if @store.key?(target_oid)
              register(oid) do |_, *args|
                yield @store.lookup(target_oid, *args)
              end
            end
          end

          def assert_valid_registration(oid, oid_type)
            raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
            oid.to_i
          end
        end
      end
    end
  end
end