Skip to content

Commit a350e5e

Browse files
committed
chore: Backport #345 to 7-0-stable
1 parent 41fff74 commit a350e5e

File tree

6 files changed

+166
-7
lines changed

6 files changed

+166
-7
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ on:
1818

1919
# This allows a subsequently queued workflow run to interrupt previous runs.
2020
concurrency:
21-
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
21+
group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
2222
cancel-in-progress: true
2323

2424
jobs:
2525
test:
26-
runs-on: ubuntu-latest
26+
runs-on: ubuntu-22.04
2727
strategy:
2828
matrix:
29-
crdb: [v23.1.5]
30-
ruby: [ruby-head]
29+
crdb: [v24.1.10]
30+
ruby: ["3.4"]
3131
name: Test (crdb=${{ matrix.crdb }} ruby=${{ matrix.ruby }})
3232
steps:
3333
- name: Set Up Actions
@@ -37,8 +37,8 @@ jobs:
3737
- name: Set Up Ruby
3838
uses: ruby/setup-ruby@v1
3939
with:
40-
ruby-version: ${{ matrix.ruby }}
41-
bundler-cache: true
40+
ruby-version: ${{ matrix.ruby }}
41+
bundler-cache: true
4242
- name: Install and Start Cockroachdb
4343
run: |
4444
# Download CockroachDB
@@ -75,7 +75,6 @@ jobs:
7575
ALTER RANGE liveness CONFIGURE ZONE USING num_replicas = 1, gc.ttlseconds = 30;
7676
7777
SET CLUSTER SETTING kv.range_merge.queue_interval = '50ms';
78-
SET CLUSTER SETTING kv.raft_log.disable_synchronization_unsafe = 'true';
7978
SET CLUSTER SETTING jobs.registry.interval.cancel = '180s';
8079
SET CLUSTER SETTING jobs.registry.interval.gc = '30s';
8180
SET CLUSTER SETTING kv.range_split.by_load_merge_delay = '5s';

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ group :development, :test do
4747
gem "byebug"
4848
gem "minitest-excludes", "~> 2.0.1"
4949

50+
# Needed for the test suite
51+
gem "msgpack", ">= 1.7.0"
52+
gem "mutex_m", "~> 0.2.0"
53+
5054
# Gems used by the ActiveRecord test suite
5155
gem "bcrypt", "~> 3.1.18"
5256
gem "mocha", "~> 1.14.0"

activerecord-cockroachdb-adapter.gemspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Gem::Specification.new do |spec|
1717
spec.add_dependency "activerecord", "~> 7.0.3"
1818
spec.add_dependency "pg", "~> 1.2"
1919
spec.add_dependency "rgeo-activerecord", "~> 7.0.0"
20+
# See https://github.com/rails/rails/issues/54263
21+
spec.add_dependency 'concurrent-ruby', '1.3.4'
2022

2123
spec.add_development_dependency "benchmark-ips", "~> 2.9.1"
2224

lib/active_record/connection_adapters/cockroachdb/schema_statements.rb

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,115 @@ def primary_key(table_name)
3232
end
3333
end
3434

35+
def primary_keys(table_name)
36+
return super unless database_version >= 24_02_02
37+
38+
query_values(<<~SQL, "SCHEMA")
39+
SELECT a.attname
40+
FROM (
41+
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
42+
FROM pg_index
43+
WHERE indrelid = #{quote(quote_table_name(table_name))}::regclass
44+
AND indisprimary
45+
) i
46+
JOIN pg_attribute a
47+
ON a.attrelid = i.indrelid
48+
AND a.attnum = i.indkey[i.idx]
49+
AND NOT a.attishidden
50+
ORDER BY i.idx
51+
SQL
52+
end
53+
54+
def column_names_from_column_numbers(table_oid, column_numbers)
55+
return super unless database_version >= 24_02_02
56+
57+
Hash[query(<<~SQL, "SCHEMA")].values_at(*column_numbers).compact
58+
SELECT a.attnum, a.attname
59+
FROM pg_attribute a
60+
WHERE a.attrelid = #{table_oid}
61+
AND a.attnum IN (#{column_numbers.join(", ")})
62+
AND NOT a.attishidden
63+
SQL
64+
end
65+
66+
# OVERRIDE: CockroachDB does not support deferrable constraints.
67+
# See: https://go.crdb.dev/issue-v/31632/v23.1
68+
def foreign_key_options(from_table, to_table, options)
69+
options = super
70+
options.delete(:deferrable) unless supports_deferrable_constraints?
71+
options
72+
end
73+
74+
# OVERRIDE: Added `unique_rowid` to the last line of the second query.
75+
# This is a CockroachDB-specific function used for primary keys.
76+
# Also make sure we don't consider `NOT VISIBLE` columns.
77+
#
78+
# Returns a table's primary key and belonging sequence.
79+
def pk_and_sequence_for(table) # :nodoc:
80+
# First try looking for a sequence with a dependency on the
81+
# given table's primary key.
82+
result = query(<<~SQL, "SCHEMA")[0]
83+
SELECT attr.attname, nsp.nspname, seq.relname
84+
FROM pg_class seq,
85+
pg_attribute attr,
86+
pg_depend dep,
87+
pg_constraint cons,
88+
pg_namespace nsp,
89+
-- TODO: use the pg_catalog.pg_attribute(attishidden) column when
90+
-- it is added instead of joining on crdb_internal.
91+
-- See https://github.com/cockroachdb/cockroach/pull/126397
92+
crdb_internal.table_columns tc
93+
WHERE seq.oid = dep.objid
94+
AND seq.relkind = 'S'
95+
AND attr.attrelid = dep.refobjid
96+
AND attr.attnum = dep.refobjsubid
97+
AND attr.attrelid = cons.conrelid
98+
AND attr.attnum = cons.conkey[1]
99+
AND seq.relnamespace = nsp.oid
100+
AND attr.attrelid = tc.descriptor_id
101+
AND attr.attname = tc.column_name
102+
AND tc.hidden = false
103+
AND cons.contype = 'p'
104+
AND dep.classid = 'pg_class'::regclass
105+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
106+
SQL
107+
108+
if result.nil? || result.empty?
109+
result = query(<<~SQL, "SCHEMA")[0]
110+
SELECT attr.attname, nsp.nspname,
111+
CASE
112+
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
113+
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
114+
substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
115+
strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
116+
ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
117+
END
118+
FROM pg_class t
119+
JOIN pg_attribute attr ON (t.oid = attrelid)
120+
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
121+
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
122+
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
123+
-- TODO: use the pg_catalog.pg_attribute(attishidden) column when
124+
-- it is added instead of joining on crdb_internal.
125+
-- See https://github.com/cockroachdb/cockroach/pull/126397
126+
JOIN crdb_internal.table_columns tc ON (attr.attrelid = tc.descriptor_id AND attr.attname = tc.column_name)
127+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
128+
AND tc.hidden = false
129+
AND cons.contype = 'p'
130+
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid|unique_rowid'
131+
SQL
132+
end
133+
134+
pk = result.shift
135+
if result.last
136+
[pk, PostgreSQL::Name.new(*result)]
137+
else
138+
[pk, nil]
139+
end
140+
rescue
141+
nil
142+
end
143+
35144
# override
36145
# Modified version of the postgresql foreign_keys method.
37146
# Replaces t2.oid::regclass::text with t2.relname since this is

test/cases/migration/hidden_column_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ def test_add_hidden_column
5353
output = dump_table_schema "rockets"
5454
assert_match %r{t.uuid "new_col", hidden: true$}, output
5555
end
56+
57+
# Since 24.2.2, hash sharded indexes add a hidden column to the table.
58+
# This tests ensure that the user can still drop the index even if they
59+
# call `#remove_index` with the column name rather than the index name.
60+
def test_remove_index_with_a_hidden_column
61+
@connection.execute <<-SQL
62+
CREATE INDEX hash_idx ON rockets (name) USING HASH WITH (bucket_count=8);
63+
SQL
64+
@connection.remove_index :rockets, :name
65+
# assert :ok
66+
end
5667
end
5768
end
5869
end

test/cases/primary_keys_test.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,38 @@ def test_schema_dump_primary_key_integer_with_default_nil
9797
assert_match %r{create_table "int_defaults", id: :bigint, default: nil}, schema
9898
end
9999
end
100+
101+
class PrimaryKeyHiddenColumnTest < ActiveRecord::TestCase
102+
class Rocket < ActiveRecord::Base
103+
end
104+
105+
def setup
106+
connection = ActiveRecord::Base.lease_connection
107+
connection.execute <<-SQL
108+
CREATE TABLE rockets(
109+
id SERIAL PRIMARY KEY USING HASH WITH (bucket_count=4)
110+
)
111+
SQL
112+
end
113+
114+
def teardown
115+
ActiveRecord::Base.connection.drop_table :rockets
116+
end
117+
118+
def test_to_key_with_hidden_primary_key_part
119+
rocket = Rocket.new
120+
assert_nil rocket.to_key
121+
rocket.save
122+
assert_equal rocket.to_key, [rocket.id]
123+
end
124+
125+
def test_read_attribute_with_hidden_primary_key_part
126+
rocket = Rocket.create!
127+
id = assert_not_deprecated(ActiveRecord.deprecator) do
128+
rocket.read_attribute(:id)
129+
end
130+
131+
assert_equal rocket.id, id
132+
end
133+
end
100134
end

0 commit comments

Comments
 (0)