rails-bigint-pk 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/.travis.yml +3 -0
- data/.vim/snippets/rspec.snippets +4 -0
- data/.vimrc +2 -0
- data/Changelog +11 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/LICENSE +7 -0
- data/Rakefile +6 -0
- data/Readme.md +61 -0
- data/lib/bigint_pk.rb +92 -0
- data/lib/bigint_pk/version.rb +3 -0
- data/lib/generators/bigint_pk.rb +5 -0
- data/lib/generators/bigint_pk/install_generator.rb +21 -0
- data/lib/generators/bigint_pk/templates/bigint_pk.rb +9 -0
- data/lib/generators/bigint_pk/templates/migration.rb +20 -0
- data/rails-bigint-pk.gemspec +37 -0
- data/spec/fixtures/Gemfile +8 -0
- data/spec/fixtures/mysql_database.yml +13 -0
- data/spec/fixtures/postgresql_database.yml +12 -0
- data/spec/fixtures/sqlite3_database.yml +11 -0
- data/spec/fixtures/test_rails_app/app/models/empire.rb +2 -0
- data/spec/fixtures/test_rails_app/app/models/ruler.rb +4 -0
- data/spec/fixtures/test_rails_app/db/migrate/19000101100000_create_empires_and_rulers.rb +10 -0
- data/spec/fixtures/test_rails_app/db/migrate/19000102100000_update_rulers_add_favourite_ruler.rb +7 -0
- data/spec/integration/generator_spec.rb +40 -0
- data/spec/integration/schema_spec.rb +222 -0
- data/spec/migration_spec.rb +102 -0
- data/spec/monkey_patch/connection_adapters_spec.rb +149 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/cmd.rb +42 -0
- data/spec/support/file_utils.rb +7 -0
- data/spec/support/logging.rb +5 -0
- data/spec/support/rails.rb +32 -0
- data/spec/support/rspec.rb +3 -0
- metadata +340 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Migrations', :integration do
|
4
|
+
after :each do
|
5
|
+
execute_sql %Q{
|
6
|
+
delete from rulers;
|
7
|
+
delete from empires;
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'create_table' do
|
12
|
+
before :each do
|
13
|
+
in_directory( RailsDir ) do
|
14
|
+
run 'rails generate bigint_pk:install'
|
15
|
+
FileUtils.rm Dir['./db/migrate/**change_keys_to_bigint*'].first
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.they_use_bigint_primary_keys
|
20
|
+
describe 'primary keys' do
|
21
|
+
they 'default to 64bit' do
|
22
|
+
expect{
|
23
|
+
execute_sql %Q{
|
24
|
+
insert into empires values(#{2 ** 31 - 1});
|
25
|
+
insert into empires values(#{2 ** 31});
|
26
|
+
}
|
27
|
+
}.to_not raise_error
|
28
|
+
|
29
|
+
expect(
|
30
|
+
execute_ruby 'puts Empire.all.collect(&:id)'
|
31
|
+
).to eq ["#{2**31-1}", "#{2**31}"].join("\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.they_use_bigint_foreign_keys
|
37
|
+
describe 'foreign keys' do
|
38
|
+
they 'can reference 64bit values' do
|
39
|
+
expect{
|
40
|
+
execute_sql %Q{
|
41
|
+
insert into empires values(#{2 ** 31 - 1});
|
42
|
+
insert into empires values(#{2 ** 31});
|
43
|
+
insert into rulers( empire_id ) values(#{2 ** 31 - 1});
|
44
|
+
insert into rulers( empire_id ) values(#{2 ** 31});
|
45
|
+
}
|
46
|
+
}.to_not raise_error
|
47
|
+
|
48
|
+
expect(
|
49
|
+
execute_ruby 'puts Ruler.all.map{|r| r.empire_id}'
|
50
|
+
).to eq ["#{2**31 - 1}", "#{2**31}"].join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'that were added to an existing table' do
|
54
|
+
they 'can reference 64bit values' do
|
55
|
+
expect{
|
56
|
+
execute_sql %Q{
|
57
|
+
insert into rulers( id ) values(#{2 ** 31 - 1});
|
58
|
+
insert into rulers( id ) values(#{2 ** 31});
|
59
|
+
|
60
|
+
insert into rulers( favourite_ruler_id ) values(#{2 ** 31 - 1});
|
61
|
+
insert into rulers( favourite_ruler_id ) values(#{2 ** 31});
|
62
|
+
}
|
63
|
+
}.to_not raise_error
|
64
|
+
|
65
|
+
expect(
|
66
|
+
execute_ruby('puts Ruler.all.map{|r| r.favourite_ruler_id}').split
|
67
|
+
).to eq ["#{2**31 - 1}", "#{2**31}"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with a mysql database' do
|
74
|
+
before(:each) do
|
75
|
+
use_database! :mysql
|
76
|
+
rake 'db:migrate'
|
77
|
+
end
|
78
|
+
|
79
|
+
they_use_bigint_primary_keys
|
80
|
+
they_use_bigint_foreign_keys
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with a postgresql database' do
|
84
|
+
before(:each) do
|
85
|
+
use_database! :postgres
|
86
|
+
rake 'db:migrate'
|
87
|
+
end
|
88
|
+
|
89
|
+
they_use_bigint_primary_keys
|
90
|
+
they_use_bigint_foreign_keys
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with a sqlite3 database' do
|
94
|
+
before(:each) do
|
95
|
+
use_database! :sqlite3
|
96
|
+
rake 'db:migrate'
|
97
|
+
end
|
98
|
+
|
99
|
+
they_use_bigint_primary_keys
|
100
|
+
they_use_bigint_foreign_keys
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'existing tables' do
|
105
|
+
InitializerFile = "./config/initializers/bigint_pk.rb"
|
106
|
+
|
107
|
+
before :each do
|
108
|
+
in_directory( RailsDir ) do
|
109
|
+
run 'rails generate bigint_pk:install'
|
110
|
+
|
111
|
+
initializer = File.read InitializerFile
|
112
|
+
initializer.gsub!( /enabled = true/, 'enabled = false' )
|
113
|
+
File.open( InitializerFile, 'w'){|f| f.print initializer }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.they_have_their_primary_keys_migrated
|
118
|
+
they 'have their primary keys migrated' do
|
119
|
+
expect{
|
120
|
+
execute_sql %Q{
|
121
|
+
insert into empires values(#{2 ** 31 - 1});
|
122
|
+
insert into empires values(#{2 ** 31});
|
123
|
+
}
|
124
|
+
}.to raise_error
|
125
|
+
|
126
|
+
execute_ruby 'Empire.delete_all'
|
127
|
+
rake 'db:migrate'
|
128
|
+
|
129
|
+
expect{
|
130
|
+
execute_sql %Q{
|
131
|
+
insert into empires values(#{2 ** 31 - 1});
|
132
|
+
insert into empires values(#{2 ** 31});
|
133
|
+
}
|
134
|
+
}.to_not raise_error
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.they_have_their_foreign_keys_migrated
|
139
|
+
they 'have their foreign keys migrated' do
|
140
|
+
expect{
|
141
|
+
execute_sql %Q{
|
142
|
+
insert into empires values(#{2 ** 31 - 1});
|
143
|
+
insert into empires values(#{2 ** 31});
|
144
|
+
insert into rulers( empire_id ) values(#{2 ** 31});
|
145
|
+
insert into rulers( empire_id ) values(#{2 ** 31 - 1});
|
146
|
+
}
|
147
|
+
}.to raise_error
|
148
|
+
|
149
|
+
execute_ruby 'Ruler.delete_all'
|
150
|
+
execute_ruby 'Empire.delete_all'
|
151
|
+
rake 'db:migrate'
|
152
|
+
|
153
|
+
expect{
|
154
|
+
execute_sql %Q{
|
155
|
+
insert into empires values(#{2 ** 31 - 1});
|
156
|
+
insert into empires values(#{2 ** 31});
|
157
|
+
insert into rulers( empire_id ) values(#{2 ** 31 - 1});
|
158
|
+
insert into rulers( empire_id ) values(#{2 ** 31});
|
159
|
+
}
|
160
|
+
}.to_not raise_error
|
161
|
+
|
162
|
+
expect(
|
163
|
+
execute_ruby 'puts Ruler.all.collect{|r| [r.id, r.empire.id]}'
|
164
|
+
).to eq [ "1", "#{2**31 - 1}",
|
165
|
+
"2", "#{2**31}"].join("\n")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'with a mysql database' do
|
170
|
+
before(:each) do
|
171
|
+
use_database! :mysql
|
172
|
+
rake 'db:migrate VERSION=19000101100000'
|
173
|
+
end
|
174
|
+
|
175
|
+
they_have_their_primary_keys_migrated
|
176
|
+
they_have_their_foreign_keys_migrated
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'with a postgresql database' do
|
180
|
+
before(:each) do
|
181
|
+
use_database! :postgres
|
182
|
+
rake 'db:migrate VERSION=19000101100000'
|
183
|
+
end
|
184
|
+
|
185
|
+
they_have_their_primary_keys_migrated
|
186
|
+
they_have_their_foreign_keys_migrated
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "with models & migrations created after bigint migration" do
|
191
|
+
before :each do
|
192
|
+
in_directory( RailsDir ) do
|
193
|
+
run 'rails generate bigint_pk:install'
|
194
|
+
run 'rails generate model subject'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.it_completes_migration
|
199
|
+
it "completes the migration" do
|
200
|
+
in_directory( RailsDir ) do
|
201
|
+
run("rake db:migrate")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "with a mysql database" do
|
207
|
+
before(:each) do
|
208
|
+
use_database! :mysql
|
209
|
+
end
|
210
|
+
|
211
|
+
it_completes_migration
|
212
|
+
end
|
213
|
+
|
214
|
+
context "with a postgresql database" do
|
215
|
+
before(:each) do
|
216
|
+
use_database! :postgres
|
217
|
+
end
|
218
|
+
|
219
|
+
it_completes_migration
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'generators/bigint_pk/templates/migration'
|
3
|
+
|
4
|
+
describe 'ChangeKeysToBigint' do
|
5
|
+
let(:connection) do
|
6
|
+
double('Connection').tap do |c|
|
7
|
+
c.stub(:quote_table_name){|tn| "`#{tn}`"}
|
8
|
+
c.stub(:quote_column_name){|column| "`#{column}`"}
|
9
|
+
c.stub(:table_exists?) { true }
|
10
|
+
c.stub(:execute){|sql| queries << sql }
|
11
|
+
c.stub(:column_exists?) do |table_name, column_name|
|
12
|
+
if table_name.to_s == 'coaches' && column_name == 'team_id'
|
13
|
+
false
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
let(:queries){ [] }
|
21
|
+
|
22
|
+
before do
|
23
|
+
stub_const('Team', double('Team', table_name: 'teams', primary_key: 'id'))
|
24
|
+
stub_const('Player', double('Player', table_name: 'players', primary_key: 'id'))
|
25
|
+
stub_const('Coach', double('Coach', table_name: 'coaches', primary_key: 'id'))
|
26
|
+
Team.stub( reflect_on_all_associations: [])
|
27
|
+
Player.stub( reflect_on_all_associations: [
|
28
|
+
double('belongs_to', macro: :belongs_to, foreign_key: 'team_id')])
|
29
|
+
Coach.stub( reflect_on_all_associations: [
|
30
|
+
double('belongs_to', macro: :belongs_to, foreign_key: 'team_id')])
|
31
|
+
|
32
|
+
stub_const('Rails', double('Rails'))
|
33
|
+
Rails.stub_chain 'application.eager_load!'
|
34
|
+
|
35
|
+
ActiveRecord::Base.stub( subclasses: [ Team, Player, Coach ], connection: connection)
|
36
|
+
ActiveRecord::Base.stub_chain('connection_pool.with_connection') do |&prok|
|
37
|
+
prok.call connection
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a postgres database' do
|
42
|
+
before { connection.stub( adapter_name: 'PostgreSQL') }
|
43
|
+
|
44
|
+
it 'migrates primary keys' do
|
45
|
+
ChangeKeysToBigint.migrate :up
|
46
|
+
expect( queries ).to include(
|
47
|
+
'ALTER TABLE `teams` ALTER COLUMN `id` TYPE bigint',
|
48
|
+
'ALTER TABLE `players` ALTER COLUMN `id` TYPE bigint',
|
49
|
+
'ALTER TABLE `coaches` ALTER COLUMN `id` TYPE bigint'
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'migrates foreign keys' do
|
54
|
+
ChangeKeysToBigint.migrate :up
|
55
|
+
expect( queries ).to include(
|
56
|
+
'ALTER TABLE `players` ALTER COLUMN `team_id` TYPE bigint'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not migrate foreign keys that do not exist in database' do
|
61
|
+
ChangeKeysToBigint.migrate :up
|
62
|
+
expect( queries ).to_not include(
|
63
|
+
'ALTER TABLE `coaches` ALTER COLUMN `team_id` TYPE bigint'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with a mysql database' do
|
69
|
+
before { connection.stub( adapter_name: 'MySQL') }
|
70
|
+
|
71
|
+
it 'migrates primary keys' do
|
72
|
+
ChangeKeysToBigint.migrate :up
|
73
|
+
expect( queries ).to include(
|
74
|
+
'ALTER TABLE `teams` MODIFY COLUMN `id` bigint(20) DEFAULT NULL auto_increment',
|
75
|
+
'ALTER TABLE `players` MODIFY COLUMN `id` bigint(20) DEFAULT NULL auto_increment',
|
76
|
+
'ALTER TABLE `coaches` MODIFY COLUMN `id` bigint(20) DEFAULT NULL auto_increment'
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'migrates foreign keys' do
|
81
|
+
ChangeKeysToBigint.migrate :up
|
82
|
+
expect( queries ).to include(
|
83
|
+
'ALTER TABLE `players` MODIFY COLUMN `team_id` bigint(20) DEFAULT NULL')
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not migrate foreign keys that do not exist in database' do
|
87
|
+
ChangeKeysToBigint.migrate :up
|
88
|
+
expect( queries ).to_not include(
|
89
|
+
'ALTER TABLE `coaches` MODIFY COLUMN `team_id` bigint(20) DEFAULT NULL'
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with a sqlite database' do
|
95
|
+
before { connection.stub( adapter_name: 'SQLite') }
|
96
|
+
|
97
|
+
it 'does not migrate any keys' do
|
98
|
+
ChangeKeysToBigint.migrate :up
|
99
|
+
expect( queries ).to eq []
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
3
|
+
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
4
|
+
|
5
|
+
describe BigintPk do
|
6
|
+
describe '::setup' do
|
7
|
+
def reset_connection_adapters!
|
8
|
+
if defined? ::ActiveRecord::ConnectionAdapters
|
9
|
+
[:PostgreSQLAdapter, :AbstractMysqlAdapter].each do |adapter|
|
10
|
+
if ActiveRecord::ConnectionAdapters.const_defined? adapter
|
11
|
+
ActiveRecord::ConnectionAdapters.send :remove_const, adapter
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
load 'active_record/connection_adapters/postgresql_adapter.rb'
|
16
|
+
load 'active_record/connection_adapters/abstract_mysql_adapter.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
ActiveRecord::Base.class_eval do
|
21
|
+
def self.establish_connection; end
|
22
|
+
end
|
23
|
+
|
24
|
+
reset_connection_adapters!
|
25
|
+
BigintPk.setup &prok
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when enabled' do
|
29
|
+
let(:prok){ lambda{|c| c.enabled = true }}
|
30
|
+
|
31
|
+
it 'updates the default primary key for the postgres adapter' do
|
32
|
+
expect(
|
33
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::
|
34
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
35
|
+
).to eq 'bigserial primary key'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'updates the default primary key for both mysql adapters' do
|
39
|
+
expect(
|
40
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::
|
41
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
42
|
+
).to eq 'bigint(20) DEFAULT NULL auto_increment PRIMARY KEY'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.it_makes_references_default_to_64bit
|
46
|
+
describe '#references' do
|
47
|
+
before { abstract_table_class.any_instance.stub :references_without_default_bigint_fk }
|
48
|
+
|
49
|
+
|
50
|
+
let(:args){ ['some_other_table', options ].compact }
|
51
|
+
|
52
|
+
context 'when a limit is specified' do
|
53
|
+
let(:options){{ limit: 6 }}
|
54
|
+
|
55
|
+
it 'uses the specified limit' do
|
56
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
57
|
+
'some_other_table', hash_including( limit: 6 )
|
58
|
+
)
|
59
|
+
abstract_table.references *args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when a limit is not specified' do
|
64
|
+
let(:options){}
|
65
|
+
|
66
|
+
it 'defaults the limit to 8' do
|
67
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
68
|
+
'some_other_table', hash_including( limit: 8 )
|
69
|
+
)
|
70
|
+
abstract_table.references *args
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when reference is polymorphic' do
|
75
|
+
context 'when there is not additional options' do
|
76
|
+
let(:options){{ polymorphic: true }}
|
77
|
+
|
78
|
+
it 'should not contain limit' do
|
79
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
80
|
+
'some_other_table', hash_including( polymorphic: {} )
|
81
|
+
)
|
82
|
+
abstract_table.references *args
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when there is common options' do
|
87
|
+
let(:options){{ null: false, polymorphic: true }}
|
88
|
+
|
89
|
+
it "should contain common options" do
|
90
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
91
|
+
'some_other_table', hash_including( polymorphic: { null: false } )
|
92
|
+
)
|
93
|
+
abstract_table.references *args
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when there is polymorphic options" do
|
98
|
+
let(:options){{ null: false, polymorphic: { limit: 120 } }}
|
99
|
+
|
100
|
+
it "should contain polymorphic options" do
|
101
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
102
|
+
'some_other_table', hash_including( polymorphic: { limit: 120 } )
|
103
|
+
)
|
104
|
+
abstract_table.references *args
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ActiveRecord::ConnectionAdapters::Table do
|
112
|
+
let(:abstract_table_class){ ActiveRecord::ConnectionAdapters::TableDefinition }
|
113
|
+
let(:abstract_table){ abstract_table_class.new Object.new }
|
114
|
+
it_makes_references_default_to_64bit
|
115
|
+
end
|
116
|
+
|
117
|
+
describe ActiveRecord::ConnectionAdapters::TableDefinition do
|
118
|
+
let(:connection_adapter_double) do
|
119
|
+
double("Connection Adapter").tap do |double|
|
120
|
+
double.stub :add_column
|
121
|
+
end
|
122
|
+
end
|
123
|
+
let(:abstract_table_class){ ActiveRecord::ConnectionAdapters::Table }
|
124
|
+
let(:abstract_table) do
|
125
|
+
abstract_table_class.new 'test_table', connection_adapter_double
|
126
|
+
end
|
127
|
+
it_makes_references_default_to_64bit
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when not enabled' do
|
132
|
+
let(:prok){ lambda{|c| c.enabled = false }}
|
133
|
+
|
134
|
+
it 'does not alter the default primary key for the postgres adapter' do
|
135
|
+
expect(
|
136
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::
|
137
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
138
|
+
).to eq 'serial primary key'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'does not alter the default primary key for either mysql adapters' do
|
142
|
+
expect(
|
143
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::
|
144
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
145
|
+
).to eq 'int(11) DEFAULT NULL auto_increment PRIMARY KEY'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|