webdack-uuid_migration 0.0.2
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 +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +106 -0
- data/Rakefile +1 -0
- data/lib/webdack/uuid_migration.rb +2 -0
- data/lib/webdack/uuid_migration/helpers.rb +90 -0
- data/lib/webdack/uuid_migration/version.rb +7 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/initial_data.rb +23 -0
- data/spec/support/initial_schema.rb +40 -0
- data/spec/support/models/city.rb +3 -0
- data/spec/support/models/college.rb +3 -0
- data/spec/support/models/school.rb +3 -0
- data/spec/support/models/student.rb +5 -0
- data/spec/support/pg_database_helper.rb +20 -0
- data/spec/uuid_custom_pk_spec.rb +117 -0
- data/spec/uuid_migrate_helper_spec.rb +225 -0
- data/webdack-uuid_migration.gemspec +30 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
M2Y5MTYzM2UyMzU0ZmEyNTU0Y2IyMWE4NmU0ODUyMWI4YjMyMDU4Yw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ODMyMWJlOWNmOTYzMTkwYWEyM2E3YmM5NzM1OThkZWUyOGIzMjI4MQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTJhOThlN2RhZTZiZWU2OWVhZWU2YWM1MjkzNDdiN2JlNWE0ZmUxODJhZDZi
|
10
|
+
ZmRhNjRlYjVmNzRlMThiMTYxZjUyM2JkN2ExOGE5MGM1YjUxODI4N2MzYWVk
|
11
|
+
M2I0NDMyY2I5ODVkYjgyOTE1ODAyOWU2MzNlOTY1YTdjZTg4YTg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZTQwMmJjZTkxZmY1MDJmOWUyNDBmYjE4MjU2MjViMTg2MzgxZTdkNjc2MzM5
|
14
|
+
ZWE4MGY2ZWUxOWU5ZWU1NDMwODFjMjZkNDQ1MGRjOGQ5MjQzYzgwYWZhZjA0
|
15
|
+
OThhZjAxOTI4MDNjZGM5OTYxZjZiNTNmNTI3YmIxN2NhZDVlNWI=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Deepak Kumar
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Webdack::UuidMigration
|
2
|
+
|
3
|
+
Helper methods to migrate Integer columns to UUID columns during migrations in PostgreSQL.
|
4
|
+
It supports migrating primary key columns as well.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'webdack-uuid_migration'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install webdack-uuid_migration
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
- Put `require 'webdack/uuid_migration/helper'` in your migration file.
|
23
|
+
- Enable `'uuid-ossp'` directly in Postgres database or by adding `enable_extension 'uuid-ossp'` to your migration.
|
24
|
+
- Use methods from {Webdack::UUIDMigration::Helpers} as appropriate.
|
25
|
+
|
26
|
+
Example:
|
27
|
+
|
28
|
+
# You must explicitly require it in your migration file
|
29
|
+
require 'webdack/uuid_migration/helper'
|
30
|
+
|
31
|
+
class UuidMigration < ActiveRecord::Migration
|
32
|
+
def change
|
33
|
+
reversible do |dir|
|
34
|
+
dir.up do
|
35
|
+
# Good idea to do the following, needs superuser rights in the database
|
36
|
+
# Alternatively the extension needs to be manually enabled in the RDBMS
|
37
|
+
enable_extension 'uuid-ossp'
|
38
|
+
|
39
|
+
primary_key_to_uuid :students
|
40
|
+
|
41
|
+
primary_key_to_uuid :cities
|
42
|
+
primary_key_to_uuid :sections
|
43
|
+
columns_to_uuid :students, :city_id, :section_id
|
44
|
+
end
|
45
|
+
|
46
|
+
dir.down do
|
47
|
+
raise ActiveRecord::IrreversibleMigration
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Integer values are converted to UUID by padding 0's to the left. This makes it possible to
|
54
|
+
retrieve old id in future.
|
55
|
+
|
56
|
+
See {Webdack::UUIDMigration::Helpers} for more details. {Webdack::UUIDMigration::Helpers} is mixed
|
57
|
+
into {ActiveRecord::Migration}, so that all methods can directly be used within migrations.
|
58
|
+
|
59
|
+
### Polymorphic references
|
60
|
+
|
61
|
+
Migrating Polymorphic references may get tricky if not all the participating entities are getting migrated to
|
62
|
+
UUID primary keys. If only some of the referenced entities are getting migrated to use UUID primary keys please use the
|
63
|
+
following steps:
|
64
|
+
|
65
|
+
- Change the corresponding <column>_id to String type (at least VARCHAR(36)).
|
66
|
+
- Call `polymorphic_column_data_for_uuid :table, :column, 'Entity1', 'Entity2', ...`
|
67
|
+
- Note that :column in is without the _id.
|
68
|
+
- See {Webdack::UUIDMigration::Helpers#polymorphic_column_data_for_uuid}
|
69
|
+
- When all remaining references also gets migrated to UUID primary keys, call `columns_to_uuid :table, :column_id`
|
70
|
+
|
71
|
+
Example:
|
72
|
+
|
73
|
+
# Student -- belongs_to :institution, :polymorphic => true
|
74
|
+
# An institution is either a School or a College
|
75
|
+
# College is migrated to use UUID as primary key
|
76
|
+
# School uses Integer primary keys
|
77
|
+
|
78
|
+
# Place the following in migration script
|
79
|
+
primary_key_to_uuid :colleges
|
80
|
+
change_column :students, :institution_id, :string
|
81
|
+
polymorphic_column_data_for_uuid :students, :institution, 'College'
|
82
|
+
|
83
|
+
# When School also gets migrated to UUID primary key
|
84
|
+
primary_key_to_uuid :schools
|
85
|
+
columns_to_uuid :students, :institution_id
|
86
|
+
|
87
|
+
# See the rspec test case in spec folder for full example
|
88
|
+
|
89
|
+
|
90
|
+
## Compatibility
|
91
|
+
|
92
|
+
Works only with Rails 4. It uses Rails4's out-of-the-box UUID support for PostgreSQL. Works with Ruby 1.9.3 and 2.0.0.
|
93
|
+
|
94
|
+
To run the test suite:
|
95
|
+
|
96
|
+
# Update connection parameters in `spec/support/pg_database_helper.rb`.
|
97
|
+
# Postgres user must have rights to create/drop database and create extensions.
|
98
|
+
$ bundle exec rspec spec
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
1. Fork it ( http://github.com/kreatio-sw/webdack-uuid_migration/fork )
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Webdack
|
2
|
+
module UUIDMigration
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
# Converts primary key from Serial Integer to UUID, migrates all data by left padding with 0's
|
6
|
+
# sets uuid_generate_v4() as default for the column
|
7
|
+
#
|
8
|
+
# @param table [Symbol]
|
9
|
+
# @param options [hash]
|
10
|
+
# @option options [Symbol] :primary_key if not supplied queries the schema (should work most of the times)
|
11
|
+
# @option options [String] :default mechanism to generate UUID for new records, default uuid_generate_v4(),
|
12
|
+
# which is Rails 4.0.0 default as well
|
13
|
+
# @return [none]
|
14
|
+
def primary_key_to_uuid(table, options={})
|
15
|
+
default= options[:default] || 'uuid_generate_v4()'
|
16
|
+
|
17
|
+
column= connection.primary_key(table)
|
18
|
+
|
19
|
+
execute %Q{ALTER TABLE #{table}
|
20
|
+
ALTER COLUMN #{column} DROP DEFAULT,
|
21
|
+
ALTER COLUMN #{column} SET DATA TYPE UUID USING (#{to_uuid_pg(column)}),
|
22
|
+
ALTER COLUMN #{column} SET DEFAULT #{default}}
|
23
|
+
|
24
|
+
execute %Q{DROP SEQUENCE #{table}_#{column}_seq} rescue nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Converts a column to UUID, migrates all data by left padding with 0's
|
28
|
+
#
|
29
|
+
# @param table [Symbol]
|
30
|
+
# @param column [Symbol]
|
31
|
+
#
|
32
|
+
# @return [none]
|
33
|
+
def column_to_uuid(table, column)
|
34
|
+
execute %Q{ALTER TABLE #{table}
|
35
|
+
ALTER COLUMN #{column} SET DATA TYPE UUID USING (#{to_uuid_pg(column)})}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Converts columns to UUID, migrates all data by left padding with 0's
|
39
|
+
#
|
40
|
+
# @param table [Symbol]
|
41
|
+
# @param columns
|
42
|
+
#
|
43
|
+
# @return [none]
|
44
|
+
def columns_to_uuid(table, *columns)
|
45
|
+
columns.each do |column|
|
46
|
+
column_to_uuid(table, column)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Convert an Integer to UUID formatted string by left padding with 0's
|
51
|
+
#
|
52
|
+
# @param num [Integer]
|
53
|
+
# @return [String]
|
54
|
+
def int_to_uuid(num)
|
55
|
+
'00000000-0000-0000-0000-%012d' % num.to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
# Convert data values to UUID format for polymorphic associations. Useful when only few
|
59
|
+
# of associated entities have switched to UUID primary keys. Before calling this ensure that
|
60
|
+
# the corresponding column_id has been changed to :string (VARCHAR(36) or larger)
|
61
|
+
#
|
62
|
+
# See Polymorphic References in {file:README.md}
|
63
|
+
#
|
64
|
+
# @param table[Symbol]
|
65
|
+
# @param column [Symbol] it will change data in corresponding <column>_id
|
66
|
+
# @param entities [String] data referring these entities will be converted
|
67
|
+
def polymorphic_column_data_for_uuid(table, column, *entities)
|
68
|
+
list_of_entities= entities.map{|e| "'#{e}'"}.join(', ')
|
69
|
+
execute %Q{
|
70
|
+
UPDATE #{table} SET #{column}_id= #{to_uuid_pg("#{column}_id")}
|
71
|
+
WHERE #{column}_type in (#{list_of_entities})
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
# Prepare a fragment that can be used in SQL statements that converts teh data value
|
77
|
+
# from integer, string, or UUID to valid UUID string as per Postgres guidelines
|
78
|
+
#
|
79
|
+
# @param column [Symbol]
|
80
|
+
# @return [String]
|
81
|
+
def to_uuid_pg(column)
|
82
|
+
"uuid(lpad(replace(text(#{column}),'-',''), 32, '0'))"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
ActiveRecord::Migration.class_eval do
|
89
|
+
include Webdack::UUIDMigration::Helpers
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
def populate_sample_data
|
3
|
+
(0..4).each do |i|
|
4
|
+
City.create(name: "City #{i}")
|
5
|
+
School.create(name: "School #{i}")
|
6
|
+
College.create(name: "College #{i}")
|
7
|
+
end
|
8
|
+
|
9
|
+
(0..49).each do |i|
|
10
|
+
|
11
|
+
institution= if i.even? then
|
12
|
+
School.where(name: "School #{(i/2)%5}").first
|
13
|
+
else
|
14
|
+
College.where(name: "College #{(i/2)%5}").first
|
15
|
+
end
|
16
|
+
|
17
|
+
Student.create(
|
18
|
+
name: "Student #{i}",
|
19
|
+
city: City.where(name: "City #{i%5}").first,
|
20
|
+
institution: institution
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
def create_initial_schema
|
4
|
+
ActiveRecord::Schema.define(version: 20140117141611) do
|
5
|
+
|
6
|
+
# These are extensions that must be enabled in order to support this database
|
7
|
+
enable_extension "plpgsql"
|
8
|
+
|
9
|
+
create_table "cities", force: true do |t|
|
10
|
+
t.string "name"
|
11
|
+
t.datetime "created_at"
|
12
|
+
t.datetime "updated_at"
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table "colleges", force: true do |t|
|
16
|
+
t.string "name"
|
17
|
+
t.datetime "created_at"
|
18
|
+
t.datetime "updated_at"
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table "schools", force: true do |t|
|
22
|
+
t.string "name"
|
23
|
+
t.datetime "created_at"
|
24
|
+
t.datetime "updated_at"
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table "students", force: true do |t|
|
28
|
+
t.string "name"
|
29
|
+
t.integer "city_id"
|
30
|
+
t.string "institution_type"
|
31
|
+
t.integer "institution_id"
|
32
|
+
t.datetime "created_at"
|
33
|
+
t.datetime "updated_at"
|
34
|
+
|
35
|
+
t.index "city_id"
|
36
|
+
t.index ["institution_type", "institution_id"]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# With thanks to http://7fff.com/2010/12/02/activerecord-dropcreate-database-run-migrations-outside-of-rails/
|
2
|
+
|
3
|
+
PG_SPEC = {
|
4
|
+
:adapter => 'postgresql',
|
5
|
+
:host => 'localhost',
|
6
|
+
:database => 'webdack_uuid_migration_helper_test',
|
7
|
+
:username => 'kdeepak',
|
8
|
+
:encoding => 'utf8',
|
9
|
+
:password => 'kreatio'
|
10
|
+
}
|
11
|
+
|
12
|
+
def init_database
|
13
|
+
# drops and create need to be performed with a connection to the 'postgres' (system) database
|
14
|
+
ActiveRecord::Base.establish_connection(PG_SPEC.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
15
|
+
# drop the old database (if it exists)
|
16
|
+
ActiveRecord::Base.connection.drop_database PG_SPEC[:database] rescue nil
|
17
|
+
# create new
|
18
|
+
ActiveRecord::Base.connection.create_database(PG_SPEC[:database])
|
19
|
+
ActiveRecord::Base.establish_connection(PG_SPEC)
|
20
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
class MigrationBase < ActiveRecord::Migration
|
4
|
+
def change
|
5
|
+
create_table :states, primary_key: :stateid do |t|
|
6
|
+
t.string :name
|
7
|
+
end
|
8
|
+
|
9
|
+
enable_extension 'uuid-ossp'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Migration01 < ActiveRecord::Migration
|
14
|
+
def change
|
15
|
+
reversible do |dir|
|
16
|
+
dir.up do
|
17
|
+
primary_key_to_uuid :states
|
18
|
+
end
|
19
|
+
|
20
|
+
dir.down do
|
21
|
+
raise ActiveRecord::IrreversibleMigration
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Migration02 < ActiveRecord::Migration
|
28
|
+
def change
|
29
|
+
reversible do |dir|
|
30
|
+
dir.up do
|
31
|
+
primary_key_to_uuid :states, primary_key: :stateid
|
32
|
+
end
|
33
|
+
|
34
|
+
dir.down do
|
35
|
+
raise ActiveRecord::IrreversibleMigration
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Migration03 < ActiveRecord::Migration
|
42
|
+
def change
|
43
|
+
reversible do |dir|
|
44
|
+
dir.up do
|
45
|
+
primary_key_to_uuid :states, default: 'uuid_generate_v1()'
|
46
|
+
end
|
47
|
+
|
48
|
+
dir.down do
|
49
|
+
raise ActiveRecord::IrreversibleMigration
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class State < ActiveRecord::Base
|
56
|
+
end
|
57
|
+
|
58
|
+
describe Webdack::UUIDMigration::Helpers do
|
59
|
+
def initial_setup
|
60
|
+
init_database
|
61
|
+
|
62
|
+
MigrationBase.migrate(:up)
|
63
|
+
|
64
|
+
(0..9).each do |i|
|
65
|
+
State.create(name: "State #{i}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def reset_columns_data
|
70
|
+
[State].each{|klass| klass.reset_column_information}
|
71
|
+
end
|
72
|
+
|
73
|
+
def key_relationships
|
74
|
+
[
|
75
|
+
State.order(:name).map { |s| [s.name] }
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
before(:each) do
|
80
|
+
initial_setup
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should migrate table with custom primary_key' do
|
84
|
+
expect {
|
85
|
+
Migration01.migrate(:up)
|
86
|
+
reset_columns_data
|
87
|
+
}.to_not change {
|
88
|
+
key_relationships
|
89
|
+
}
|
90
|
+
|
91
|
+
expect(State.connection.primary_key(:states)).to eq 'stateid'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should honour primary_key with explicit hint' do
|
95
|
+
expect {
|
96
|
+
Migration02.migrate(:up)
|
97
|
+
reset_columns_data
|
98
|
+
}.to_not change {
|
99
|
+
key_relationships
|
100
|
+
}
|
101
|
+
|
102
|
+
expect(State.connection.primary_key(:states)).to eq 'stateid'
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should honour default' do
|
106
|
+
expect {
|
107
|
+
Migration03.migrate(:up)
|
108
|
+
reset_columns_data
|
109
|
+
}.to_not change {
|
110
|
+
key_relationships
|
111
|
+
}
|
112
|
+
|
113
|
+
default_function = State.connection.columns(:states).find { |c| c.name == 'stateid' }.default_function
|
114
|
+
expect(default_function).to eq 'uuid_generate_v1()'
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
class BasicMigration < ActiveRecord::Migration
|
4
|
+
def change
|
5
|
+
reversible do |dir|
|
6
|
+
dir.up do
|
7
|
+
enable_extension 'uuid-ossp'
|
8
|
+
|
9
|
+
primary_key_to_uuid :students
|
10
|
+
columns_to_uuid :students, :city_id, :institution_id
|
11
|
+
end
|
12
|
+
|
13
|
+
dir.down do
|
14
|
+
raise ActiveRecord::IrreversibleMigration
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class MigrateAllOneGo < ActiveRecord::Migration
|
21
|
+
def change
|
22
|
+
reversible do |dir|
|
23
|
+
dir.up do
|
24
|
+
enable_extension 'uuid-ossp'
|
25
|
+
|
26
|
+
primary_key_to_uuid :cities
|
27
|
+
primary_key_to_uuid :colleges
|
28
|
+
primary_key_to_uuid :schools
|
29
|
+
|
30
|
+
primary_key_to_uuid :students
|
31
|
+
columns_to_uuid :students, :city_id, :institution_id
|
32
|
+
end
|
33
|
+
|
34
|
+
dir.down do
|
35
|
+
raise ActiveRecord::IrreversibleMigration
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class MigrateStep01 < ActiveRecord::Migration
|
42
|
+
def change
|
43
|
+
reversible do |dir|
|
44
|
+
dir.up do
|
45
|
+
enable_extension 'uuid-ossp'
|
46
|
+
|
47
|
+
primary_key_to_uuid :cities
|
48
|
+
primary_key_to_uuid :colleges
|
49
|
+
|
50
|
+
primary_key_to_uuid :students
|
51
|
+
columns_to_uuid :students, :city_id
|
52
|
+
|
53
|
+
change_column :students, :institution_id, :string
|
54
|
+
polymorphic_column_data_for_uuid :students, :institution, 'College'
|
55
|
+
end
|
56
|
+
|
57
|
+
dir.down do
|
58
|
+
raise ActiveRecord::IrreversibleMigration
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class MigrateStep02 < ActiveRecord::Migration
|
65
|
+
def change
|
66
|
+
reversible do |dir|
|
67
|
+
dir.up do
|
68
|
+
primary_key_to_uuid :schools
|
69
|
+
columns_to_uuid :students, :institution_id
|
70
|
+
end
|
71
|
+
|
72
|
+
dir.down do
|
73
|
+
raise ActiveRecord::IrreversibleMigration
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe Webdack::UUIDMigration::Helpers do
|
80
|
+
def initial_setup
|
81
|
+
init_database
|
82
|
+
create_initial_schema
|
83
|
+
populate_sample_data
|
84
|
+
end
|
85
|
+
|
86
|
+
def reset_columns_data
|
87
|
+
[City, College, School, Student].each{|klass| klass.reset_column_information}
|
88
|
+
end
|
89
|
+
|
90
|
+
def key_relationships
|
91
|
+
[
|
92
|
+
Student.order(:name).map { |s| [s.name, s.city.name, s.institution.name] },
|
93
|
+
City.order(:name).map { |c| [c.name, c.students.order(:name).map(&:name)] },
|
94
|
+
School.order(:name).map { |s| [s.name, s.students.order(:name).map(&:name)] },
|
95
|
+
College.order(:name).map { |c| [c.name, c.students.order(:name).map(&:name)] }
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
before(:each) do
|
100
|
+
initial_setup
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'Basic Test' do
|
104
|
+
it 'should migrate keys correctly' do
|
105
|
+
# Select a random student
|
106
|
+
student = Student.all.to_a.sample
|
107
|
+
|
108
|
+
# Store these values to check against later
|
109
|
+
original_name= student.name
|
110
|
+
original_ids= [student.id, student.city_id, student.institution_id].map{|i| i.to_i}
|
111
|
+
|
112
|
+
# Migrate and verify that all indexes and primary keys are intact
|
113
|
+
expect {
|
114
|
+
BasicMigration.migrate(:up)
|
115
|
+
reset_columns_data
|
116
|
+
}.to_not change {
|
117
|
+
indexes= Student.connection.indexes(:students).sort_by { |i| i.name }.map do |i|
|
118
|
+
[i.table, i.name, i.unique, i.columns, i.lengths, i.orders, i.where]
|
119
|
+
end
|
120
|
+
|
121
|
+
[indexes, Student.connection.primary_key(:students)]
|
122
|
+
}
|
123
|
+
|
124
|
+
# Verify that our data is still there
|
125
|
+
student= Student.where(name: original_name).first
|
126
|
+
|
127
|
+
# Verify that data in id columns have been migrated to UUID by verifying teh format
|
128
|
+
[student.id, student.city_id, student.institution_id].each do |id|
|
129
|
+
expect(id).to match(/^0{8}-0{4}-0{4}-0{4}-\d{12}$/)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Verify that it is possible to retirve original id values
|
133
|
+
ids= [student.id, student.city_id, student.institution_id].map{|i| i.gsub('-','').to_i}
|
134
|
+
expect(ids).to eq(original_ids)
|
135
|
+
|
136
|
+
# Verify that schema reprts the migrated columns to be uuid type
|
137
|
+
columns= Student.connection.columns(:students)
|
138
|
+
[:id, :city_id, :institution_id].each do |column|
|
139
|
+
expect(columns.find{|c| c.name == column.to_s}.type).to eq :uuid
|
140
|
+
end
|
141
|
+
|
142
|
+
# Verify that primary key has correct default
|
143
|
+
expect(columns.find{|c| c.name == 'id'}.default_function).to eq 'uuid_generate_v4()'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should migrate entire database in one go' do
|
148
|
+
expect {
|
149
|
+
MigrateAllOneGo.migrate(:up)
|
150
|
+
reset_columns_data
|
151
|
+
}.to_not change {
|
152
|
+
key_relationships
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should migrate in steps for polymorphic association' do
|
157
|
+
expect {
|
158
|
+
MigrateStep01.migrate(:up)
|
159
|
+
reset_columns_data
|
160
|
+
}.to_not change {
|
161
|
+
key_relationships
|
162
|
+
}
|
163
|
+
|
164
|
+
expect {
|
165
|
+
MigrateStep02.migrate(:up)
|
166
|
+
reset_columns_data
|
167
|
+
}.to_not change {
|
168
|
+
key_relationships
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should allow running same migration data even if it was already migrated' do
|
173
|
+
expect {
|
174
|
+
MigrateStep01.migrate(:up)
|
175
|
+
# Run again
|
176
|
+
MigrateStep01.migrate(:up)
|
177
|
+
reset_columns_data
|
178
|
+
}.to_not change {
|
179
|
+
key_relationships
|
180
|
+
}
|
181
|
+
|
182
|
+
expect {
|
183
|
+
MigrateStep02.migrate(:up)
|
184
|
+
# Run again
|
185
|
+
MigrateStep02.migrate(:up)
|
186
|
+
reset_columns_data
|
187
|
+
}.to_not change {
|
188
|
+
key_relationships
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should allow updation, deletion, and new entity creation' do
|
193
|
+
MigrateAllOneGo.migrate(:up)
|
194
|
+
reset_columns_data
|
195
|
+
|
196
|
+
# Select a random student
|
197
|
+
student = Student.all.to_a.sample
|
198
|
+
|
199
|
+
id= student.id
|
200
|
+
student.name= 'New student 01'
|
201
|
+
student.save
|
202
|
+
student = Student.find(id)
|
203
|
+
|
204
|
+
expect(student.name).to eq 'New student 01'
|
205
|
+
|
206
|
+
expect { student.destroy }.to change { Student.count }.by(-1)
|
207
|
+
|
208
|
+
expect {Student.find(id)}.to raise_exception(ActiveRecord::RecordNotFound)
|
209
|
+
|
210
|
+
student= Student.create(
|
211
|
+
name: 'New student 02',
|
212
|
+
city: City.where(name: 'City 2').first,
|
213
|
+
institution: School.where(name: 'School 1').first
|
214
|
+
)
|
215
|
+
|
216
|
+
expect(City.where(name: 'City 2').first.students.where(name: 'New student 02').first.name).to eq 'New student 02'
|
217
|
+
expect(School.where(name: 'School 1').first.students.where(name: 'New student 02').first.name).to eq 'New student 02'
|
218
|
+
|
219
|
+
College.where(name: 'College 3').first.students << student
|
220
|
+
|
221
|
+
student.reload
|
222
|
+
|
223
|
+
expect(student.institution.name).to eq 'College 3'
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'webdack/uuid_migration/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "webdack-uuid_migration"
|
8
|
+
spec.version = Webdack::UUIDMigration::VERSION
|
9
|
+
spec.authors = ["Deepak Kumar"]
|
10
|
+
spec.email = ["deepak@kreatio.com"]
|
11
|
+
spec.summary = %q{Useful helpers to migrate Integer id columns to UUID in PostgreSql.}
|
12
|
+
spec.description = %q{Useful helpers to migrate Integer id columns to UUID in PostgreSql. Special support for primary keys.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "yard"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency "pg"
|
26
|
+
|
27
|
+
spec.add_dependency 'activerecord', '~> 4.0.0'
|
28
|
+
|
29
|
+
spec.has_rdoc= 'yard'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: webdack-uuid_migration
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Deepak Kumar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pg
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activerecord
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.0.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.0.0
|
97
|
+
description: Useful helpers to migrate Integer id columns to UUID in PostgreSql. Special
|
98
|
+
support for primary keys.
|
99
|
+
email:
|
100
|
+
- deepak@kreatio.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- .gitignore
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- lib/webdack/uuid_migration.rb
|
111
|
+
- lib/webdack/uuid_migration/helpers.rb
|
112
|
+
- lib/webdack/uuid_migration/version.rb
|
113
|
+
- spec/spec_helper.rb
|
114
|
+
- spec/support/initial_data.rb
|
115
|
+
- spec/support/initial_schema.rb
|
116
|
+
- spec/support/models/city.rb
|
117
|
+
- spec/support/models/college.rb
|
118
|
+
- spec/support/models/school.rb
|
119
|
+
- spec/support/models/student.rb
|
120
|
+
- spec/support/pg_database_helper.rb
|
121
|
+
- spec/uuid_custom_pk_spec.rb
|
122
|
+
- spec/uuid_migrate_helper_spec.rb
|
123
|
+
- webdack-uuid_migration.gemspec
|
124
|
+
homepage: ''
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.1.11
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Useful helpers to migrate Integer id columns to UUID in PostgreSql.
|
148
|
+
test_files:
|
149
|
+
- spec/spec_helper.rb
|
150
|
+
- spec/support/initial_data.rb
|
151
|
+
- spec/support/initial_schema.rb
|
152
|
+
- spec/support/models/city.rb
|
153
|
+
- spec/support/models/college.rb
|
154
|
+
- spec/support/models/school.rb
|
155
|
+
- spec/support/models/student.rb
|
156
|
+
- spec/support/pg_database_helper.rb
|
157
|
+
- spec/uuid_custom_pk_spec.rb
|
158
|
+
- spec/uuid_migrate_helper_spec.rb
|
159
|
+
has_rdoc: yard
|