Permalink
Browse files

Merge pull request #7324 from MSch/pg-geometric

Add support for pg geometric datatypes point and box
  • Loading branch information...
2 parents c2e2031 + f8675eb commit fc8411ad6c939cb4d39910d4b012291e8a9e148d @rafaelfranca rafaelfranca committed Mar 25, 2013
View
4 activerecord/CHANGELOG.md
@@ -1,5 +1,9 @@
## Rails 4.0.0 (unreleased) ##
+* PostgreSQL geometric type point is supported by ActiveRecord. Fixes #7324.
+
+ *Martin Schuerrer*
+
* Add an `add_index` override in Postgresql adapter and MySQL adapter
to allow custom index type support. Fixes #6101.
View
11 activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -2,6 +2,17 @@ module ActiveRecord
module ConnectionAdapters
class PostgreSQLColumn < Column
module Cast
+ def point_to_string(point)
+ "(#{point[0]},#{point[1]})"
+ end
+
+ def string_to_point(string)
+ if string[0] == '(' && string[-1] == ')'
+ string = string[1...-1]
+ end
+ string.split(',').map{ |v| Float(v) }
+ end
+
def string_to_time(string)
return string unless String === string
View
11 activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -63,6 +63,16 @@ def type_cast(value)
end
end
+ class Point < Type
+ def type_cast(value)
+ if String === value
+ ConnectionAdapters::PostgreSQLColumn.string_to_point value
+ else
+ value
+ end
+ end
+ end
+
class Array < Type
attr_reader :subtype
def initialize(subtype)
@@ -330,6 +340,7 @@ def self.registered_type?(name)
register_type 'time', OID::Time.new
register_type 'path', OID::Identity.new
+ register_type 'point', OID::Point.new
register_type 'polygon', OID::Identity.new
register_type 'circle', OID::Identity.new
register_type 'hstore', OID::Hstore.new
View
27 activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -28,10 +28,14 @@ def quote(value, column = nil) #:nodoc:
super
end
when Array
- if column.array
- "'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
+ case sql_type
+ when 'point' then super(PostgreSQLColumn.point_to_string(value))
else
- super
+ if column.array
+ "'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
+ else
+ super
+ end
end
when Hash
case sql_type
@@ -79,9 +83,10 @@ def quote(value, column = nil) #:nodoc:
def type_cast(value, column, array_member = false)
return super(value, column) unless column
+ sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
case value
when Range
- return super(value, column) unless /range$/ =~ column.sql_type
+ return super(value, column) unless /range$/ =~ sql_type
PostgreSQLColumn.range_to_string(value)
when NilClass
if column.array && array_member
@@ -92,19 +97,23 @@ def type_cast(value, column, array_member = false)
super(value, column)
end
when Array
- return super(value, column) unless column.array
- PostgreSQLColumn.array_to_string(value, column, self)
+ case sql_type
+ when 'point' then PostgreSQLColumn.point_to_string(value)
+ else
+ return super(value, column) unless column.array
+ PostgreSQLColumn.array_to_string(value, column, self)
+ end
when String
- return super(value, column) unless 'bytea' == column.sql_type
+ return super(value, column) unless 'bytea' == sql_type
{ :value => value, :format => 1 }
when Hash
- case column.sql_type
+ case sql_type
when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
when 'json' then PostgreSQLColumn.json_to_string(value)
else super(value, column)
end
when IPAddr
- return super(value, column) unless ['inet','cidr'].include? column.sql_type
+ return super(value, column) unless ['inet','cidr'].include? sql_type
PostgreSQLColumn.cidr_to_string(value)
else
super(value, column)
View
9 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -711,7 +711,14 @@ def initialize_type_map
# populate composite types
nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
- vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
+ if OID.registered_type? row['typname']
+ # this composite type is explicitly registered
+ vector = OID::NAMES[row['typname']]
+ else
+ # use the default for composite types
+ vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
+ end
+
OID::TYPE_MAP[row['oid'].to_i] = vector
end
View
27 activerecord/test/cases/base_test.rb
@@ -840,7 +840,7 @@ def test_geometric_content
# Reload and check that we have all the geometric attributes.
h = Geometric.find(g.id)
- assert_equal '(5,6.1)', h.a_point
+ assert_equal [5.0, 6.1], h.a_point
assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
@@ -869,7 +869,7 @@ def test_geometric_content
# Reload and check that we have all the geometric attributes.
h = Geometric.find(g.id)
- assert_equal '(5,6.1)', h.a_point
+ assert_equal [5.0, 6.1], h.a_point
assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
@@ -880,6 +880,29 @@ def test_geometric_content
objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
assert_equal true, objs[0].isclosed
+
+ # test native ruby formats when defining the geometric types
+ g = Geometric.new(
+ :a_point => [5.0, 6.1],
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
+ :a_box => '(2.0, 3), (5.5, 7.0)',
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
+ :a_circle => '((5.3, 10.4), 2)'
+ )
+
+ assert g.save
+
+ # Reload and check that we have all the geometric attributes.
+ h = Geometric.find(g.id)
+
+ assert_equal [5.0, 6.1], h.a_point
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
+ assert_equal '<(5.3,10.4),2>', h.a_circle
end
end

0 comments on commit fc8411a

Please sign in to comment.