dbgeni 0.10.0
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 +7 -0
- data/Rakefile +29 -0
- data/bin/dbgeni +2 -0
- data/lib/dbgeni/base.rb +146 -0
- data/lib/dbgeni/base_code.rb +143 -0
- data/lib/dbgeni/blank_slate.rb +24 -0
- data/lib/dbgeni/cli.rb +96 -0
- data/lib/dbgeni/code.rb +235 -0
- data/lib/dbgeni/code_list.rb +60 -0
- data/lib/dbgeni/commands/code.rb +151 -0
- data/lib/dbgeni/commands/commands.rb +41 -0
- data/lib/dbgeni/commands/config.rb +36 -0
- data/lib/dbgeni/commands/dmls.rb +244 -0
- data/lib/dbgeni/commands/generate.rb +257 -0
- data/lib/dbgeni/commands/initialize.rb +41 -0
- data/lib/dbgeni/commands/migrations.rb +243 -0
- data/lib/dbgeni/commands/milestones.rb +52 -0
- data/lib/dbgeni/commands/new.rb +178 -0
- data/lib/dbgeni/config.rb +325 -0
- data/lib/dbgeni/connectors/connector.rb +59 -0
- data/lib/dbgeni/connectors/mysql.rb +146 -0
- data/lib/dbgeni/connectors/oracle.rb +149 -0
- data/lib/dbgeni/connectors/sqlite.rb +166 -0
- data/lib/dbgeni/connectors/sybase.rb +97 -0
- data/lib/dbgeni/dml_cli.rb +35 -0
- data/lib/dbgeni/environment.rb +161 -0
- data/lib/dbgeni/exceptions/exception.rb +69 -0
- data/lib/dbgeni/file_converter.rb +44 -0
- data/lib/dbgeni/initializers/initializer.rb +44 -0
- data/lib/dbgeni/initializers/mysql.rb +36 -0
- data/lib/dbgeni/initializers/oracle.rb +38 -0
- data/lib/dbgeni/initializers/sqlite.rb +33 -0
- data/lib/dbgeni/initializers/sybase.rb +34 -0
- data/lib/dbgeni/logger.rb +60 -0
- data/lib/dbgeni/migration.rb +302 -0
- data/lib/dbgeni/migration_cli.rb +204 -0
- data/lib/dbgeni/migration_list.rb +91 -0
- data/lib/dbgeni/migrators/migrator.rb +40 -0
- data/lib/dbgeni/migrators/migrator_interface.rb +51 -0
- data/lib/dbgeni/migrators/mysql.rb +82 -0
- data/lib/dbgeni/migrators/oracle.rb +211 -0
- data/lib/dbgeni/migrators/sqlite.rb +90 -0
- data/lib/dbgeni/migrators/sybase.rb +118 -0
- data/lib/dbgeni/plugin.rb +92 -0
- data/lib/dbgeni.rb +52 -0
- metadata +87 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module DBGeni
|
2
|
+
|
3
|
+
class FileConverter
|
4
|
+
|
5
|
+
def self.convert(directory, file, config)
|
6
|
+
fc = new(directory, file, config)
|
7
|
+
fc.convert
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(directory, file, config)
|
11
|
+
@directory = directory
|
12
|
+
@file = file
|
13
|
+
@config = config
|
14
|
+
create_temp
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert
|
18
|
+
original_file = File.join(@directory, @file)
|
19
|
+
output_file = File.join(@temp_dir, @file)
|
20
|
+
begin
|
21
|
+
of = File.open(output_file, 'w')
|
22
|
+
File.foreach(original_file) do |line|
|
23
|
+
# remove potential \r\n from dos files. isql chokes on these on linux
|
24
|
+
# but not on windows.
|
25
|
+
line.chomp!
|
26
|
+
of.print line
|
27
|
+
of.print "\n"
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
of.close
|
31
|
+
end
|
32
|
+
output_file
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def create_temp
|
38
|
+
@temp_dir = File.join(@config.base_directory, 'log', 'temp')
|
39
|
+
FileUtils.mkdir_p(@temp_dir)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module DBGeni
|
2
|
+
module Initializer
|
3
|
+
|
4
|
+
def self.initialize(db_connection, config)
|
5
|
+
required_module = setup(config.db_type)
|
6
|
+
begin
|
7
|
+
required_method = required_module.method("initialize")
|
8
|
+
rescue NameError
|
9
|
+
raise DBGeni::InvalidInitializerForDBType, config.db_type
|
10
|
+
end
|
11
|
+
required_method.call(db_connection, config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.initialized?(db_connection, config)
|
15
|
+
required_module = setup(config.db_type)
|
16
|
+
begin
|
17
|
+
required_method = required_module.method("initialized?")
|
18
|
+
rescue NameError
|
19
|
+
raise DBGeni::InvalidInitializerForDBType, config.db_type
|
20
|
+
end
|
21
|
+
required_method.call(db_connection, config)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.setup(db_type)
|
27
|
+
begin
|
28
|
+
require "dbgeni/initializers/#{db_type}"
|
29
|
+
rescue
|
30
|
+
raise DBGeni::NoInitializerForDBType, db_type
|
31
|
+
end
|
32
|
+
|
33
|
+
required_module = nil
|
34
|
+
if Initializer.const_defined?(db_type.capitalize)
|
35
|
+
required_module = Initializer.const_get(db_type.capitalize)
|
36
|
+
else
|
37
|
+
raise raise DBGeni::NoInitializerForDBType, db_type
|
38
|
+
end
|
39
|
+
required_module
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DBGeni
|
2
|
+
module Initializer
|
3
|
+
module Mysql
|
4
|
+
|
5
|
+
def self.initialize(db_connection, config)
|
6
|
+
raise DBGeni::DatabaseAlreadyInitialized if self.initialized?(db_connection, config)
|
7
|
+
db_connection.execute("create table #{config.db_table}
|
8
|
+
(
|
9
|
+
sequence_or_hash varchar(100) not null,
|
10
|
+
migration_name varchar(4000) not null,
|
11
|
+
migration_type varchar(20) not null,
|
12
|
+
migration_state varchar(20) not null,
|
13
|
+
start_dtm datetime,
|
14
|
+
completed_dtm datetime
|
15
|
+
)")
|
16
|
+
db_connection.execute("create unique index #{config.db_table}_uk1 on #{config.db_table} (sequence_or_hash, migration_name(500), migration_type)")
|
17
|
+
db_connection.execute("create index #{config.db_table}_idx2 on #{config.db_table} (migration_name)")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.initialized?(db_connection, config)
|
21
|
+
# it is initialized if a table called dbgeni_migrations or whatever is
|
22
|
+
# defined in config exists
|
23
|
+
results = db_connection.execute("show tables like '#{config.db_table.downcase}'")
|
24
|
+
if 0 == results.length
|
25
|
+
false
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module DBGeni
|
2
|
+
module Initializer
|
3
|
+
module Oracle
|
4
|
+
|
5
|
+
def self.initialize(db_connection, config)
|
6
|
+
raise DBGeni::DatabaseAlreadyInitialized if self.initialized?(db_connection, config)
|
7
|
+
db_connection.execute("create table #{config.db_table}
|
8
|
+
(
|
9
|
+
sequence_or_hash varchar2(100) not null,
|
10
|
+
migration_name varchar2(4000) not null,
|
11
|
+
migration_type varchar2(20) not null,
|
12
|
+
migration_state varchar2(20) not null,
|
13
|
+
start_dtm date,
|
14
|
+
completed_dtm date
|
15
|
+
)")
|
16
|
+
db_connection.execute("create unique index #{config.db_table}_uk1 on #{config.db_table} (sequence_or_hash, migration_name, migration_type)")
|
17
|
+
db_connection.execute("create index #{config.db_table}_idx2 on #{config.db_table} (migration_name)")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.initialized?(db_connection, config)
|
21
|
+
# it is initialized if a table called dbgeni_migrations or whatever is
|
22
|
+
# defined in config exists
|
23
|
+
results = db_connection.execute("select table_name from all_tables where table_name = :t and owner = :o",
|
24
|
+
config.db_table.upcase,
|
25
|
+
config.env.install_schema ? config.env.install_schema.upcase : config.env.username.upcase)
|
26
|
+
if 0 == results.length
|
27
|
+
false
|
28
|
+
else
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module DBGeni
|
2
|
+
module Initializer
|
3
|
+
module Sqlite
|
4
|
+
|
5
|
+
def self.initialize(db_connection, config)
|
6
|
+
raise DBGeni::DatabaseAlreadyInitialized if self.initialized?(db_connection, config)
|
7
|
+
db_connection.execute("create table #{config.db_table}
|
8
|
+
(
|
9
|
+
sequence_or_hash varchar2(100) not null,
|
10
|
+
migration_name varchar2(4000) not null,
|
11
|
+
migration_type varchar2(20) not null,
|
12
|
+
migration_state varchar2(20) not null,
|
13
|
+
start_dtm date,
|
14
|
+
completed_dtm date
|
15
|
+
)")
|
16
|
+
db_connection.execute("create unique index #{config.db_table}_uk1 on #{config.db_table} (sequence_or_hash, migration_name, migration_type)")
|
17
|
+
db_connection.execute("create index #{config.db_table}_idx2 on #{config.db_table} (migration_name)")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.initialized?(db_connection, config)
|
21
|
+
# it is initialized if a table called dbgeni_migrations or whatever is
|
22
|
+
# defined in config exists
|
23
|
+
results = db_connection.execute("SELECT name FROM sqlite_master WHERE name = :t", config.db_table.downcase)
|
24
|
+
if 0 == results.length
|
25
|
+
false
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module DBGeni
|
2
|
+
module Initializer
|
3
|
+
module Sybase
|
4
|
+
|
5
|
+
def self.initialize(db_connection, config)
|
6
|
+
raise DBGeni::DatabaseAlreadyInitialized if self.initialized?(db_connection, config)
|
7
|
+
db_connection.execute("create table #{config.db_table}
|
8
|
+
(
|
9
|
+
sequence_or_hash varchar(100) not null,
|
10
|
+
migration_name varchar(1100) not null,
|
11
|
+
migration_type varchar(20) not null,
|
12
|
+
migration_state varchar(20) not null,
|
13
|
+
start_dtm datetime null,
|
14
|
+
completed_dtm datetime null
|
15
|
+
)")
|
16
|
+
db_connection.execute("create unique index #{config.db_table}_uk1 on #{config.db_table} (sequence_or_hash, migration_name, migration_type)")
|
17
|
+
db_connection.execute("create index #{config.db_table}_idx2 on #{config.db_table} (migration_name)")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.initialized?(db_connection, config)
|
21
|
+
# it is initialized if a table called dbgeni_migrations or whatever is
|
22
|
+
# defined in config exists
|
23
|
+
results = db_connection.execute("select name from sysobjects where name = \?", config.db_table.downcase)
|
24
|
+
if 0 == results.length
|
25
|
+
false
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module DBGeni
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
|
5
|
+
def self.instance(location=nil)
|
6
|
+
@@singleton_instance ||= self.new(location)
|
7
|
+
end
|
8
|
+
|
9
|
+
def info(msg)
|
10
|
+
write_msg(msg)
|
11
|
+
end
|
12
|
+
|
13
|
+
def error(msg)
|
14
|
+
write_msg("ERROR - #{msg}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def close
|
18
|
+
if @fh && !@fh.closed?
|
19
|
+
@fh.close
|
20
|
+
end
|
21
|
+
@@singleton_instance = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# This could be done in the initialize block, but then even for
|
25
|
+
# non destructive commands, there would be a detailed log dir
|
26
|
+
# created, so only create the dir when the directory is asked for.
|
27
|
+
def detailed_log_dir
|
28
|
+
FileUtils.mkdir_p(File.join(@log_location, @detailed_log_dir))
|
29
|
+
File.join(@log_location, @detailed_log_dir)
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_detailed_log_dir
|
33
|
+
@detailed_log_dir = Time.now.strftime('%Y%m%d%H%M%S')
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def write_msg(msg, echo=true)
|
39
|
+
if @fh && !@fh.closed?
|
40
|
+
@fh.puts "#{Time.now.strftime('%Y%m%d %H:%M:%S')} - #{msg}"
|
41
|
+
end
|
42
|
+
puts msg
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(location=nil)
|
46
|
+
# If location is nil, then error
|
47
|
+
@log_location = File.expand_path(location)
|
48
|
+
if @log_location
|
49
|
+
FileUtils.mkdir_p(location)
|
50
|
+
@fh = File.open("#{location}/log.txt", 'a')
|
51
|
+
@fh.puts("\n\n\n###################################################")
|
52
|
+
@fh.puts("dbgeni initialized")
|
53
|
+
reset_detailed_log_dir
|
54
|
+
@fh.puts("Detailed log files will be written in #{File.join(@log_location, @detailed_log_dir)}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
module DBGeni
|
2
|
+
|
3
|
+
class Migration
|
4
|
+
|
5
|
+
# These are all the states a migration can be in. The NEW status is never in the
|
6
|
+
# database, as that is the default state when it has been created as a file,
|
7
|
+
# but never applied to the database.
|
8
|
+
#
|
9
|
+
# PENDING - this is the state a migration goes into before the migration is runin
|
10
|
+
# and while it is running.
|
11
|
+
# COMPLETED - after the migration completes, if it was successful it gets moved to this state
|
12
|
+
# FAILED - after the migration completes, if it failed it gets moved to this state
|
13
|
+
# ROLLEDBACK - if a migration has been rolledback, it goes into this state.
|
14
|
+
NEW = 'New'
|
15
|
+
PENDING = 'Pending'
|
16
|
+
FAILED = 'Failed'
|
17
|
+
COMPLETED = 'Completed'
|
18
|
+
ROLLEDBACK = 'Rolledback'
|
19
|
+
# TODO - add verified state?
|
20
|
+
|
21
|
+
attr_reader :directory, :migration_file, :rollback_file, :name, :sequence, :logfile, :error_messages
|
22
|
+
attr_accessor :migration_type
|
23
|
+
|
24
|
+
def self.internal_name_from_filename(filename)
|
25
|
+
filename =~ /^(\d{12})_(up|down)_(.+)\.sql$/
|
26
|
+
"#{$1}::#{$3}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.filename_from_internal_name(internal_name)
|
30
|
+
internal_name =~ /^(\d{12})::(.+)$/
|
31
|
+
"#{$1}_up_#{$2}.sql"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.initialize_from_internal_name(directory, name)
|
35
|
+
self.new(directory, Migration.filename_from_internal_name(name))
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.get_milestone_migration(directory, name)
|
39
|
+
migration = ''
|
40
|
+
begin
|
41
|
+
f = File.open(File.join(directory,name), 'r')
|
42
|
+
migration = f.readline.chomp
|
43
|
+
rescue EOFError
|
44
|
+
ensure
|
45
|
+
f.close if f
|
46
|
+
end
|
47
|
+
unless migration =~ /^(\d{12})_(up|down)_(.+)\.sql$/
|
48
|
+
raise DBGeni::MilestoneHasNoMigration, name
|
49
|
+
end
|
50
|
+
migration
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(directory, migration)
|
54
|
+
@migration_type = 'Migration'
|
55
|
+
@directory = directory
|
56
|
+
@migration_file = migration
|
57
|
+
parse_file
|
58
|
+
@rollback_file = "#{sequence}_down_#{name}.sql"
|
59
|
+
@runnable_migration = nil
|
60
|
+
@runnable_rollback = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def migration_file(dir='up')
|
64
|
+
"#{@sequence}_#{dir}_#{name}.sql"
|
65
|
+
end
|
66
|
+
|
67
|
+
def ==(other)
|
68
|
+
if other.migration_file == @migration_file and other.directory == @directory
|
69
|
+
true
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def applied?(config, connection)
|
76
|
+
result = status(config, connection)
|
77
|
+
result == COMPLETED ? true : false
|
78
|
+
end
|
79
|
+
|
80
|
+
def status(config, connection)
|
81
|
+
set_env(config, connection)
|
82
|
+
results = connection.execute("select migration_state
|
83
|
+
from #{@config.db_table}
|
84
|
+
where sequence_or_hash = ?
|
85
|
+
and migration_name = ?
|
86
|
+
and migration_type = ?", @sequence, @name, @migration_type)
|
87
|
+
results.length == 1 ? results[0][0] : NEW
|
88
|
+
end
|
89
|
+
#"
|
90
|
+
|
91
|
+
def apply!(config, connection, force=nil)
|
92
|
+
set_env(config, connection)
|
93
|
+
if applied?(config, connection) and force != true
|
94
|
+
raise DBGeni::MigrationAlreadyApplied, self.to_s
|
95
|
+
end
|
96
|
+
ensure_file_exists
|
97
|
+
migrator = DBGeni::Migrator.initialize(config, connection)
|
98
|
+
convert_migration(config)
|
99
|
+
set_pending!
|
100
|
+
begin
|
101
|
+
migrator.apply(self, force)
|
102
|
+
set_completed!
|
103
|
+
rescue Exception => e
|
104
|
+
set_failed!
|
105
|
+
if e.class == DBGeni::MigratorCouldNotConnect
|
106
|
+
raise e
|
107
|
+
else
|
108
|
+
raise DBGeni::MigrationApplyFailed, self.to_s
|
109
|
+
end
|
110
|
+
ensure
|
111
|
+
@logfile = migrator.logfile
|
112
|
+
@error_messages = migrator.migration_errors
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def rollback!(config, connection, force=nil)
|
117
|
+
set_env(config, connection)
|
118
|
+
if [NEW, ROLLEDBACK].include? status(config, connection) and force != true
|
119
|
+
raise DBGeni::MigrationNotApplied, self.to_s
|
120
|
+
end
|
121
|
+
ensure_file_exists('down')
|
122
|
+
migrator = DBGeni::Migrator.initialize(config, connection)
|
123
|
+
convert_rollback(config)
|
124
|
+
set_pending!
|
125
|
+
begin
|
126
|
+
migrator.rollback(self, force)
|
127
|
+
set_rolledback!()
|
128
|
+
|
129
|
+
rescue Exception => e
|
130
|
+
set_failed!
|
131
|
+
if e.class == DBGeni::MigratorCouldNotConnect
|
132
|
+
raise e
|
133
|
+
else
|
134
|
+
raise DBGeni::MigrationApplyFailed, self.to_s
|
135
|
+
end
|
136
|
+
ensure
|
137
|
+
@logfile = migrator.logfile
|
138
|
+
@error_messages = migrator.migration_errors
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def verify!(config, connection)
|
143
|
+
end
|
144
|
+
|
145
|
+
def set_pending(config, connection)
|
146
|
+
set_env(config, connection)
|
147
|
+
set_pending!
|
148
|
+
end
|
149
|
+
|
150
|
+
def set_completed(config, connection)
|
151
|
+
set_env(config, connection)
|
152
|
+
set_completed!
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_failed(config, connection)
|
156
|
+
set_env(config, connection)
|
157
|
+
set_failed!
|
158
|
+
end
|
159
|
+
|
160
|
+
def set_rolledback(config, connection)
|
161
|
+
set_env(config, connection)
|
162
|
+
set_rolledback!
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
"#{@sequence}::#{@name}"
|
167
|
+
end
|
168
|
+
|
169
|
+
def convert_migration(config)
|
170
|
+
@runnable_migration = FileConverter.convert(@directory, @migration_file, config)
|
171
|
+
end
|
172
|
+
|
173
|
+
def convert_rollback(config)
|
174
|
+
@runnable_rollback = FileConverter.convert(@directory, @rollback_file, config)
|
175
|
+
end
|
176
|
+
|
177
|
+
def runnable_migration
|
178
|
+
if @runnable_migration
|
179
|
+
@runnable_migration
|
180
|
+
else
|
181
|
+
File.join(@directory, @migration_file)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def runnable_rollback
|
186
|
+
if @runnable_rollback
|
187
|
+
@runnable_rollback
|
188
|
+
else
|
189
|
+
File.join(@directory, @rollback_file)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def set_pending!
|
197
|
+
insert_or_set_state(PENDING)
|
198
|
+
end
|
199
|
+
|
200
|
+
def set_completed!
|
201
|
+
insert_or_set_state(COMPLETED)
|
202
|
+
end
|
203
|
+
|
204
|
+
def set_failed!
|
205
|
+
insert_or_set_state(FAILED)
|
206
|
+
end
|
207
|
+
|
208
|
+
def set_rolledback!
|
209
|
+
insert_or_set_state(ROLLEDBACK)
|
210
|
+
end
|
211
|
+
|
212
|
+
def set_env(config, connection)
|
213
|
+
@config = config
|
214
|
+
@connection = connection
|
215
|
+
end
|
216
|
+
|
217
|
+
def insert_or_set_state(state)
|
218
|
+
results = existing_db_record
|
219
|
+
if results.length == 0 then
|
220
|
+
add_db_record(state)
|
221
|
+
else
|
222
|
+
update_db_state(state)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def existing_db_record
|
227
|
+
results = @connection.execute("select sequence_or_hash, migration_name, migration_type, migration_state, start_dtm, completed_dtm
|
228
|
+
from #{@config.db_table}
|
229
|
+
where sequence_or_hash = ?
|
230
|
+
and migration_name = ?
|
231
|
+
and migration_type = ?", @sequence, @name, @migration_type)
|
232
|
+
end
|
233
|
+
#"
|
234
|
+
|
235
|
+
def add_db_record(state)
|
236
|
+
results = @connection.execute("insert into #{@config.db_table}
|
237
|
+
(
|
238
|
+
sequence_or_hash,
|
239
|
+
migration_name,
|
240
|
+
migration_type,
|
241
|
+
migration_state,
|
242
|
+
start_dtm
|
243
|
+
)
|
244
|
+
values
|
245
|
+
(
|
246
|
+
?,
|
247
|
+
?,
|
248
|
+
?,
|
249
|
+
?,
|
250
|
+
#{@connection.date_placeholder('sdtm')}
|
251
|
+
)", @sequence, @name, @migration_type, state, @connection.date_as_string(Time.now))
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
def update_db_state(state)
|
256
|
+
# What to set the dates to? If going to PENDING, then you want to make
|
257
|
+
# completed_dtm null and reset start_dtm to now.
|
258
|
+
#
|
259
|
+
# If going to anything else, then set completed_dtm to now
|
260
|
+
if state == PENDING
|
261
|
+
results = @connection.execute("update #{@config.db_table}
|
262
|
+
set migration_state = ?,
|
263
|
+
completed_dtm = null,
|
264
|
+
start_dtm = #{@connection.date_placeholder('sdtm')}
|
265
|
+
where sequence_or_hash = ?
|
266
|
+
and migration_name = ?
|
267
|
+
and migration_type = ?", state, @connection.date_as_string(Time.now), @sequence, @name, @migration_type)
|
268
|
+
else
|
269
|
+
results = @connection.execute("update #{@config.db_table}
|
270
|
+
set migration_state = ?,
|
271
|
+
completed_dtm = #{@connection.date_placeholder('sdtm')}
|
272
|
+
where sequence_or_hash = ?
|
273
|
+
and migration_name = ?
|
274
|
+
and migration_type = ?", state, @connection.date_as_string(Time.now), @sequence, @name, @migration_type)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
def parse_file
|
280
|
+
# filename is made up of 3 parts
|
281
|
+
# Sequence - YYYYMMDDHH(24)MI - ie datestamp down to minute
|
282
|
+
# Operation - allowed are up, down, verify
|
283
|
+
# Migration_name - any amount of text
|
284
|
+
# eg
|
285
|
+
# 201107011644_up_my_shiny_new_table.sql
|
286
|
+
#
|
287
|
+
unless @migration_file =~ /^(\d{12})_up_(.+)\.sql$/
|
288
|
+
raise DBGeni::MigrationFilenameInvalid, self.migration_file
|
289
|
+
end
|
290
|
+
@sequence = $1
|
291
|
+
@name = $2
|
292
|
+
end
|
293
|
+
|
294
|
+
def ensure_file_exists(dir='up')
|
295
|
+
unless File.exists? File.join(@directory, self.migration_file(dir))
|
296
|
+
raise DBGeni::MigrationFileNotExist, File.join(@directory, migration_file(dir))
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|