activerecord-cockroachdb-adapter 6.0.0beta1 → 6.1.0.pre.beta.2
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/CHANGELOG.md +12 -0
- data/CONTRIBUTING.md +20 -0
- data/README.md +291 -0
- data/Rakefile +20 -0
- data/activerecord-cockroachdb-adapter.gemspec +4 -3
- data/build/teamcity-test.sh +2 -2
- data/lib/active_record/connection_adapters/cockroachdb/arel_tosql.rb +27 -0
- data/lib/active_record/connection_adapters/cockroachdb/column.rb +78 -1
- data/lib/active_record/connection_adapters/cockroachdb/column_methods.rb +53 -0
- data/lib/active_record/connection_adapters/cockroachdb/oid/interval.rb +126 -0
- data/lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb +121 -0
- data/lib/active_record/connection_adapters/cockroachdb/oid/type_map_initializer.rb +26 -0
- data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +10 -2
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +66 -1
- data/lib/active_record/connection_adapters/cockroachdb/setup.rb +19 -0
- data/lib/active_record/connection_adapters/cockroachdb/spatial_column_info.rb +44 -0
- data/lib/active_record/connection_adapters/cockroachdb/table_definition.rb +56 -0
- data/lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb +11 -5
- data/lib/active_record/connection_adapters/cockroachdb/type.rb +1 -3
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +188 -22
- metadata +31 -8
@@ -2,8 +2,85 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
module CockroachDB
|
4
4
|
module PostgreSQLColumnMonkeyPatch
|
5
|
+
# most functions taken from activerecord-postgis-adapter spatial_column
|
6
|
+
# https://github.com/rgeo/activerecord-postgis-adapter/blob/master/lib/active_record/connection_adapters/postgis/spatial_column.rb
|
7
|
+
def initialize(name, default, sql_type_metadata = nil, null = true,
|
8
|
+
default_function = nil, collation: nil, comment: nil,
|
9
|
+
serial: nil, spatial: nil)
|
10
|
+
@sql_type_metadata = sql_type_metadata
|
11
|
+
@geographic = !!(sql_type_metadata.sql_type =~ /geography\(/i)
|
12
|
+
|
13
|
+
if spatial
|
14
|
+
# This case comes from an entry in the geometry_columns table
|
15
|
+
set_geometric_type_from_name(spatial[:type])
|
16
|
+
@srid = spatial[:srid].to_i
|
17
|
+
@has_z = !!spatial[:has_z]
|
18
|
+
@has_m = !!spatial[:has_m]
|
19
|
+
elsif @geographic
|
20
|
+
# Geographic type information is embedded in the SQL type
|
21
|
+
@srid = 4326
|
22
|
+
@has_z = @has_m = false
|
23
|
+
build_from_sql_type(sql_type_metadata.sql_type)
|
24
|
+
elsif sql_type =~ /geography|geometry|point|linestring|polygon/i
|
25
|
+
build_from_sql_type(sql_type_metadata.sql_type)
|
26
|
+
elsif sql_type_metadata.sql_type =~ /geography|geometry|point|linestring|polygon/i
|
27
|
+
# A geometry column with no geometry_columns entry.
|
28
|
+
# @geometric_type = geo_type_from_sql_type(sql_type)
|
29
|
+
build_from_sql_type(sql_type_metadata.sql_type)
|
30
|
+
end
|
31
|
+
super(name, default, sql_type_metadata, null, default_function,
|
32
|
+
collation: collation, comment: comment, serial: serial)
|
33
|
+
if spatial? && @srid
|
34
|
+
@limit = { srid: @srid, type: to_type_name(geometric_type) }
|
35
|
+
@limit[:has_z] = true if @has_z
|
36
|
+
@limit[:has_m] = true if @has_m
|
37
|
+
@limit[:geographic] = true if @geographic
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :geographic,
|
42
|
+
:geometric_type,
|
43
|
+
:has_m,
|
44
|
+
:has_z,
|
45
|
+
:srid
|
46
|
+
|
47
|
+
alias geographic? geographic
|
48
|
+
alias has_z? has_z
|
49
|
+
alias has_m? has_m
|
50
|
+
|
51
|
+
def limit
|
52
|
+
spatial? ? @limit : super
|
53
|
+
end
|
54
|
+
|
55
|
+
def spatial?
|
56
|
+
%i[geometry geography].include?(@sql_type_metadata.type)
|
57
|
+
end
|
58
|
+
|
5
59
|
def serial?
|
6
|
-
default_function ==
|
60
|
+
default_function == 'unique_rowid()'
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def set_geometric_type_from_name(name)
|
66
|
+
@geometric_type = RGeo::ActiveRecord.geometric_type_from_name(name) || RGeo::Feature::Geometry
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_from_sql_type(sql_type)
|
70
|
+
geo_type, @srid, @has_z, @has_m = OID::Spatial.parse_sql_type(sql_type)
|
71
|
+
set_geometric_type_from_name(geo_type)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_type_name(geometric_type)
|
75
|
+
name = geometric_type.type_name.underscore
|
76
|
+
case name
|
77
|
+
when 'point'
|
78
|
+
'st_point'
|
79
|
+
when 'polygon'
|
80
|
+
'st_polygon'
|
81
|
+
else
|
82
|
+
name
|
83
|
+
end
|
7
84
|
end
|
8
85
|
end
|
9
86
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module CockroachDB
|
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 geography(name, options = {})
|
14
|
+
column(name, :geography, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def geometry(name, options = {})
|
18
|
+
column(name, :geometry, **options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def geometry_collection(name, options = {})
|
22
|
+
column(name, :geometry_collection, **options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def line_string(name, options = {})
|
26
|
+
column(name, :line_string, **options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def multi_line_string(name, options = {})
|
30
|
+
column(name, :multi_line_string, **options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def multi_point(name, options = {})
|
34
|
+
column(name, :multi_point, **options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def multi_polygon(name, options = {})
|
38
|
+
column(name, :multi_polygon, **options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def st_point(name, options = {})
|
42
|
+
column(name, :st_point, **options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def st_polygon(name, options = {})
|
46
|
+
column(name, :st_polygon, **options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
PostgreSQL::Table.include CockroachDB::ColumnMethods
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "active_support/duration"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module CockroachDB
|
8
|
+
module OID
|
9
|
+
module Interval # :nodoc:
|
10
|
+
DEFAULT_PRECISION = 6 # microseconds
|
11
|
+
|
12
|
+
def cast_value(value)
|
13
|
+
case value
|
14
|
+
when ::ActiveSupport::Duration
|
15
|
+
value
|
16
|
+
when ::String
|
17
|
+
begin
|
18
|
+
PostgresqlInterval::Parser.parse(value)
|
19
|
+
rescue PostgresqlInterval::ParseError
|
20
|
+
# Try ISO 8601
|
21
|
+
super
|
22
|
+
end
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def serialize(value)
|
29
|
+
precision = self.precision || DEFAULT_PRECISION
|
30
|
+
case value
|
31
|
+
when ::ActiveSupport::Duration
|
32
|
+
serialize_duration(value, precision)
|
33
|
+
when ::Numeric
|
34
|
+
serialize_duration(value.seconds, precision)
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_cast_for_schema(value)
|
41
|
+
serialize(value).inspect
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Convert an ActiveSupport::Duration to
|
47
|
+
# the postgres interval style
|
48
|
+
# ex. 1 year 2 mons 3 days 4 hours 5 minutes 6 seconds
|
49
|
+
def serialize_duration(value, precision)
|
50
|
+
yrs = value.parts.fetch(:years, 0)
|
51
|
+
mons = value.parts.fetch(:months, 0)
|
52
|
+
days = value.parts.fetch(:days, 0)
|
53
|
+
hrs = value.parts.fetch(:hours, 0)
|
54
|
+
mins = value.parts.fetch(:minutes, 0)
|
55
|
+
secs = value.parts.fetch(:seconds, 0).round(precision)
|
56
|
+
|
57
|
+
"#{yrs} years #{mons} mons #{days} days #{hrs} hours #{mins} minutes #{secs} seconds"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
PostgreSQL::OID::Interval.prepend(Interval)
|
62
|
+
end
|
63
|
+
|
64
|
+
module PostgresqlInterval
|
65
|
+
class Parser
|
66
|
+
PARTS = ActiveSupport::Duration::PARTS
|
67
|
+
PARTS_IN_SECONDS = ActiveSupport::Duration::PARTS_IN_SECONDS
|
68
|
+
|
69
|
+
# modified regex from https://github.com/jeremyevans/sequel/blob/master/lib/sequel/extensions/pg_interval.rb#L86
|
70
|
+
REGEX = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:([+-])?(\d{2,10}):(\d\d):(\d\d(\.\d+)?))?\z/
|
71
|
+
|
72
|
+
def self.parse(string)
|
73
|
+
matches = REGEX.match(string)
|
74
|
+
raise(ParseError) unless matches
|
75
|
+
|
76
|
+
# 1 => years, 2 => months, 3 => days, 4 => nil, 5 => hours,
|
77
|
+
# 6 => minutes, 7 => seconds with fraction digits, 8 => fractional portion of 7
|
78
|
+
duration = 0
|
79
|
+
parts = {}
|
80
|
+
|
81
|
+
if matches[1]
|
82
|
+
val = matches[1].to_i
|
83
|
+
duration += val * PARTS_IN_SECONDS[:years]
|
84
|
+
parts[:years] = val
|
85
|
+
end
|
86
|
+
|
87
|
+
if matches[2]
|
88
|
+
val = matches[2].to_i
|
89
|
+
duration += val * PARTS_IN_SECONDS[:months]
|
90
|
+
parts[:months] = val
|
91
|
+
end
|
92
|
+
|
93
|
+
if matches[3]
|
94
|
+
val = matches[3].to_i
|
95
|
+
duration += val * PARTS_IN_SECONDS[:days]
|
96
|
+
parts[:days] = val
|
97
|
+
end
|
98
|
+
|
99
|
+
if matches[5]
|
100
|
+
val = matches[5].to_i
|
101
|
+
duration += val * PARTS_IN_SECONDS[:hours]
|
102
|
+
parts[:hours] = val
|
103
|
+
end
|
104
|
+
|
105
|
+
if matches[6]
|
106
|
+
val = matches[6].to_i
|
107
|
+
duration += val * PARTS_IN_SECONDS[:minutes]
|
108
|
+
parts[:minutes] = val
|
109
|
+
end
|
110
|
+
|
111
|
+
if matches[7]
|
112
|
+
val = matches[7].to_f
|
113
|
+
duration += val * PARTS_IN_SECONDS[:seconds]
|
114
|
+
parts[:seconds] = val
|
115
|
+
end
|
116
|
+
|
117
|
+
ActiveSupport::Duration.new(duration, parts)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class ParseError < StandardError
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module CockroachDB
|
6
|
+
module OID
|
7
|
+
class Spatial < Type::Value
|
8
|
+
# sql_type is a string that comes from the database definition
|
9
|
+
# examples:
|
10
|
+
# "geometry(Point,4326)"
|
11
|
+
# "geography(Point,4326)"
|
12
|
+
# "geometry(Polygon,4326) NOT NULL"
|
13
|
+
# "geometry(Geography,4326)"
|
14
|
+
def initialize(oid, sql_type)
|
15
|
+
@sql_type = sql_type
|
16
|
+
@geo_type, @srid, @has_z, @has_m = self.class.parse_sql_type(sql_type)
|
17
|
+
end
|
18
|
+
|
19
|
+
# sql_type: geometry, geometry(Point), geometry(Point,4326), ...
|
20
|
+
#
|
21
|
+
# returns [geo_type, srid, has_z, has_m]
|
22
|
+
# geo_type: geography, geometry, point, line_string, polygon, ...
|
23
|
+
# srid: 1234
|
24
|
+
# has_z: false
|
25
|
+
# has_m: false
|
26
|
+
def self.parse_sql_type(sql_type)
|
27
|
+
geo_type = nil
|
28
|
+
srid = 0
|
29
|
+
has_z = false
|
30
|
+
has_m = false
|
31
|
+
|
32
|
+
if sql_type =~ /(geography|geometry)\((.*)\)$/i
|
33
|
+
# geometry(Point)
|
34
|
+
# geometry(Point,4326)
|
35
|
+
params = Regexp.last_match(2).split(',')
|
36
|
+
if params.first =~ /([a-z]+[^zm])(z?)(m?)/i
|
37
|
+
has_z = Regexp.last_match(2).length > 0
|
38
|
+
has_m = Regexp.last_match(3).length > 0
|
39
|
+
geo_type = Regexp.last_match(1)
|
40
|
+
end
|
41
|
+
srid = Regexp.last_match(1).to_i if params.last =~ /(\d+)/
|
42
|
+
else
|
43
|
+
geo_type = sql_type
|
44
|
+
end
|
45
|
+
[geo_type, srid, has_z, has_m]
|
46
|
+
end
|
47
|
+
|
48
|
+
def spatial_factory
|
49
|
+
@spatial_factory ||=
|
50
|
+
RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
|
51
|
+
factory_attrs
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def geographic?
|
56
|
+
@sql_type =~ /geography/
|
57
|
+
end
|
58
|
+
|
59
|
+
def spatial?
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def type
|
64
|
+
geographic? ? :geography : :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
|
+
RGeo::WKRep::WKBGenerator.new(hex_format: true, type_format: :ewkb, emit_ewkb_srid: true)
|
78
|
+
.generate(geo_value)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def cast_value(value)
|
84
|
+
return if value.nil?
|
85
|
+
|
86
|
+
value.is_a?(String) ? parse_wkt(value) : value
|
87
|
+
end
|
88
|
+
|
89
|
+
# convert WKT string into RGeo object
|
90
|
+
def parse_wkt(string)
|
91
|
+
wkt_parser(string).parse(string)
|
92
|
+
rescue RGeo::Error::ParseError
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def binary_string?(string)
|
97
|
+
string[0] == "\x00" || string[0] == "\x01" || string[0, 4] =~ /[0-9a-fA-F]{4}/
|
98
|
+
end
|
99
|
+
|
100
|
+
def wkt_parser(string)
|
101
|
+
if binary_string?(string)
|
102
|
+
RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid)
|
103
|
+
else
|
104
|
+
RGeo::WKRep::WKTParser.new(spatial_factory, support_ewkt: true, default_srid: @srid)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def factory_attrs
|
109
|
+
{
|
110
|
+
geo_type: @geo_type.underscore,
|
111
|
+
has_m: @has_m,
|
112
|
+
has_z: @has_z,
|
113
|
+
srid: @srid,
|
114
|
+
sql_type: type.to_s
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module CockroachDB
|
4
|
+
module OID
|
5
|
+
module TypeMapInitializer
|
6
|
+
# override
|
7
|
+
# Replaces the query with a faster version that doesn't rely on the
|
8
|
+
# use of 'array_in(cstring,oid,integer)'::regprocedure.
|
9
|
+
def query_conditions_for_initial_load
|
10
|
+
known_type_names = @store.keys.map { |n| "'#{n}'" }
|
11
|
+
known_type_types = %w('r' 'e' 'd')
|
12
|
+
<<~SQL % [known_type_names.join(", "), known_type_types.join(", ")]
|
13
|
+
WHERE
|
14
|
+
t.typname IN (%s)
|
15
|
+
OR t.typtype IN (%s)
|
16
|
+
OR (t.typarray = 0 AND t.typcategory='A')
|
17
|
+
OR t.typelem != 0
|
18
|
+
SQL
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
PostgreSQL::OID::TypeMapInitializer.prepend(TypeMapInitializer)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -14,10 +14,18 @@ module ActiveRecord
|
|
14
14
|
# always be strings. Then, we won't have to make any additional changes
|
15
15
|
# to ActiveRecord to support inserting integer values into string
|
16
16
|
# columns.
|
17
|
+
#
|
18
|
+
# For spatial types, data is stored as Well-known Binary (WKB) strings
|
19
|
+
# (https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary)
|
20
|
+
# but when creating objects, using RGeo features is more convenient than
|
21
|
+
# converting to WKB, so this does it automatically.
|
17
22
|
def _quote(value)
|
18
|
-
|
19
|
-
when Numeric
|
23
|
+
if value.is_a?(Numeric)
|
20
24
|
"'#{quote_string(value.to_s)}'"
|
25
|
+
elsif RGeo::Feature::Geometry.check_type(value)
|
26
|
+
"'#{RGeo::WKRep::WKBGenerator.new(hex_format: true, type_format: :ewkb, emit_ewkb_srid: true).generate(value)}'"
|
27
|
+
elsif value.is_a?(RGeo::Cartesian::BoundingBox)
|
28
|
+
"'#{value.min_x},#{value.min_y},#{value.max_x},#{value.max_y}'::box"
|
21
29
|
else
|
22
30
|
super
|
23
31
|
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
module SchemaStatements
|
5
5
|
include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
|
6
6
|
|
7
|
-
def add_index(table_name, column_name, options
|
7
|
+
def add_index(table_name, column_name, **options)
|
8
8
|
super
|
9
9
|
rescue ActiveRecord::StatementInvalid => error
|
10
10
|
if debugging? && error.cause.class == PG::FeatureNotSupported
|
@@ -39,8 +39,43 @@ module ActiveRecord
|
|
39
39
|
nil
|
40
40
|
end
|
41
41
|
|
42
|
+
# override
|
43
|
+
# https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L624
|
44
|
+
def new_column_from_field(table_name, field)
|
45
|
+
column_name, type, default, notnull, oid, fmod, collation, comment = field
|
46
|
+
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
|
47
|
+
default_value = extract_value_from_default(default)
|
48
|
+
default_function = extract_default_function(default_value, default)
|
49
|
+
|
50
|
+
serial =
|
51
|
+
if (match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/))
|
52
|
+
sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
|
53
|
+
end
|
54
|
+
|
55
|
+
# {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
|
56
|
+
spatial = spatial_column_info(table_name).get(column_name, type_metadata.sql_type)
|
57
|
+
|
58
|
+
PostgreSQL::Column.new(
|
59
|
+
column_name,
|
60
|
+
default_value,
|
61
|
+
type_metadata,
|
62
|
+
!notnull,
|
63
|
+
default_function,
|
64
|
+
collation: collation,
|
65
|
+
comment: comment.presence,
|
66
|
+
serial: serial,
|
67
|
+
spatial: spatial
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
42
71
|
# CockroachDB will use INT8 if the SQL type is INTEGER, so we make it use
|
43
72
|
# INT4 explicitly when needed.
|
73
|
+
#
|
74
|
+
# For spatial columns, include the limit to properly format the column name
|
75
|
+
# since type alone is not enough to format the column.
|
76
|
+
# Ex. type_to_sql(:geography, limit: "Point,4326")
|
77
|
+
# => "geography(Point,4326)"
|
78
|
+
#
|
44
79
|
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
|
45
80
|
sql = \
|
46
81
|
case type.to_s
|
@@ -52,6 +87,8 @@ module ActiveRecord
|
|
52
87
|
when 5..8; "int8"
|
53
88
|
else super
|
54
89
|
end
|
90
|
+
when "geometry", "geography"
|
91
|
+
"#{type}(#{limit})"
|
55
92
|
else
|
56
93
|
super
|
57
94
|
end
|
@@ -86,6 +123,34 @@ module ActiveRecord
|
|
86
123
|
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
87
124
|
end
|
88
125
|
end
|
126
|
+
|
127
|
+
# override
|
128
|
+
def native_database_types
|
129
|
+
# Add spatial types
|
130
|
+
super.merge(
|
131
|
+
geography: { name: "geography" },
|
132
|
+
geometry: { name: "geometry" },
|
133
|
+
geometry_collection: { name: "geometry_collection" },
|
134
|
+
line_string: { name: "line_string" },
|
135
|
+
multi_line_string: { name: "multi_line_string" },
|
136
|
+
multi_point: { name: "multi_point" },
|
137
|
+
multi_polygon: { name: "multi_polygon" },
|
138
|
+
spatial: { name: "geometry" },
|
139
|
+
st_point: { name: "st_point" },
|
140
|
+
st_polygon: { name: "st_polygon" }
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
# override
|
145
|
+
def create_table_definition(*args, **kwargs)
|
146
|
+
CockroachDB::TableDefinition.new(self, *args, **kwargs)
|
147
|
+
end
|
148
|
+
|
149
|
+
# memoize hash of column infos for tables
|
150
|
+
def spatial_column_info(table_name)
|
151
|
+
@spatial_column_info ||= {}
|
152
|
+
@spatial_column_info[table_name.to_sym] ||= SpatialColumnInfo.new(self, table_name.to_s)
|
153
|
+
end
|
89
154
|
end
|
90
155
|
end
|
91
156
|
end
|