dbgeni 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +29 -0
  3. data/bin/dbgeni +2 -0
  4. data/lib/dbgeni/base.rb +146 -0
  5. data/lib/dbgeni/base_code.rb +143 -0
  6. data/lib/dbgeni/blank_slate.rb +24 -0
  7. data/lib/dbgeni/cli.rb +96 -0
  8. data/lib/dbgeni/code.rb +235 -0
  9. data/lib/dbgeni/code_list.rb +60 -0
  10. data/lib/dbgeni/commands/code.rb +151 -0
  11. data/lib/dbgeni/commands/commands.rb +41 -0
  12. data/lib/dbgeni/commands/config.rb +36 -0
  13. data/lib/dbgeni/commands/dmls.rb +244 -0
  14. data/lib/dbgeni/commands/generate.rb +257 -0
  15. data/lib/dbgeni/commands/initialize.rb +41 -0
  16. data/lib/dbgeni/commands/migrations.rb +243 -0
  17. data/lib/dbgeni/commands/milestones.rb +52 -0
  18. data/lib/dbgeni/commands/new.rb +178 -0
  19. data/lib/dbgeni/config.rb +325 -0
  20. data/lib/dbgeni/connectors/connector.rb +59 -0
  21. data/lib/dbgeni/connectors/mysql.rb +146 -0
  22. data/lib/dbgeni/connectors/oracle.rb +149 -0
  23. data/lib/dbgeni/connectors/sqlite.rb +166 -0
  24. data/lib/dbgeni/connectors/sybase.rb +97 -0
  25. data/lib/dbgeni/dml_cli.rb +35 -0
  26. data/lib/dbgeni/environment.rb +161 -0
  27. data/lib/dbgeni/exceptions/exception.rb +69 -0
  28. data/lib/dbgeni/file_converter.rb +44 -0
  29. data/lib/dbgeni/initializers/initializer.rb +44 -0
  30. data/lib/dbgeni/initializers/mysql.rb +36 -0
  31. data/lib/dbgeni/initializers/oracle.rb +38 -0
  32. data/lib/dbgeni/initializers/sqlite.rb +33 -0
  33. data/lib/dbgeni/initializers/sybase.rb +34 -0
  34. data/lib/dbgeni/logger.rb +60 -0
  35. data/lib/dbgeni/migration.rb +302 -0
  36. data/lib/dbgeni/migration_cli.rb +204 -0
  37. data/lib/dbgeni/migration_list.rb +91 -0
  38. data/lib/dbgeni/migrators/migrator.rb +40 -0
  39. data/lib/dbgeni/migrators/migrator_interface.rb +51 -0
  40. data/lib/dbgeni/migrators/mysql.rb +82 -0
  41. data/lib/dbgeni/migrators/oracle.rb +211 -0
  42. data/lib/dbgeni/migrators/sqlite.rb +90 -0
  43. data/lib/dbgeni/migrators/sybase.rb +118 -0
  44. data/lib/dbgeni/plugin.rb +92 -0
  45. data/lib/dbgeni.rb +52 -0
  46. metadata +87 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 662e5bb250525f1053d901edbae3d01ac733878f
4
+ data.tar.gz: d58cfb81adf70cd4674b47fa0259992fbc15d168
5
+ SHA512:
6
+ metadata.gz: 3e1a1d3e269c4e96e38abd113c056b70bab9732de9533d2ccf13499a7de5882de56a36c320e442029bf1f27b438f3f810e8aad53b1113dd3d7193189dff49c64
7
+ data.tar.gz: 242e3e2f823249aed271d674b19eebe23af1553939be7af93cee81af64ef1a88e6bc082cd41b980ea3e747bf52d04b3ea7908e9542d6ef0f3ebf597334a5d7e6
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/gempackagetask'
4
+
5
+
6
+ spec = Gem::Specification.new do |s|
7
+ s.name = "dbgeni"
8
+ s.version = "0.1.0"
9
+ s.author = "Stephen O'Donnell"
10
+ s.email = "stephen@betteratoracle.com"
11
+ s.homepage = "http://somewebsite.com"
12
+ s.platform = Gem::Platform::RUBY
13
+ s.summary = "A generic database installer"
14
+ s.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*'].reject{ |fn| fn.include? "temp" }
15
+ s.require_path = "lib"
16
+ s.bindir = "bin"
17
+ s.executables << 'dbgeni'
18
+ s.description = "Generic installer to manage database migrations for various databases"
19
+ # s.autorequire = "name"
20
+ # s.test_files = FileList["{test}/**/*test.rb"].to_a
21
+ s.has_rdoc = false
22
+ # s.extra_rdoc_files = ["README"]
23
+ # s.add_dependency("dependency", ">= 0.x.x")
24
+ end
25
+
26
+ Rake::GemPackageTask.new(spec) do |pkg|
27
+ pkg.need_tar = true
28
+ end
29
+
data/bin/dbgeni ADDED
@@ -0,0 +1,2 @@
1
+
2
+ require 'dbgeni/cli'
@@ -0,0 +1,146 @@
1
+ require 'dbgeni/logger'
2
+ require 'dbgeni/blank_slate'
3
+ require 'dbgeni/config'
4
+ require 'dbgeni/environment'
5
+ require 'dbgeni/file_converter'
6
+ require 'dbgeni/base_code'
7
+ require 'dbgeni/migration_list'
8
+ require 'dbgeni/migration'
9
+ require 'dbgeni/code_list'
10
+ require 'dbgeni/code'
11
+ require 'dbgeni/plugin'
12
+ require 'dbgeni/exceptions/exception'
13
+ require 'dbgeni/initializers/initializer'
14
+ require 'dbgeni/migrators/migrator'
15
+ require 'dbgeni/migrators/migrator_interface'
16
+ require 'dbgeni/connectors/connector'
17
+ require 'dbgeni/migration_cli'
18
+ require 'dbgeni/dml_cli'
19
+
20
+ require 'fileutils'
21
+
22
+ module DBGeni
23
+
24
+ class Base
25
+ attr_reader :config
26
+ # attr_reader :migrations
27
+
28
+ # This pulls in all the code related methods - listing, applying, removing
29
+ # TODO - turn this into a class like with the migrations and DML
30
+ include DBGeni::BaseModules::Code
31
+
32
+ def self.installer_for_environment(config_file, environment_name=nil)
33
+ installer = self.new(config_file)
34
+ # If environment is nil, then it assumes there is only a single environment
35
+ # defined. So pass the nil value to select_environment - if there is more than
36
+ # one environment then select_environment will error out after making a call
37
+ # to get_environment.
38
+ installer.select_environment(environment_name)
39
+ installer
40
+ end
41
+
42
+ def initialize(config_file)
43
+ load_config(config_file)
44
+ initialize_logger
45
+ end
46
+
47
+ def select_environment(environment_name)
48
+ current_environment = selected_environment_name
49
+ if current_environment != nil && current_environment != environment_name
50
+ # disconnect from database as the connection may well have changed!
51
+ disconnect
52
+ end
53
+ @config.set_env(environment_name)
54
+ end
55
+
56
+ def selected_environment_name
57
+ begin
58
+ @config.env.__environment_name
59
+ rescue DBGeni::ConfigAmbiguousEnvironment
60
+ nil
61
+ end
62
+ end
63
+
64
+
65
+ def run_plugin(hook, object, params={})
66
+ pdir = @config.plugin_directory
67
+ if pdir && pdir != ''
68
+ unless @plugin_manager
69
+ @plugin_manager = DBGeni::Plugin.new
70
+ @plugin_manager.load_plugins(pdir)
71
+ end
72
+ @plugin_manager.run_plugins(hook,
73
+ {
74
+ :logger => @logger,
75
+ :object => object,
76
+ :environment => @config.env,
77
+ :connection => connection
78
+ }.merge!(params)
79
+ )
80
+ end
81
+ end
82
+
83
+
84
+ def connect
85
+ raise DBGeni::NoEnvironmentSelected unless selected_environment_name
86
+ return @connection if @connection
87
+
88
+ @connection = DBGeni::Connector.initialize(@config)
89
+ end
90
+
91
+ def disconnect
92
+ if @connection
93
+ @connection.disconnect
94
+ end
95
+ @connection = nil
96
+ end
97
+
98
+ def connection
99
+ @connection ||= connect
100
+ end
101
+
102
+ def initialize_database
103
+ DBGeni::Initializer.initialize(connection, @config)
104
+ end
105
+
106
+ def database_initialized?
107
+ DBGeni::Initializer.initialized?(connection, @config)
108
+ end
109
+
110
+
111
+ def ensure_initialized
112
+ raise DBGeni::DatabaseNotInitialized unless database_initialized?
113
+ end
114
+
115
+ private
116
+
117
+ def initialize_logger
118
+ @logger = DBGeni::Logger.instance("#{@config.base_directory}/log")
119
+ end
120
+
121
+ def load_config(config)
122
+ @config = Config.load_from_file(config)
123
+ end
124
+
125
+ def delegate_to_migration_cli(meth, *args)
126
+ @migration_cli ||= MigrationCLI.new(self, @config, @logger)
127
+ @migration_cli.send(meth, *args)
128
+ end
129
+
130
+ def delegate_to_dml_cli(meth, *args)
131
+ @dml_cli ||= DmlCLI.new(self, @config, @logger)
132
+ @dml_cli.send(meth, *args)
133
+ end
134
+
135
+ def method_missing(meth, *args, &blk)
136
+ if meth =~ /migration/
137
+ delegate_to_migration_cli(meth.intern, *args)
138
+ elsif meth =~ /dml/
139
+ delegate_to_dml_cli(meth.to_s.gsub(/dml/,'migration').intern, *args)
140
+ else
141
+ super
142
+ end
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,143 @@
1
+ module DBGeni
2
+ module BaseModules
3
+ module Code
4
+
5
+ # This module isn't much good on its own, but it is here purely to
6
+ # break up the code in the Base class. This module should be included
7
+ # into base for code operations to work correctly
8
+
9
+ def code
10
+ @code_list ||= DBGeni::CodeList.new(@config.code_dir)
11
+ @code_list.code
12
+ end
13
+
14
+ def current_code
15
+ ensure_initialized
16
+ code
17
+ @code_list.current(@config, connection)
18
+ end
19
+
20
+ def outstanding_code
21
+ ensure_initialized
22
+ code
23
+ @code_list.outstanding(@config, connection)
24
+ end
25
+
26
+ def list_of_code(list)
27
+ ensure_initialized
28
+ code
29
+ @code_list.list(list, @config, connection)
30
+ end
31
+
32
+ # Applying
33
+
34
+ def apply_all_code(force=nil)
35
+ ensure_initialized
36
+ code_files = code
37
+ if code_files.length == 0
38
+ raise DBGeni::NoOutstandingCode
39
+ end
40
+ apply_code_list(code_files, true)
41
+ end
42
+
43
+ def apply_outstanding_code(force=nil)
44
+ ensure_initialized
45
+ code_files = outstanding_code
46
+ if code_files.length == 0
47
+ raise DBGeni::NoOutstandingCode
48
+ end
49
+ apply_code_list(code_files, force)
50
+ end
51
+
52
+ def apply_list_of_code(object_names, force=nil)
53
+ ensure_initialized
54
+ code_files = list_of_code(object_names)
55
+ apply_code_list(code_files, force)
56
+ end
57
+
58
+ def apply_code(code_obj, force=nil)
59
+ ensure_initialized
60
+ begin
61
+ run_plugin(:before_code_apply, code_obj)
62
+ code_obj.apply!(@config, connection, force)
63
+ if code_obj.error_messages
64
+ # Oracle can apply procs that still have errors. This is expected. Other databases
65
+ # have errors raised for invalid procs, except when force is on, so this logic is
66
+ # for when they are being forced through.
67
+ if @config.db_type == 'oracle'
68
+ @logger.info "Applied #{code_obj.to_s} (with errors)\n\n#{code_obj.error_messages}\nFull errors in #{code_obj.logfile}\n\n"
69
+ else
70
+ @logger.error "Failed to apply #{code_obj.filename}. Errors in #{code_obj.logfile}\n\n#{code_obj.error_messages}\n\n"
71
+ end
72
+ else
73
+ @logger.info "Applied #{code_obj.to_s}"
74
+ end
75
+ run_plugin(:after_code_apply, code_obj)
76
+ rescue DBGeni::MigratorCouldNotConnect
77
+ @logger.error "Failed to connect to the database CLI"
78
+ raise DBGeni::CodeApplyFailed
79
+ rescue DBGeni::CodeApplyFailed => e
80
+ # The only real way code can get here is if the user had insufficient privs
81
+ # to create the proc, or there was other bad stuff in the proc file.
82
+ # In this case, dbgeni should stop - but also treat the error like a migration error
83
+ # as the error message will be in the logfile in the format standard SQL errors are.
84
+ @logger.error "Failed to apply #{code_obj.filename}. Errors in #{code_obj.logfile}\n\n#{code_obj.error_messages}\n\n"
85
+ raise DBGeni::CodeApplyFailed, e.to_s
86
+ end
87
+ end
88
+
89
+ def remove_all_code(force=nil)
90
+ ensure_initialized
91
+ code_files = code
92
+ if code_files.length == 0
93
+ raise DBGeni::NoCodeFilesExist
94
+ end
95
+ apply_code_list(code_files, force, false)
96
+ end
97
+
98
+ def remove_list_of_code(object_names, force=nil)
99
+ ensure_initialized
100
+ code_files = list_of_code(object_names)
101
+ apply_code_list(code_files, force, false)
102
+ end
103
+
104
+ def remove_code(code_obj, force=nil)
105
+ ensure_initialized
106
+ begin
107
+ run_plugin(:before_code_remove, code_obj)
108
+ code_obj.remove!(@config, connection, force)
109
+ @logger.info "Removed #{code_obj.to_s}"
110
+ run_plugin(:after_code_remove, code_obj)
111
+ rescue DBGeni::MigratorCouldNotConnect
112
+ @logger.error "Failed to connect to the database CLI"
113
+ raise DBGeni::CodeRemoveFailed
114
+ rescue DBGeni::CodeRemoveFailed => e
115
+ # Not sure if the code can even get here. Many if timeout waiting for lock on object?
116
+ # In this case, dbgeni should stop - but also treat the error like a migration error
117
+
118
+ # TODO - not sure this is even correct - dropping code doesn't create a logfile ...
119
+ @logger.error "Failed to remove #{code_obj.filename}. Errors in #{code_obj.logfile}"
120
+ raise DBGeni::CodeRemoveFailed
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def apply_code_list(code_list, force, up=true)
127
+ params = {
128
+ :operation => up == true ? 'apply' : 'remove'
129
+ }
130
+ run_plugin(:before_modifying_code, code_list, params)
131
+ code_list.each do |c|
132
+ if up
133
+ apply_code(c, force)
134
+ else
135
+ remove_code(c, force)
136
+ end
137
+ end
138
+ run_plugin(:after_modifying_code, code_list, params)
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,24 @@
1
+ module DBGeni
2
+
3
+ class ::BlankSlate
4
+ KEEP_METHODS = [
5
+ :respond_to?,
6
+ :__id__,
7
+ :__send__,
8
+ :instance_eval,
9
+ :==,
10
+ :equal?,
11
+ :initialize,
12
+ :method_missing,
13
+ :instance_variable_set,
14
+ :send,
15
+ :alias_method
16
+ ]
17
+ suppress_warnings {
18
+ (instance_methods - KEEP_METHODS).each do |m|
19
+ undef_method(m)
20
+ end
21
+ }
22
+ end
23
+
24
+ end
data/lib/dbgeni/cli.rb ADDED
@@ -0,0 +1,96 @@
1
+ # hack to get the CLI working witout being properly installed as a gem
2
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib"))
3
+
4
+ $config_file = './.dbgeni'
5
+ $environment_name = nil
6
+ $force = false
7
+ $username = nil
8
+ $password = nil
9
+
10
+ if index = ARGV.index('--config-file') or index = ARGV.index('-c')
11
+ unless ARGV[index+1]
12
+ puts "error: --config-file switch present, but no file specified"
13
+ exit
14
+ end
15
+ $config_file = ARGV[index+1]
16
+ # remove all references to config file from the argument list
17
+ ARGV.delete_at(index)
18
+ ARGV.delete_at(index)
19
+ end
20
+
21
+ if index = ARGV.index('--environment-name') or index = ARGV.index('-e')
22
+ unless ARGV[index+1]
23
+ puts "error: --environment-name switch present, but no environment specified"
24
+ exit
25
+ end
26
+ $environment_name = ARGV[index+1]
27
+ # remove all references to config file from the argument list
28
+ ARGV.delete_at(index)
29
+ ARGV.delete_at(index)
30
+ end
31
+
32
+ if index = ARGV.index('--password') or index = ARGV.index('-p')
33
+ unless ARGV[index+1]
34
+ puts "error: --password switch present, but no password specified"
35
+ exit
36
+ end
37
+ $password = ARGV[index+1]
38
+ ARGV.delete_at(index)
39
+ ARGV.delete_at(index)
40
+ end
41
+
42
+ if index = ARGV.index('--username') or index = ARGV.index('-u')
43
+ unless ARGV[index+1]
44
+ puts "error: --username switch present, but no user specified"
45
+ exit
46
+ end
47
+ $username = ARGV[index+1]
48
+ ARGV.delete_at(index)
49
+ ARGV.delete_at(index)
50
+ end
51
+
52
+ if index = ARGV.index('--force') or index = ARGV.index('-f')
53
+ $force = true
54
+ ARGV.delete_at(index)
55
+ end
56
+
57
+ $build_installer = Proc.new {
58
+ installer = DBGeni::Base.installer_for_environment($config_file, $environment_name)
59
+ if $password or $username
60
+ env = installer.config.env
61
+ env.__enable_loading
62
+ if $password
63
+ env.password $password
64
+ end
65
+ if $username
66
+ env.username $username
67
+ end
68
+ env.__completed_loading
69
+ end
70
+ installer
71
+ }
72
+
73
+ require 'dbgeni'
74
+
75
+ begin
76
+ require 'dbgeni/commands/commands'
77
+ rescue DBGeni::ConfigSyntaxError => e
78
+ puts "There is an error in the config file: #{e.to_s}"
79
+ exit(1)
80
+ rescue DBGeni::ConfigFileNotExist => e
81
+ puts "The config file #{$config_file} does not exist"
82
+ exit(1)
83
+ rescue DBGeni::ConfigFileNotSpecified => e
84
+ puts "No config file was specified"
85
+ exit(1)
86
+ rescue DBGeni::ConfigAmbiguousEnvironment => e
87
+ puts "No environment specified and config file defines more than one environment"
88
+ exit(1)
89
+ rescue DBGeni::EnvironmentNotExist => e
90
+ puts "The environment #{$environment_name} does not exist"
91
+ exit(1)
92
+ rescue DBGeni::DBConnectionError => e
93
+ puts "Failed to establish databse connection: #{e.to_s}"
94
+ exit(1)
95
+ end
96
+
@@ -0,0 +1,235 @@
1
+ module DBGeni
2
+ class Code
3
+
4
+ attr_reader :directory, :filename, :type, :name, :logfile, :error_messages
5
+ PACKAGE_SPEC = 'PACKAGE SPEC'
6
+ PACKAGE_BODY = 'PACKAGE BODY'
7
+ PROCEDURE = 'PROCEDURE'
8
+ FUNCTION = 'FUNCTION'
9
+ TRIGGER = 'TRIGGER'
10
+ TYPE = 'TYPE'
11
+
12
+ UNKNOWN = 'UNKNOWN'
13
+
14
+ APPLIED = 'Applied'
15
+
16
+ EXT_MAP = {
17
+ 'pks' => PACKAGE_SPEC,
18
+ 'pkb' => PACKAGE_BODY,
19
+ 'prc' => PROCEDURE,
20
+ 'fnc' => FUNCTION,
21
+ 'trg' => TRIGGER,
22
+ 'typ' => TYPE,
23
+ 'sql' => UNKNOWN
24
+ }
25
+
26
+ def initialize(directory, filename)
27
+ @directory = directory
28
+ @filename = filename
29
+ @runnable_code = nil
30
+ set_type
31
+ set_name
32
+ end
33
+
34
+ def ==(other)
35
+ if @directory == other.directory && @type == other.type && @name == other.name
36
+ true
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ def sort_field
43
+ # Normally alphabetical sorting is enough, but this means pkg specs sort after
44
+ # bodies. So need to do something about that. The simplest *hack* is to replace
45
+ # pks with pka to get it to sort before pkb, just for sorting purposes
46
+ sortable = @filename.gsub(/\.pks$/, '.pka')
47
+ end
48
+
49
+ def db_hash(config, connection)
50
+ results = connection.execute("select sequence_or_hash
51
+ from #{config.db_table}
52
+ where migration_name = ?
53
+ and migration_type = ?", @name, @type)
54
+ results.length == 1 ? results[0][0] : nil
55
+ end
56
+ #" THis line is just to fix the bad syntax highlighting in emacs!
57
+
58
+ def hash
59
+ # TODO what if file is empty?
60
+ @hash ||= Digest::SHA1.file(File.join(@directory, @filename)).hexdigest
61
+ end
62
+
63
+ # if the DB hash equals the file hash then it is current
64
+ def current?(config, connection)
65
+ hash == nil_to_s(db_hash(config, connection))
66
+ end
67
+
68
+ # if a hash is found in the DB then it is applied.
69
+ def applied?(config, connection)
70
+ db_hash(config, connection) ? true : false
71
+ end
72
+
73
+ # If there is no db_hash then its outstanding.
74
+ def outstanding?(config, connection)
75
+ db_hash(config, connection) ? false : true
76
+ end
77
+
78
+ def set_applied(config, connection)
79
+ env(config, connection)
80
+ insert_or_set_state(APPLIED)
81
+ end
82
+
83
+ def set_removed(config, connection)
84
+ env(config, connection)
85
+ remove_db_record
86
+ end
87
+
88
+ def apply!(config, connection, force=false)
89
+ env(config, connection)
90
+ ensure_file_exists
91
+ if current?(config, connection) and force != true
92
+ raise DBGeni::CodeModuleCurrent, self.to_s
93
+ end
94
+ migrator = DBGeni::Migrator.initialize(config, connection)
95
+ convert_code(config)
96
+ begin
97
+ migrator.compile(self)
98
+ set_applied(config,connection)
99
+ rescue DBGeni::MigratorCouldNotConnect
100
+ @error_messages = ""
101
+ raise DBGeni::MigratorCouldNotConnect
102
+ rescue DBGeni::MigrationContainsErrors
103
+ # MYSQL (and sybase is like mysql) and Oracle procedures are handled different.
104
+ # In Oracle if the code fails to
105
+ # compile, it can be because missing objects are not there, but in mysql the proc
106
+ # will compile fine if objects are missing - the only reason it seems to not compile
107
+ # is if the syntax is bad.
108
+ # Also, an error loading mysql proc will result in the proc not being on the DB at all.
109
+ # Can argue either way if DBGeni should stop on code errors. As many oracle compile errors
110
+ # could be caused by objects that have not been created yet, best for Oracle to continue,
111
+ # but for mysql I think it is best to stop.
112
+ if migrator.class.to_s =~ /Oracle/
113
+ @error_messages = migrator.migration_errors
114
+ elsif migrator.class.to_s =~ /(Mysql|Sybase)/
115
+ @error_messages = migrator.code_errors
116
+ end
117
+ unless force
118
+ raise DBGeni::CodeApplyFailed
119
+ end
120
+ ensure
121
+ @logfile = migrator.logfile
122
+ # Only set this if it has not been set in the exception handler
123
+ @error_messages ||= migrator.code_errors
124
+ end
125
+ end
126
+
127
+ def remove!(config, connection, force=false)
128
+ env(config, connection)
129
+ ensure_file_exists
130
+ migrator = DBGeni::Migrator.initialize(config, connection)
131
+ begin
132
+ migrator.remove(self)
133
+ remove_db_record
134
+ rescue Exception => e
135
+ raise DBGeni::CodeRemoveFailed, e.to_s
136
+ end
137
+ end
138
+
139
+ def to_s
140
+ "#{@type.ljust(12)} #{@name}"
141
+ end
142
+
143
+ def convert_code(config)
144
+ @runnable_code = DBGeni::FileConverter.convert(@directory, @filename, config)
145
+ end
146
+
147
+ def runnable_code
148
+ if @runnable_code
149
+ @runnable_code
150
+ else
151
+ File.join(@directory, @filename)
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def insert_or_set_state(state)
158
+ # no hash in the DB means there is no db record...
159
+ results = db_hash(@config, @connection)
160
+ if results == nil then
161
+ add_db_record(state)
162
+ else
163
+ update_db_record(state)
164
+ end
165
+ end
166
+
167
+
168
+ def add_db_record(state)
169
+ results = @connection.execute("insert into #{@config.db_table}
170
+ (
171
+ sequence_or_hash,
172
+ migration_name,
173
+ migration_type,
174
+ migration_state,
175
+ start_dtm
176
+ )
177
+ values
178
+ (
179
+ ?,
180
+ ?,
181
+ ?,
182
+ ?,
183
+ #{@connection.date_placeholder('sdtm')}
184
+ )", hash, @name, @type, state, @connection.date_as_string(Time.now))
185
+ end
186
+
187
+
188
+ def update_db_record(state)
189
+ results = @connection.execute("update #{@config.db_table}
190
+ set sequence_or_hash = ?,
191
+ completed_dtm = #{@connection.date_placeholder('sdtm')},
192
+ migration_state = ?
193
+ where migration_type = ?
194
+ and migration_name = ?", hash, @connection.date_as_string(Time.now), state, @type, @name)
195
+ end
196
+
197
+
198
+ def remove_db_record
199
+ results = @connection.execute("delete from #{@config.db_table}
200
+ where migration_type = ?
201
+ and migration_name = ?", @type, @name)
202
+ end
203
+
204
+ def set_type
205
+ @filename =~ /\.(.+)$/
206
+ if EXT_MAP.has_key?($1)
207
+ @type = EXT_MAP[$1]
208
+ else
209
+ raise DBGeni::UnknownCodeType, $1
210
+ end
211
+ end
212
+
213
+ def set_name
214
+ # If a filename starts with <digit>+_ then that part should be
215
+ # stripped away to get the real name. It is purely for ordering.
216
+ @filename =~ /^(?:\d+_{1}){0,1}(.+)\.[a-z]{3}$/ #/^[\d+_]*(.+)\.[a-z]{3}$/
217
+ @name = $1.upcase
218
+ end
219
+
220
+ def env(config, connection)
221
+ @config = config
222
+ @connection = connection
223
+ end
224
+
225
+ def ensure_file_exists
226
+ unless File.exists? File.join(@directory, @filename)
227
+ raise DBGeni::CodeFileNotExist, File.join(@directory, @filename)
228
+ end
229
+ end
230
+
231
+ def nil_to_s(obj)
232
+ obj ? obj : ''
233
+ end
234
+ end
235
+ end