activerecord-cockroachdb-adapter 6.0.0 → 6.1.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/CHANGELOG.md +6 -13
- data/CONTRIBUTING.md +20 -0
- data/README.md +5 -3
- 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 +130 -86
- metadata +13 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e12a061e79d389fd1c698456bf368bb8a2971d3c8e1580242aadb669bfa30809
|
4
|
+
data.tar.gz: e12b269976f47ff593b3ece0f7848e56b76dda1d6a797dc826c483d301dc3082
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52a6c9404a44dac790e2e7c61548d703d53f27c9654d492e9e58ba2b0f20cd7d93f9c92d26391038eccb590695b71c90d15872d44e299fd7a4e6e3f1185a2986
|
7
|
+
data.tar.gz: fff215842d13491523a7001273df0abec8a95b31294c71b6873f1c604e77f3b43d98beac514bca747ef8bd55aec9160e2ff47cfb7181eac1e230a8a9d16ec6a7
|
data/CHANGELOG.md
CHANGED
@@ -1,31 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## 6.
|
3
|
+
## 6.1.0 - 2021-04-26
|
4
4
|
|
5
5
|
- Add a telemetry query on start-up. This helps the Cockroach Labs team
|
6
6
|
prioritize support for the adapter. It can be disabled by setting the
|
7
7
|
`disable_cockroachdb_telemetry` configuration option to false.
|
8
8
|
|
9
|
-
## 6.
|
9
|
+
## 6.1.0-beta.3 - 2021-04-02
|
10
10
|
|
11
11
|
- Added a configuration option named `use_follower_reads_for_type_introspection`.
|
12
12
|
If true, it improves the speed of type introspection by allowing potentially stale
|
13
13
|
type metadata to be read. Defaults to false.
|
14
14
|
|
15
|
-
## 6.
|
15
|
+
## 6.1.0-beta.2 - 2021-03-06
|
16
16
|
|
17
17
|
- Improved connection performance by refactoring an introspection
|
18
18
|
that loads types.
|
19
19
|
- Changed version numbers to semver.
|
20
20
|
|
21
|
-
## 6.
|
21
|
+
## 6.1.0beta1
|
22
22
|
|
23
|
-
-
|
24
|
-
|
25
|
-
## 6.0.0beta2
|
26
|
-
|
27
|
-
- Updated transaction retry logic to work with Rails 6.
|
28
|
-
|
29
|
-
## 6.0.0beta1
|
30
|
-
|
31
|
-
- Initial support for Rails 6.
|
23
|
+
- Initial support for Rails 6.1.
|
24
|
+
- Support for spatial functionality.
|
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
@@ -1,16 +1,18 @@
|
|
1
1
|
# ActiveRecord CockroachDB Adapter
|
2
2
|
|
3
|
-
CockroachDB adapter for ActiveRecord
|
3
|
+
CockroachDB adapter for ActiveRecord 5 and 6. This is a lightweight extension of the PostgreSQL adapter that establishes compatibility with [CockroachDB](https://github.com/cockroachdb/cockroach).
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
7
|
Add this line to your project's Gemfile:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem 'activerecord-cockroachdb-adapter', '~>
|
10
|
+
gem 'activerecord-cockroachdb-adapter', '~> 6.1.0'
|
11
11
|
```
|
12
12
|
|
13
|
-
If you're using Rails
|
13
|
+
If you're using Rails 5.2, use the `5.2.x` versions of this gem.
|
14
|
+
|
15
|
+
If you're using Rails 6.0, use the `6.0.x` versions of this gem.
|
14
16
|
|
15
17
|
In `database.yml`, use the following adapter setting:
|
16
18
|
|
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.1"
|
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"
|
@@ -15,6 +15,7 @@ require "active_record/connection_adapters/cockroachdb/spatial_column_info"
|
|
15
15
|
require "active_record/connection_adapters/cockroachdb/setup"
|
16
16
|
require "active_record/connection_adapters/cockroachdb/oid/type_map_initializer"
|
17
17
|
require "active_record/connection_adapters/cockroachdb/oid/spatial"
|
18
|
+
require "active_record/connection_adapters/cockroachdb/oid/interval"
|
18
19
|
require "active_record/connection_adapters/cockroachdb/arel_tosql"
|
19
20
|
|
20
21
|
# Run to ignore spatial tables that will break schemna dumper.
|
@@ -25,25 +26,29 @@ module ActiveRecord
|
|
25
26
|
module ConnectionHandling
|
26
27
|
def cockroachdb_connection(config)
|
27
28
|
# This is copied from the PostgreSQL adapter.
|
28
|
-
conn_params = config.symbolize_keys
|
29
|
-
|
30
|
-
conn_params.delete_if { |_, v| v.nil? }
|
29
|
+
conn_params = config.symbolize_keys.compact
|
31
30
|
|
32
31
|
# Map ActiveRecords param names to PGs.
|
33
32
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
34
33
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
35
34
|
|
36
35
|
# Forward only valid config params to PG::Connection.connect.
|
37
|
-
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:
|
36
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
38
37
|
conn_params.slice!(*valid_conn_param_keys)
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
ConnectionAdapters::CockroachDBAdapter.new(
|
40
|
+
ConnectionAdapters::CockroachDBAdapter.new_client(conn_params),
|
41
|
+
logger,
|
42
|
+
conn_params,
|
43
|
+
config
|
44
|
+
)
|
45
|
+
# This rescue flow appears in new_client, but it is needed here as well
|
46
|
+
# since Cockroach will sometimes not raise until a query is made.
|
47
|
+
rescue ActiveRecord::StatementInvalid => error
|
48
|
+
if conn_params && conn_params[:dbname] && error.cause.message.include?(conn_params[:dbname])
|
44
49
|
raise ActiveRecord::NoDatabaseError
|
45
50
|
else
|
46
|
-
raise
|
51
|
+
raise ActiveRecord::ConnectionNotEstablished, error.message
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -52,24 +57,29 @@ end
|
|
52
57
|
module ActiveRecord
|
53
58
|
module ConnectionAdapters
|
54
59
|
module CockroachDBConnectionPool
|
55
|
-
def initialize(
|
56
|
-
super(
|
57
|
-
disable_telemetry =
|
58
|
-
adapter =
|
60
|
+
def initialize(pool_config)
|
61
|
+
super(pool_config)
|
62
|
+
disable_telemetry = pool_config.db_config.configuration_hash[:disable_cockroachdb_telemetry]
|
63
|
+
adapter = pool_config.db_config.configuration_hash[:adapter]
|
59
64
|
return if disable_telemetry || adapter != "cockroachdb"
|
60
65
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
|
67
|
+
begin
|
68
|
+
with_connection do |conn|
|
69
|
+
if conn.active?
|
70
|
+
begin
|
71
|
+
query = "SELECT crdb_internal.increment_feature_counter('ActiveRecord %d.%d')"
|
72
|
+
conn.execute(query % [ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR])
|
73
|
+
rescue ActiveRecord::StatementInvalid
|
74
|
+
# The increment_feature_counter built-in is not supported on this
|
75
|
+
# CockroachDB version. Ignore.
|
76
|
+
rescue StandardError => e
|
77
|
+
conn.logger.warn "Unexpected error when incrementing feature counter: #{e}"
|
78
|
+
end
|
71
79
|
end
|
72
80
|
end
|
81
|
+
rescue ActiveRecord::NoDatabaseError
|
82
|
+
# Prevent failures on db creation and parallel testing.
|
73
83
|
end
|
74
84
|
end
|
75
85
|
end
|
@@ -93,7 +103,7 @@ module ActiveRecord
|
|
93
103
|
st_polygon: {},
|
94
104
|
}
|
95
105
|
|
96
|
-
# http://postgis.17.x6.nabble.com/Default-SRID-td5001115.html
|
106
|
+
# http://postgis.17.x6.nabble.com/Default-SRID-td5001115.html
|
97
107
|
DEFAULT_SRID = 0
|
98
108
|
|
99
109
|
include CockroachDB::SchemaStatements
|
@@ -101,56 +111,6 @@ module ActiveRecord
|
|
101
111
|
include CockroachDB::DatabaseStatements
|
102
112
|
include CockroachDB::Quoting
|
103
113
|
|
104
|
-
# override
|
105
|
-
# This method makes a sql query to gather information about columns
|
106
|
-
# in a table. It returns an array of arrays (one for each col) and
|
107
|
-
# passes each to the SchemaStatements#new_column_from_field method
|
108
|
-
# as the field parameter. This data is then used to format the column
|
109
|
-
# objects for the model and sent to the OID for data casting.
|
110
|
-
#
|
111
|
-
# The issue with the default method is that the sql_type field is
|
112
|
-
# retrieved with the `format_type` function, but this is implemented
|
113
|
-
# differently in CockroachDB than PostGIS, so geometry/geography
|
114
|
-
# types are missing information which makes parsing them impossible.
|
115
|
-
# Below is an example of what `format_type` returns for a geometry
|
116
|
-
# column.
|
117
|
-
#
|
118
|
-
# column_type: geometry(POINT, 4326)
|
119
|
-
# Expected: geometry(POINT, 4326)
|
120
|
-
# Actual: geometry
|
121
|
-
#
|
122
|
-
# The solution is to make the default query with super, then
|
123
|
-
# iterate through the columns and if it is a spatial type,
|
124
|
-
# access the proper column_type with the information_schema.columns
|
125
|
-
# table.
|
126
|
-
#
|
127
|
-
# @see: https://github.com/rails/rails/blob/8695b028261bdd244e254993255c6641bdbc17a5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L829
|
128
|
-
def column_definitions(table_name)
|
129
|
-
fields = super
|
130
|
-
# iterate through and identify all spatial fields based on format_type
|
131
|
-
# being geometry or geography, then query for the information_schema.column
|
132
|
-
# column_type because that contains the necessary information.
|
133
|
-
fields.map do |field|
|
134
|
-
dtype = field[1]
|
135
|
-
if dtype == 'geometry' || dtype == 'geography'
|
136
|
-
col_name = field[0]
|
137
|
-
data_type = \
|
138
|
-
query(<<~SQL, "SCHEMA")
|
139
|
-
SELECT c.data_type
|
140
|
-
FROM information_schema.columns c
|
141
|
-
WHERE c.table_name = #{quote(table_name)}
|
142
|
-
AND c.column_name = #{quote(col_name)}
|
143
|
-
SQL
|
144
|
-
field[1] = data_type[0][0]
|
145
|
-
end
|
146
|
-
field
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def arel_visitor
|
151
|
-
Arel::Visitors::CockroachDB.new(self)
|
152
|
-
end
|
153
|
-
|
154
114
|
def self.spatial_column_options(key)
|
155
115
|
SPATIAL_COLUMN_OPTIONS[key]
|
156
116
|
end
|
@@ -306,19 +266,28 @@ module ActiveRecord
|
|
306
266
|
end
|
307
267
|
end
|
308
268
|
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
|
313
|
-
|
269
|
+
# Belongs after other types are defined because of issues described
|
270
|
+
# in this https://github.com/rails/rails/pull/38571
|
271
|
+
# Once that PR is merged, we can call super at the top.
|
272
|
+
super(m)
|
273
|
+
|
274
|
+
# Override numeric type. This is almost identical to the default,
|
275
|
+
# except that the conditional based on the fmod is changed.
|
276
|
+
m.register_type "numeric" do |_, fmod, sql_type|
|
314
277
|
precision = extract_precision(sql_type)
|
315
|
-
|
316
|
-
|
278
|
+
scale = extract_scale(sql_type)
|
279
|
+
|
280
|
+
# If fmod is -1, that means that precision is defined but not
|
281
|
+
# scale, or neither is defined.
|
282
|
+
if fmod && fmod == -1 && !precision.nil?
|
283
|
+
# Below comment is from ActiveRecord
|
284
|
+
# FIXME: Remove this class, and the second argument to
|
285
|
+
# lookups on PG
|
286
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
287
|
+
else
|
288
|
+
OID::Decimal.new(precision: precision, scale: scale)
|
317
289
|
end
|
318
|
-
OID::SpecializedString.new(:interval, precision: precision)
|
319
290
|
end
|
320
|
-
|
321
|
-
super(m)
|
322
291
|
end
|
323
292
|
|
324
293
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -423,11 +392,85 @@ module ActiveRecord
|
|
423
392
|
# In general, it is hard to parse that, but it is easy to handle the common
|
424
393
|
# case of an empty array.
|
425
394
|
def extract_empty_array_from_default(default)
|
426
|
-
return unless supports_string_to_array_coercion?
|
395
|
+
return unless supports_string_to_array_coercion?
|
427
396
|
return unless default =~ /\AARRAY\[\]\z/
|
428
397
|
return "{}"
|
429
398
|
end
|
430
399
|
|
400
|
+
# override
|
401
|
+
# This method makes a query to gather information about columns
|
402
|
+
# in a table. It returns an array of arrays (one for each col) and
|
403
|
+
# passes each to the SchemaStatements#new_column_from_field method
|
404
|
+
# as the field parameter. This data is then used to format the column
|
405
|
+
# objects for the model and sent to the OID for data casting.
|
406
|
+
#
|
407
|
+
# Sometimes there are differences between how data is formatted
|
408
|
+
# in Postgres and CockroachDB, so additional queries for certain types
|
409
|
+
# may be necessary to properly form the column definition.
|
410
|
+
#
|
411
|
+
# @see: https://github.com/rails/rails/blob/8695b028261bdd244e254993255c6641bdbc17a5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L829
|
412
|
+
def column_definitions(table_name)
|
413
|
+
fields = super
|
414
|
+
|
415
|
+
# Use regex comparison because if a type is an array it will
|
416
|
+
# have [] appended to the end of it.
|
417
|
+
target_types = [
|
418
|
+
/geometry/,
|
419
|
+
/geography/,
|
420
|
+
/interval/,
|
421
|
+
/numeric/
|
422
|
+
]
|
423
|
+
re = Regexp.union(target_types)
|
424
|
+
fields.map do |field|
|
425
|
+
dtype = field[1]
|
426
|
+
if re.match(dtype)
|
427
|
+
crdb_column_definition(field, table_name)
|
428
|
+
else
|
429
|
+
field
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# Use the crdb_sql_type instead of the sql_type returned by
|
435
|
+
# column_definitions. This will include limit,
|
436
|
+
# precision, and scale information in the type.
|
437
|
+
# Ex. geometry -> geometry(point, 4326)
|
438
|
+
def crdb_column_definition(field, table_name)
|
439
|
+
col_name = field[0]
|
440
|
+
data_type = \
|
441
|
+
query(<<~SQL, "SCHEMA")
|
442
|
+
SELECT c.crdb_sql_type
|
443
|
+
FROM information_schema.columns c
|
444
|
+
WHERE c.table_name = #{quote(table_name)}
|
445
|
+
AND c.column_name = #{quote(col_name)}
|
446
|
+
SQL
|
447
|
+
field[1] = data_type[0][0].downcase
|
448
|
+
field
|
449
|
+
end
|
450
|
+
|
451
|
+
# override
|
452
|
+
# This method is used to determine if a
|
453
|
+
# FEATURE_NOT_SUPPORTED error from the PG gem should
|
454
|
+
# be an ActiveRecord::PreparedStatementCacheExpired
|
455
|
+
# error.
|
456
|
+
#
|
457
|
+
# ActiveRecord handles this by checking that the sql state matches the
|
458
|
+
# FEATURE_NOT_SUPPORTED code and that the source function
|
459
|
+
# is "RevalidateCachedQuery" since that is the only function
|
460
|
+
# in postgres that will create this error.
|
461
|
+
#
|
462
|
+
# That method will not work for CockroachDB because the error
|
463
|
+
# originates from the "runExecBuilder" function, so we need
|
464
|
+
# to modify the original to match the CockroachDB behavior.
|
465
|
+
def is_cached_plan_failure?(e)
|
466
|
+
pgerror = e.cause
|
467
|
+
|
468
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
469
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "runExecBuilder"
|
470
|
+
rescue
|
471
|
+
false
|
472
|
+
end
|
473
|
+
|
431
474
|
# override
|
432
475
|
# This method loads info about data types from the database to
|
433
476
|
# populate the TypeMap.
|
@@ -505,6 +548,7 @@ module ActiveRecord
|
|
505
548
|
@type_map_for_results = PG::TypeMapByOid.new
|
506
549
|
@type_map_for_results.default_type_map = map
|
507
550
|
@type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
|
551
|
+
@type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
|
508
552
|
|
509
553
|
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
510
554
|
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
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.1
|
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-05-14 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
|
@@ -82,6 +82,7 @@ files:
|
|
82
82
|
- lib/active_record/connection_adapters/cockroachdb/column_methods.rb
|
83
83
|
- lib/active_record/connection_adapters/cockroachdb/database_statements.rb
|
84
84
|
- lib/active_record/connection_adapters/cockroachdb/database_tasks.rb
|
85
|
+
- lib/active_record/connection_adapters/cockroachdb/oid/interval.rb
|
85
86
|
- lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb
|
86
87
|
- lib/active_record/connection_adapters/cockroachdb/oid/type_map_initializer.rb
|
87
88
|
- lib/active_record/connection_adapters/cockroachdb/quoting.rb
|
@@ -99,7 +100,7 @@ licenses:
|
|
99
100
|
- Apache-2.0
|
100
101
|
metadata:
|
101
102
|
allowed_push_host: https://rubygems.org
|
102
|
-
post_install_message:
|
103
|
+
post_install_message:
|
103
104
|
rdoc_options: []
|
104
105
|
require_paths:
|
105
106
|
- lib
|
@@ -114,8 +115,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
115
|
- !ruby/object:Gem::Version
|
115
116
|
version: '0'
|
116
117
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
118
|
-
signing_key:
|
118
|
+
rubygems_version: 3.0.3
|
119
|
+
signing_key:
|
119
120
|
specification_version: 4
|
120
121
|
summary: CockroachDB adapter for ActiveRecord.
|
121
122
|
test_files: []
|