activerecord-mysql2rgeo-adapter 8.0.4 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/active_record/connection_adapters/mysql2rgeo/arel_tosql.rb +8 -14
- data/lib/active_record/connection_adapters/mysql2rgeo/schema_creation.rb +21 -22
- data/lib/active_record/connection_adapters/mysql2rgeo/schema_statements.rb +34 -25
- data/lib/active_record/connection_adapters/mysql2rgeo/spatial_column.rb +23 -9
- data/lib/active_record/connection_adapters/mysql2rgeo/spatial_column_info.rb +5 -5
- data/lib/active_record/connection_adapters/mysql2rgeo/spatial_table_definition.rb +18 -12
- data/lib/active_record/connection_adapters/mysql2rgeo/version.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2rgeo_adapter.rb +98 -77
- data/lib/active_record/type/spatial.rb +33 -26
- data/lib/activerecord-mysql2rgeo-adapter.rb +3 -0
- metadata +6 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f886ffec9c1cc39dd75d324940ebb386a49b5a8b23d65f3e2016e9610dd0c23
|
|
4
|
+
data.tar.gz: ba5b9e5e18655b6a52f0e73a612679efd55f5a097e8eeaf6d13736631c9dcaf9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd759c023b7736b538039efbb1ebe0e289f77532758df317b6c7273269cc22d5888a662365cc5dafcfb5eade82732f6e531cba88df7df652c15e085b9d839a9d
|
|
7
|
+
data.tar.gz: 6e6b9e64f3662ed48fd16a18fa9fcb4741d70748c306a2ad07abf8e72eddb8819c4f0a3c47f185c743c8b5a336aac8910d0affff618a94258c8eac23887d97a6
|
|
@@ -9,12 +9,10 @@ module Arel # :nodoc:
|
|
|
9
9
|
MySQL
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
class Mysql2Rgeo < MySQLSuperclass
|
|
12
|
+
class Mysql2Rgeo < MySQLSuperclass # :nodoc:
|
|
13
13
|
include RGeo::ActiveRecord::SpatialToSql
|
|
14
14
|
|
|
15
|
-
if ::Arel::Visitors.const_defined?(:BindVisitor)
|
|
16
|
-
include ::Arel::Visitors::BindVisitor
|
|
17
|
-
end
|
|
15
|
+
include ::Arel::Visitors::BindVisitor if ::Arel::Visitors.const_defined?(:BindVisitor)
|
|
18
16
|
|
|
19
17
|
FUNC_MAP = {
|
|
20
18
|
"st_wkttosql" => "ST_GeomFromText",
|
|
@@ -26,16 +24,11 @@ module Arel # :nodoc:
|
|
|
26
24
|
FUNC_MAP[standard_name.downcase] || standard_name
|
|
27
25
|
end
|
|
28
26
|
|
|
29
|
-
def
|
|
27
|
+
def visit_RGeo_Feature_Instance(node, collector)
|
|
30
28
|
node, srid = Mysql2Rgeo.parse_node(node)
|
|
31
29
|
collector << spatial_constant_sql(node, srid)
|
|
32
30
|
end
|
|
33
|
-
alias visit_RGeo_Feature_Instance
|
|
34
|
-
alias visit_RGeo_Cartesian_BoundingBox visit_String
|
|
35
|
-
|
|
36
|
-
def visit_RGeo_ActiveRecord_SpatialNamedFunction(node, collector)
|
|
37
|
-
aggregate(st_func(node.name), node, collector)
|
|
38
|
-
end
|
|
31
|
+
alias visit_RGeo_Cartesian_BoundingBox visit_RGeo_Feature_Instance
|
|
39
32
|
|
|
40
33
|
def visit_in_spatial_context(node, collector)
|
|
41
34
|
case node
|
|
@@ -52,18 +45,19 @@ module Arel # :nodoc:
|
|
|
52
45
|
end
|
|
53
46
|
|
|
54
47
|
def self.parse_node(node)
|
|
55
|
-
if RGeo::Feature::Instance
|
|
48
|
+
if node.is_a?(RGeo::Feature::Instance)
|
|
56
49
|
wkt = RGeo::WKRep::WKTGenerator.new(tag_format: :wkt11, emit_ewkt_srid: false).generate(node)
|
|
57
50
|
return [wkt, node.srid]
|
|
58
51
|
end
|
|
59
52
|
|
|
60
|
-
if RGeo::Cartesian::BoundingBox
|
|
53
|
+
if node.is_a?(RGeo::Cartesian::BoundingBox)
|
|
61
54
|
geometry = node.to_geometry
|
|
62
55
|
wkt = RGeo::WKRep::WKTGenerator.new(tag_format: :wkt11, emit_ewkt_srid: false).generate(geometry)
|
|
63
56
|
return [wkt, geometry.srid]
|
|
64
57
|
end
|
|
65
58
|
|
|
66
|
-
value
|
|
59
|
+
value = nil
|
|
60
|
+
srid = 0
|
|
67
61
|
if node =~ /.*;.*$/i
|
|
68
62
|
params = Regexp.last_match(0).split(";")
|
|
69
63
|
if params.first =~ /(srid|SRID)=\d*/
|
|
@@ -5,34 +5,33 @@ module ActiveRecord
|
|
|
5
5
|
module Mysql2Rgeo
|
|
6
6
|
class SchemaCreation < MySQL::SchemaCreation # :nodoc:
|
|
7
7
|
private
|
|
8
|
-
def visit_IndexDefinition(o, create = false)
|
|
9
|
-
if o.using&.to_sym == :gist
|
|
10
|
-
sql = create ? ["CREATE SPATIAL INDEX"] : ["SPATIAL INDEX"]
|
|
11
|
-
sql << quote_column_name(o.name)
|
|
12
|
-
sql << "ON #{quote_table_name(o.table)}" if create
|
|
13
|
-
sql << "(#{quoted_columns(o)})"
|
|
14
|
-
return sql.join(" ")
|
|
15
|
-
end
|
|
16
8
|
|
|
17
|
-
|
|
9
|
+
def visit_IndexDefinition(o, create = false)
|
|
10
|
+
if o.using&.to_sym == :gist
|
|
11
|
+
sql = create ? ["CREATE SPATIAL INDEX"] : ["SPATIAL INDEX"]
|
|
12
|
+
sql << quote_column_name(o.name)
|
|
13
|
+
sql << "ON #{quote_table_name(o.table)}" if create
|
|
14
|
+
sql << "(#{quoted_columns(o)})"
|
|
15
|
+
return sql.join(" ")
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
sql << " /*!80003 SRID #{options[:srid]} */"
|
|
23
|
-
options = options.except(:srid)
|
|
24
|
-
end
|
|
18
|
+
super
|
|
19
|
+
end
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
super
|
|
21
|
+
def add_column_options!(sql, options)
|
|
22
|
+
if options[:srid]
|
|
23
|
+
sql << " /*!80003 SRID #{options[:srid]} */"
|
|
24
|
+
options = options.except(:srid)
|
|
31
25
|
end
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
options = options.except(:default) if options_include_default?(options) && spatial_column_definition?(options[:column])
|
|
28
|
+
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def spatial_column_definition?(column)
|
|
33
|
+
column.respond_to?(:type) && column.type && @conn.class.spatial_column_options(column.type.to_sym)
|
|
34
|
+
end
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
end
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
# super: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
|
|
8
8
|
|
|
9
9
|
# override
|
|
10
|
-
def indexes(table_name)
|
|
10
|
+
def indexes(table_name) # :nodoc:
|
|
11
11
|
indexes = super
|
|
12
12
|
# HACK(aleks, 06/15/18): MySQL 5 does not support prefix lengths for spatial indexes
|
|
13
13
|
# https://dev.mysql.com/doc/refman/5.6/en/create-index.html
|
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
|
33
33
|
|
|
34
34
|
column_options = column.limit.is_a?(Hash) ? column.limit.symbolize_keys.except(:type) : {}
|
|
35
35
|
column_options[:comment] = column.comment if column.comment.present?
|
|
36
|
-
change_column(table_name, name, column.limit[:type].to_sym, **column_options
|
|
36
|
+
change_column(table_name, name, column.limit[:type].to_sym, **column_options, null: false)
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -42,11 +42,9 @@ module ActiveRecord
|
|
|
42
42
|
|
|
43
43
|
# override
|
|
44
44
|
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
|
45
|
-
if type.to_sym == :geometry && limit.is_a?(String)
|
|
46
|
-
return "geometry(#{limit})"
|
|
47
|
-
end
|
|
45
|
+
return "geometry(#{limit})" if type.to_sym == :geometry && limit.is_a?(String)
|
|
48
46
|
|
|
49
|
-
if
|
|
47
|
+
if RGeo::ActiveRecord.geometric_type_from_name(type.to_s.delete("_"))
|
|
50
48
|
type = limit[:type] || type if limit.is_a?(::Hash)
|
|
51
49
|
type = type.to_s.delete("_").upcase
|
|
52
50
|
end
|
|
@@ -65,39 +63,44 @@ module ActiveRecord
|
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
# override
|
|
68
|
-
def create_table_definition(
|
|
69
|
-
Mysql2Rgeo::TableDefinition.new(self,
|
|
66
|
+
def create_table_definition(*, **)
|
|
67
|
+
Mysql2Rgeo::TableDefinition.new(self, *, **)
|
|
70
68
|
end
|
|
71
69
|
|
|
72
70
|
# override
|
|
73
71
|
def new_column_from_field(table_name, field, _definitions)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
field_name = field_value(field, "Field")
|
|
73
|
+
type_metadata = fetch_type_metadata(field_value(field, "Type"), field_value(field, "Extra"))
|
|
74
|
+
default = field_value(field, "Default")
|
|
75
|
+
default_function = nil
|
|
76
|
+
metadata_comment = field_value(field, "Comment")
|
|
77
|
+
metadata = Mysql2Rgeo::ColumnDefinitionUtils.extract_metadata(metadata_comment)
|
|
78
|
+
comment = Mysql2Rgeo::ColumnDefinitionUtils.strip_metadata_comment(metadata_comment)
|
|
78
79
|
default = metadata[:default_hex] if default.nil? && metadata[:default_hex].present?
|
|
79
80
|
|
|
80
81
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
|
81
|
-
|
|
82
|
+
default_function = default
|
|
83
|
+
default = nil
|
|
82
84
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
|
83
85
|
if default == "NULL" && metadata[:default_hex].present?
|
|
84
86
|
default = metadata[:default_hex]
|
|
85
87
|
elsif default == "NULL"
|
|
86
|
-
default = generated_default_for(table_name,
|
|
88
|
+
default = generated_default_for(table_name, field_name)
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
if default&.match?(/\Ast_geomfromtext\(/i)
|
|
90
|
-
default =
|
|
91
|
-
|
|
92
|
+
default = "(#{default})" unless default.start_with?("(")
|
|
93
|
+
default_function = default
|
|
94
|
+
default = nil
|
|
92
95
|
end
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
if type_metadata.extra.to_s.match?(/(?:VIRTUAL|STORED|PERSISTENT)\s+GENERATED/i)
|
|
96
|
-
default_function = generation_expression_for(table_name,
|
|
99
|
+
default_function = generation_expression_for(table_name, field_name)
|
|
97
100
|
end
|
|
98
101
|
|
|
99
102
|
# {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
|
|
100
|
-
spatial = spatial_column_info(table_name).get(
|
|
103
|
+
spatial = spatial_column_info(table_name).get(field_name, type_metadata.sql_type)
|
|
101
104
|
if spatial
|
|
102
105
|
spatial[:has_z] ||= metadata[:has_z]
|
|
103
106
|
spatial[:has_m] ||= metadata[:has_m]
|
|
@@ -116,9 +119,9 @@ module ActiveRecord
|
|
|
116
119
|
type: :geometry,
|
|
117
120
|
limit: nil,
|
|
118
121
|
precision: type_metadata.precision,
|
|
119
|
-
scale: type_metadata.scale
|
|
122
|
+
scale: type_metadata.scale
|
|
120
123
|
),
|
|
121
|
-
extra: type_metadata.extra
|
|
124
|
+
extra: type_metadata.extra
|
|
122
125
|
)
|
|
123
126
|
|
|
124
127
|
if default_function&.match?(/\A\(?st_geomfromtext\(/i)
|
|
@@ -128,12 +131,13 @@ module ActiveRecord
|
|
|
128
131
|
end
|
|
129
132
|
|
|
130
133
|
SpatialColumn.new(
|
|
131
|
-
|
|
134
|
+
field_name,
|
|
135
|
+
lookup_cast_type(type_metadata.sql_type),
|
|
132
136
|
default,
|
|
133
137
|
type_metadata,
|
|
134
|
-
field
|
|
138
|
+
field_value(field, "Null") == "YES",
|
|
135
139
|
default_function,
|
|
136
|
-
collation: field
|
|
140
|
+
collation: field_value(field, "Collation"),
|
|
137
141
|
comment: comment,
|
|
138
142
|
spatial: spatial,
|
|
139
143
|
array: metadata[:array]
|
|
@@ -146,6 +150,10 @@ module ActiveRecord
|
|
|
146
150
|
@spatial_column_info[table_name.to_sym] = SpatialColumnInfo.new(self, table_name.to_s)
|
|
147
151
|
end
|
|
148
152
|
|
|
153
|
+
def field_value(field, key)
|
|
154
|
+
field[key] || field[key.to_sym]
|
|
155
|
+
end
|
|
156
|
+
|
|
149
157
|
def extract_spatial_default_hex(default_function, spatial)
|
|
150
158
|
default_sql = default_function.delete_prefix("(").delete_suffix(")").delete("\\")
|
|
151
159
|
match = default_sql.match(/\Ast_geomfromtext\(_utf8mb4'(.+?)',\s*(\d+)\)\z/i)
|
|
@@ -159,10 +167,11 @@ module ActiveRecord
|
|
|
159
167
|
srid: srid,
|
|
160
168
|
geographic: spatial[:geographic],
|
|
161
169
|
has_z: spatial[:has_z],
|
|
162
|
-
has_m: spatial[:has_m]
|
|
170
|
+
has_m: spatial[:has_m]
|
|
163
171
|
)
|
|
164
172
|
geometry = type.serialize(wkt)
|
|
165
173
|
return unless geometry
|
|
174
|
+
|
|
166
175
|
geometry = RGeo::Feature.cast(geometry, factory: type.send(:spatial_factory), project: true) if spatial[:geographic]
|
|
167
176
|
|
|
168
177
|
wkb = RGeo::WKRep::WKBGenerator.new(
|
|
@@ -190,7 +199,7 @@ module ActiveRecord
|
|
|
190
199
|
AND table_name = #{quote(table_name)}
|
|
191
200
|
AND column_name = #{quote(column_name)}
|
|
192
201
|
SQL
|
|
193
|
-
|
|
202
|
+
&.gsub(/st_buffer\(([^,]+),\s*(\d+)\)/i, 'st_buffer(\1, (\2)::double precision)')
|
|
194
203
|
end
|
|
195
204
|
|
|
196
205
|
def generated_default_for(table_name, column_name)
|
|
@@ -4,7 +4,18 @@ module ActiveRecord # :nodoc:
|
|
|
4
4
|
module ConnectionAdapters # :nodoc:
|
|
5
5
|
module Mysql2Rgeo # :nodoc:
|
|
6
6
|
class SpatialColumn < ConnectionAdapters::MySQL::Column # :nodoc:
|
|
7
|
-
|
|
7
|
+
# ActiveRecord 8.1 added cast_type as Column#initialize's second argument.
|
|
8
|
+
COLUMN_TAKES_CAST_TYPE = ActiveRecord.gem_version >= Gem::Version.new("8.1")
|
|
9
|
+
|
|
10
|
+
def initialize(name, cast_type, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil,
|
|
11
|
+
spatial: nil, array: false, **)
|
|
12
|
+
if default.respond_to?(:sql_type) && (sql_type_metadata.nil? || sql_type_metadata == true || sql_type_metadata == false)
|
|
13
|
+
null = sql_type_metadata unless sql_type_metadata.nil?
|
|
14
|
+
sql_type_metadata = default
|
|
15
|
+
default = cast_type
|
|
16
|
+
cast_type = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
8
19
|
@sql_type_metadata = sql_type_metadata
|
|
9
20
|
@array = array
|
|
10
21
|
@geographic = !!(sql_type_metadata&.sql_type =~ /geography\(/i)
|
|
@@ -26,15 +37,18 @@ module ActiveRecord # :nodoc:
|
|
|
26
37
|
# @geometric_type = geo_type_from_sql_type(sql_type)
|
|
27
38
|
build_from_sql_type(sql_type_metadata.sql_type)
|
|
28
39
|
end
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@limit[:geographic] = true if geographic?
|
|
34
|
-
@limit[:has_z] = true if has_z?
|
|
35
|
-
@limit[:has_m] = true if has_m?
|
|
36
|
-
end
|
|
40
|
+
if COLUMN_TAKES_CAST_TYPE
|
|
41
|
+
super(name, cast_type, default, sql_type_metadata, null, default_function, collation: collation, comment: comment)
|
|
42
|
+
else
|
|
43
|
+
super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment)
|
|
37
44
|
end
|
|
45
|
+
return unless spatial?
|
|
46
|
+
return unless @srid
|
|
47
|
+
|
|
48
|
+
@limit = { type: limit_type_name, srid: @srid }
|
|
49
|
+
@limit[:geographic] = true if geographic?
|
|
50
|
+
@limit[:has_z] = true if has_z?
|
|
51
|
+
@limit[:has_m] = true if has_m?
|
|
38
52
|
end
|
|
39
53
|
|
|
40
54
|
attr_reader :geometric_type, :srid
|
|
@@ -29,11 +29,11 @@ module ActiveRecord # :nodoc:
|
|
|
29
29
|
has_z = type.sub!(/z$/i, "").present?
|
|
30
30
|
has_m = type.sub!(/m$/i, "").present?
|
|
31
31
|
result[name] = {
|
|
32
|
-
name:
|
|
33
|
-
srid:
|
|
34
|
-
type:
|
|
35
|
-
has_z:
|
|
36
|
-
has_m:
|
|
32
|
+
name: name,
|
|
33
|
+
srid: row[1].to_i,
|
|
34
|
+
type: type,
|
|
35
|
+
has_z: has_z,
|
|
36
|
+
has_m: has_m,
|
|
37
37
|
geographic: column_comment.include?("mysql2rgeo:geographic"),
|
|
38
38
|
}
|
|
39
39
|
end
|
|
@@ -5,6 +5,7 @@ module ActiveRecord # :nodoc:
|
|
|
5
5
|
module Mysql2Rgeo # :nodoc:
|
|
6
6
|
class TableDefinition < MySQL::TableDefinition # :nodoc:
|
|
7
7
|
include ColumnMethods
|
|
8
|
+
|
|
8
9
|
# super: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
|
|
9
10
|
def new_column_definition(name, type, **options)
|
|
10
11
|
spatial_type = type.to_sym == :virtual ? options[:type]&.to_sym : type.to_sym
|
|
@@ -20,7 +21,9 @@ module ActiveRecord # :nodoc:
|
|
|
20
21
|
ColumnDefinitionUtils.geo_type(options[:type] || spatial_type || info[:type])
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
if type.to_sym == :virtual
|
|
25
|
+
options.delete(:srid)
|
|
26
|
+
else
|
|
24
27
|
options[:srid] ||= ColumnDefinitionUtils.default_srid(options)
|
|
25
28
|
options[:comment] = ColumnDefinitionUtils.add_metadata_comment(
|
|
26
29
|
options[:comment],
|
|
@@ -32,19 +35,20 @@ module ActiveRecord # :nodoc:
|
|
|
32
35
|
srid: options[:srid],
|
|
33
36
|
geo_type: geo_type
|
|
34
37
|
)
|
|
35
|
-
else
|
|
36
|
-
options.delete(:srid)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
options[:spatial_type] = geo_type
|
|
40
|
-
if type.to_sym == :virtual
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
column = if type.to_sym == :virtual
|
|
42
|
+
super(name, type, **options.merge(type: geo_type.downcase.to_sym))
|
|
43
|
+
else
|
|
44
|
+
super(name, geo_type.downcase.to_sym, **options)
|
|
45
|
+
end
|
|
45
46
|
else
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
if options[:array]
|
|
48
|
+
options[:comment] =
|
|
49
|
+
ColumnDefinitionUtils.add_metadata_comment(options[:comment], array: options[:array])
|
|
50
|
+
end
|
|
51
|
+
column = super
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
column
|
|
@@ -77,14 +81,16 @@ module ActiveRecord # :nodoc:
|
|
|
77
81
|
options[:geographic] ? 4326 : Mysql2RgeoAdapter::DEFAULT_SRID
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
def add_metadata_comment(comment, geographic: false, has_m: false, has_z: false, array: false, default: nil, srid: nil,
|
|
84
|
+
def add_metadata_comment(comment, geographic: false, has_m: false, has_z: false, array: false, default: nil, srid: nil,
|
|
85
|
+
geo_type: nil)
|
|
81
86
|
values = [comment]
|
|
82
87
|
values << METADATA_TOKENS[:geographic] if geographic
|
|
83
88
|
values << METADATA_TOKENS[:has_m] if has_m
|
|
84
89
|
values << METADATA_TOKENS[:has_z] if has_z
|
|
85
90
|
values << METADATA_TOKENS[:array] if array
|
|
86
91
|
if default
|
|
87
|
-
values << "#{METADATA_TOKENS[:default_prefix]}#{encode_default(default, geographic: geographic, srid: srid,
|
|
92
|
+
values << "#{METADATA_TOKENS[:default_prefix]}#{encode_default(default, geographic: geographic, srid: srid,
|
|
93
|
+
geo_type: geo_type, has_m: has_m, has_z: has_z)}"
|
|
88
94
|
end
|
|
89
95
|
values.compact_blank.join(" ")
|
|
90
96
|
end
|
|
@@ -34,8 +34,9 @@ module ActiveRecord
|
|
|
34
34
|
def mysql2rgeo_connection(config)
|
|
35
35
|
config = config.symbolize_keys
|
|
36
36
|
config[:flags] ||= 0
|
|
37
|
+
config[:database_timezone] ||= ConnectionAdapters::AbstractAdapter.validate_default_timezone(config[:default_timezone])
|
|
37
38
|
|
|
38
|
-
if config[:flags].
|
|
39
|
+
if config[:flags].is_a? Array
|
|
39
40
|
config[:flags].push "FOUND_ROWS"
|
|
40
41
|
else
|
|
41
42
|
config[:flags] |= Mysql2::Client::FOUND_ROWS
|
|
@@ -45,7 +46,7 @@ module ActiveRecord
|
|
|
45
46
|
ConnectionAdapters::Mysql2RgeoAdapter.new_client(config),
|
|
46
47
|
logger,
|
|
47
48
|
nil,
|
|
48
|
-
config
|
|
49
|
+
config
|
|
49
50
|
)
|
|
50
51
|
end
|
|
51
52
|
end
|
|
@@ -58,23 +59,23 @@ module ActiveRecord
|
|
|
58
59
|
|
|
59
60
|
SPATIAL_COLUMN_OPTIONS =
|
|
60
61
|
{
|
|
61
|
-
geography:
|
|
62
|
-
geometry:
|
|
63
|
-
geometrycollection:
|
|
62
|
+
geography: { type: "geometry", geographic: true },
|
|
63
|
+
geometry: {},
|
|
64
|
+
geometrycollection: {},
|
|
64
65
|
geometry_collection: { type: "geometrycollection" },
|
|
65
|
-
linestring:
|
|
66
|
-
line_string:
|
|
67
|
-
multilinestring:
|
|
68
|
-
multi_line_string:
|
|
69
|
-
multipoint:
|
|
70
|
-
multi_point:
|
|
71
|
-
multipolygon:
|
|
72
|
-
multi_polygon:
|
|
73
|
-
spatial:
|
|
74
|
-
point:
|
|
75
|
-
polygon:
|
|
76
|
-
st_point:
|
|
77
|
-
st_polygon:
|
|
66
|
+
linestring: {},
|
|
67
|
+
line_string: { type: "linestring" },
|
|
68
|
+
multilinestring: {},
|
|
69
|
+
multi_line_string: { type: "multilinestring" },
|
|
70
|
+
multipoint: {},
|
|
71
|
+
multi_point: { type: "multipoint" },
|
|
72
|
+
multipolygon: {},
|
|
73
|
+
multi_polygon: { type: "multipolygon" },
|
|
74
|
+
spatial: { type: "geometry" },
|
|
75
|
+
point: {},
|
|
76
|
+
polygon: {},
|
|
77
|
+
st_point: { type: "point" },
|
|
78
|
+
st_polygon: { type: "polygon" }
|
|
78
79
|
}.freeze
|
|
79
80
|
|
|
80
81
|
DEFAULT_SRID = 0
|
|
@@ -96,79 +97,92 @@ module ActiveRecord
|
|
|
96
97
|
def native_database_types
|
|
97
98
|
# Add spatial types
|
|
98
99
|
# Reference: https://dev.mysql.com/doc/refman/5.6/en/spatial-type-overview.html
|
|
99
|
-
|
|
100
|
-
geometry: { name: "geometry" },
|
|
101
|
-
geometrycollection: { name: "geometrycollection" },
|
|
102
|
-
line_string: { name: "linestring" },
|
|
103
|
-
linestring: { name: "linestring" },
|
|
104
|
-
st_point: { name: "point" },
|
|
105
|
-
st_polygon: { name: "polygon" },
|
|
106
|
-
multi_line_string: { name: "multilinestring" },
|
|
107
|
-
multi_point: { name: "multipoint" },
|
|
108
|
-
multi_polygon: { name: "multipolygon" },
|
|
109
|
-
spatial: { name: "geometry" },
|
|
110
|
-
point: { name: "point" },
|
|
111
|
-
polygon: { name: "polygon" }
|
|
112
|
-
)
|
|
100
|
+
self.class.native_database_types
|
|
113
101
|
end
|
|
114
102
|
|
|
115
103
|
class << self
|
|
104
|
+
def native_database_types
|
|
105
|
+
# ActiveRecord < 8.1 only has the instance method, backed by NATIVE_DATABASE_TYPES.
|
|
106
|
+
(defined?(super) ? super : self::NATIVE_DATABASE_TYPES).merge(
|
|
107
|
+
geography: { name: "geometry" },
|
|
108
|
+
geometry: { name: "geometry" },
|
|
109
|
+
geometrycollection: { name: "geometrycollection" },
|
|
110
|
+
line_string: { name: "linestring" },
|
|
111
|
+
linestring: { name: "linestring" },
|
|
112
|
+
st_point: { name: "point" },
|
|
113
|
+
st_polygon: { name: "polygon" },
|
|
114
|
+
multi_line_string: { name: "multilinestring" },
|
|
115
|
+
multi_point: { name: "multipoint" },
|
|
116
|
+
multi_polygon: { name: "multipolygon" },
|
|
117
|
+
spatial: { name: "geometry" },
|
|
118
|
+
point: { name: "point" },
|
|
119
|
+
polygon: { name: "polygon" }
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def extended_type_map(emulate_booleans:, default_timezone: nil)
|
|
124
|
+
Type::TypeMap.new(emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP).tap do |m|
|
|
125
|
+
register_class_with_precision m, /\A[^(]*time/i, Type::Time, timezone: default_timezone
|
|
126
|
+
register_class_with_precision m, /\A[^(]*datetime/i, Type::DateTime, timezone: default_timezone
|
|
127
|
+
m.alias_type(/\A[^(]*timestamp/i, "datetime")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
116
130
|
|
|
117
131
|
private
|
|
118
|
-
def initialize_type_map(m)
|
|
119
|
-
super
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
"geography" => "geometry",
|
|
123
|
-
"geometry" => "geometry",
|
|
124
|
-
"geometry_collection" => "geometrycollection",
|
|
125
|
-
"line_string" => "linestring",
|
|
126
|
-
"multi_line_string" => "multilinestring",
|
|
127
|
-
"multi_point" => "multipoint",
|
|
128
|
-
"multi_polygon" => "multipolygon",
|
|
129
|
-
"st_point" => "point",
|
|
130
|
-
"st_polygon" => "polygon",
|
|
131
|
-
}.each do |registered_type, geo_type|
|
|
132
|
-
m.register_type(registered_type) do |sql_type|
|
|
133
|
-
Type::Spatial.new(sql_type.to_s, geo_type: geo_type)
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
132
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
133
|
+
def initialize_type_map(m)
|
|
134
|
+
super
|
|
135
|
+
|
|
136
|
+
{
|
|
137
|
+
"geography" => "geometry",
|
|
138
|
+
"geometry" => "geometry",
|
|
139
|
+
"geometry_collection" => "geometrycollection",
|
|
140
|
+
"line_string" => "linestring",
|
|
141
|
+
"multi_line_string" => "multilinestring",
|
|
142
|
+
"multi_point" => "multipoint",
|
|
143
|
+
"multi_polygon" => "multipolygon",
|
|
144
|
+
"st_point" => "point",
|
|
145
|
+
"st_polygon" => "polygon",
|
|
146
|
+
}.each do |registered_type, geo_type|
|
|
147
|
+
m.register_type(registered_type) do |sql_type|
|
|
148
|
+
Type::Spatial.new(sql_type.to_s, geo_type: geo_type)
|
|
151
149
|
end
|
|
150
|
+
end
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
152
|
+
[
|
|
153
|
+
/\Ageometry(?:\(.*\))?\z/i,
|
|
154
|
+
/\Ageography(?:\(.*\))?\z/i,
|
|
155
|
+
/\Apoint(?:\s.*)?\z/i,
|
|
156
|
+
/\Alinestring(?:\s.*)?\z/i,
|
|
157
|
+
/\Apolygon(?:\s.*)?\z/i,
|
|
158
|
+
/\Amultipoint(?:\s.*)?\z/i,
|
|
159
|
+
/\Amultilinestring(?:\s.*)?\z/i,
|
|
160
|
+
/\Amultipolygon(?:\s.*)?\z/i,
|
|
161
|
+
/\Ageometrycollection(?:\s.*)?\z/i,
|
|
162
|
+
].each do |pattern|
|
|
163
|
+
m.register_type(pattern) do |sql_type|
|
|
164
|
+
Type::Spatial.new(sql_type.to_s)
|
|
164
165
|
end
|
|
166
|
+
end
|
|
165
167
|
|
|
168
|
+
{
|
|
169
|
+
st_point: "point",
|
|
170
|
+
st_polygon: "polygon",
|
|
171
|
+
line_string: "linestring",
|
|
172
|
+
multi_line_string: "multilinestring",
|
|
173
|
+
multi_point: "multipoint",
|
|
174
|
+
multi_polygon: "multipolygon",
|
|
175
|
+
}.each do |alias_type, geo_type|
|
|
176
|
+
ActiveRecord::Type.register(alias_type) do |_, **kwargs|
|
|
177
|
+
Type::Spatial.new(geo_type, geo_type: geo_type, **kwargs)
|
|
178
|
+
end
|
|
166
179
|
end
|
|
180
|
+
end
|
|
167
181
|
end
|
|
168
182
|
|
|
169
183
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
170
184
|
TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
|
|
171
|
-
m.register_type
|
|
185
|
+
m.register_type(/^tinyint\(1\)/i, Type::Boolean.new)
|
|
172
186
|
end
|
|
173
187
|
|
|
174
188
|
def supports_spatial?
|
|
@@ -211,9 +225,16 @@ module ActiveRecord
|
|
|
211
225
|
end
|
|
212
226
|
|
|
213
227
|
private
|
|
214
|
-
|
|
228
|
+
|
|
229
|
+
def type_map
|
|
230
|
+
if (key = extended_type_map_key)
|
|
231
|
+
self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
|
|
232
|
+
self.class.extended_type_map(**key)
|
|
233
|
+
end
|
|
234
|
+
else
|
|
215
235
|
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
|
|
216
236
|
end
|
|
237
|
+
end
|
|
217
238
|
end
|
|
218
239
|
end
|
|
219
240
|
end
|
|
@@ -10,13 +10,14 @@ module ActiveRecord
|
|
|
10
10
|
# "geometry NOT NULL"
|
|
11
11
|
# "geometry"
|
|
12
12
|
def initialize(sql_type = "geometry", geo_type: nil, srid: nil, geographic: false, has_z: false, has_m: false, **_options)
|
|
13
|
-
@sql_type = geographic ? "geography" : sql_type
|
|
13
|
+
@sql_type = -(geographic ? "geography" : sql_type.to_s)
|
|
14
14
|
parsed_geo_type, parsed_srid, parsed_has_z, parsed_has_m, parsed_geographic = self.class.parse_sql_type(@sql_type)
|
|
15
|
-
@geo_type = self.class.normalize_geo_type(geo_type || parsed_geo_type)
|
|
15
|
+
@geo_type = -self.class.normalize_geo_type(geo_type || parsed_geo_type).to_s
|
|
16
16
|
@srid = srid || parsed_srid
|
|
17
17
|
@has_z = has_z || parsed_has_z
|
|
18
18
|
@has_m = has_m || parsed_has_m
|
|
19
19
|
@geographic = geographic || parsed_geographic
|
|
20
|
+
freeze
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
# sql_type: geometry, geometry(Point), geometry(Point,4326), ...
|
|
@@ -41,9 +42,7 @@ module ActiveRecord
|
|
|
41
42
|
has_z = Regexp.last_match(2).casecmp("z").zero?
|
|
42
43
|
has_m = Regexp.last_match(3).casecmp("m").zero?
|
|
43
44
|
end
|
|
44
|
-
if params.last =~ /(\d+)/
|
|
45
|
-
srid = Regexp.last_match(1).to_i
|
|
46
|
-
end
|
|
45
|
+
srid = Regexp.last_match(1).to_i if params.last =~ /(\d+)/
|
|
47
46
|
elsif sql_type =~ /\A(geography|geometry)\z/i
|
|
48
47
|
geographic = Regexp.last_match(1).casecmp("geography").zero?
|
|
49
48
|
geo_type = Regexp.last_match(1)
|
|
@@ -72,16 +71,16 @@ module ActiveRecord
|
|
|
72
71
|
end
|
|
73
72
|
end
|
|
74
73
|
|
|
75
|
-
def spatial_factory
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
def spatial_factory(srid = @srid)
|
|
75
|
+
# Don't use instance variable caching since Type objects are frozen in Rails 8.1+
|
|
76
|
+
# The SpatialFactoryStore already caches factories internally
|
|
77
|
+
# Use provided srid or fall back to instance srid
|
|
78
|
+
RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
|
|
79
|
+
geo_type: @geo_type,
|
|
80
|
+
has_m: @has_m,
|
|
81
|
+
has_z: @has_z,
|
|
82
|
+
sql_type: @geographic ? "geography" : "geometry",
|
|
83
|
+
srid: srid
|
|
85
84
|
)
|
|
86
85
|
end
|
|
87
86
|
|
|
@@ -118,37 +117,35 @@ module ActiveRecord
|
|
|
118
117
|
def cast_value(value)
|
|
119
118
|
return if value.nil?
|
|
120
119
|
|
|
121
|
-
::String
|
|
120
|
+
value.is_a?(::String) ? parse_wkt(value) : value
|
|
122
121
|
end
|
|
123
122
|
|
|
124
123
|
# convert WKT string into RGeo object
|
|
125
124
|
def parse_wkt(string)
|
|
126
125
|
marker = string[4, 1]
|
|
127
126
|
if ["\x00", "\x01"].include?(marker)
|
|
128
|
-
|
|
129
|
-
RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid:
|
|
127
|
+
srid = string[0, 4].unpack1(marker == "\x01" ? "V" : "N")
|
|
128
|
+
RGeo::WKRep::WKBParser.new(spatial_factory(srid), support_ewkb: true, default_srid: srid).parse(string[4..])
|
|
130
129
|
elsif string.match?(/\A[0-9a-fA-F]+\z/)
|
|
131
|
-
original_srid = @srid
|
|
132
130
|
parser = RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid)
|
|
133
131
|
|
|
134
132
|
begin
|
|
135
133
|
return parser.parse([string].pack("H*"))
|
|
136
134
|
rescue RGeo::Error::ParseError, RGeo::Error::InvalidGeometry
|
|
137
|
-
@srid = original_srid
|
|
138
135
|
end
|
|
139
136
|
|
|
140
137
|
if string[0, 10] =~ /[0-9a-fA-F]{8}0[01]/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
parser = RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid:
|
|
144
|
-
return parser.parse([string[8
|
|
138
|
+
srid = string[0, 8].to_i(16)
|
|
139
|
+
srid = [srid].pack("V").unpack1("N") if string[9, 1] == "1"
|
|
140
|
+
parser = RGeo::WKRep::WKBParser.new(spatial_factory(srid), support_ewkb: true, default_srid: srid)
|
|
141
|
+
return parser.parse([string[8..]].pack("H*"))
|
|
145
142
|
end
|
|
146
143
|
|
|
147
144
|
parser.parse([string].pack("H*"))
|
|
148
145
|
else
|
|
149
146
|
string, srid = Arel::Visitors::Mysql2Rgeo.parse_node(string)
|
|
150
|
-
|
|
151
|
-
RGeo::WKRep::WKTParser.new(spatial_factory, support_ewkt: true, default_srid:
|
|
147
|
+
srid = @srid if srid.zero?
|
|
148
|
+
RGeo::WKRep::WKTParser.new(spatial_factory(srid), support_ewkt: true, default_srid: srid).parse(string)
|
|
152
149
|
end
|
|
153
150
|
rescue RGeo::Error::ParseError, RGeo::Error::InvalidGeometry
|
|
154
151
|
nil
|
|
@@ -156,3 +153,13 @@ module ActiveRecord
|
|
|
156
153
|
end
|
|
157
154
|
end
|
|
158
155
|
end
|
|
156
|
+
|
|
157
|
+
module ActiveRecord
|
|
158
|
+
module ConnectionAdapters
|
|
159
|
+
module Mysql2Rgeo
|
|
160
|
+
module OID
|
|
161
|
+
Spatial = ActiveRecord::Type::Spatial
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-mysql2rgeo-adapter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 9.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yongdae Hwang
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activerecord
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
18
|
+
version: 8.0.0
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
25
|
+
version: 8.0.0
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: rgeo-activerecord
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,20 +79,6 @@ dependencies:
|
|
|
80
79
|
- - "~>"
|
|
81
80
|
- !ruby/object:Gem::Version
|
|
82
81
|
version: '2.0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: mocha
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '1.1'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '1.1'
|
|
97
82
|
- !ruby/object:Gem::Dependency
|
|
98
83
|
name: benchmark-ips
|
|
99
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -152,7 +137,6 @@ metadata:
|
|
|
152
137
|
source_code_uri: https://github.com/stadia/activerecord-mysql2rgeo-adapter
|
|
153
138
|
documentation_uri: https://github.com/stadia/activerecord-mysql2rgeo-adapter/blob/main/README.md
|
|
154
139
|
rubygems_mfa_required: 'true'
|
|
155
|
-
post_install_message:
|
|
156
140
|
rdoc_options: []
|
|
157
141
|
require_paths:
|
|
158
142
|
- lib
|
|
@@ -160,15 +144,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
160
144
|
requirements:
|
|
161
145
|
- - ">="
|
|
162
146
|
- !ruby/object:Gem::Version
|
|
163
|
-
version: 3.
|
|
147
|
+
version: 3.2.0
|
|
164
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
149
|
requirements:
|
|
166
150
|
- - ">="
|
|
167
151
|
- !ruby/object:Gem::Version
|
|
168
152
|
version: '0'
|
|
169
153
|
requirements: []
|
|
170
|
-
rubygems_version:
|
|
171
|
-
signing_key:
|
|
154
|
+
rubygems_version: 4.0.20
|
|
172
155
|
specification_version: 4
|
|
173
156
|
summary: ActiveRecord adapter for MySQL, based on RGeo.
|
|
174
157
|
test_files: []
|