activerecord-cockroachdb-adapter 7.2.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/issue_template.md +46 -0
- data/.github/reproduction_scripts/migrations.rb +60 -0
- data/.github/reproduction_scripts/models_and_database.rb +49 -0
- data/.github/workflows/ci.yml +6 -3
- data/CHANGELOG.md +8 -0
- data/CONTRIBUTING.md +4 -3
- data/Gemfile +1 -1
- data/README.md +3 -3
- data/activerecord-cockroachdb-adapter.gemspec +2 -2
- data/bin/console +6 -4
- data/bin/console_schemas/default.rb +2 -0
- data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +8 -0
- data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +85 -9
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +31 -26
- data/lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb +21 -0
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +38 -23
- data/lib/active_record/relation/query_methods_ext.rb +3 -3
- data/lib/version.rb +1 -1
- metadata +13 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9721532ff04318993f0835604dff8899289ee81e84e0f6daff67ad8a0024b608
|
4
|
+
data.tar.gz: 82db81fab9b8358ea7953bfd7a9a3863794ea0946b56dfd5b57aefe7f2951eba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6089da8ff8fcbb51435f6c408c663fe31d76c2652e7053b1f074ca36769ef88e99356a25b3530eb36dc2dbbc93e3a1db14d52ef76323abda301a093ed0217200
|
7
|
+
data.tar.gz: c236373fdc39156b05aa546b596eda2aba501de27f9a486947fc4ec25a8f871210e436675e191d0b369ad1d159dd851544d0df538725f1bef18b3fc2efb1354f
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<!-- NOTE: This template is copying most of the Rails repo template -->
|
2
|
+
|
3
|
+
### Steps to reproduce
|
4
|
+
|
5
|
+
<!-- (Guidelines for creating a bug report are [available
|
6
|
+
here](https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#creating-a-bug-report)) -->
|
7
|
+
|
8
|
+
<!-- Paste your executable test case created from one of the scripts found [here](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/tree/master/.github/reproduction_scripts) below: -->
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
# Your reproduction script goes here
|
12
|
+
```
|
13
|
+
|
14
|
+
### Expected behavior
|
15
|
+
|
16
|
+
<!-- Tell us what should happen -->
|
17
|
+
|
18
|
+
### Actual behavior
|
19
|
+
|
20
|
+
<!-- Tell us what happens instead -->
|
21
|
+
|
22
|
+
### System configuration
|
23
|
+
|
24
|
+
<!-- Either fill manually or paste the output of this script within code blocks:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
bundle info rails | head -1 &&
|
28
|
+
ruby -v &&
|
29
|
+
bundle info activerecord-cockroachdb-adapter | head -1 &&
|
30
|
+
cockroach --version
|
31
|
+
```
|
32
|
+
-->
|
33
|
+
|
34
|
+
<!--
|
35
|
+
```
|
36
|
+
# output here (and uncomment!)
|
37
|
+
```
|
38
|
+
-->
|
39
|
+
|
40
|
+
**Rails version**:
|
41
|
+
|
42
|
+
**Ruby version**:
|
43
|
+
|
44
|
+
**Adapter version**:
|
45
|
+
|
46
|
+
**CockroachDB version**:
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Adapted from https://github.com/rails/rails/blob/main/guides/bug_report_templates/active_record_migrations.rb
|
4
|
+
|
5
|
+
require "bundler/inline"
|
6
|
+
|
7
|
+
gemfile(true) do
|
8
|
+
source "https://rubygems.org"
|
9
|
+
|
10
|
+
gem "activerecord"
|
11
|
+
|
12
|
+
gem "activerecord-cockroachdb-adapter"
|
13
|
+
end
|
14
|
+
|
15
|
+
require "activerecord-cockroachdb-adapter"
|
16
|
+
require "minitest/autorun"
|
17
|
+
require "logger"
|
18
|
+
|
19
|
+
# You might want to change the database name for another one.
|
20
|
+
ActiveRecord::Base.establish_connection("cockroachdb://root@localhost:26257/defaultdb")
|
21
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
22
|
+
|
23
|
+
ActiveRecord::Schema.define do
|
24
|
+
create_table :payments, force: true do |t|
|
25
|
+
t.decimal :amount, precision: 10, scale: 0, default: 0, null: false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Payment < ActiveRecord::Base
|
30
|
+
end
|
31
|
+
|
32
|
+
class ChangeAmountToAddScale < ActiveRecord::Migration::Current # or use a specific version via `Migration[number]`
|
33
|
+
def change
|
34
|
+
reversible do |dir|
|
35
|
+
dir.up do
|
36
|
+
change_column :payments, :amount, :decimal, precision: 10, scale: 2
|
37
|
+
end
|
38
|
+
|
39
|
+
dir.down do
|
40
|
+
change_column :payments, :amount, :decimal, precision: 10, scale: 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class BugTest < ActiveSupport::TestCase
|
47
|
+
def test_migration_up
|
48
|
+
ChangeAmountToAddScale.migrate(:up)
|
49
|
+
Payment.reset_column_information
|
50
|
+
|
51
|
+
assert_equal "decimal(10,2)", Payment.columns.last.sql_type
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_migration_down
|
55
|
+
ChangeAmountToAddScale.migrate(:down)
|
56
|
+
Payment.reset_column_information
|
57
|
+
|
58
|
+
assert_equal "decimal(10)", Payment.columns.last.sql_type
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Adapted from https://github.com/rails/rails/blob/main/guides/bug_report_templates/active_record.rb
|
4
|
+
|
5
|
+
require "bundler/inline"
|
6
|
+
|
7
|
+
gemfile(true) do
|
8
|
+
source "https://rubygems.org"
|
9
|
+
|
10
|
+
gem "activerecord"
|
11
|
+
|
12
|
+
gem "activerecord-cockroachdb-adapter"
|
13
|
+
end
|
14
|
+
|
15
|
+
require "activerecord-cockroachdb-adapter"
|
16
|
+
require "minitest/autorun"
|
17
|
+
require "logger"
|
18
|
+
|
19
|
+
# You might want to change the database name for another one.
|
20
|
+
ActiveRecord::Base.establish_connection("cockroachdb://root@localhost:26257/defaultdb")
|
21
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
22
|
+
|
23
|
+
ActiveRecord::Schema.define do
|
24
|
+
create_table :posts, force: true do |t|
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :comments, force: true do |t|
|
28
|
+
t.integer :post_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Post < ActiveRecord::Base
|
33
|
+
has_many :comments
|
34
|
+
end
|
35
|
+
|
36
|
+
class Comment < ActiveRecord::Base
|
37
|
+
belongs_to :post
|
38
|
+
end
|
39
|
+
|
40
|
+
class BugTest < ActiveSupport::TestCase
|
41
|
+
def test_association_stuff
|
42
|
+
post = Post.create!
|
43
|
+
post.comments << Comment.create!
|
44
|
+
|
45
|
+
assert_equal 1, post.comments.count
|
46
|
+
assert_equal 1, Comment.count
|
47
|
+
assert_equal post.id, Comment.first.post.id
|
48
|
+
end
|
49
|
+
end
|
data/.github/workflows/ci.yml
CHANGED
@@ -40,8 +40,8 @@ jobs:
|
|
40
40
|
fail-fast: false
|
41
41
|
matrix:
|
42
42
|
# https://www.cockroachlabs.com/docs/releases/release-support-policy
|
43
|
-
crdb: [v23.2, v24.1, v24.
|
44
|
-
ruby: ["3.
|
43
|
+
crdb: [v23.2, v24.1, v24.3]
|
44
|
+
ruby: ["3.4"]
|
45
45
|
name: Test (crdb=${{ matrix.crdb }} ruby=${{ matrix.ruby }})
|
46
46
|
steps:
|
47
47
|
- name: Set Up Actions
|
@@ -88,4 +88,7 @@ jobs:
|
|
88
88
|
done
|
89
89
|
cat ${{ github.workspace }}/setup.sql | cockroach sql --insecure
|
90
90
|
- name: Test
|
91
|
-
run: bundle exec rake test
|
91
|
+
run: bundle exec rake test
|
92
|
+
env:
|
93
|
+
TESTOPTS: "--profile=5"
|
94
|
+
RAILS_MINITEST_PLUGIN: "1" # Make sure that we use the minitest plugin for profiling.
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
## Ongoing
|
4
4
|
|
5
|
+
## 8.0.1 - 2025-03-04
|
6
|
+
|
7
|
+
- Fixed retry logic after transaction rollback ([#364](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/364))
|
8
|
+
|
9
|
+
## 8.0.0 - 2024-12-19
|
10
|
+
|
11
|
+
- Add support for Rails 8.0 ([#356](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/356))
|
12
|
+
|
5
13
|
## 7.2.0 - 2024-09-24
|
6
14
|
|
7
15
|
- Add support for Rails 7.2 ([#337](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/337))
|
data/CONTRIBUTING.md
CHANGED
@@ -115,9 +115,10 @@ This section intent to help you with a checklist.
|
|
115
115
|
- Verify the written text at the beginning of the test suite, there are likely
|
116
116
|
some changes in excluded tests.
|
117
117
|
- Check for some important methods, some will change for sure:
|
118
|
-
- [
|
119
|
-
- [
|
120
|
-
- [
|
118
|
+
- [ ] `def new_column_from_field(`
|
119
|
+
- [ ] `def column_definitions(`
|
120
|
+
- [ ] `def pk_and_sequence_for(`
|
121
|
+
- [ ] `def foreign_keys(` and `def all_foreign_keys(`
|
121
122
|
- [ ] ...
|
122
123
|
- Check for setups containing `drop_table` in the test suite.
|
123
124
|
Especially if you have tons of failure, this is likely the cause.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,16 +8,16 @@ of the PostgreSQL adapter that establishes compatibility with [CockroachDB](http
|
|
8
8
|
Add this line to your project's Gemfile:
|
9
9
|
|
10
10
|
```ruby
|
11
|
-
gem 'activerecord-cockroachdb-adapter', '~>
|
11
|
+
gem 'activerecord-cockroachdb-adapter', '~> 8.0.0'
|
12
12
|
```
|
13
13
|
|
14
|
-
If you're using Rails 7.0, use the `7.0.x` versions of this gem.
|
15
|
-
|
16
14
|
If you're using Rails 7.1, use the `7.1.x` versions of this gem.
|
17
15
|
|
18
16
|
If you're using Rails 7.2, use the `7.2.x` versions of this gem.
|
19
17
|
The minimal CockroachDB version required is 23.1.12 for this version.
|
20
18
|
|
19
|
+
If you're using Rails 8.0, use the `8.0.x` versions of this gem.
|
20
|
+
|
21
21
|
In `database.yml`, use the following adapter setting:
|
22
22
|
|
23
23
|
```
|
@@ -14,9 +14,9 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.description = "Allows the use of CockroachDB as a backend for ActiveRecord and Rails apps."
|
15
15
|
spec.homepage = "https://github.com/cockroachdb/activerecord-cockroachdb-adapter"
|
16
16
|
|
17
|
-
spec.add_dependency "activerecord", "~>
|
17
|
+
spec.add_dependency "activerecord", "~> 8.0.0"
|
18
18
|
spec.add_dependency "pg", "~> 1.5"
|
19
|
-
spec.add_dependency "rgeo-activerecord", "~>
|
19
|
+
spec.add_dependency "rgeo-activerecord", "~> 8.0.0"
|
20
20
|
|
21
21
|
spec.add_development_dependency "benchmark-ips", "~> 2.9.1"
|
22
22
|
|
data/bin/console
CHANGED
@@ -12,20 +12,22 @@ require "activerecord-cockroachdb-adapter"
|
|
12
12
|
# structure_load(Post.connection_db_config, "awesome-file.sql")
|
13
13
|
require "active_record/connection_adapters/cockroachdb/database_tasks"
|
14
14
|
|
15
|
+
DB_NAME = "ar_crdb_console"
|
16
|
+
|
15
17
|
schema_kind = ENV.fetch("SCHEMA_KIND", ENV.fetch("SCHEMA", "default"))
|
16
18
|
|
17
|
-
system("cockroach sql --insecure --host=localhost:26257 --execute='drop database if exists
|
19
|
+
system("cockroach sql --insecure --host=localhost:26257 --execute='drop database if exists #{DB_NAME}'",
|
18
20
|
exception: true)
|
19
|
-
system("cockroach sql --insecure --host=localhost:26257 --execute='create database
|
21
|
+
system("cockroach sql --insecure --host=localhost:26257 --execute='create database #{DB_NAME}'",
|
20
22
|
exception: true)
|
21
23
|
|
22
24
|
ActiveRecord::Base.establish_connection(
|
23
|
-
#Alternative version: "cockroachdb://root@localhost:26257
|
25
|
+
#Alternative version: "cockroachdb://root@localhost:26257/#{DB_NAME}"
|
24
26
|
adapter: "cockroachdb",
|
25
27
|
host: "localhost",
|
26
28
|
port: 26257,
|
27
29
|
user: "root",
|
28
|
-
database:
|
30
|
+
database: DB_NAME
|
29
31
|
)
|
30
32
|
|
31
33
|
load "#{__dir__}/console_schemas/#{schema_kind}.rb"
|
@@ -24,7 +24,11 @@ module ActiveRecord
|
|
24
24
|
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
|
25
25
|
statements = table_deletes + fixture_inserts
|
26
26
|
|
27
|
-
|
27
|
+
begin # much faster without disabling referential integrity, worth trying.
|
28
|
+
transaction(requires_new: true) do
|
29
|
+
execute_batch(statements, "Fixtures Load")
|
30
|
+
end
|
31
|
+
rescue
|
28
32
|
disable_referential_integrity do
|
29
33
|
execute_batch(statements, "Fixtures Load")
|
30
34
|
end
|
@@ -50,6 +50,14 @@ module ActiveRecord
|
|
50
50
|
super
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
def quoted_date(value)
|
55
|
+
# CockroachDB differs from PostgreSQL in its representation of
|
56
|
+
# a `timestamp with timezone`, it does not always include the
|
57
|
+
# timezone offset (e.g. `+00`), so we need to add it here.
|
58
|
+
# This is tested by `BasicsTest#test_default_in_local_time`.
|
59
|
+
super + value.strftime("%z")
|
60
|
+
end
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
@@ -34,11 +34,18 @@ module ActiveRecord
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def disable_referential_integrity
|
37
|
-
foreign_keys =
|
37
|
+
foreign_keys = all_foreign_keys
|
38
38
|
|
39
|
-
foreign_keys.
|
40
|
-
remove_foreign_key
|
39
|
+
statements = foreign_keys.map do |foreign_key|
|
40
|
+
# We do not use the `#remove_foreign_key` method here because it
|
41
|
+
# checks for foreign keys existance in the schema cache. This method
|
42
|
+
# is performance critical and we know the foreign key exist.
|
43
|
+
at = create_alter_table foreign_key.from_table
|
44
|
+
at.drop_foreign_key foreign_key.name
|
45
|
+
|
46
|
+
schema_creation.accept(at)
|
41
47
|
end
|
48
|
+
execute_batch(statements, "Disable referential integrity -> remove foreign keys")
|
42
49
|
|
43
50
|
yield
|
44
51
|
|
@@ -52,19 +59,88 @@ module ActiveRecord
|
|
52
59
|
ActiveRecord::Base.table_name_suffix = ""
|
53
60
|
|
54
61
|
begin
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
# Avoid having PG:DuplicateObject error if a test is ran in transaction.
|
63
|
+
# TODO: verify that there is no cache issue related to running this (e.g: fk
|
64
|
+
# still in cache but not in db)
|
65
|
+
#
|
66
|
+
# We avoid using `foreign_key_exists?` here because it checks the schema cache
|
67
|
+
# for every key. This method is performance critical for the test suite, hence
|
68
|
+
# we use the `#all_foreign_keys` method that only make one query to the database.
|
69
|
+
already_inserted_foreign_keys = all_foreign_keys
|
70
|
+
statements = foreign_keys.map do |foreign_key|
|
71
|
+
next if already_inserted_foreign_keys.any? { |fk| fk.from_table == foreign_key.from_table && fk.options[:name] == foreign_key.options[:name] }
|
72
|
+
|
73
|
+
options = foreign_key_options(foreign_key.from_table, foreign_key.to_table, foreign_key.options)
|
74
|
+
at = create_alter_table foreign_key.from_table
|
75
|
+
at.add_foreign_key foreign_key.to_table, options
|
60
76
|
|
61
|
-
|
77
|
+
schema_creation.accept(at)
|
62
78
|
end
|
79
|
+
execute_batch(statements.compact, "Disable referential integrity -> add foreign keys")
|
63
80
|
ensure
|
64
81
|
ActiveRecord::Base.table_name_prefix = old_prefix
|
65
82
|
ActiveRecord::Base.table_name_suffix = old_suffix
|
66
83
|
end
|
67
84
|
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Copy/paste of the `#foreign_keys(table)` method adapted to return every single
|
89
|
+
# foreign key in the database.
|
90
|
+
def all_foreign_keys
|
91
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
92
|
+
SELECT CASE
|
93
|
+
WHEN n1.nspname = current_schema()
|
94
|
+
THEN ''
|
95
|
+
ELSE n1.nspname || '.'
|
96
|
+
END || t1.relname AS from_table,
|
97
|
+
CASE
|
98
|
+
WHEN n2.nspname = current_schema()
|
99
|
+
THEN ''
|
100
|
+
ELSE n2.nspname || '.'
|
101
|
+
END || t2.relname AS to_table,
|
102
|
+
a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred,
|
103
|
+
c.conkey, c.confkey, c.conrelid, c.confrelid
|
104
|
+
FROM pg_constraint c
|
105
|
+
JOIN pg_class t1 ON c.conrelid = t1.oid
|
106
|
+
JOIN pg_class t2 ON c.confrelid = t2.oid
|
107
|
+
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
|
108
|
+
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
109
|
+
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
110
|
+
JOIN pg_namespace n1 ON t1.relnamespace = n1.oid
|
111
|
+
JOIN pg_namespace n2 ON t2.relnamespace = n2.oid
|
112
|
+
WHERE c.contype = 'f'
|
113
|
+
ORDER BY c.conname
|
114
|
+
SQL
|
115
|
+
|
116
|
+
fk_info.map do |row|
|
117
|
+
from_table = PostgreSQL::Utils.unquote_identifier(row["from_table"])
|
118
|
+
to_table = PostgreSQL::Utils.unquote_identifier(row["to_table"])
|
119
|
+
conkey = row["conkey"].scan(/\d+/).map(&:to_i)
|
120
|
+
confkey = row["confkey"].scan(/\d+/).map(&:to_i)
|
121
|
+
|
122
|
+
if conkey.size > 1
|
123
|
+
column = column_names_from_column_numbers(row["conrelid"], conkey)
|
124
|
+
primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
|
125
|
+
else
|
126
|
+
column = PostgreSQL::Utils.unquote_identifier(row["column"])
|
127
|
+
primary_key = row["primary_key"]
|
128
|
+
end
|
129
|
+
|
130
|
+
options = {
|
131
|
+
column: column,
|
132
|
+
name: row["name"],
|
133
|
+
primary_key: primary_key
|
134
|
+
}
|
135
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
136
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
137
|
+
options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])
|
138
|
+
|
139
|
+
options[:validate] = row["valid"]
|
140
|
+
|
141
|
+
ForeignKeyDefinition.new(from_table, to_table, options)
|
142
|
+
end
|
143
|
+
end
|
68
144
|
end
|
69
145
|
end
|
70
146
|
end
|
@@ -55,6 +55,37 @@ module ActiveRecord
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
def primary_keys(table_name)
|
59
|
+
return super unless database_version >= 24_02_02
|
60
|
+
|
61
|
+
query_values(<<~SQL, "SCHEMA")
|
62
|
+
SELECT a.attname
|
63
|
+
FROM (
|
64
|
+
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
|
65
|
+
FROM pg_index
|
66
|
+
WHERE indrelid = #{quote(quote_table_name(table_name))}::regclass
|
67
|
+
AND indisprimary
|
68
|
+
) i
|
69
|
+
JOIN pg_attribute a
|
70
|
+
ON a.attrelid = i.indrelid
|
71
|
+
AND a.attnum = i.indkey[i.idx]
|
72
|
+
AND NOT a.attishidden
|
73
|
+
ORDER BY i.idx
|
74
|
+
SQL
|
75
|
+
end
|
76
|
+
|
77
|
+
def column_names_from_column_numbers(table_oid, column_numbers)
|
78
|
+
return super unless database_version >= 24_02_02
|
79
|
+
|
80
|
+
Hash[query(<<~SQL, "SCHEMA")].values_at(*column_numbers).compact
|
81
|
+
SELECT a.attnum, a.attname
|
82
|
+
FROM pg_attribute a
|
83
|
+
WHERE a.attrelid = #{table_oid}
|
84
|
+
AND a.attnum IN (#{column_numbers.join(", ")})
|
85
|
+
AND NOT a.attishidden
|
86
|
+
SQL
|
87
|
+
end
|
88
|
+
|
58
89
|
# OVERRIDE: CockroachDB does not support deferrable constraints.
|
59
90
|
# See: https://go.crdb.dev/issue-v/31632/v23.1
|
60
91
|
def foreign_key_options(from_table, to_table, options)
|
@@ -185,7 +216,6 @@ module ActiveRecord
|
|
185
216
|
options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])
|
186
217
|
|
187
218
|
options[:validate] = row["valid"]
|
188
|
-
to_table = PostgreSQL::Utils.unquote_identifier(row["to_table"])
|
189
219
|
|
190
220
|
ForeignKeyDefinition.new(table_name, to_table, options)
|
191
221
|
end
|
@@ -265,31 +295,6 @@ module ActiveRecord
|
|
265
295
|
sql
|
266
296
|
end
|
267
297
|
|
268
|
-
# This overrides the method from PostegreSQL adapter
|
269
|
-
# Resets the sequence of a table's primary key to the maximum value.
|
270
|
-
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
271
|
-
unless pk && sequence
|
272
|
-
default_pk, default_sequence = pk_and_sequence_for(table)
|
273
|
-
|
274
|
-
pk ||= default_pk
|
275
|
-
sequence ||= default_sequence
|
276
|
-
end
|
277
|
-
|
278
|
-
if @logger && pk && !sequence
|
279
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence."
|
280
|
-
end
|
281
|
-
|
282
|
-
if pk && sequence
|
283
|
-
quoted_sequence = quote_table_name(sequence)
|
284
|
-
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
285
|
-
if max_pk.nil?
|
286
|
-
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
287
|
-
end
|
288
|
-
|
289
|
-
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
298
|
# override
|
294
299
|
def native_database_types
|
295
300
|
# Add spatial types
|
@@ -45,6 +45,27 @@ module ActiveRecord
|
|
45
45
|
within_new_transaction(isolation: isolation, joinable: joinable, attempts: attempts + 1) { yield }
|
46
46
|
end
|
47
47
|
|
48
|
+
# OVERRIDE: the `rescue ActiveRecord::StatementInvalid` block is new, see comment.
|
49
|
+
def rollback_transaction(transaction = nil)
|
50
|
+
@connection.lock.synchronize do
|
51
|
+
transaction ||= @stack.last
|
52
|
+
begin
|
53
|
+
transaction.rollback
|
54
|
+
rescue ActiveRecord::StatementInvalid => err
|
55
|
+
# This is important to make Active Record aware the record was not inserted/saved
|
56
|
+
# Otherwise Active Record will assume save was successful and it doesn't retry the transaction
|
57
|
+
# See this thread for more details:
|
58
|
+
# https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/258#issuecomment-2256633329
|
59
|
+
transaction.rollback_records if err.cause.is_a?(PG::NoActiveSqlTransaction)
|
60
|
+
|
61
|
+
raise
|
62
|
+
ensure
|
63
|
+
@stack.pop if @stack.last == transaction
|
64
|
+
end
|
65
|
+
transaction.rollback_records
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
48
69
|
def retryable?(error)
|
49
70
|
return true if serialization_error?(error)
|
50
71
|
return true if error.is_a? ActiveRecord::SerializationFailure
|
@@ -161,6 +161,15 @@ module ActiveRecord
|
|
161
161
|
false
|
162
162
|
end
|
163
163
|
|
164
|
+
# Partitioning is quite different from PostgreSQL, so we don't support it.
|
165
|
+
# If you need partitioning, you should default to using raw SQL queries.
|
166
|
+
#
|
167
|
+
# See https://www.postgresql.org/docs/current/ddl-partitioning.html
|
168
|
+
# See https://www.cockroachlabs.com/docs/stable/partitioning
|
169
|
+
def supports_native_partitioning?
|
170
|
+
false
|
171
|
+
end
|
172
|
+
|
164
173
|
def supports_ddl_transactions?
|
165
174
|
false
|
166
175
|
end
|
@@ -181,6 +190,17 @@ module ActiveRecord
|
|
181
190
|
false
|
182
191
|
end
|
183
192
|
|
193
|
+
# OVERRIDE: UNIQUE CONSTRAINTS will create indexes anyway, so we only consider
|
194
|
+
# then as indexes.
|
195
|
+
# See https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/347.
|
196
|
+
# See https://www.cockroachlabs.com/docs/stable/unique.
|
197
|
+
#
|
198
|
+
# NOTE: support is actually partial, one can still use the `#unique_constraints`
|
199
|
+
# method to get the unique constraints.
|
200
|
+
def supports_unique_constraints?
|
201
|
+
false
|
202
|
+
end
|
203
|
+
|
184
204
|
def supports_expression_index?
|
185
205
|
# Expression indexes are partially supported by CockroachDB v21.2,
|
186
206
|
# but activerecord requires "ON CONFLICT expression" support.
|
@@ -393,32 +413,29 @@ module ActiveRecord
|
|
393
413
|
# have [] appended to the end of it.
|
394
414
|
re = /\A(?:geometry|geography|interval|numeric)/
|
395
415
|
|
396
|
-
|
397
|
-
|
398
|
-
# 2
|
399
|
-
# 3
|
400
|
-
# 4
|
401
|
-
# 5
|
402
|
-
# 6
|
403
|
-
|
404
|
-
# 8
|
405
|
-
# 9
|
406
|
-
|
416
|
+
f_attname = 0
|
417
|
+
f_type = 1
|
418
|
+
# f_default = 2
|
419
|
+
# f_attnotnull = 3
|
420
|
+
# f_atttypid = 4
|
421
|
+
# f_atttypmod = 5
|
422
|
+
# f_collname = 6
|
423
|
+
f_comment = 7
|
424
|
+
# f_attidentity = 8
|
425
|
+
# f_attgenerated = 9
|
426
|
+
f_is_hidden = 10
|
407
427
|
fields.map do |field|
|
408
|
-
dtype = field[
|
409
|
-
field[
|
410
|
-
field[
|
411
|
-
field[
|
428
|
+
dtype = field[f_type]
|
429
|
+
field[f_type] = crdb_fields[field[f_attname]][2].downcase if re.match(dtype)
|
430
|
+
field[f_comment] = crdb_fields[field[f_attname]][1]&.gsub!(/^\'|\'?$/, '')
|
431
|
+
field[f_is_hidden] = true if crdb_fields[field[f_attname]][3]
|
412
432
|
field
|
413
433
|
end
|
414
434
|
fields.delete_if do |field|
|
415
435
|
# Don't include rowid column if it is hidden and the primary key
|
416
436
|
# is not defined (meaning CRDB implicitly created it).
|
417
|
-
|
418
|
-
field[
|
419
|
-
else
|
420
|
-
false # Keep this entry.
|
421
|
-
end
|
437
|
+
field[f_attname] == CockroachDBAdapter::DEFAULT_PRIMARY_KEY &&
|
438
|
+
field[f_is_hidden] && !primary_key(table_name)
|
422
439
|
end
|
423
440
|
end
|
424
441
|
|
@@ -458,9 +475,7 @@ module ActiveRecord
|
|
458
475
|
# That method will not work for CockroachDB because the error
|
459
476
|
# originates from the "runExecBuilder" function, so we need
|
460
477
|
# to modify the original to match the CockroachDB behavior.
|
461
|
-
def is_cached_plan_failure?(
|
462
|
-
pgerror = e.cause
|
463
|
-
|
478
|
+
def is_cached_plan_failure?(pgerror)
|
464
479
|
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
465
480
|
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "runExecBuilder"
|
466
481
|
rescue
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
def force_index!(index_name, direction: nil)
|
58
58
|
return self unless from_clause_is_a_table_name?
|
59
59
|
|
60
|
-
index_name = sanitize_sql(index_name.to_s)
|
60
|
+
index_name = model.sanitize_sql(index_name.to_s)
|
61
61
|
direction = direction.to_s.upcase
|
62
62
|
direction = %w[ASC DESC].include?(direction) ? ",#{direction}" : ""
|
63
63
|
|
@@ -84,7 +84,7 @@ module ActiveRecord
|
|
84
84
|
def index_hint!(hint)
|
85
85
|
return self unless from_clause_is_a_table_name?
|
86
86
|
|
87
|
-
hint = sanitize_sql(hint.to_s)
|
87
|
+
hint = model.sanitize_sql(hint.to_s)
|
88
88
|
@index_hint = hint.to_s
|
89
89
|
self.from_clause = build_from_clause_with_hints
|
90
90
|
self
|
@@ -120,7 +120,7 @@ module ActiveRecord
|
|
120
120
|
|
121
121
|
table_name =
|
122
122
|
if from_clause.empty?
|
123
|
-
quoted_table_name
|
123
|
+
model.quoted_table_name
|
124
124
|
else
|
125
125
|
# Remove previous table hints if any. And spaces.
|
126
126
|
from_clause.value.partition("@").first.strip
|
data/lib/version.rb
CHANGED
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:
|
4
|
+
version: 8.0.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:
|
11
|
+
date: 2025-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 8.0.0
|
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:
|
26
|
+
version: 8.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 8.0.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 8.0.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: benchmark-ips
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,6 +75,9 @@ extensions: []
|
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
77
|
- ".editorconfig"
|
78
|
+
- ".github/issue_template.md"
|
79
|
+
- ".github/reproduction_scripts/migrations.rb"
|
80
|
+
- ".github/reproduction_scripts/models_and_database.rb"
|
78
81
|
- ".github/workflows/ci.yml"
|
79
82
|
- ".github/workflows/docker.yml"
|
80
83
|
- ".gitignore"
|
@@ -127,7 +130,7 @@ licenses:
|
|
127
130
|
- Apache-2.0
|
128
131
|
metadata:
|
129
132
|
allowed_push_host: https://rubygems.org
|
130
|
-
post_install_message:
|
133
|
+
post_install_message:
|
131
134
|
rdoc_options: []
|
132
135
|
require_paths:
|
133
136
|
- lib
|
@@ -142,8 +145,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
145
|
- !ruby/object:Gem::Version
|
143
146
|
version: '0'
|
144
147
|
requirements: []
|
145
|
-
rubygems_version: 3.
|
146
|
-
signing_key:
|
148
|
+
rubygems_version: 3.5.23
|
149
|
+
signing_key:
|
147
150
|
specification_version: 4
|
148
151
|
summary: CockroachDB adapter for ActiveRecord.
|
149
152
|
test_files: []
|