activerecord-trilogis-adapter 7.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 +7 -0
- data/LICENSE.txt +29 -0
- data/lib/active_record/connection_adapters/trilogis/arel_tosql.rb +79 -0
- data/lib/active_record/connection_adapters/trilogis/column_methods.rb +54 -0
- data/lib/active_record/connection_adapters/trilogis/connection.rb +17 -0
- data/lib/active_record/connection_adapters/trilogis/rails/dbconsole.rb +48 -0
- data/lib/active_record/connection_adapters/trilogis/railtie.rb +23 -0
- data/lib/active_record/connection_adapters/trilogis/schema_creation.rb +19 -0
- data/lib/active_record/connection_adapters/trilogis/schema_statements.rb +76 -0
- data/lib/active_record/connection_adapters/trilogis/spatial_column.rb +64 -0
- data/lib/active_record/connection_adapters/trilogis/spatial_column_info.rb +42 -0
- data/lib/active_record/connection_adapters/trilogis/spatial_expressions.rb +20 -0
- data/lib/active_record/connection_adapters/trilogis/spatial_table_definition.rb +36 -0
- data/lib/active_record/connection_adapters/trilogis/version.rb +9 -0
- data/lib/active_record/connection_adapters/trilogis_adapter.rb +164 -0
- data/lib/active_record/tasks/trilogis_database_tasks.rb +7 -0
- data/lib/active_record/type/spatial.rb +107 -0
- data/lib/activerecord-trilogis-adapter.rb +4 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1d6b6ce09a2e73348f084b7b7e31377e967eae10b488f54f596bdc5f403a8426
|
4
|
+
data.tar.gz: 39b183fe7a3760381975f41902b4d863b90475877a3b3bee1afc0aaa24fb2ea7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a0626d4c083466e776be97d2b4c009837ae33e74be2ca64a6bf2b216717e0f740f1397554b27c064d0158dc69d4f9a5eac47c82afac78fdaf0655861c38275cb
|
7
|
+
data.tar.gz: 14bb624d74ecb71c9b898a4ecf9e3c518433ff462041902e171400a5dabff6773289b4a707617263c6bbe43585e78b5003d11c9e208d22b580a19d3ffb16ee55
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
# Copyright 2012 Daniel Azuma
|
3
|
+
#
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
13
|
+
# and/or other materials provided with the distribution.
|
14
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
15
|
+
# contributors to this software, may be used to endorse or promote products
|
16
|
+
# derived from this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
# -----------------------------------------------------------------------------
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc:
|
4
|
+
module Visitors # :nodoc:
|
5
|
+
class Trilogis < MySQL # :nodoc:
|
6
|
+
|
7
|
+
include RGeo::ActiveRecord::SpatialToSql
|
8
|
+
|
9
|
+
if ::Arel::Visitors.const_defined?(:BindVisitor)
|
10
|
+
include ::Arel::Visitors::BindVisitor
|
11
|
+
end
|
12
|
+
|
13
|
+
FUNC_MAP = {
|
14
|
+
"st_wkttosql" => "ST_GeomFromText",
|
15
|
+
"st_wkbtosql" => "ST_GeomFromWKB",
|
16
|
+
"st_length" => "ST_Length"
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
def st_func(standard_name)
|
20
|
+
FUNC_MAP[standard_name.downcase] || standard_name
|
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)
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_in_spatial_context(node, collector)
|
33
|
+
case node
|
34
|
+
when String
|
35
|
+
node, srid = Trilogis.parse_node(node)
|
36
|
+
collector << wkttosql_statement(node, srid)
|
37
|
+
when RGeo::Feature::Instance
|
38
|
+
collector << visit_RGeo_Feature_Instance(node, collector)
|
39
|
+
when RGeo::Cartesian::BoundingBox
|
40
|
+
collector << visit_RGeo_Cartesian_BoundingBox(node, collector)
|
41
|
+
else
|
42
|
+
visit(node, collector)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse_node(node)
|
47
|
+
value, srid = nil, 0
|
48
|
+
if node =~ /.*;.*$/i
|
49
|
+
params = Regexp.last_match(0).split(";")
|
50
|
+
if params.first =~ /(srid|SRID)=\d*/
|
51
|
+
srid = params.first.split("=").last.to_i
|
52
|
+
else
|
53
|
+
value = params.first
|
54
|
+
end
|
55
|
+
if params.last =~ /(srid|SRID)=\d*/
|
56
|
+
srid = params.last.split("=").last.to_i
|
57
|
+
else
|
58
|
+
value = params.last
|
59
|
+
end
|
60
|
+
else
|
61
|
+
value = node
|
62
|
+
end
|
63
|
+
[value, srid]
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def wkttosql_statement(node, srid)
|
69
|
+
func_name = st_func("ST_WKTToSQL")
|
70
|
+
|
71
|
+
args = [quote(node)]
|
72
|
+
args << srid unless srid.zero?
|
73
|
+
args << ActiveRecord::ConnectionAdapters::TrilogisAdapter::AXIS_ORDER_LONG_LAT
|
74
|
+
|
75
|
+
"#{func_name}(#{args.join(', ')})"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Trilogis
|
6
|
+
module ColumnMethods
|
7
|
+
def spatial(name, options = {})
|
8
|
+
raise "You must set a type. For example: 't.spatial type: :st_point'" unless options[:type]
|
9
|
+
|
10
|
+
column(name, options[:type], **options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def geometry(name, options = {})
|
14
|
+
column(name, :geometry, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def geometry_collection(name, options = {})
|
18
|
+
column(name, :geometrycollection, **options)
|
19
|
+
end
|
20
|
+
alias geometrycollection geometry_collection
|
21
|
+
|
22
|
+
def line_string(name, options = {})
|
23
|
+
column(name, :linestring, **options)
|
24
|
+
end
|
25
|
+
alias linestring line_string
|
26
|
+
|
27
|
+
def multi_line_string(name, options = {})
|
28
|
+
column(name, :multilinestring, **options)
|
29
|
+
end
|
30
|
+
alias multilinestring multi_line_string
|
31
|
+
|
32
|
+
def multi_point(name, options = {})
|
33
|
+
column(name, :multipoint, **options)
|
34
|
+
end
|
35
|
+
alias multipoint multi_point
|
36
|
+
|
37
|
+
def multi_polygon(name, options = {})
|
38
|
+
column(name, :multipolygon, **options)
|
39
|
+
end
|
40
|
+
alias multipolygon multi_polygon
|
41
|
+
|
42
|
+
def point(name, options = {})
|
43
|
+
column(name, :point, **options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def polygon(name, options = {})
|
47
|
+
column(name, :polygon, **options)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
MySQL::Table.include Trilogis::ColumnMethods
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "trilogy"
|
4
|
+
require "active_record/connection_adapters/trilogy_adapter"
|
5
|
+
require "active_record/connection_adapters/trilogis_adapter"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionAdapters
|
9
|
+
class TrilogisAdapter
|
10
|
+
module Connection
|
11
|
+
def trilogy_adapter_class
|
12
|
+
ActiveRecord::ConnectionAdapters::TrilogisAdapter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
class TrilogisAdapter
|
6
|
+
module Rails
|
7
|
+
module DBConsole
|
8
|
+
class AdapterAdapter < SimpleDelegator
|
9
|
+
def adapter
|
10
|
+
"mysql"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def db_config
|
15
|
+
if super.adapter == "trilogis"
|
16
|
+
AdapterAdapter.new(super)
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Rails
|
28
|
+
class DBConsole
|
29
|
+
# require "rails/commands/dbconsole/dbconsole_command"
|
30
|
+
if ActiveRecord.version < ::Gem::Version.new('6.1.a')
|
31
|
+
alias _brick_start start
|
32
|
+
|
33
|
+
def start
|
34
|
+
ENV["RAILS_ENV"] ||= @options[:environment] || environment
|
35
|
+
|
36
|
+
if config["adapter"] == "trilogis"
|
37
|
+
begin
|
38
|
+
::ActiveRecord::ConnectionAdapters::TrilogisAdapter.dbconsole(config, @options)
|
39
|
+
rescue NotImplementedError
|
40
|
+
abort "Unknown command-line client for #{db_config.database}."
|
41
|
+
end
|
42
|
+
else
|
43
|
+
_brick_start
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined?(Rails)
|
4
|
+
require "rails/railtie"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionAdapters
|
8
|
+
class TrilogisAdapter
|
9
|
+
class Railtie < ::Rails::Railtie
|
10
|
+
ActiveSupport.on_load(:active_record) do
|
11
|
+
require "active_record/connection_adapters/trilogis/connection"
|
12
|
+
ActiveRecord::Base.public_send :extend, ActiveRecord::ConnectionAdapters::TrilogisAdapter::Connection
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if defined?(Rails::DBConsole)
|
21
|
+
require "trilogy_adapter/rails/dbconsole"
|
22
|
+
Rails::DBConsole.prepend(ActiveRecord::ConnectionAdapters::TrilogisAdapter::Rails::DBConsole)
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Trilogis
|
6
|
+
class SchemaCreation < MySQL::SchemaCreation # :nodoc:
|
7
|
+
private
|
8
|
+
|
9
|
+
def add_column_options!(sql, options)
|
10
|
+
if options[:srid]
|
11
|
+
sql << " /*!80003 SRID #{options[:srid]} */"
|
12
|
+
end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Trilogis
|
6
|
+
module SchemaStatements
|
7
|
+
# super: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
|
8
|
+
|
9
|
+
# override
|
10
|
+
def indexes(table_name) #:nodoc:
|
11
|
+
indexes = super
|
12
|
+
# HACK(aleks, 06/15/18): MySQL 5 does not support prefix lengths for spatial indexes
|
13
|
+
# https://dev.mysql.com/doc/refman/5.6/en/create-index.html
|
14
|
+
indexes.select do |idx|
|
15
|
+
idx.type == :spatial
|
16
|
+
end.each { |idx| idx.is_a?(Struct) ? idx.lengths = {} : idx.instance_variable_set(:@lengths, {}) }
|
17
|
+
indexes
|
18
|
+
end
|
19
|
+
|
20
|
+
# override
|
21
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
22
|
+
if (info = RGeo::ActiveRecord.geometric_type_from_name(type.to_s.delete("_")))
|
23
|
+
type = limit[:type] || type if limit.is_a?(::Hash)
|
24
|
+
type = type.to_s.delete("_").upcase
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# override
|
32
|
+
def schema_creation
|
33
|
+
Trilogis::SchemaCreation.new(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
# override
|
37
|
+
def create_table_definition(*args, **options)
|
38
|
+
Trilogis::TableDefinition.new(self, *args, **options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# override
|
42
|
+
def new_column_from_field(table_name, field)
|
43
|
+
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
44
|
+
default, default_function = field[:Default], nil
|
45
|
+
|
46
|
+
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
47
|
+
default, default_function = nil, default
|
48
|
+
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
49
|
+
default = +"(#{default})" unless default.start_with?("(")
|
50
|
+
default, default_function = nil, default
|
51
|
+
end
|
52
|
+
|
53
|
+
# {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
|
54
|
+
spatial = spatial_column_info(table_name).get(field[:Field], type_metadata.sql_type)
|
55
|
+
|
56
|
+
SpatialColumn.new(
|
57
|
+
field[:Field],
|
58
|
+
default,
|
59
|
+
type_metadata,
|
60
|
+
field[:Null] == "YES",
|
61
|
+
default_function,
|
62
|
+
collation: field[:Collation],
|
63
|
+
comment: field[:Comment].presence,
|
64
|
+
spatial: spatial
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# memoize hash of column infos for tables
|
69
|
+
def spatial_column_info(table_name)
|
70
|
+
@spatial_column_info ||= {}
|
71
|
+
@spatial_column_info[table_name.to_sym] = SpatialColumnInfo.new(self, table_name.to_s)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module ConnectionAdapters # :nodoc:
|
5
|
+
module Trilogis # :nodoc:
|
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, **)
|
8
|
+
@sql_type_metadata = sql_type_metadata
|
9
|
+
if spatial
|
10
|
+
# This case comes from an entry in the geometry_columns table
|
11
|
+
set_geometric_type_from_name(spatial[:type])
|
12
|
+
@srid = spatial[:srid].to_i
|
13
|
+
elsif sql_type =~ /geometry|point|linestring|polygon/i
|
14
|
+
build_from_sql_type(sql_type_metadata.sql_type)
|
15
|
+
elsif sql_type_metadata.sql_type =~ /geometry|point|linestring|polygon/i
|
16
|
+
# A geometry column with no geometry_columns entry.
|
17
|
+
# @geometric_type = geo_type_from_sql_type(sql_type)
|
18
|
+
build_from_sql_type(sql_type_metadata.sql_type)
|
19
|
+
end
|
20
|
+
super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment)
|
21
|
+
if spatial? && @srid
|
22
|
+
@limit = {type: geometric_type.type_name.underscore, srid: @srid}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :geometric_type, :srid
|
27
|
+
|
28
|
+
def has_z
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_m
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
alias has_z? has_z
|
37
|
+
alias has_m? has_m
|
38
|
+
|
39
|
+
def multi?
|
40
|
+
/^(geometrycollection|multi)/i.match?(sql_type)
|
41
|
+
end
|
42
|
+
|
43
|
+
def limit
|
44
|
+
spatial? ? @limit : super
|
45
|
+
end
|
46
|
+
|
47
|
+
def spatial?
|
48
|
+
%i[geometry].include?(@sql_type_metadata.type)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def set_geometric_type_from_name(name)
|
54
|
+
@geometric_type = RGeo::ActiveRecord.geometric_type_from_name(name) || RGeo::Feature::Geometry
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_from_sql_type(sql_type)
|
58
|
+
geo_type, @srid = Type::Spatial.parse_sql_type(sql_type)
|
59
|
+
set_geometric_type_from_name(geo_type)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module ConnectionAdapters # :nodoc:
|
5
|
+
module Trilogis # :nodoc:
|
6
|
+
# Do spatial sql queries for column info and memoize that info.
|
7
|
+
class SpatialColumnInfo
|
8
|
+
def initialize(adapter, table_name)
|
9
|
+
@adapter = adapter
|
10
|
+
@table_name = table_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
info = @adapter.query(
|
15
|
+
"SELECT column_name, srs_id, column_type FROM INFORMATION_SCHEMA.Columns WHERE table_name='#{@table_name}'"
|
16
|
+
)
|
17
|
+
|
18
|
+
result = {}
|
19
|
+
info.each do |row|
|
20
|
+
name = row[0]
|
21
|
+
type = row[2]
|
22
|
+
type.sub!(/m$/, "")
|
23
|
+
result[name] = {
|
24
|
+
name: name,
|
25
|
+
srid: row[1].to_i,
|
26
|
+
type: type,
|
27
|
+
}
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
# do not query the database for non-spatial columns/tables
|
33
|
+
def get(column_name, type)
|
34
|
+
return unless TrilogisAdapter.spatial_column_options(type.to_sym)
|
35
|
+
|
36
|
+
@spatial_column_info ||= all
|
37
|
+
@spatial_column_info[column_name]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RGeo
|
4
|
+
module ActiveRecord
|
5
|
+
module Trilogis
|
6
|
+
module SpatialExpressions
|
7
|
+
def st_distance_sphere(rhs, units = nil)
|
8
|
+
args = [self, rhs]
|
9
|
+
args << units.to_s if units
|
10
|
+
SpatialNamedFunction.new("ST_Distance_Sphere", args, [false, true, true, false])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Allow chaining of spatial expressions from attributes
|
18
|
+
Arel::Attribute.include RGeo::ActiveRecord::Trilogis::SpatialExpressions
|
19
|
+
RGeo::ActiveRecord::SpatialConstantNode.include RGeo::ActiveRecord::Trilogis::SpatialExpressions
|
20
|
+
RGeo::ActiveRecord::SpatialNamedFunction.include RGeo::ActiveRecord::Trilogis::SpatialExpressions
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module ConnectionAdapters # :nodoc:
|
5
|
+
module Trilogis # :nodoc:
|
6
|
+
class TableDefinition < MySQL::TableDefinition # :nodoc:
|
7
|
+
include ColumnMethods
|
8
|
+
# super: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
|
9
|
+
def new_column_definition(name, type, **options)
|
10
|
+
if (info = TrilogisAdapter.spatial_column_options(type.to_sym))
|
11
|
+
if (limit = options.delete(:limit)) && limit.is_a?(::Hash)
|
12
|
+
options.merge!(limit)
|
13
|
+
end
|
14
|
+
|
15
|
+
geo_type = ColumnDefinitionUtils.geo_type(options[:type] || type || info[:type])
|
16
|
+
|
17
|
+
options[:spatial_type] = geo_type
|
18
|
+
column = super(name, geo_type.downcase.to_sym, **options)
|
19
|
+
else
|
20
|
+
column = super(name, type, **options)
|
21
|
+
end
|
22
|
+
|
23
|
+
column
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ColumnDefinitionUtils
|
28
|
+
class << self
|
29
|
+
def geo_type(type = "GEOMETRY")
|
30
|
+
type.to_s.delete("_").upcase
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The activerecord-trilogis-adapter gem installs the *trilogis*
|
4
|
+
# connection adapter into ActiveRecord.
|
5
|
+
|
6
|
+
# :stopdoc:
|
7
|
+
|
8
|
+
require "rgeo/active_record"
|
9
|
+
|
10
|
+
require "active_record/connection_adapters"
|
11
|
+
require "active_record/connection_adapters/trilogy_adapter"
|
12
|
+
require "active_record/connection_adapters/trilogis/version"
|
13
|
+
require "active_record/connection_adapters/trilogis/column_methods"
|
14
|
+
require "active_record/connection_adapters/trilogis/schema_creation"
|
15
|
+
require "active_record/connection_adapters/trilogis/schema_statements"
|
16
|
+
require "active_record/connection_adapters/trilogis/spatial_table_definition"
|
17
|
+
require "active_record/connection_adapters/trilogis/spatial_column"
|
18
|
+
require "active_record/connection_adapters/trilogis/spatial_column_info"
|
19
|
+
require "active_record/connection_adapters/trilogis/spatial_expressions"
|
20
|
+
require "active_record/connection_adapters/trilogis/arel_tosql"
|
21
|
+
require "active_record/tasks/trilogis_database_tasks"
|
22
|
+
require "active_record/type/spatial"
|
23
|
+
|
24
|
+
# :startdoc:
|
25
|
+
|
26
|
+
module ActiveRecord
|
27
|
+
module ConnectionHandling # :nodoc:
|
28
|
+
# Establishes a connection to the database that's used by all Active Record objects.
|
29
|
+
def trilogis_connection(config)
|
30
|
+
configuration = config.dup
|
31
|
+
|
32
|
+
# Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
|
33
|
+
# matched rather than number of rows updated.
|
34
|
+
configuration[:found_rows] = true
|
35
|
+
|
36
|
+
options = [
|
37
|
+
configuration[:host],
|
38
|
+
configuration[:port],
|
39
|
+
configuration[:database],
|
40
|
+
configuration[:username],
|
41
|
+
configuration[:password],
|
42
|
+
configuration[:socket],
|
43
|
+
0
|
44
|
+
]
|
45
|
+
|
46
|
+
ActiveRecord::ConnectionAdapters::TrilogisAdapter.new nil, logger, options, configuration
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module ConnectionAdapters
|
51
|
+
class TrilogisAdapter < TrilogyAdapter
|
52
|
+
ADAPTER_NAME = "Trilogis"
|
53
|
+
AXIS_ORDER_LONG_LAT = "'axis-order=long-lat'".freeze
|
54
|
+
|
55
|
+
include Trilogis::SchemaStatements
|
56
|
+
|
57
|
+
SPATIAL_COLUMN_OPTIONS =
|
58
|
+
{
|
59
|
+
geometry: {},
|
60
|
+
geometrycollection: {},
|
61
|
+
linestring: {},
|
62
|
+
multilinestring: {},
|
63
|
+
multipoint: {},
|
64
|
+
multipolygon: {},
|
65
|
+
spatial: { type: "geometry" },
|
66
|
+
point: {},
|
67
|
+
polygon: {}
|
68
|
+
}.freeze
|
69
|
+
|
70
|
+
# http://postgis.17.x6.nabble.com/Default-SRID-td5001115.html
|
71
|
+
DEFAULT_SRID = 0
|
72
|
+
GEOGRAPHIC_SRID = 4326
|
73
|
+
|
74
|
+
%w[
|
75
|
+
geometry
|
76
|
+
geometrycollection
|
77
|
+
point
|
78
|
+
linestring
|
79
|
+
polygon
|
80
|
+
multipoint
|
81
|
+
multilinestring
|
82
|
+
multipolygon
|
83
|
+
].each do |geo_type|
|
84
|
+
ActiveRecord::Type.register(geo_type.to_sym, adapter: :trilogis) do |sql_type|
|
85
|
+
Type::Spatial.new(sql_type.to_s)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def initialize(connection, logger, connection_options, config)
|
90
|
+
super
|
91
|
+
|
92
|
+
@visitor = Arel::Visitors::Trilogis.new(self)
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.spatial_column_options(key)
|
96
|
+
SPATIAL_COLUMN_OPTIONS[key]
|
97
|
+
end
|
98
|
+
|
99
|
+
def default_srid
|
100
|
+
DEFAULT_SRID
|
101
|
+
end
|
102
|
+
|
103
|
+
def native_database_types
|
104
|
+
# Add spatial types
|
105
|
+
# Reference: https://dev.mysql.com/doc/refman/5.6/en/spatial-type-overview.html
|
106
|
+
super.merge(
|
107
|
+
geometry: { name: "geometry" },
|
108
|
+
geometrycollection: { name: "geometrycollection" },
|
109
|
+
linestring: { name: "linestring" },
|
110
|
+
multi_line_string: { name: "multilinestring" },
|
111
|
+
multi_point: { name: "multipoint" },
|
112
|
+
multi_polygon: { name: "multipolygon" },
|
113
|
+
spatial: { name: "geometry" },
|
114
|
+
point: { name: "point" },
|
115
|
+
polygon: { name: "polygon" }
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
class << self
|
120
|
+
|
121
|
+
private
|
122
|
+
def initialize_type_map(m)
|
123
|
+
super
|
124
|
+
|
125
|
+
%w[
|
126
|
+
geometry
|
127
|
+
geometrycollection
|
128
|
+
point
|
129
|
+
linestring
|
130
|
+
polygon
|
131
|
+
multipoint
|
132
|
+
multilinestring
|
133
|
+
multipolygon
|
134
|
+
].each do |geo_type|
|
135
|
+
m.register_type(geo_type,Type.lookup(geo_type.to_sym, adapter: :trilogis))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
141
|
+
TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
|
142
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
143
|
+
end
|
144
|
+
|
145
|
+
def supports_spatial?
|
146
|
+
!mariadb? && version >= "5.7.6"
|
147
|
+
end
|
148
|
+
|
149
|
+
def quote(value)
|
150
|
+
dbval = value.try(:value_for_database) || value
|
151
|
+
if RGeo::Feature::Geometry.check_type(dbval)
|
152
|
+
"ST_GeomFromWKB(0x#{RGeo::WKRep::WKBGenerator.new(hex_format: true, little_endian: true).generate(dbval)},#{dbval.srid}, #{AXIS_ORDER_LONG_LAT})"
|
153
|
+
else
|
154
|
+
super
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
def type_map
|
160
|
+
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Type
|
5
|
+
class Spatial < Binary # :nodoc:
|
6
|
+
# sql_type is a string that comes from the database definition
|
7
|
+
# examples:
|
8
|
+
# "geometry"
|
9
|
+
# "geometry NOT NULL"
|
10
|
+
# "geometry"
|
11
|
+
def initialize(sql_type = "geometry")
|
12
|
+
@sql_type = sql_type
|
13
|
+
@geo_type, @srid = self.class.parse_sql_type(sql_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
# sql_type: geometry, geometry(Point), geometry(Point,4326), ...
|
17
|
+
#
|
18
|
+
# returns [geo_type, srid]
|
19
|
+
# geo_type: geometry, point, line_string, polygon, ...
|
20
|
+
# srid: 1234
|
21
|
+
def self.parse_sql_type(sql_type)
|
22
|
+
geo_type, srid = nil, 0
|
23
|
+
if sql_type =~ /(geometry)\((.*)\)$/i
|
24
|
+
# geometry(Point)
|
25
|
+
# geometry(Point,4326)
|
26
|
+
params = Regexp.last_match(2).split(",")
|
27
|
+
if params.first =~ /([a-z]+[^zm])(z?)(m?)/i
|
28
|
+
geo_type = Regexp.last_match(1)
|
29
|
+
end
|
30
|
+
if params.last =~ /(\d+)/
|
31
|
+
srid = Regexp.last_match(1).to_i
|
32
|
+
end
|
33
|
+
else
|
34
|
+
# geometry
|
35
|
+
# otherType(a,b)
|
36
|
+
geo_type = sql_type
|
37
|
+
end
|
38
|
+
[geo_type, srid]
|
39
|
+
end
|
40
|
+
|
41
|
+
def spatial_factory
|
42
|
+
@spatial_factories ||= {}
|
43
|
+
|
44
|
+
@spatial_factories[@srid] ||= if @srid == ConnectionAdapters::TrilogisAdapter::GEOGRAPHIC_SRID
|
45
|
+
RGeo::Geographic.spherical_factory(srid: ConnectionAdapters::TrilogisAdapter::GEOGRAPHIC_SRID)
|
46
|
+
else
|
47
|
+
RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
|
48
|
+
geo_type: @geo_type,
|
49
|
+
sql_type: @sql_type,
|
50
|
+
srid: @srid
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def klass
|
56
|
+
type == :geometry ? RGeo::Feature::Geometry : super
|
57
|
+
end
|
58
|
+
|
59
|
+
def spatial?
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def type
|
64
|
+
:geometry
|
65
|
+
end
|
66
|
+
|
67
|
+
# support setting an RGeo object or a WKT string
|
68
|
+
def serialize(value)
|
69
|
+
return if value.nil?
|
70
|
+
|
71
|
+
geo_value = cast_value(value)
|
72
|
+
|
73
|
+
# TODO: - only valid types should be allowed
|
74
|
+
# e.g. linestring is not valid for point column
|
75
|
+
raise "maybe should raise" unless RGeo::Feature::Geometry.check_type(geo_value)
|
76
|
+
|
77
|
+
geo_value
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def cast_value(value)
|
83
|
+
return if value.nil?
|
84
|
+
|
85
|
+
::String === value ? parse_wkt(value) : value
|
86
|
+
end
|
87
|
+
|
88
|
+
# convert WKT string into RGeo object
|
89
|
+
def parse_wkt(string)
|
90
|
+
marker = string[4, 1]
|
91
|
+
if ["\x00", "\x01"].include?(marker)
|
92
|
+
@srid = string[0, 4].unpack1(marker == "\x01" ? "V" : "N")
|
93
|
+
RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid).parse(string[4..-1])
|
94
|
+
elsif string[0, 10] =~ /[0-9a-fA-F]{8}0[01]/
|
95
|
+
@srid = string[0, 8].to_i(16)
|
96
|
+
@srid = [@srid].pack("V").unpack("N").first if string[9, 1] == "1"
|
97
|
+
RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid).parse(string[8..-1])
|
98
|
+
else
|
99
|
+
string, @srid = Arel::Visitors::Trilogis.parse_node(string)
|
100
|
+
RGeo::WKRep::WKTParser.new(spatial_factory, support_ewkt: true, default_srid: @srid).parse(string)
|
101
|
+
end
|
102
|
+
rescue RGeo::Error::ParseError, RGeo::Error::InvalidGeometry
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-trilogis-adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 7.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ether Moon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-03-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 7.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rgeo-activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 7.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 7.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: appraisal
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
97
|
+
description: ActiveRecord connection adapter for MySQL. It is based on the stock MySQL
|
98
|
+
adapter, and adds built-in support for the spatial extensions provided by MySQL.
|
99
|
+
It uses the RGeo library to represent spatial data in Ruby.
|
100
|
+
email: ether.moon@kakao.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- LICENSE.txt
|
106
|
+
- lib/active_record/connection_adapters/trilogis/arel_tosql.rb
|
107
|
+
- lib/active_record/connection_adapters/trilogis/column_methods.rb
|
108
|
+
- lib/active_record/connection_adapters/trilogis/connection.rb
|
109
|
+
- lib/active_record/connection_adapters/trilogis/rails/dbconsole.rb
|
110
|
+
- lib/active_record/connection_adapters/trilogis/railtie.rb
|
111
|
+
- lib/active_record/connection_adapters/trilogis/schema_creation.rb
|
112
|
+
- lib/active_record/connection_adapters/trilogis/schema_statements.rb
|
113
|
+
- lib/active_record/connection_adapters/trilogis/spatial_column.rb
|
114
|
+
- lib/active_record/connection_adapters/trilogis/spatial_column_info.rb
|
115
|
+
- lib/active_record/connection_adapters/trilogis/spatial_expressions.rb
|
116
|
+
- lib/active_record/connection_adapters/trilogis/spatial_table_definition.rb
|
117
|
+
- lib/active_record/connection_adapters/trilogis/version.rb
|
118
|
+
- lib/active_record/connection_adapters/trilogis_adapter.rb
|
119
|
+
- lib/active_record/tasks/trilogis_database_tasks.rb
|
120
|
+
- lib/active_record/type/spatial.rb
|
121
|
+
- lib/activerecord-trilogis-adapter.rb
|
122
|
+
homepage: http://github.com/ether-moon/activerecord-trilogis-adapter
|
123
|
+
licenses:
|
124
|
+
- BSD-3-Clause
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 2.7.0
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubygems_version: 3.5.6
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: ActiveRecord adapter for MySQL, based on RGeo.
|
145
|
+
test_files: []
|