activerecord-trilogis-adapter 7.0.2 → 8.0.1
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/LICENSE +21 -0
- data/lib/active_record/connection_adapters/trilogis/arel_tosql.rb +120 -45
- data/lib/active_record/connection_adapters/trilogis/railtie.rb +21 -14
- data/lib/active_record/connection_adapters/trilogis/schema_creation.rb +10 -7
- data/lib/active_record/connection_adapters/trilogis/schema_statements.rb +171 -40
- data/lib/active_record/connection_adapters/trilogis/spatial_column.rb +67 -42
- data/lib/active_record/connection_adapters/trilogis/spatial_column_info.rb +39 -20
- data/lib/active_record/connection_adapters/trilogis/spatial_expressions.rb +78 -4
- data/lib/active_record/connection_adapters/trilogis/spatial_table_definition.rb +79 -22
- data/lib/active_record/connection_adapters/trilogis/version.rb +1 -1
- data/lib/active_record/connection_adapters/trilogis_adapter.rb +220 -111
- data/lib/active_record/dependency_loader.rb +38 -0
- data/lib/active_record/tasks/trilogis_database_tasks.rb +2 -1
- data/lib/active_record/type/spatial.rb +217 -63
- data/lib/activerecord-trilogis-adapter.rb +15 -2
- metadata +104 -35
- data/LICENSE.txt +0 -29
- data/lib/active_record/connection_adapters/trilogis/column_methods.rb +0 -54
- data/lib/active_record/connection_adapters/trilogis/connection.rb +0 -17
- data/lib/active_record/connection_adapters/trilogis/rails/dbconsole.rb +0 -48
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c50e53fc7f5c471c74e8f587e0c0bdab734181845cd438359537735cc1adbe19
|
|
4
|
+
data.tar.gz: 930f29abad6e57fbbbe3d6e922a191c21cbb75365ef99233633e728a4e4b8b0e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8c504d937064d0ffbd6a57a4c6bb854db79436d1e6b9f933e138aeaf155e769316a54f77a37fe1ca1c704e3c30dd4f7a519559e355ca3159533b21fd3cd1c694
|
|
7
|
+
data.tar.gz: 2cafe377ca75657049c47678ad97eb41d74f1990b96aaa859820822aa4c7aa5d32c50fb555b0c7d0f6958e0a5d91ff0d9eaa9e9b6aec4f7dc9c8fe788ef7905c
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ether
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,78 +1,153 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module Arel
|
|
4
|
-
module Visitors
|
|
5
|
-
class Trilogis < MySQL
|
|
6
|
-
|
|
3
|
+
module Arel
|
|
4
|
+
module Visitors
|
|
5
|
+
class Trilogis < Arel::Visitors::MySQL
|
|
7
6
|
include RGeo::ActiveRecord::SpatialToSql
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
8
|
+
# MySQL spatial function mappings
|
|
9
|
+
SPATIAL_FUNCTIONS = {
|
|
10
|
+
"st_contains" => "ST_Contains",
|
|
11
|
+
"st_crosses" => "ST_Crosses",
|
|
12
|
+
"st_disjoint" => "ST_Disjoint",
|
|
13
|
+
"st_distance" => "ST_Distance",
|
|
14
|
+
"st_equals" => "ST_Equals",
|
|
15
|
+
"st_intersects" => "ST_Intersects",
|
|
16
|
+
"st_overlaps" => "ST_Overlaps",
|
|
17
|
+
"st_touches" => "ST_Touches",
|
|
18
|
+
"st_within" => "ST_Within",
|
|
19
|
+
"st_area" => "ST_Area",
|
|
20
|
+
"st_length" => "ST_Length",
|
|
21
|
+
"st_buffer" => "ST_Buffer",
|
|
22
|
+
"st_centroid" => "ST_Centroid",
|
|
23
|
+
"st_envelope" => "ST_Envelope",
|
|
24
|
+
"st_geomfromtext" => "ST_GeomFromText",
|
|
25
|
+
"st_geomfromwkb" => "ST_GeomFromWKB",
|
|
26
|
+
"st_astext" => "ST_AsText",
|
|
27
|
+
"st_asbinary" => "ST_AsBinary",
|
|
28
|
+
"st_srid" => "ST_SRID"
|
|
17
29
|
}.freeze
|
|
18
30
|
|
|
31
|
+
# Override st_func to use our function mappings
|
|
19
32
|
def st_func(standard_name)
|
|
20
|
-
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def visit_String(node, collector)
|
|
24
|
-
node, srid = Trilogis.parse_node(node)
|
|
25
|
-
collector << wkttosql_statement(node, srid)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def visit_RGeo_ActiveRecord_SpatialNamedFunction(node, collector)
|
|
29
|
-
aggregate(st_func(node.name), node, collector)
|
|
33
|
+
SPATIAL_FUNCTIONS[standard_name.downcase] || standard_name
|
|
30
34
|
end
|
|
31
35
|
|
|
36
|
+
# Override visit_in_spatial_context to use our axis-order logic
|
|
32
37
|
def visit_in_spatial_context(node, collector)
|
|
33
38
|
case node
|
|
34
39
|
when String
|
|
35
|
-
node,
|
|
36
|
-
collector << wkttosql_statement(node, srid)
|
|
40
|
+
visit_wkt_string(node, collector)
|
|
37
41
|
when RGeo::Feature::Instance
|
|
38
|
-
|
|
42
|
+
visit_RGeo_Feature_Instance(node, collector)
|
|
39
43
|
when RGeo::Cartesian::BoundingBox
|
|
40
|
-
|
|
44
|
+
geom = node.to_geometry
|
|
45
|
+
visit_RGeo_Feature_Instance(geom, collector)
|
|
41
46
|
else
|
|
42
47
|
visit(node, collector)
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
def visit_spatial_value(node, collector)
|
|
52
|
+
case node
|
|
53
|
+
when RGeo::Feature::Instance
|
|
54
|
+
visit_RGeo_Feature_Instance(node, collector)
|
|
55
|
+
when String
|
|
56
|
+
if node.match?(/^[A-Z]/) # WKT string
|
|
57
|
+
visit_wkt_string(node, collector)
|
|
52
58
|
else
|
|
53
|
-
|
|
59
|
+
super
|
|
54
60
|
end
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def visit_RGeo_Feature_Instance(node, collector)
|
|
67
|
+
srid = node.srid || 0
|
|
68
|
+
wkt = node.as_text
|
|
69
|
+
|
|
70
|
+
# MySQL ST_GeomFromText supports axis-order option for geographic SRIDs
|
|
71
|
+
# This ensures longitude-latitude order for SRID 4326 (WGS84)
|
|
72
|
+
if srid == 4326
|
|
73
|
+
collector << "ST_GeomFromText('#{wkt}', #{srid}, #{ActiveRecord::ConnectionAdapters::TrilogisAdapter::AXIS_ORDER_LONG_LAT})"
|
|
74
|
+
else
|
|
75
|
+
collector << "ST_GeomFromText('#{wkt}', #{srid})"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def visit_wkt_string(wkt, collector)
|
|
80
|
+
# Extract SRID if present in EWKT format
|
|
81
|
+
if wkt =~ /^SRID=(\d+);(.+)$/i
|
|
82
|
+
srid = Regexp.last_match(1).to_i
|
|
83
|
+
clean_wkt = Regexp.last_match(2)
|
|
84
|
+
# Use axis-order for geographic SRID
|
|
85
|
+
if srid == 4326
|
|
86
|
+
collector << "ST_GeomFromText('#{clean_wkt}', #{srid}, #{ActiveRecord::ConnectionAdapters::TrilogisAdapter::AXIS_ORDER_LONG_LAT})"
|
|
57
87
|
else
|
|
58
|
-
|
|
88
|
+
collector << "ST_GeomFromText('#{clean_wkt}', #{srid})"
|
|
59
89
|
end
|
|
60
90
|
else
|
|
61
|
-
|
|
91
|
+
collector << "ST_GeomFromText('#{wkt}', 0)"
|
|
62
92
|
end
|
|
63
|
-
[value, srid]
|
|
64
93
|
end
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
# Handle spatial function calls
|
|
96
|
+
def visit_Arel_Nodes_NamedFunction(o, collector)
|
|
97
|
+
name = o.name.downcase
|
|
98
|
+
if SPATIAL_FUNCTIONS.key?(name)
|
|
99
|
+
collector << SPATIAL_FUNCTIONS[name]
|
|
100
|
+
collector << "("
|
|
101
|
+
o.expressions.each_with_index do |arg, i|
|
|
102
|
+
collector << ", " if i.positive?
|
|
103
|
+
# Handle string arguments (WKT/EWKT)
|
|
104
|
+
if arg.is_a?(String)
|
|
105
|
+
visit_wkt_string(arg, collector)
|
|
106
|
+
else
|
|
107
|
+
visit(arg, collector)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
collector << ")"
|
|
111
|
+
else
|
|
112
|
+
super
|
|
113
|
+
end
|
|
114
|
+
end
|
|
67
115
|
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
# Override literal visiting for spatial values
|
|
117
|
+
def visit_Arel_Nodes_Quoted(o, collector)
|
|
118
|
+
if o.value.is_a?(RGeo::Feature::Instance)
|
|
119
|
+
visit_RGeo_Feature_Instance(o.value, collector)
|
|
120
|
+
else
|
|
121
|
+
super
|
|
122
|
+
end
|
|
123
|
+
end
|
|
70
124
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
125
|
+
# Handle RGeo spatial constant nodes from rgeo-activerecord gem
|
|
126
|
+
def visit_RGeo_ActiveRecord_SpatialConstantNode(node, collector)
|
|
127
|
+
value = node.delegate
|
|
128
|
+
if value.is_a?(RGeo::Feature::Instance)
|
|
129
|
+
visit_RGeo_Feature_Instance(value, collector)
|
|
130
|
+
elsif value.is_a?(String)
|
|
131
|
+
# Handle WKT strings
|
|
132
|
+
if value.match?(/^[A-Z]/)
|
|
133
|
+
visit_wkt_string(value, collector)
|
|
134
|
+
else
|
|
135
|
+
# Regular string literal
|
|
136
|
+
collector << quote(value)
|
|
137
|
+
end
|
|
138
|
+
else
|
|
139
|
+
# For numeric or other values
|
|
140
|
+
collector << quote(value)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
74
143
|
|
|
75
|
-
|
|
144
|
+
# Support for spatial predicates in WHERE clauses
|
|
145
|
+
def visit_spatial_predicate(predicate_name, left, right, collector)
|
|
146
|
+
collector << "#{SPATIAL_FUNCTIONS[predicate_name]}("
|
|
147
|
+
visit(left, collector)
|
|
148
|
+
collector << ", "
|
|
149
|
+
visit(right, collector)
|
|
150
|
+
collector << ")"
|
|
76
151
|
end
|
|
77
152
|
end
|
|
78
153
|
end
|
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TrilogisAdapter
|
|
9
|
-
class Railtie < ::Rails::Railtie
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module Trilogis
|
|
6
|
+
class Railtie < Rails::Railtie
|
|
7
|
+
initializer "trilogis.initialize" do
|
|
10
8
|
ActiveSupport.on_load(:active_record) do
|
|
11
|
-
require "active_record/connection_adapters/
|
|
12
|
-
|
|
9
|
+
require "active_record/connection_adapters/trilogis_adapter"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Register database tasks for spatial databases
|
|
14
|
+
rake_tasks do
|
|
15
|
+
namespace :db do
|
|
16
|
+
namespace :trilogis do
|
|
17
|
+
desc "Create spatial extensions if needed"
|
|
18
|
+
task setup: :environment do
|
|
19
|
+
ActiveRecord::Base.connection.execute(
|
|
20
|
+
"SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() LIMIT 1"
|
|
21
|
+
)
|
|
22
|
+
puts "Trilogis adapter is ready. MySQL spatial support enabled."
|
|
23
|
+
end
|
|
24
|
+
end
|
|
13
25
|
end
|
|
14
26
|
end
|
|
15
27
|
end
|
|
16
28
|
end
|
|
17
29
|
end
|
|
18
30
|
end
|
|
19
|
-
|
|
20
|
-
if defined?(Rails::DBConsole)
|
|
21
|
-
require "trilogy_adapter/rails/dbconsole"
|
|
22
|
-
Rails::DBConsole.prepend(ActiveRecord::ConnectionAdapters::TrilogisAdapter::Rails::DBConsole)
|
|
23
|
-
end
|
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module Trilogis
|
|
6
|
-
class SchemaCreation < MySQL::SchemaCreation
|
|
6
|
+
class SchemaCreation < MySQL::SchemaCreation
|
|
7
7
|
private
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
def add_column_options!(sql, options)
|
|
10
|
+
# Add SRID option for spatial columns in MySQL 8.0+
|
|
11
|
+
# Format: /*!80003 SRID #{srid} */
|
|
12
|
+
if options[:srid]
|
|
13
|
+
sql_result = "#{sql} /*!80003 SRID #{options[:srid]} */"
|
|
14
|
+
sql.replace(sql_result)
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
super
|
|
18
|
+
end
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
end
|
|
@@ -4,71 +4,202 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module Trilogis
|
|
6
6
|
module SchemaStatements
|
|
7
|
-
|
|
7
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
|
|
8
|
+
if spatial_type?(type.to_s)
|
|
9
|
+
# If limit[:type] is specified, use it as the geometry type (e.g., "point")
|
|
10
|
+
# Otherwise use the base type (e.g., "geometry")
|
|
11
|
+
base_type = if limit.is_a?(Hash) && limit[:type]
|
|
12
|
+
limit[:type]
|
|
13
|
+
else
|
|
14
|
+
type
|
|
15
|
+
end
|
|
16
|
+
sql_type = spatial_sql_type(base_type, nil)
|
|
17
|
+
sql_type = "#{sql_type} SRID #{limit[:srid]}" if limit.is_a?(Hash) && limit[:srid]
|
|
18
|
+
sql_type
|
|
19
|
+
else
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
end
|
|
8
23
|
|
|
9
|
-
|
|
10
|
-
|
|
24
|
+
def add_index(table_name, column_name, **options)
|
|
25
|
+
index_type = options[:type]
|
|
26
|
+
|
|
27
|
+
# Handle spatial indexes - MySQL uses SPATIAL keyword, not USING
|
|
28
|
+
if index_type == :spatial
|
|
29
|
+
options = options.dup
|
|
30
|
+
options.delete(:using) # Remove any USING clause for spatial indexes
|
|
31
|
+
options[:type] = :spatial
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def indexes(table_name)
|
|
11
38
|
indexes = super
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
indexes.
|
|
15
|
-
|
|
16
|
-
|
|
39
|
+
|
|
40
|
+
# MySQL doesn't support prefix lengths for spatial indexes
|
|
41
|
+
indexes.each do |index|
|
|
42
|
+
if index.using == :gist || index.comment&.include?("spatial") || index.type == :spatial
|
|
43
|
+
index.instance_variable_set(:@lengths, {})
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
17
47
|
indexes
|
|
18
48
|
end
|
|
19
49
|
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
50
|
+
# Override columns to use parent's implementation but enhance spatial columns
|
|
51
|
+
# DO NOT override - let parent class handle all column creation
|
|
52
|
+
|
|
53
|
+
# Override to properly handle spatial columns creation
|
|
54
|
+
def new_column_from_field(table_name, field, definitions = nil)
|
|
55
|
+
field_name = extract_field_value(field, :Field, :field)
|
|
56
|
+
sql_type = extract_field_value(field, :Type, :type)
|
|
57
|
+
|
|
58
|
+
if spatial_type?(sql_type)
|
|
59
|
+
build_spatial_column(table_name, field, field_name, sql_type)
|
|
60
|
+
else
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def create_table_definition(name, **)
|
|
66
|
+
Trilogis::TableDefinition.new(self, name, **)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def create_table(table_name, **options, &)
|
|
70
|
+
# Clear spatial cache when creating table with force: true
|
|
71
|
+
# This ensures we don't have stale cache from a previously dropped table
|
|
72
|
+
clear_spatial_cache_for(table_name) if options[:force]
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def add_column(table_name, column_name, type, **options)
|
|
77
|
+
if spatial_type?(type.to_s)
|
|
78
|
+
# Build ALTER TABLE statement for spatial column
|
|
79
|
+
sql_type = spatial_sql_type(type, options[:type])
|
|
80
|
+
base_sql = "ALTER TABLE #{quote_table_name(table_name)} " \
|
|
81
|
+
"ADD #{quote_column_name(column_name)} #{sql_type}"
|
|
82
|
+
sql_parts = [base_sql]
|
|
83
|
+
|
|
84
|
+
# Add SRID if specified
|
|
85
|
+
sql_parts << " SRID #{options[:srid]}" if options[:srid] && options[:srid] != 0
|
|
86
|
+
|
|
87
|
+
# Add NULL constraint
|
|
88
|
+
sql_parts << " NOT NULL" if options[:null] == false
|
|
89
|
+
|
|
90
|
+
# Add DEFAULT if specified (allow falsy values like 0/false)
|
|
91
|
+
sql_parts << " DEFAULT #{quote_default_expression(options[:default], nil)}" if options.key?(:default)
|
|
92
|
+
|
|
93
|
+
execute sql_parts.join
|
|
94
|
+
|
|
95
|
+
# Clear memoized spatial column info for this table
|
|
96
|
+
clear_spatial_cache_for(table_name)
|
|
97
|
+
else
|
|
98
|
+
super
|
|
25
99
|
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def drop_table(table_name, **options)
|
|
103
|
+
# Clear memoized spatial column info for this table before dropping
|
|
104
|
+
clear_spatial_cache_for(table_name)
|
|
26
105
|
super
|
|
27
106
|
end
|
|
28
107
|
|
|
108
|
+
def rename_table(table_name, new_name)
|
|
109
|
+
# Clear cache for both old and new table names
|
|
110
|
+
clear_spatial_cache_for(table_name)
|
|
111
|
+
clear_spatial_cache_for(new_name)
|
|
112
|
+
super
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Clear all spatial column caches (useful for tests)
|
|
116
|
+
def clear_spatial_cache!
|
|
117
|
+
@spatial_column_info = {}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def spatial_sql_type(base_type, subtype = nil)
|
|
121
|
+
sql_type = base_type.to_s.delete("_").upcase
|
|
122
|
+
subtype_sql = subtype.to_s
|
|
123
|
+
if subtype_sql.empty?
|
|
124
|
+
sql_type
|
|
125
|
+
else
|
|
126
|
+
"#{sql_type}(#{subtype_sql.delete('_').upcase})"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
29
130
|
private
|
|
30
131
|
|
|
31
|
-
|
|
132
|
+
def spatial_type?(type)
|
|
133
|
+
TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(type.to_s.downcase)
|
|
134
|
+
end
|
|
135
|
+
|
|
32
136
|
def schema_creation
|
|
33
|
-
|
|
137
|
+
SchemaCreation.new(self)
|
|
34
138
|
end
|
|
35
139
|
|
|
36
|
-
#
|
|
37
|
-
def
|
|
38
|
-
|
|
140
|
+
# Memoized spatial column info per table
|
|
141
|
+
def spatial_column_info(table_name)
|
|
142
|
+
@spatial_column_info ||= {}
|
|
143
|
+
@spatial_column_info[table_name.to_sym] ||= SpatialColumnInfo.new(self, table_name.to_s)
|
|
39
144
|
end
|
|
40
145
|
|
|
41
|
-
#
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
146
|
+
# Clear spatial cache for a specific table
|
|
147
|
+
def clear_spatial_cache_for(table_name)
|
|
148
|
+
@spatial_column_info&.delete(table_name.to_sym)
|
|
149
|
+
@spatial_column_info&.delete(table_name.to_s)
|
|
150
|
+
end
|
|
45
151
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
152
|
+
# Extract field value with case-insensitive key lookup
|
|
153
|
+
def extract_field_value(field, *keys)
|
|
154
|
+
keys.each do |key|
|
|
155
|
+
return field[key] if field.key?(key)
|
|
156
|
+
return field[key.to_s] if field.key?(key.to_s)
|
|
51
157
|
end
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
52
160
|
|
|
53
|
-
|
|
54
|
-
|
|
161
|
+
# Build a spatial column from field metadata
|
|
162
|
+
def build_spatial_column(table_name, field, field_name, sql_type)
|
|
163
|
+
spatial_info = spatial_column_info(table_name).get(field_name, sql_type)
|
|
164
|
+
type_metadata = fetch_type_metadata(sql_type)
|
|
55
165
|
|
|
56
166
|
SpatialColumn.new(
|
|
57
|
-
|
|
58
|
-
default,
|
|
167
|
+
field_name,
|
|
168
|
+
extract_field_value(field, :Default, :default),
|
|
59
169
|
type_metadata,
|
|
60
|
-
field
|
|
61
|
-
|
|
62
|
-
collation: field
|
|
63
|
-
comment: field
|
|
64
|
-
|
|
170
|
+
extract_field_value(field, :Null, :null) == "YES",
|
|
171
|
+
extract_field_value(field, :Extra, :extra),
|
|
172
|
+
collation: extract_field_value(field, :Collation, :collation),
|
|
173
|
+
comment: extract_field_value(field, :Comment, :comment).presence,
|
|
174
|
+
spatial_info: spatial_info
|
|
65
175
|
)
|
|
66
176
|
end
|
|
177
|
+
end
|
|
67
178
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
179
|
+
class SchemaCreation < ActiveRecord::ConnectionAdapters::MySQL::SchemaCreation
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def visit_ColumnDefinition(o)
|
|
183
|
+
if spatial_column?(o)
|
|
184
|
+
sql_type = spatial_sql_type(o.sql_type, o.options[:type])
|
|
185
|
+
column_sql_parts = ["#{quote_column_name(o.name)} #{sql_type}"]
|
|
186
|
+
|
|
187
|
+
# Add SRID if specified (MySQL 8.0+ syntax: COLUMN TYPE SRID value)
|
|
188
|
+
column_sql_parts << " SRID #{o.options[:srid]}" if o.options[:srid] && o.options[:srid] != 0
|
|
189
|
+
|
|
190
|
+
column_sql_parts << " NOT NULL" unless o.null
|
|
191
|
+
column_sql_parts << " DEFAULT #{quote_default_expression(o.default, o)}" unless o.default.nil?
|
|
192
|
+
column_sql_parts.join
|
|
193
|
+
else
|
|
194
|
+
super
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def spatial_column?(column)
|
|
199
|
+
# Check both with and without underscores
|
|
200
|
+
sql_type = column.sql_type.to_s.downcase
|
|
201
|
+
TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(sql_type) ||
|
|
202
|
+
TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(sql_type.delete("_"))
|
|
72
203
|
end
|
|
73
204
|
end
|
|
74
205
|
end
|