Skip to content

Commit 1d98132

Browse files
BuonOmorafiss
authored andcommitted
fix(schema_statements): add unique_rowid to pk_and_sequence_for
Allow to get the primary key in the default case.
1 parent 01097e9 commit 1d98132

File tree

4 files changed

+99
-34
lines changed

4 files changed

+99
-34
lines changed

lib/active_record/connection_adapters/cockroachdb/schema_statements.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,63 @@ def primary_key(table_name)
5555
end
5656
end
5757

58+
# OVERRIDE: Added `unique_rowid` to the last line of the second query.
59+
# This is a CockroachDB-specific function used for primary keys.
60+
#
61+
# Returns a table's primary key and belonging sequence.
62+
def pk_and_sequence_for(table) # :nodoc:
63+
# First try looking for a sequence with a dependency on the
64+
# given table's primary key.
65+
result = query(<<~SQL, "SCHEMA")[0]
66+
SELECT attr.attname, nsp.nspname, seq.relname
67+
FROM pg_class seq,
68+
pg_attribute attr,
69+
pg_depend dep,
70+
pg_constraint cons,
71+
pg_namespace nsp
72+
WHERE seq.oid = dep.objid
73+
AND seq.relkind = 'S'
74+
AND attr.attrelid = dep.refobjid
75+
AND attr.attnum = dep.refobjsubid
76+
AND attr.attrelid = cons.conrelid
77+
AND attr.attnum = cons.conkey[1]
78+
AND seq.relnamespace = nsp.oid
79+
AND cons.contype = 'p'
80+
AND dep.classid = 'pg_class'::regclass
81+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
82+
SQL
83+
84+
if result.nil? || result.empty?
85+
result = query(<<~SQL, "SCHEMA")[0]
86+
SELECT attr.attname, nsp.nspname,
87+
CASE
88+
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
89+
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
90+
substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
91+
strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
92+
ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
93+
END
94+
FROM pg_class t
95+
JOIN pg_attribute attr ON (t.oid = attrelid)
96+
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
97+
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
98+
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
99+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
100+
AND cons.contype = 'p'
101+
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid|unique_rowid'
102+
SQL
103+
end
104+
105+
pk = result.shift
106+
if result.last
107+
[pk, PostgreSQL::Name.new(*result)]
108+
else
109+
[pk, nil]
110+
end
111+
rescue
112+
nil
113+
end
114+
58115
# override
59116
# Modified version of the postgresql foreign_keys method.
60117
# Replaces t2.oid::regclass::text with t2.relname since this is

test/cases/adapters/postgresql/postgresql_adapter_test.rb

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require "cases/helper"
33
require "support/ddl_helper"
44
require "support/connection_helper"
5+
require "support/copy_cat"
56

67
module CockroachDB
78
module ConnectionAdapters
@@ -12,7 +13,6 @@ class PostgreSQLAdapterTest < ActiveRecord::PostgreSQLTestCase
1213

1314
def setup
1415
@connection = ActiveRecord::Base.connection
15-
@connection_handler = ActiveRecord::Base.connection_handler
1616
end
1717

1818
def teardown
@@ -87,11 +87,27 @@ def test_invalid_index
8787
end
8888
end
8989

90+
# OVERRIDE: the `default_sequence_name` is `nil`, let's `to_s` it
91+
# for a fair comparison.
92+
CopyCat.copy_methods(self, ActiveRecord::ConnectionAdapters::PostgreSQLAdapterTest,
93+
:test_pk_and_sequence_for,
94+
:test_pk_and_sequence_for_with_non_standard_primary_key
95+
) do
96+
attr_accessor :already_updated
97+
def on_send(node)
98+
return super unless node in [:send, _, :default_sequence_name, [:str, "ex"], [:str, "id"|"code"]]
99+
100+
raise "The source code must have changed" if already_updated
101+
already_updated ||= :yes
102+
insert_after(node.loc.expression, ".to_s")
103+
end
104+
end
105+
90106
private
91107

92-
def with_example_table(definition = "id serial primary key, number integer, data character varying(255)", &block)
93-
super(@connection, "ex", definition, &block)
94-
end
108+
CopyCat.copy_methods(self, ActiveRecord::ConnectionAdapters::PostgreSQLAdapterTest,
109+
:with_example_table
110+
)
95111
end
96112
end
97113
end

test/cases/migration_test.rb

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "cases/helper_cockroachdb"
22
require "models/person"
3+
require "support/copy_cat"
34

45
class Reminder < ActiveRecord::Base; end unless Object.const_defined?(:Reminder)
56
class Thing < ActiveRecord::Base; end unless Object.const_defined?(:Thing)
@@ -92,7 +93,6 @@ def migrate(x)
9293

9394
class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
9495
def setup
95-
@original_test = ::BulkAlterTableMigrationsTest.new(name)
9696
@connection = Person.connection
9797
@connection.create_table(:delete_me, force: true) { |t| }
9898
Person.reset_column_information
@@ -103,38 +103,28 @@ def setup
103103
Person.connection.drop_table(:delete_me) rescue nil
104104
end
105105

106-
%i(
107-
test_adding_indexes
108-
test_removing_index
109-
test_adding_multiple_columns
110-
test_changing_index
111-
).each do |method_name|
112-
file, line = ::BulkAlterTableMigrationsTest.instance_method(method_name).source_location
113-
iter = File.foreach(file)
114-
(line - 1).times { iter.next }
115-
indent = iter.next[/\A\s*/]
116-
content = +""
117-
content << iter.next until iter.peek == indent + "end\n"
118-
content['"PostgreSQLAdapter" =>'] = '"CockroachDBAdapter" =>'
119-
eval(<<-RUBY, binding, __FILE__, __LINE__ + 1)
120-
def #{method_name}
121-
#{content}
122-
end
123-
RUBY
106+
# Change expected query count from PostgreSQLAdapter to CockroachDBAdapter.
107+
CopyCat.copy_methods(self, ::BulkAlterTableMigrationsTest,
108+
:test_adding_indexes,
109+
:test_removing_index,
110+
:test_adding_multiple_columns,
111+
:test_changing_index
112+
) do
113+
def on_str(node)
114+
return unless node in [:str, "PostgreSQLAdapter"]
115+
116+
replace(node.loc.expression, '"CockroachDBAdapter"')
117+
end
124118
end
125119

126120
private
127121

128-
%i[
129-
with_bulk_change_table
130-
column
131-
columns
132-
index
133-
indexes
134-
].each do |method|
135-
define_method(method) do |*args, &block|
136-
@original_test.send(method, *args, &block)
137-
end
138-
end
122+
CopyCat.copy_methods(self, ::BulkAlterTableMigrationsTest,
123+
:with_bulk_change_table,
124+
:column,
125+
:columns,
126+
:index,
127+
:indexes
128+
)
139129
end
140130
end

test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterTest.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
exclude :test_partial_index_on_column_named_like_keyword, "CockroachDB adds parentheses around the WHERE definition."
1818
exclude :test_only_check_for_insensitive_comparison_capability_once, "the DROP DOMAIN syntax is not supported by CRDB"
1919

20+
exclude :test_pk_and_sequence_for, "The test needs a little rework since the sequence is empty in CRDB"
21+
exclude :test_pk_and_sequence_for_with_non_standard_primary_key, "The test needs a little rework since the sequence is empty in CRDB"
2022

2123
plpgsql_needed = "Will be testable with CRDB 23.2, introducing PL-PGSQL"
2224
exclude :test_ignores_warnings_when_behaviour_ignore, plpgsql_needed

0 commit comments

Comments
 (0)