activerecord-cockroachdb-adapter 6.0.0.pre.beta.5 → 6.1.0beta1
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/CONTRIBUTING.md +20 -0
- data/README.md +0 -6
- data/Rakefile +20 -0
- data/activerecord-cockroachdb-adapter.gemspec +3 -3
- data/lib/active_record/connection_adapters/cockroachdb/oid/interval.rb +126 -0
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/type.rb +1 -3
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +100 -154
- metadata +13 -14
- data/CHANGELOG.md +0 -25
- data/lib/active_record/connection_adapters/cockroachdb/oid/type_map_initializer.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d0954fd425d90342f04c1b6559b78e398be9e523e67779476d81f925a1aa611
|
4
|
+
data.tar.gz: 1f990c34784394bafa77933eb0d49965a0eac81151f2468c92a66c1bc9beb78a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3074426e81a83bb4ae2e50047c6db97dc75d9b8a822c3fd1153fb3403457bb56e7f36acb42802e5dd777c056dd0864db7dc26e875e7a644cb158f0a1c8001f3a
|
7
|
+
data.tar.gz: 8ff7c637812c1b262452ca1ef6ad8308a3f620a47e3798c6ef7b93096fedb362ce52372f6c0b870af1b475a31cf83f670f4e6d65498c231f5759a475820568af
|
data/CONTRIBUTING.md
CHANGED
@@ -78,6 +78,26 @@ RAILS_SOURCE="path/to/local_copy" bundle exec rake test
|
|
78
78
|
|
79
79
|
`test/config.yml` assumes CockroachDB will be running at localhost:26257 with a root user. Make changes to `test/config.yml` as needed.
|
80
80
|
|
81
|
+
### Run Tests from a Backup
|
82
|
+
|
83
|
+
Loading the full test schema every time a test runs can take a while, so for cases where loading the schema sequentially is unimportant, it is possible to use a backup to set up the database. This is significantly faster than the standard method and is provided to run individual tests faster, but should not be used to validate a build.
|
84
|
+
|
85
|
+
First create the template database.
|
86
|
+
|
87
|
+
```bash
|
88
|
+
bundle exec rake db:create_test_template
|
89
|
+
```
|
90
|
+
|
91
|
+
This will create a template database for the current version (ex. `activerecord_test_template611` for version 6.1.1) and create a `BACKUP` in the `nodelocal://self/activerecord-crdb-adapter/#{activerecord_version}` directory.
|
92
|
+
|
93
|
+
To load from the template, use the `COCKROACH_LOAD_FROM_TEMPLATE` flag.
|
94
|
+
|
95
|
+
```bash
|
96
|
+
COCKROACH_LOAD_FROM_TEMPLATE=1 TEST_FILES="test/cases/adapters/postgresql/ddl_test.rb" bundle exec rake test
|
97
|
+
```
|
98
|
+
|
99
|
+
And the `activerecord_unittest` database will use the `RESTORE` command to load the schema from the template database.
|
100
|
+
|
81
101
|
# Improvements
|
82
102
|
|
83
103
|
|
data/README.md
CHANGED
@@ -22,12 +22,6 @@ development:
|
|
22
22
|
user: <username>
|
23
23
|
```
|
24
24
|
|
25
|
-
## Configuration
|
26
|
-
|
27
|
-
In addition to the standard adapter settings, CockroachDB also supports the following:
|
28
|
-
|
29
|
-
- `use_follower_reads_for_type_introspection`: Use follower reads on queries to the `pg_type` catalog when set to `true`. This helps to speed up initialization by reading historical data, but may not find recently created user-defined types.
|
30
|
-
|
31
25
|
## Working with Spatial Data
|
32
26
|
|
33
27
|
The adapter uses [RGeo](https://github.com/rgeo/rgeo) and [RGeo-ActiveRecord](https://github.com/rgeo/rgeo-activerecord) to represent geometric and geographic data as Ruby objects and easily interface them with the adapter. The following is a brief introduction to RGeo and tips to help setup your spatial application. More documentation about RGeo can be found in the [YARD Docs](https://rubydoc.info/github/rgeo/rgeo) and [wiki](https://github.com/rgeo/rgeo/wiki).
|
data/Rakefile
CHANGED
@@ -2,10 +2,30 @@ require "bundler/gem_tasks"
|
|
2
2
|
require "rake/testtask"
|
3
3
|
require_relative 'test/support/paths_cockroachdb'
|
4
4
|
require_relative 'test/support/rake_helpers'
|
5
|
+
require_relative 'test/support/template_creator'
|
5
6
|
|
6
7
|
task test: ["test:cockroachdb"]
|
7
8
|
task default: [:test]
|
8
9
|
|
10
|
+
namespace :db do
|
11
|
+
task "create_test_template" do
|
12
|
+
ENV['DEBUG_COCKROACHDB_ADAPTER'] = "1"
|
13
|
+
ENV['COCKROACH_SKIP_LOAD_SCHEMA'] = "1"
|
14
|
+
ENV["ARCONN"] = "cockroachdb"
|
15
|
+
|
16
|
+
TemplateCreator.connect
|
17
|
+
require_relative 'test/cases/helper'
|
18
|
+
|
19
|
+
# TODO: look into this more, but for some reason the blob alias
|
20
|
+
# is not defined while running this task.
|
21
|
+
ActiveRecord::ConnectionAdapters::CockroachDB::TableDefinition.class_eval do
|
22
|
+
alias :blob :binary
|
23
|
+
end
|
24
|
+
|
25
|
+
TemplateCreator.create_test_template
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
9
29
|
namespace :test do
|
10
30
|
Rake::TestTask.new("cockroachdb") do |t|
|
11
31
|
t.libs = ARTest::CockroachDB.test_load_paths
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "activerecord-cockroachdb-adapter"
|
7
|
-
spec.version = "6.
|
7
|
+
spec.version = "6.1.0beta1"
|
8
8
|
spec.licenses = ["Apache-2.0"]
|
9
9
|
spec.authors = ["Cockroach Labs"]
|
10
10
|
spec.email = ["cockroach-db@googlegroups.com"]
|
@@ -13,8 +13,8 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.description = "Allows the use of CockroachDB as a backend for ActiveRecord and Rails apps."
|
14
14
|
spec.homepage = "https://github.com/cockroachdb/activerecord-cockroachdb-adapter"
|
15
15
|
|
16
|
-
spec.add_dependency "activerecord", "~> 6.
|
17
|
-
spec.add_dependency "pg", "
|
16
|
+
spec.add_dependency "activerecord", "~> 6.1"
|
17
|
+
spec.add_dependency "pg", "~> 1.2"
|
18
18
|
spec.add_dependency "rgeo-activerecord", "~> 7.0.0"
|
19
19
|
|
20
20
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
@@ -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
|
@@ -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
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
# transactions will be retried until they pass or the max retry limit is
|
10
10
|
# exceeded.
|
11
11
|
def within_new_transaction(isolation: nil, joinable: true, attempts: 0)
|
12
|
-
super
|
12
|
+
super(isolation: isolation, joinable: joinable)
|
13
13
|
rescue ActiveRecord::StatementInvalid => error
|
14
14
|
raise unless retryable? error
|
15
15
|
raise if attempts >= @connection.max_transaction_retries
|
@@ -1,12 +1,10 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Type
|
3
3
|
class << self
|
4
|
-
private
|
5
|
-
|
6
4
|
# Return :postgresql instead of :cockroachdb for current_adapter_name so
|
7
5
|
# we can continue using the ActiveRecord::Types defined in
|
8
6
|
# PostgreSQLAdapter.
|
9
|
-
def
|
7
|
+
def adapter_name_from(_model)
|
10
8
|
:postgresql
|
11
9
|
end
|
12
10
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "rgeo/active_record"
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
4
4
|
require "active_record/connection_adapters/cockroachdb/column_methods"
|
5
5
|
require "active_record/connection_adapters/cockroachdb/schema_statements"
|
6
6
|
require "active_record/connection_adapters/cockroachdb/referential_integrity"
|
@@ -13,8 +13,8 @@ require "active_record/connection_adapters/cockroachdb/attribute_methods"
|
|
13
13
|
require "active_record/connection_adapters/cockroachdb/column"
|
14
14
|
require "active_record/connection_adapters/cockroachdb/spatial_column_info"
|
15
15
|
require "active_record/connection_adapters/cockroachdb/setup"
|
16
|
-
require "active_record/connection_adapters/cockroachdb/oid/type_map_initializer"
|
17
16
|
require "active_record/connection_adapters/cockroachdb/oid/spatial"
|
17
|
+
require "active_record/connection_adapters/cockroachdb/oid/interval"
|
18
18
|
require "active_record/connection_adapters/cockroachdb/arel_tosql"
|
19
19
|
|
20
20
|
# Run to ignore spatial tables that will break schemna dumper.
|
@@ -25,25 +25,29 @@ module ActiveRecord
|
|
25
25
|
module ConnectionHandling
|
26
26
|
def cockroachdb_connection(config)
|
27
27
|
# This is copied from the PostgreSQL adapter.
|
28
|
-
conn_params = config.symbolize_keys
|
29
|
-
|
30
|
-
conn_params.delete_if { |_, v| v.nil? }
|
28
|
+
conn_params = config.symbolize_keys.compact
|
31
29
|
|
32
30
|
# Map ActiveRecords param names to PGs.
|
33
31
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
34
32
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
35
33
|
|
36
34
|
# Forward only valid config params to PG::Connection.connect.
|
37
|
-
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:
|
35
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
38
36
|
conn_params.slice!(*valid_conn_param_keys)
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
ConnectionAdapters::CockroachDBAdapter.new(
|
39
|
+
ConnectionAdapters::CockroachDBAdapter.new_client(conn_params),
|
40
|
+
logger,
|
41
|
+
conn_params,
|
42
|
+
config
|
43
|
+
)
|
44
|
+
# This rescue flow appears in new_client, but it is needed here as well
|
45
|
+
# since Cockroach will sometimes not raise until a query is made.
|
46
|
+
rescue ActiveRecord::StatementInvalid => error
|
47
|
+
if conn_params && conn_params[:dbname] && error.cause.message.include?(conn_params[:dbname])
|
44
48
|
raise ActiveRecord::NoDatabaseError
|
45
49
|
else
|
46
|
-
raise
|
50
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
@@ -77,56 +81,6 @@ module ActiveRecord
|
|
77
81
|
include CockroachDB::DatabaseStatements
|
78
82
|
include CockroachDB::Quoting
|
79
83
|
|
80
|
-
# override
|
81
|
-
# This method makes a sql query to gather information about columns
|
82
|
-
# in a table. It returns an array of arrays (one for each col) and
|
83
|
-
# passes each to the SchemaStatements#new_column_from_field method
|
84
|
-
# as the field parameter. This data is then used to format the column
|
85
|
-
# objects for the model and sent to the OID for data casting.
|
86
|
-
#
|
87
|
-
# The issue with the default method is that the sql_type field is
|
88
|
-
# retrieved with the `format_type` function, but this is implemented
|
89
|
-
# differently in CockroachDB than PostGIS, so geometry/geography
|
90
|
-
# types are missing information which makes parsing them impossible.
|
91
|
-
# Below is an example of what `format_type` returns for a geometry
|
92
|
-
# column.
|
93
|
-
#
|
94
|
-
# column_type: geometry(POINT, 4326)
|
95
|
-
# Expected: geometry(POINT, 4326)
|
96
|
-
# Actual: geometry
|
97
|
-
#
|
98
|
-
# The solution is to make the default query with super, then
|
99
|
-
# iterate through the columns and if it is a spatial type,
|
100
|
-
# access the proper column_type with the information_schema.columns
|
101
|
-
# table.
|
102
|
-
#
|
103
|
-
# @see: https://github.com/rails/rails/blob/8695b028261bdd244e254993255c6641bdbc17a5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L829
|
104
|
-
def column_definitions(table_name)
|
105
|
-
fields = super
|
106
|
-
# iterate through and identify all spatial fields based on format_type
|
107
|
-
# being geometry or geography, then query for the information_schema.column
|
108
|
-
# column_type because that contains the necessary information.
|
109
|
-
fields.map do |field|
|
110
|
-
dtype = field[1]
|
111
|
-
if dtype == 'geometry' || dtype == 'geography'
|
112
|
-
col_name = field[0]
|
113
|
-
data_type = \
|
114
|
-
query(<<~SQL, "SCHEMA")
|
115
|
-
SELECT c.data_type
|
116
|
-
FROM information_schema.columns c
|
117
|
-
WHERE c.table_name = #{quote(table_name)}
|
118
|
-
AND c.column_name = #{quote(col_name)}
|
119
|
-
SQL
|
120
|
-
field[1] = data_type[0][0]
|
121
|
-
end
|
122
|
-
field
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def arel_visitor
|
127
|
-
Arel::Visitors::CockroachDB.new(self)
|
128
|
-
end
|
129
|
-
|
130
84
|
def self.spatial_column_options(key)
|
131
85
|
SPATIAL_COLUMN_OPTIONS[key]
|
132
86
|
end
|
@@ -218,10 +172,6 @@ module ActiveRecord
|
|
218
172
|
@crdb_version >= 202
|
219
173
|
end
|
220
174
|
|
221
|
-
def supports_partitioned_indexes?
|
222
|
-
false
|
223
|
-
end
|
224
|
-
|
225
175
|
# This is hardcoded to 63 (as previously was in ActiveRecord 5.0) to aid in
|
226
176
|
# migration from PostgreSQL to CockroachDB. In practice, this limitation
|
227
177
|
# is arbitrary since CockroachDB supports index name lengths and table alias
|
@@ -281,19 +231,31 @@ module ActiveRecord
|
|
281
231
|
end
|
282
232
|
end
|
283
233
|
|
284
|
-
#
|
285
|
-
#
|
286
|
-
#
|
287
|
-
|
288
|
-
|
234
|
+
# Belongs after other types are defined because of issues described
|
235
|
+
# in this https://github.com/rails/rails/pull/38571
|
236
|
+
# Once that PR is merged, we can call super at the top.
|
237
|
+
super(m)
|
238
|
+
|
239
|
+
# Override numeric type. This is almost identical to the default,
|
240
|
+
# except that the conditional based on the fmod is changed.
|
241
|
+
m.register_type "numeric" do |_, fmod, sql_type|
|
289
242
|
precision = extract_precision(sql_type)
|
290
|
-
|
291
|
-
|
243
|
+
scale = extract_scale(sql_type)
|
244
|
+
|
245
|
+
# TODO(#178) this should never use DecimalWithoutScale since scale
|
246
|
+
# is assumed to be 0 if it is not explicitly defined.
|
247
|
+
#
|
248
|
+
# If fmod is -1, that means that precision is defined but not
|
249
|
+
# scale, or neither is defined.
|
250
|
+
if fmod && fmod == -1
|
251
|
+
# Below comment is from ActiveRecord
|
252
|
+
# FIXME: Remove this class, and the second argument to
|
253
|
+
# lookups on PG
|
254
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
255
|
+
else
|
256
|
+
OID::Decimal.new(precision: precision, scale: scale)
|
292
257
|
end
|
293
|
-
OID::SpecializedString.new(:interval, precision: precision)
|
294
258
|
end
|
295
|
-
|
296
|
-
super(m)
|
297
259
|
end
|
298
260
|
|
299
261
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -404,93 +366,77 @@ module ActiveRecord
|
|
404
366
|
end
|
405
367
|
|
406
368
|
# override
|
407
|
-
# This method
|
408
|
-
#
|
369
|
+
# This method makes a query to gather information about columns
|
370
|
+
# in a table. It returns an array of arrays (one for each col) and
|
371
|
+
# passes each to the SchemaStatements#new_column_from_field method
|
372
|
+
# as the field parameter. This data is then used to format the column
|
373
|
+
# objects for the model and sent to the OID for data casting.
|
374
|
+
#
|
375
|
+
# Sometimes there are differences between how data is formatted
|
376
|
+
# in Postgres and CockroachDB, so additional queries for certain types
|
377
|
+
# may be necessary to properly form the column definition.
|
409
378
|
#
|
410
|
-
#
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
379
|
+
# @see: https://github.com/rails/rails/blob/8695b028261bdd244e254993255c6641bdbc17a5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L829
|
380
|
+
def column_definitions(table_name)
|
381
|
+
fields = super
|
382
|
+
|
383
|
+
# Use regex comparison because if a type is an array it will
|
384
|
+
# have [] appended to the end of it.
|
385
|
+
target_types = [
|
386
|
+
/geometry/,
|
387
|
+
/geography/,
|
388
|
+
/interval/,
|
389
|
+
/numeric/
|
390
|
+
]
|
391
|
+
re = Regexp.union(target_types)
|
392
|
+
fields.map do |field|
|
393
|
+
dtype = field[1]
|
394
|
+
if re.match(dtype)
|
395
|
+
crdb_column_definition(field, table_name)
|
424
396
|
else
|
425
|
-
|
426
|
-
end
|
427
|
-
|
428
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
429
|
-
initializer.run(records)
|
397
|
+
field
|
430
398
|
end
|
431
|
-
else
|
432
|
-
super
|
433
399
|
end
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
400
|
+
end
|
401
|
+
|
402
|
+
# Use the crdb_sql_type instead of the sql_type returned by
|
403
|
+
# column_definitions. This will include limit,
|
404
|
+
# precision, and scale information in the type.
|
405
|
+
# Ex. geometry -> geometry(point, 4326)
|
406
|
+
def crdb_column_definition(field, table_name)
|
407
|
+
col_name = field[0]
|
408
|
+
data_type = \
|
409
|
+
query(<<~SQL, "SCHEMA")
|
410
|
+
SELECT c.crdb_sql_type
|
411
|
+
FROM information_schema.columns c
|
412
|
+
WHERE c.table_name = #{quote(table_name)}
|
413
|
+
AND c.column_name = #{quote(col_name)}
|
414
|
+
SQL
|
415
|
+
field[1] = data_type[0][0].downcase
|
416
|
+
field
|
438
417
|
end
|
439
418
|
|
440
419
|
# override
|
441
|
-
# This method
|
420
|
+
# This method is used to determine if a
|
421
|
+
# FEATURE_NOT_SUPPORTED error from the PG gem should
|
422
|
+
# be an ActiveRecord::PreparedStatementCacheExpired
|
423
|
+
# error.
|
442
424
|
#
|
443
|
-
#
|
444
|
-
#
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
"bool" => PG::TextDecoder::Boolean,
|
459
|
-
"timestamp" => PG::TextDecoder::TimestampUtc,
|
460
|
-
"timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
|
461
|
-
}
|
462
|
-
|
463
|
-
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
464
|
-
query = <<~SQL % known_coder_types.join(", ")
|
465
|
-
SELECT t.oid, t.typname
|
466
|
-
FROM pg_type as t AS OF SYSTEM TIME '-10s'
|
467
|
-
WHERE t.typname IN (%s)
|
468
|
-
SQL
|
469
|
-
|
470
|
-
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
471
|
-
result
|
472
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
473
|
-
.compact
|
474
|
-
end
|
475
|
-
|
476
|
-
map = PG::TypeMapByOid.new
|
477
|
-
coders.each { |coder| map.add_coder(coder) }
|
478
|
-
@connection.type_map_for_results = map
|
479
|
-
|
480
|
-
@type_map_for_results = PG::TypeMapByOid.new
|
481
|
-
@type_map_for_results.default_type_map = map
|
482
|
-
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
483
|
-
|
484
|
-
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
485
|
-
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
486
|
-
update_typemap_for_default_timezone
|
487
|
-
else
|
488
|
-
super
|
489
|
-
end
|
490
|
-
rescue ActiveRecord::StatementInvalid => e
|
491
|
-
raise e unless e.cause.is_a? PG::InvalidCatalogName
|
492
|
-
# use original if database is younger than 10s
|
493
|
-
super
|
425
|
+
# ActiveRecord handles this by checking that the sql state matches the
|
426
|
+
# FEATURE_NOT_SUPPORTED code and that the source function
|
427
|
+
# is "RevalidateCachedQuery" since that is the only function
|
428
|
+
# in postgres that will create this error.
|
429
|
+
#
|
430
|
+
# That method will not work for CockroachDB because the error
|
431
|
+
# originates from the "runExecBuilder" function, so we need
|
432
|
+
# to modify the original to match the CockroachDB behavior.
|
433
|
+
def is_cached_plan_failure?(e)
|
434
|
+
pgerror = e.cause
|
435
|
+
|
436
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
437
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "runExecBuilder"
|
438
|
+
rescue
|
439
|
+
false
|
494
440
|
end
|
495
441
|
|
496
442
|
def arel_visitor
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-cockroachdb-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.0beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cockroach Labs
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
19
|
+
version: '6.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.
|
26
|
+
version: '6.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rgeo-activerecord
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,7 +62,6 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- ".gitignore"
|
64
64
|
- ".gitmodules"
|
65
|
-
- CHANGELOG.md
|
66
65
|
- CONTRIBUTING.md
|
67
66
|
- Gemfile
|
68
67
|
- LICENSE
|
@@ -82,8 +81,8 @@ files:
|
|
82
81
|
- lib/active_record/connection_adapters/cockroachdb/column_methods.rb
|
83
82
|
- lib/active_record/connection_adapters/cockroachdb/database_statements.rb
|
84
83
|
- lib/active_record/connection_adapters/cockroachdb/database_tasks.rb
|
84
|
+
- lib/active_record/connection_adapters/cockroachdb/oid/interval.rb
|
85
85
|
- lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb
|
86
|
-
- lib/active_record/connection_adapters/cockroachdb/oid/type_map_initializer.rb
|
87
86
|
- lib/active_record/connection_adapters/cockroachdb/quoting.rb
|
88
87
|
- lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb
|
89
88
|
- lib/active_record/connection_adapters/cockroachdb/schema_statements.rb
|
@@ -99,7 +98,7 @@ licenses:
|
|
99
98
|
- Apache-2.0
|
100
99
|
metadata:
|
101
100
|
allowed_push_host: https://rubygems.org
|
102
|
-
post_install_message:
|
101
|
+
post_install_message:
|
103
102
|
rdoc_options: []
|
104
103
|
require_paths:
|
105
104
|
- lib
|
@@ -114,8 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
113
|
- !ruby/object:Gem::Version
|
115
114
|
version: 1.3.1
|
116
115
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
118
|
-
signing_key:
|
116
|
+
rubygems_version: 3.0.3
|
117
|
+
signing_key:
|
119
118
|
specification_version: 4
|
120
119
|
summary: CockroachDB adapter for ActiveRecord.
|
121
120
|
test_files: []
|
data/CHANGELOG.md
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
## 6.0.0-beta.5 - 2021-04-02
|
4
|
-
|
5
|
-
- Added a configuration option named `use_follower_reads_for_type_introspection`.
|
6
|
-
If true, it improves the speed of type introspection by allowing potentially stale
|
7
|
-
type metadata to be read. Defaults to false.
|
8
|
-
|
9
|
-
## 6.0.0-beta.4 - 2021-03-06
|
10
|
-
|
11
|
-
- Improved connection performance by refactoring an introspection
|
12
|
-
that loads types.
|
13
|
-
- Changed version numbers to semver.
|
14
|
-
|
15
|
-
## 6.0.0beta3
|
16
|
-
|
17
|
-
- Added support for spatial features.
|
18
|
-
|
19
|
-
## 6.0.0beta2
|
20
|
-
|
21
|
-
- Updated transaction retry logic to work with Rails 6.
|
22
|
-
|
23
|
-
## 6.0.0beta1
|
24
|
-
|
25
|
-
- Initial support for Rails 6.
|
@@ -1,26 +0,0 @@
|
|
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
|