blue-shift 0.0.1 → 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 +4 -4
- data/.gitignore +2 -0
- data/Gemfile +6 -0
- data/README.md +14 -23
- data/Rakefile +1 -0
- data/blueshift.gemspec +2 -1
- data/lib/blueshift.rb +6 -0
- data/lib/blueshift/migration.rb +59 -0
- data/lib/blueshift/version.rb +1 -1
- data/lib/sequel/adapters/redshift.rb +53 -8
- data/lib/sequel/extensions/redshift_schema_dumper.rb +52 -0
- data/lib/tasks/generate_migration.rake +22 -0
- data/lib/tasks/schema.rake +50 -0
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a52af4406d213a8437391b173949c1a052275e6
|
4
|
+
data.tar.gz: c9626f7ca6049730d8afbabe686b515b9e4a9ecf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75d418562c8142619bb5e055d1645e75c8c5ca362f1dfbd2601339ca4c50103768f81a8afe28ce8920ac8b0ccc95fb18121810a47ad6f7974ff07f4a1843de2c
|
7
|
+
data.tar.gz: d96f4afb5b6cc5963eaa3c8693fe6d28f714724e5a435bb8affdd148211fb4dd953a4568cbf58be09eb3a3f3d23d46baeb4f213bd8d54b2399c5330106abc662
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -26,9 +26,9 @@ Or install it yourself as:
|
|
26
26
|
|
27
27
|
The Distribution Key. When specified, the :diststyle is set to :key unless otherwise specified
|
28
28
|
|
29
|
-
- `:diststyle` => `:even` (default), `:key`, or `:all`
|
29
|
+
- `:diststyle` => `:even` (implicit default), `:key`, or `:all`
|
30
30
|
|
31
|
-
The Distribution Style.
|
31
|
+
The Distribution Style. When `:distkey` is also specified, only `:key` DISTSTYLE is supported by Redshift.
|
32
32
|
|
33
33
|
- `:sortkeys` => a list of column names
|
34
34
|
|
@@ -54,45 +54,32 @@ end
|
|
54
54
|
|
55
55
|
### Migrations (coming soon)
|
56
56
|
|
57
|
-
Blueshift unifies migrations for your Postgres and Redshift databases into one file. Postgres migrations
|
58
|
-
(I hate that this is separated, but that is the state that our app currently exists in until we sort it out.)
|
57
|
+
Blueshift unifies migrations for your Postgres and Redshift databases into one file. Postgres migrations and Redshift migrations use Sequel.
|
59
58
|
|
60
59
|
```ruby
|
61
60
|
Blueshift.migration do
|
62
61
|
up do
|
63
|
-
# applies to Postgres
|
64
|
-
end
|
65
|
-
|
66
|
-
down do
|
67
|
-
# applies to Postgres + Redshift
|
68
|
-
end
|
69
|
-
end
|
70
|
-
```
|
71
|
-
|
72
|
-
If you want different migration behaviours for Redshift than for Postgres, you can override them by using `redup` and `reddown`:
|
73
|
-
|
74
|
-
```ruby
|
75
|
-
Blueshift.migration do
|
76
|
-
up do
|
77
|
-
# applies to Postgres only, because redup is defined below
|
62
|
+
# applies to Postgres only
|
78
63
|
end
|
79
64
|
|
80
65
|
down do
|
81
|
-
# applies to Postgres
|
66
|
+
# applies to Postgres only
|
82
67
|
end
|
83
68
|
|
84
69
|
redup do
|
85
70
|
# applies to Redshift only
|
86
71
|
end
|
72
|
+
|
73
|
+
reddown do
|
74
|
+
# applies to Redshift only
|
75
|
+
end
|
87
76
|
end
|
88
77
|
```
|
89
78
|
|
90
|
-
If you want your migration to only run on Postgres, you can specify
|
79
|
+
If you want your migration to only run on Postgres, you can specify an empty block:
|
91
80
|
|
92
81
|
```ruby
|
93
82
|
Blueshift.migration do
|
94
|
-
pg_only!
|
95
|
-
|
96
83
|
up do
|
97
84
|
# applies to Postgres only
|
98
85
|
end
|
@@ -100,6 +87,10 @@ Blueshift.migration do
|
|
100
87
|
down do
|
101
88
|
# applies to Postgres only
|
102
89
|
end
|
90
|
+
|
91
|
+
redup {}
|
92
|
+
|
93
|
+
reddown {}
|
103
94
|
end
|
104
95
|
```
|
105
96
|
|
data/Rakefile
CHANGED
data/blueshift.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_dependency 'sequel', '~>
|
21
|
+
spec.add_dependency 'sequel', '~> 4'
|
22
22
|
spec.add_dependency 'pg', '~> 0'
|
23
23
|
|
24
24
|
spec.add_development_dependency 'bundler', '~> 1.10'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
26
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
+
spec.add_development_dependency 'dotenv', '~> 2.1.0'
|
27
28
|
end
|
data/lib/blueshift.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'sequel/extensions/migration'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Blueshift
|
6
|
+
REDSHIFT_DB = Sequel.connect(ENV.fetch('REDSHIFT_URL'), logger: Logger.new('redshift.log'))
|
7
|
+
POSTGRES_DB = Sequel.connect(ENV.fetch('DATABASE_URL'), logger: Logger.new('postgres.log'))
|
8
|
+
|
9
|
+
class Migration
|
10
|
+
attr_reader :postgres_migration, :redshift_migration
|
11
|
+
MIGRATION_DIR = File.join(Dir.pwd, 'db/migrations')
|
12
|
+
|
13
|
+
def initialize(&block)
|
14
|
+
@postgres_migration = Sequel::SimpleMigration.new
|
15
|
+
@redshift_migration = Sequel::SimpleMigration.new
|
16
|
+
|
17
|
+
Sequel::Migration.descendants << self
|
18
|
+
instance_eval(&block)
|
19
|
+
validate!
|
20
|
+
end
|
21
|
+
|
22
|
+
def up(&block)
|
23
|
+
postgres_migration.up = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def down(&block)
|
27
|
+
postgres_migration.down = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def redup(&block)
|
31
|
+
redshift_migration.up = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def reddown(&block)
|
35
|
+
redshift_migration.down = block
|
36
|
+
end
|
37
|
+
|
38
|
+
def apply(db, direction)
|
39
|
+
if db.is_a?(Sequel::Redshift::Database)
|
40
|
+
redshift_migration.apply(db, direction)
|
41
|
+
else
|
42
|
+
postgres_migration.apply(db, direction)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.run!
|
47
|
+
Sequel::Migrator.run(POSTGRES_DB, MIGRATION_DIR, column: :postgres_version)
|
48
|
+
Sequel::Migrator.run(REDSHIFT_DB, MIGRATION_DIR, column: :redshift_version)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def validate!
|
54
|
+
unless [postgres_migration.up, postgres_migration.down, redshift_migration.up, redshift_migration.down].all?
|
55
|
+
raise ArgumentError, 'must declare blocks for up, down, redup, and reddown'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/blueshift/version.rb
CHANGED
@@ -9,12 +9,6 @@ module Sequel
|
|
9
9
|
SORTSTYLES = [:compound, :interleaved].freeze
|
10
10
|
DISTSTYLES = [:even, :key, :all].freeze
|
11
11
|
|
12
|
-
def column_definition_primary_key_sql(sql, column)
|
13
|
-
result = super
|
14
|
-
result << ' IDENTITY' if result
|
15
|
-
result
|
16
|
-
end
|
17
|
-
|
18
12
|
def serial_primary_key_options
|
19
13
|
# redshift doesn't support serial type
|
20
14
|
super.merge(serial: false)
|
@@ -29,15 +23,21 @@ module Sequel
|
|
29
23
|
def create_table_sql(name, generator, options)
|
30
24
|
validate_options!(options)
|
31
25
|
super.tap do |sql|
|
26
|
+
sql << diststyle_sql(options)
|
32
27
|
sql << distkey_sql(options)
|
33
28
|
sql << sortstyle_sql(options)
|
34
29
|
end
|
35
30
|
end
|
36
31
|
|
32
|
+
def diststyle_sql(options)
|
33
|
+
if options[:diststyle]
|
34
|
+
" DISTSTYLE #{options[:diststyle].to_s.upcase}"
|
35
|
+
end.to_s
|
36
|
+
end
|
37
|
+
|
37
38
|
def distkey_sql(options)
|
38
39
|
if options[:distkey]
|
39
|
-
options[:
|
40
|
-
" DISTSTYLE #{options[:diststyle].to_s.upcase} DISTKEY (#{options[:distkey]})"
|
40
|
+
" DISTKEY (#{options[:distkey]})"
|
41
41
|
end.to_s
|
42
42
|
end
|
43
43
|
|
@@ -56,6 +56,51 @@ module Sequel
|
|
56
56
|
def invalid?(value, allowed)
|
57
57
|
value && !allowed.include?(value)
|
58
58
|
end
|
59
|
+
|
60
|
+
# OVERRIDE for Redshift. Now always expect the "id" column to be the primary key
|
61
|
+
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
62
|
+
def schema_parse_table(table_name, opts)
|
63
|
+
m = output_identifier_meth(opts[:dataset])
|
64
|
+
ds = metadata_dataset.select(:pg_attribute__attname___name,
|
65
|
+
SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
|
66
|
+
SQL::Cast.new(:basetype__oid, :integer).as(:base_oid),
|
67
|
+
SQL::Function.new(:format_type, :basetype__oid, :pg_type__typtypmod).as(:db_base_type),
|
68
|
+
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
69
|
+
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
70
|
+
SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
|
71
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:name => 'id'), false).as(:primary_key)).
|
72
|
+
from(:pg_class).
|
73
|
+
join(:pg_attribute, :attrelid=>:oid).
|
74
|
+
join(:pg_type, :oid=>:atttypid).
|
75
|
+
left_outer_join(:pg_type___basetype, :oid=>:typbasetype).
|
76
|
+
left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
|
77
|
+
left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
|
78
|
+
filter(:pg_attribute__attisdropped=>false).
|
79
|
+
filter{|o| o.pg_attribute__attnum > 0}.
|
80
|
+
filter(:pg_class__oid=>regclass_oid(table_name, opts)).
|
81
|
+
order(:pg_attribute__attnum)
|
82
|
+
ds.map do |row|
|
83
|
+
row[:default] = nil if blank_object?(row[:default])
|
84
|
+
if row[:base_oid]
|
85
|
+
row[:domain_oid] = row[:oid]
|
86
|
+
row[:oid] = row.delete(:base_oid)
|
87
|
+
row[:db_domain_type] = row[:db_type]
|
88
|
+
row[:db_type] = row.delete(:db_base_type)
|
89
|
+
else
|
90
|
+
row.delete(:base_oid)
|
91
|
+
row.delete(:db_base_type)
|
92
|
+
end
|
93
|
+
row[:type] = schema_column_type(row[:db_type])
|
94
|
+
if row[:primary_key]
|
95
|
+
row[:auto_increment] = !!(row[:default] =~ /\Anextval/io)
|
96
|
+
end
|
97
|
+
[m.call(row.delete(:name)), row]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# OVERRIDE since indexes are not enforced by Redshift
|
102
|
+
def indexes(table, opts=OPTS)
|
103
|
+
end
|
59
104
|
end
|
60
105
|
|
61
106
|
class Dataset < Postgres::Dataset
|
@@ -0,0 +1,52 @@
|
|
1
|
+
Sequel.extension :schema_dumper
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Redshift
|
5
|
+
module SchemaDumper
|
6
|
+
include Sequel::SchemaDumper
|
7
|
+
|
8
|
+
def dump_table_schema(table, options=OPTS)
|
9
|
+
gen = dump_table_generator(table, options)
|
10
|
+
commands = [gen.dump_columns, gen.dump_constraints, gen.dump_indexes].reject { |x| x == '' }.join("\n\n")
|
11
|
+
|
12
|
+
"create_table(#{table.inspect}#{table_options(table, gen, options)}) do\n#{commands.gsub(/^/o, ' ')}\nend"
|
13
|
+
end
|
14
|
+
|
15
|
+
def table_options(table, gen, options)
|
16
|
+
s = {distkey: table_distkey(table),
|
17
|
+
sortkeys: table_sortkeys(table),
|
18
|
+
sortstyle: table_sortstyle(table),
|
19
|
+
ignore_index_errors: (!options[:same_db] && options[:indexes] != false && !gen.indexes.empty?)
|
20
|
+
}.select { |_,v| v }.inspect[1...-1]
|
21
|
+
|
22
|
+
s.empty? ? s : ", #{s}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def table_distkey(table)
|
28
|
+
key = pg_table_def(table).filter(distkey: true).map(:column).first
|
29
|
+
key.to_sym if key
|
30
|
+
end
|
31
|
+
|
32
|
+
def table_sortkeys(table)
|
33
|
+
keys = sortkey_columns(table).map{ |r| r[:column].to_sym }
|
34
|
+
keys unless keys.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def table_sortstyle(table)
|
38
|
+
:interleaved if sortkey_columns(table).any? { |row| row[:sortkey] < 0 }
|
39
|
+
end
|
40
|
+
|
41
|
+
def sortkey_columns(table)
|
42
|
+
pg_table_def(table).exclude(sortkey: 0).order(Sequel.function(:abs, :sortkey))
|
43
|
+
end
|
44
|
+
|
45
|
+
def pg_table_def(table)
|
46
|
+
self[:pg_table_def].where(schemaname: 'public', tablename: table.to_s).select(:column, :sortkey)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Database.register_extension(:redshift_schema_dumper, Redshift::SchemaDumper)
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
namespace :blueshift do
|
4
|
+
namespace :g do
|
5
|
+
desc 'Generate a timestamped, empty Blueshift migration.'
|
6
|
+
task :migration, :name do |_, args|
|
7
|
+
if args[:name].nil?
|
8
|
+
puts 'You must specify a migration name (e.g. rake generate:migration[create_events])!'
|
9
|
+
exit false
|
10
|
+
end
|
11
|
+
|
12
|
+
content = "Blueshift.migration do\n up do\n \n end\n\n down do\n \n end\n\n redup do\n \n end\n\n reddown do\n \n end\nend\n"
|
13
|
+
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
14
|
+
filename = File.join(Dir.pwd, 'db/migrations', "#{timestamp}_#{args[:name]}.rb")
|
15
|
+
|
16
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
17
|
+
File.open(filename, 'w') { |f| f << content }
|
18
|
+
|
19
|
+
puts "Created the migration #{filename}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'dotenv'
|
2
|
+
Dotenv.load
|
3
|
+
require 'fileutils'
|
4
|
+
require 'blueshift'
|
5
|
+
|
6
|
+
path = File.join(Dir.pwd, 'db')
|
7
|
+
|
8
|
+
task :ensure_db_dir do
|
9
|
+
FileUtils.mkdir_p(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
namespace :pg do
|
13
|
+
namespace :schema do
|
14
|
+
desc 'Dumps the Postgres schema to a file'
|
15
|
+
task :dump => :ensure_db_dir do
|
16
|
+
Blueshift::POSTGRES_DB.extension :redshift_schema_dumper
|
17
|
+
File.open(File.join(path, 'schema.rb'), 'w') { |f| f << Blueshift::POSTGRES_DB.dump_schema_migration(same_db: true) }
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Loads the Postgres schema from the file to the database'
|
21
|
+
task :load => :ensure_db_dir do
|
22
|
+
eval(File.join(path, 'schema.rb')).apply(Blueshift::POSTGRES_DB, :up)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
namespace :redshift do
|
29
|
+
namespace :schema do
|
30
|
+
desc 'Dumps the Postgres schema to a file'
|
31
|
+
task :dump => :ensure_db_dir do
|
32
|
+
Blueshift::REDSHIFT_DB.extension :redshift_schema_dumper
|
33
|
+
File.open(File.join(path, 'schema_redshift.rb'), 'w') { |f| f << Blueshift::REDSHIFT_DB.dump_schema_migration(same_db: true) }
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Loads the Postgres schema from the file to the database'
|
37
|
+
task :load => :ensure_db_dir do
|
38
|
+
eval(File.join(path, 'schema_redshift.rb')).apply(Blueshift::REDSHIFT_DB, :up)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
namespace :blueshift do
|
44
|
+
desc 'Runs migrations for both Postgres and Redshift'
|
45
|
+
task :migrate do
|
46
|
+
puts 'Running migrations for Postgres and Redshift...', ''
|
47
|
+
Blueshift::Migration.run!
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blue-shift
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Mansour
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '4'
|
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: '4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: dotenv
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.1.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.1.0
|
83
97
|
description: Amazon Redshift adapter for Sequel
|
84
98
|
email:
|
85
99
|
- dev+gabriel@influitive.com
|
@@ -101,8 +115,12 @@ files:
|
|
101
115
|
- bin/setup
|
102
116
|
- blueshift.gemspec
|
103
117
|
- lib/blueshift.rb
|
118
|
+
- lib/blueshift/migration.rb
|
104
119
|
- lib/blueshift/version.rb
|
105
120
|
- lib/sequel/adapters/redshift.rb
|
121
|
+
- lib/sequel/extensions/redshift_schema_dumper.rb
|
122
|
+
- lib/tasks/generate_migration.rake
|
123
|
+
- lib/tasks/schema.rake
|
106
124
|
homepage: https://github.com/influitive/blueshift
|
107
125
|
licenses: []
|
108
126
|
metadata: {}
|