aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/postgresql
diff options
context:
space:
mode:
authorRafael Mendonça França <rafaelmfranca@gmail.com>2012-09-16 20:07:28 -0700
committerRafael Mendonça França <rafaelmfranca@gmail.com>2012-09-16 20:07:28 -0700
commitbeaac33f9ff732fc01ebdb84ff66a529b79fa118 (patch)
tree4632932a6ce826a8b057df55bd98ae762026103d /activerecord/lib/active_record/connection_adapters/postgresql
parent4434e3f72b14cc9fdbd3bd5afa0784406c1c305b (diff)
parent4544d2bc90bea93c38bb21d912dba00f51cf620f (diff)
downloadrails-beaac33f9ff732fc01ebdb84ff66a529b79fa118.tar.gz
rails-beaac33f9ff732fc01ebdb84ff66a529b79fa118.tar.bz2
rails-beaac33f9ff732fc01ebdb84ff66a529b79fa118.zip
Merge pull request #7547 from danmcclain/pg-arrays
Adds migration and type casting support for PostgreSQL Array datatype
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/postgresql')
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb97
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb28
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb29
4 files changed, 163 insertions, 6 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
new file mode 100644
index 0000000000..b7d24f2bb3
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb
@@ -0,0 +1,97 @@
+module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLColumn < Column
+ module ArrayParser
+ private
+ # 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
+ def parse_pg_array(string)
+ parse_data(string, 0)
+ end
+ end
+
+ def parse_data(string, index)
+ local_index = index
+ array = []
+ while(local_index < string.length)
+ case string[local_index]
+ when '{'
+ local_index,array = parse_array_contents(array, string, local_index + 1)
+ when '}'
+ return array
+ end
+ local_index += 1
+ end
+
+ array
+ end
+
+ 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 '"'
+ is_quoted = false
+ was_quoted = true
+ when "\\"
+ is_escaping = true
+ else
+ current_item << token
+ end
+ else
+ case token
+ when "\\"
+ is_escaping = true
+ when ','
+ add_item_to_array(array, current_item, was_quoted)
+ current_item = ''
+ was_quoted = false
+ when '"'
+ is_quoted = true
+ when '{'
+ internal_items = []
+ local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
+ array.push(internal_items)
+ when '}'
+ 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)
+ if current_item.length == 0
+ elsif !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/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index b59195f98a..62d091357d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -45,6 +45,21 @@ module ActiveRecord
end
end
+ def array_to_string(value, column, adapter, should_be_quoted = false)
+ casted_values = value.map do |val|
+ if String === val
+ if val == "NULL"
+ "\"#{val}\""
+ else
+ quote_and_escape(adapter.type_cast(val, column, true))
+ end
+ else
+ adapter.type_cast(val, column, true)
+ end
+ end
+ "{#{casted_values.join(',')}}"
+ end
+
def string_to_json(string)
if String === string
ActiveSupport::JSON.decode(string)
@@ -71,6 +86,10 @@ module ActiveRecord
end
end
+ def string_to_array(string, oid)
+ parse_pg_array(string).map{|val| oid.type_cast val}
+ end
+
private
HstorePair = begin
@@ -90,6 +109,15 @@ module ActiveRecord
end
end
end
+
+ def quote_and_escape(value)
+ case value
+ when "NULL"
+ value
+ else
+ "\"#{value.gsub(/"/,"\\\"")}\""
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index b8e7687b21..52344f61c0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -63,6 +63,21 @@ module ActiveRecord
end
end
+ class Array < Type
+ attr_reader :subtype
+ def initialize(subtype)
+ @subtype = subtype
+ end
+
+ def type_cast(value)
+ if String === value
+ ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
+ else
+ value
+ end
+ end
+ end
+
class Integer < Type
def type_cast(value)
return if value.nil?
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 85721601a9..37d43d891d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -19,6 +19,12 @@ module ActiveRecord
return super unless column
case value
+ when Array
+ if column.array
+ "'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
+ else
+ super
+ end
when Hash
case column.sql_type
when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
@@ -59,24 +65,35 @@ module ActiveRecord
end
end
- def type_cast(value, column)
- return super unless column
+ def type_cast(value, column, array_member = false)
+ return super(value, column) unless column
case value
+ when NilClass
+ if column.array && array_member
+ 'NULL'
+ elsif column.array
+ value
+ else
+ super(value, column)
+ end
+ when Array
+ return super(value, column) unless column.array
+ PostgreSQLColumn.array_to_string(value, column, self)
when String
- return super unless 'bytea' == column.sql_type
+ return super(value, column) unless 'bytea' == column.sql_type
{ :value => value, :format => 1 }
when Hash
case column.sql_type
when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
when 'json' then PostgreSQLColumn.json_to_string(value)
- else super
+ else super(value, column)
end
when IPAddr
- return super unless ['inet','cidr'].includes? column.sql_type
+ return super(value, column) unless ['inet','cidr'].includes? column.sql_type
PostgreSQLColumn.cidr_to_string(value)
else
- super
+ super(value, column)
end
end