activerecord-mysql2rgeo-adapter 8.0.4 → 9.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8e687a3f8a5ccfc68ed65b3a4580741bc2b68aa33f18b11a0db6aa3eb5de58d
4
- data.tar.gz: f86a9aa0893e135f478701e58bbfd23d99ddf3e5e2ae078dcbb4b5870baa69de
3
+ metadata.gz: '031329a387d0d51596c163fd22288c28de87babc0da6badd2a3e290ccf8c43d8'
4
+ data.tar.gz: efa70a6f4698f785c49bee8efa7f16f87464b18e653ab954b9709358dbd11d82
5
5
  SHA512:
6
- metadata.gz: 6fc4adc88222d567042ff0017179e86cab2a637ceb601b0406a1835aa7b850d0b7c13861f93d45bc5e83954ec4e956d731cb3d47a586c73048f177dd1b487bb2
7
- data.tar.gz: b853618805dc918bb1c10ac1f5cf57ba582deabda7f5ae0dcca4cf8d4657d4c35bf41c22e1e89e3667feceae043d0f7f06ffe3df56781825a6dd723a51a9807d
6
+ metadata.gz: 76b363aed427aa7c56c2f6c58121559ef68c9b42c2ca637ac5ce0fc93b75f80ee727d43e79089577c4bbafb85433ee1a662694e13b4ceeb827c0552aff8e3727
7
+ data.tar.gz: 6960182b12ed30c19824277f2d15409e3e444692d0b6ab652935e1d63e1c35b260e0b742dd21545c43b9ee7149b0b66a3506e8db75ff67a4d647def10c9bf9b5
@@ -9,12 +9,10 @@ module Arel # :nodoc:
9
9
  MySQL
10
10
  end
11
11
 
12
- class Mysql2Rgeo < MySQLSuperclass # :nodoc:
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 visit_String(node, collector)
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 visit_String
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 === node
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 === node
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, srid = nil, 0
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
- super
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
- def add_column_options!(sql, options)
21
- if options[:srid]
22
- sql << " /*!80003 SRID #{options[:srid]} */"
23
- options = options.except(:srid)
24
- end
18
+ super
19
+ end
25
20
 
26
- if options_include_default?(options) && spatial_column_definition?(options[:column])
27
- options = options.except(:default)
28
- end
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
- def spatial_column_definition?(column)
34
- column.respond_to?(:type) && column.type && @conn.class.spatial_column_options(column.type.to_sym)
35
- end
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) #:nodoc:
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.merge(null: false))
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 (info = RGeo::ActiveRecord.geometric_type_from_name(type.to_s.delete("_")))
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(*args, **options)
69
- Mysql2Rgeo::TableDefinition.new(self, *args, **options)
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
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
75
- default, default_function = field[:Default], nil
76
- metadata = Mysql2Rgeo::ColumnDefinitionUtils.extract_metadata(field[:Comment])
77
- comment = Mysql2Rgeo::ColumnDefinitionUtils.strip_metadata_comment(field[:Comment])
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
- default, default_function = nil, default
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, field[:Field])
88
+ default = generated_default_for(table_name, field_name)
87
89
  end
88
90
 
89
91
  if default&.match?(/\Ast_geomfromtext\(/i)
90
- default = +"(#{default})" unless default.start_with?("(")
91
- default, default_function = nil, default
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, field[:Field])
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(field[:Field], type_metadata.sql_type)
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
- field[:Field],
134
+ field_name,
135
+ lookup_cast_type(type_metadata.sql_type),
132
136
  default,
133
137
  type_metadata,
134
- field[:Null] == "YES",
138
+ field_value(field, "Null") == "YES",
135
139
  default_function,
136
- collation: field[:Collation],
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
- &.gsub(/st_buffer\(([^,]+),\s*(\d+)\)/i, 'st_buffer(\1, (\2)::double precision)')
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
- def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, spatial: nil, array: false, **)
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
- super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment)
30
- if spatial?
31
- if @srid
32
- @limit = { type: limit_type_name, srid: @srid }
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: name,
33
- srid: row[1].to_i,
34
- type: type,
35
- has_z: has_z,
36
- has_m: 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
- unless type.to_sym == :virtual
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
- column = super(name, type, **options.merge(type: geo_type.downcase.to_sym))
42
- else
43
- column = super(name, geo_type.downcase.to_sym, **options)
44
- end
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
- options[:comment] = ColumnDefinitionUtils.add_metadata_comment(options[:comment], array: options[:array]) if options[:array]
47
- column = super(name, type, **options)
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, geo_type: 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, geo_type: geo_type, has_m: has_m, has_z: has_z)}"
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
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module Mysql2Rgeo
6
- VERSION = "8.0.4"
6
+ VERSION = "9.1.0"
7
7
  end
8
8
  end
9
9
  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].kind_of? Array
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: { type: "geometry", geographic: true },
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: { type: "linestring" },
67
- multilinestring: {},
68
- multi_line_string: { type: "multilinestring" },
69
- multipoint: {},
70
- multi_point: { type: "multipoint" },
71
- multipolygon: {},
72
- multi_polygon: { type: "multipolygon" },
73
- spatial: { type: "geometry" },
74
- point: {},
75
- polygon: {},
76
- st_point: { type: "point" },
77
- st_polygon: { type: "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
- super.merge(
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
- /\Ageometry(?:\(.*\))?\z/i,
139
- /\Ageography(?:\(.*\))?\z/i,
140
- /\Apoint(?:\s.*)?\z/i,
141
- /\Alinestring(?:\s.*)?\z/i,
142
- /\Apolygon(?:\s.*)?\z/i,
143
- /\Amultipoint(?:\s.*)?\z/i,
144
- /\Amultilinestring(?:\s.*)?\z/i,
145
- /\Amultipolygon(?:\s.*)?\z/i,
146
- /\Ageometrycollection(?:\s.*)?\z/i,
147
- ].each do |pattern|
148
- m.register_type(pattern) do |sql_type|
149
- Type::Spatial.new(sql_type.to_s)
150
- end
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
- st_point: "point",
155
- st_polygon: "polygon",
156
- line_string: "linestring",
157
- multi_line_string: "multilinestring",
158
- multi_point: "multipoint",
159
- multi_polygon: "multipolygon",
160
- }.each do |alias_type, geo_type|
161
- ActiveRecord::Type.register(alias_type) do |_, **kwargs|
162
- Type::Spatial.new(geo_type, geo_type: geo_type, **kwargs)
163
- end
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 %r(^tinyint\(1\))i, Type::Boolean.new
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
- def type_map
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
- @spatial_factories ||= {}
77
-
78
- @spatial_factories[@srid] ||=
79
- RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
80
- geo_type: @geo_type,
81
- has_m: @has_m,
82
- has_z: @has_z,
83
- sql_type: @geographic ? "geography" : "geometry",
84
- srid: @srid
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 === value ? parse_wkt(value) : value
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
- @srid = string[0, 4].unpack1(marker == "\x01" ? "V" : "N")
129
- RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid).parse(string[4..-1])
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
- @srid = string[0, 8].to_i(16)
142
- @srid = [@srid].pack("V").unpack("N").first if string[9, 1] == "1"
143
- parser = RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid)
144
- return parser.parse([string[8..-1]].pack("H*"))
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
- @srid = srid.zero? ? @srid : srid
151
- RGeo::WKRep::WKTParser.new(spatial_factory, support_ewkt: true, default_srid: @srid).parse(string)
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
@@ -1,3 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record/connection_adapters/mysql2rgeo_adapter"
4
+
5
+ ActiveRecord::ConnectionAdapters.register("mysql2rgeo", "ActiveRecord::ConnectionAdapters::Mysql2RgeoAdapter",
6
+ "active_record/connection_adapters/mysql2rgeo_adapter")
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: 8.0.4
4
+ version: 9.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yongdae Hwang
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-03-31 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activerecord
@@ -16,28 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 7.2.0
18
+ version: 8.1.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: 7.2.0
25
+ version: 8.1.0
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: rgeo-activerecord
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: 8.0.0
32
+ version: 8.1.0
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: 8.0.0
39
+ version: 8.1.0
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: rake
43
42
  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.1.0
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: 3.3.27
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: []