diff options
author | Lars Kanis <lars@greiz-reinsdorf.de> | 2015-03-22 18:27:55 +0100 |
---|---|---|
committer | Lars Kanis <lars@greiz-reinsdorf.de> | 2015-03-25 15:05:24 +0100 |
commit | 647eb2cf1cd65b0391e3584361f0fc76246e64f3 (patch) | |
tree | 29ba4ebaabf3068cbc08ea52bcf5a87509c8c630 /activerecord/lib/active_record | |
parent | 9f2f268666e2f5d861905f4740943c85ea70f76e (diff) | |
download | rails-647eb2cf1cd65b0391e3584361f0fc76246e64f3.tar.gz rails-647eb2cf1cd65b0391e3584361f0fc76246e64f3.tar.bz2 rails-647eb2cf1cd65b0391e3584361f0fc76246e64f3.zip |
PostgreSQL, Use ruby-pg's built-in capabilities for array en-/decoding in C.
This obsoletes the ruby based implementations.
Diffstat (limited to 'activerecord/lib/active_record')
3 files changed, 10 insertions, 147 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb b/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb deleted file mode 100644 index 1b74c039ce..0000000000 --- a/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb +++ /dev/null @@ -1,93 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module PostgreSQL - module ArrayParser # :nodoc: - - DOUBLE_QUOTE = '"' - BACKSLASH = "\\" - COMMA = ',' - BRACKET_OPEN = '{' - BRACKET_CLOSE = '}' - - def parse_pg_array(string) # :nodoc: - 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 diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb index f486c5ecb7..3de794f797 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb @@ -5,29 +5,20 @@ module ActiveRecord class Array < Type::Value # :nodoc: include Type::Helpers::Mutable - # Loads pg_array_parser if available. String parsing can be - # performed quicker by a native extension, which will not create - # a large amount of Ruby objects that will need to be garbage - # collected. pg_array_parser has a C and Java extension - begin - require 'pg_array_parser' - include PgArrayParser - rescue LoadError - require 'active_record/connection_adapters/postgresql/array_parser' - include PostgreSQL::ArrayParser - end - attr_reader :subtype, :delimiter delegate :type, :user_input_in_time_zone, :limit, to: :subtype def initialize(subtype, delimiter = ',') @subtype = subtype @delimiter = delimiter + + @pg_encoder = PG::TextEncoder::Array.new name: "#{type}[]", delimiter: delimiter + @pg_decoder = PG::TextDecoder::Array.new name: "#{type}[]", delimiter: delimiter end def deserialize(value) if value.is_a?(::String) - type_cast_array(parse_pg_array(value), :deserialize) + type_cast_array(@pg_decoder.decode(value), :deserialize) else super end @@ -35,14 +26,14 @@ module ActiveRecord def cast(value) if value.is_a?(::String) - value = parse_pg_array(value) + value = @pg_decoder.decode(value) end type_cast_array(value, :cast) end def serialize(value) if value.is_a?(::Array) - cast_value_for_database(value) + @pg_encoder.encode(type_cast_array(value, :serialize)) else super end @@ -63,41 +54,6 @@ module ActiveRecord @subtype.public_send(method, value) end end - - def cast_value_for_database(value) - if value.is_a?(::Array) - casted_values = value.map { |item| cast_value_for_database(item) } - "{#{casted_values.join(delimiter)}}" - else - quote_and_escape(subtype.serialize(value)) - end - end - - ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays - - def quote_and_escape(value) - case value - when ::String - if string_requires_quoting?(value) - value = value.gsub(/\\/, ARRAY_ESCAPE) - value.gsub!(/"/,"\\\"") - %("#{value}") - else - value - end - when nil then "NULL" - else value - end - end - - # See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO - # for a list of all cases in which strings will be quoted. - def string_requires_quoting?(string) - string.empty? || - string == "NULL" || - string =~ /[\{\}"\\\s]/ || - string.include?(delimiter) - end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 5a304e6ec5..e616cee5eb 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1,3 +1,7 @@ +# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility +gem 'pg', '~> 0.18' +require 'pg' + require "active_record/connection_adapters/abstract_adapter" require "active_record/connection_adapters/postgresql/column" require "active_record/connection_adapters/postgresql/database_statements" @@ -12,10 +16,6 @@ require "active_record/connection_adapters/statement_pool" require 'arel/visitors/bind_visitor' -# Make sure we're using pg high enough for Ruby 2.2+ compatibility -gem 'pg', '~> 0.18' -require 'pg' - require 'ipaddr' module ActiveRecord |