-
Notifications
You must be signed in to change notification settings - Fork 224
/
schema_statements.rb
116 lines (106 loc) · 4.25 KB
/
schema_statements.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# frozen_string_literal: true
module ActiveRecord
module ConnectionAdapters
module PostGIS
module SchemaStatements
# override
# https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L624
# Create a SpatialColumn instead of a PostgreSQL::Column
def new_column_from_field(table_name, field)
column_name, type, default, notnull, oid, fmod, collation, comment = field
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
default_value = extract_value_from_default(default)
default_function = extract_default_function(default_value, default)
serial =
if (match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/))
sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
end
# {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
spatial = spatial_column_info(table_name).get(column_name, type_metadata.sql_type)
SpatialColumn.new(
column_name,
default_value,
type_metadata,
!notnull,
default_function,
collation: collation,
comment: comment.presence,
serial: serial,
spatial: spatial
)
end
# override
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L544
#
# returns Postgresql sql type string
# examples:
# "geometry(Point,4326)"
# "geography(Point,4326)"
#
# note: type alone is not enough to detect the sql type,
# so `limit` is used to pass the additional information. :(
#
# type_to_sql(:geography, limit: "Point,4326")
# => "geography(Point,4326)"
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **)
case type.to_s
when "geometry", "geography"
"#{type}(#{limit})"
else
super
end
end
# override
def native_database_types
# Add spatial types
super.merge(
geography: { name: "geography" },
geometry: { name: "geometry" },
geometry_collection: { name: "geometry_collection" },
line_string: { name: "line_string" },
multi_line_string: { name: "multi_line_string" },
multi_point: { name: "multi_point" },
multi_polygon: { name: "multi_polygon" },
spatial: { name: "geometry" },
st_point: { name: "st_point" },
st_polygon: { name: "st_polygon" }
)
end
# override
def create_table_definition(*args, **kwargs)
PostGIS::TableDefinition.new(self, *args, **kwargs)
end
# memoize hash of column infos for tables
def spatial_column_info(table_name)
@spatial_column_info ||= {}
@spatial_column_info[table_name.to_sym] ||= SpatialColumnInfo.new(self, table_name.to_s)
end
def initialize_type_map(map = type_map)
%w(
geography
geometry
geometry_collection
line_string
multi_line_string
multi_point
multi_polygon
st_point
st_polygon
).each do |geo_type|
map.register_type(geo_type) do |_, _, sql_type|
# sql_type is a string that comes from the database definition
# examples:
# "geometry(Point,4326)"
# "geography(Point,4326)"
# "geometry(Polygon,4326) NOT NULL"
# "geometry(Geography,4326)"
geo_type, srid, has_z, has_m, geographic = OID::Spatial.parse_sql_type(sql_type)
OID::Spatial.new(geo_type: geo_type, srid: srid, has_z: has_z, has_m: has_m, geographic: geographic)
end
end
super
end
end
end
end
end