aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorSean Griffin <sean@thoughtbot.com>2015-03-26 17:40:36 -0600
committerSean Griffin <sean@thoughtbot.com>2015-03-26 17:41:47 -0600
commitcd09261846053bb40f8c859b8644b28208b6d7b9 (patch)
treefac9abae08696d097401c7703c474305a1e23215 /activerecord/lib
parent3b50a7a3e18e1ae5ea1d76927e37021f50389abc (diff)
parent647eb2cf1cd65b0391e3584361f0fc76246e64f3 (diff)
downloadrails-cd09261846053bb40f8c859b8644b28208b6d7b9.tar.gz
rails-cd09261846053bb40f8c859b8644b28208b6d7b9.tar.bz2
rails-cd09261846053bb40f8c859b8644b28208b6d7b9.zip
Partially merge #17650
Merges 647eb2cf1cd65b0391e3584361f0fc76246e64f3. The pull request as a whole is quite large, and I'm reviewing the smaller pieces individually.
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb93
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb56
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb18
3 files changed, 20 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 37bf4d77f8..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
@@ -278,6 +278,7 @@ module ActiveRecord
@table_alias_length = nil
connect
+ add_pg_encoders
@statements = StatementPool.new @connection,
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
@@ -803,6 +804,15 @@ module ActiveRecord
end
end
+ def add_pg_encoders
+ map = PG::TypeMapByClass.new
+ map[Integer] = PG::TextEncoder::Integer.new
+ map[TrueClass] = PG::TextEncoder::Boolean.new
+ map[FalseClass] = PG::TextEncoder::Boolean.new
+ map[Float] = PG::TextEncoder::Float.new
+ @connection.type_map_for_queries = map
+ end
+
def add_pg_decoders
coders_by_name = {
'int2' => PG::TextDecoder::Integer,