diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2012-02-10 10:55:34 -0800 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2012-02-10 10:55:34 -0800 |
commit | f7b915b50718c86d3644941cd0bbe7df08888a5b (patch) | |
tree | 694847bdcdf374afc65eec9a81aa76fa63855ccf /activerecord/lib/active_record | |
parent | 522b72fd28b898c914cfbdbc31837e56702f60d5 (diff) | |
parent | 2dd0178229ccd5e7f19f120ded85fd4a80306cac (diff) | |
download | rails-f7b915b50718c86d3644941cd0bbe7df08888a5b.tar.gz rails-f7b915b50718c86d3644941cd0bbe7df08888a5b.tar.bz2 rails-f7b915b50718c86d3644941cd0bbe7df08888a5b.zip |
Merge branch 'joelhoffman-postgres_schema_builder' into instance_reader
* joelhoffman-postgres_schema_builder:
Also support writing the hstore back to the database
Hstore values are all strings
string_to_hstore / hstore_to_string, serializing
don't test schema where hstore not installed
schema dumper tests for hstore
Additional hstore tests, supporting null values, better compliance with postgres docs
add hstore to postgres native types and defaults
Conflicts:
activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
Diffstat (limited to 'activerecord/lib/active_record')
3 files changed, 41 insertions, 27 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 34d88edff3..7ef3218a49 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -98,7 +98,7 @@ module ActiveRecord when :date then klass.value_to_date(value) when :binary then klass.binary_to_string(value) when :boolean then klass.value_to_boolean(value) - when :hstore then klass.cast_hstore(value) + when :hstore then klass.string_to_hstore(value) else value end end @@ -116,7 +116,7 @@ module ActiveRecord when :date then "#{klass}.value_to_date(#{var_name})" when :binary then "#{klass}.binary_to_string(#{var_name})" when :boolean then "#{klass}.value_to_boolean(#{var_name})" - when :hstore then "#{klass}.cast_hstore(#{var_name})" + when :hstore then "#{klass}.string_to_hstore(#{var_name})" else var_name 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 006742688e..8110db78c4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -133,7 +133,7 @@ module ActiveRecord def type_cast(value) return if value.nil? - ConnectionAdapters::PostgreSQLColumn.cast_hstore value + ConnectionAdapters::PostgreSQLColumn.string_to_hstore value 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 f38fca6576..eccf3595aa 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -53,40 +53,42 @@ module ActiveRecord end end - def cast_hstore(object) + def hstore_to_string(object) if Hash === object object.map { |k,v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" - }.join ', ' + }.join ',' else - kvs = object.scan(/(?<!\\)".*?(?<!\\)"/).map { |o| - unescape_hstore(o[1...-1]) - } - Hash[kvs.each_slice(2).to_a] + object end end - private - HSTORE_ESCAPE = { - ' ' => '\\ ', - '\\' => '\\\\', - '"' => '\\"', - '=' => '\\=', - } - HSTORE_ESCAPE_RE = Regexp.union(HSTORE_ESCAPE.keys) - HSTORE_UNESCAPE = HSTORE_ESCAPE.invert - HSTORE_UNESCAPE_RE = Regexp.union(HSTORE_UNESCAPE.keys) - - def unescape_hstore(value) - value.gsub(HSTORE_UNESCAPE_RE) do |match| - HSTORE_UNESCAPE[match] + def string_to_hstore(string) + if string.nil? + nil + elsif String === string + Hash[string.scan(HstorePair).map { |k,v| + v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1') + k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1') + [k,v] + }] + else + string end end + private + HstorePair = begin + quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/ + unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/ + /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/ + end + def escape_hstore(value) - value.gsub(HSTORE_ESCAPE_RE) do |match| - HSTORE_ESCAPE[match] - end + value.nil? ? 'NULL' + : value =~ /[=\s,>]/ ? '"%s"' % value.gsub(/(["\\])/, '\\\\\1') + : value == "" ? '""' + : value.to_s.gsub(/(["\\])/, '\\\\\1') end end # :startdoc: @@ -139,6 +141,9 @@ module ActiveRecord # Arrays when /\A'(.*)'::"?\D+"?\[\]\z/ $1 + # Hstore + when /\A'(.*)'::hstore\z/ + $1 # Object identifier types when /\A-?\d+\z/ $1 @@ -285,7 +290,8 @@ module ActiveRecord :binary => { :name => "bytea" }, :boolean => { :name => "boolean" }, :xml => { :name => "xml" }, - :tsvector => { :name => "tsvector" } + :tsvector => { :name => "tsvector" }, + :hstore => { :name => "hstore" } } # Returns 'PostgreSQL' as adapter name for identification purposes. @@ -475,6 +481,11 @@ module ActiveRecord return super unless column case value + when Hash + case column.sql_type + when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column) + else super + end when Float return super unless value.infinite? && column.type == :datetime "'#{value.to_s.downcase}'" @@ -506,6 +517,9 @@ module ActiveRecord when String return super unless 'bytea' == column.sql_type { :value => value, :format => 1 } + when Hash + return super unless 'hstore' == column.sql_type + PostgreSQLColumn.hstore_to_string(value) else super end |