aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb
blob: 5394ea0b7c20aae04e233ff9fd09b3056b4446be (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
module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLColumn < Column
      module ArrayParser

        DOUBLE_QUOTE = '"'
        BACKSLASH = "\\"
        COMMA = ','
        BRACKET_OPEN = '{'
        BRACKET_CLOSE = '}'

        def parse_pg_array(string)
          local_index = 0
          array = []
          while(local_index < string.length)
            case string[local_index]
            when BRACKET_OPEN
              local_index,array = parse_array_contents(array, string, local_index + 1)
            when BRACKET_CLOSE
              return array
            end
            local_index += 1
          end

          array
        end

        private

          def parse_array_contents(array, string, index)
            is_escaping  = false
            is_quoted    = false
            was_quoted   = false
            current_item = ''

            local_index = index
            while local_index
              token = string[local_index]
              if is_escaping
                current_item << token
                is_escaping = false
              else
                if is_quoted
                  case token
                  when DOUBLE_QUOTE
                    is_quoted = false
                    was_quoted = true
                  when BACKSLASH
                    is_escaping = true
                  else
                    current_item << token
                  end
                else
                  case token
                  when BACKSLASH
                    is_escaping = true
                  when COMMA
                    add_item_to_array(array, current_item, was_quoted)
                    current_item = ''
                    was_quoted = false
                  when DOUBLE_QUOTE
                    is_quoted = true
                  when BRACKET_OPEN
                    internal_items = []
                    local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
                    array.push(internal_items)
                  when BRACKET_CLOSE
                    add_item_to_array(array, current_item, was_quoted)
                    return local_index,array
                  else
                    current_item << token
                  end
                end
              end

              local_index += 1
            end
            return local_index,array
          end

          def add_item_to_array(array, current_item, quoted)
            return if !quoted && current_item.length == 0

            if !quoted && current_item == 'NULL'
              array.push nil
            else
              array.push current_item
            end
          end
      end
    end
  end
end