dbgeni 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|