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.
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