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
@@ -0,0 +1,90 @@
1
+ module DBGeni
2
+ module Migrator
3
+
4
+ class Sqlite < DBGeni::Migrator::MigratorInterface
5
+
6
+ def initialize(config, connection)
7
+ super(config, connection)
8
+ end
9
+
10
+ def migration_errors
11
+ ''
12
+ end
13
+
14
+ # def apply(migration, force=nil)
15
+ # end
16
+
17
+ # def rollback(migration, force=nil)
18
+ # end
19
+
20
+ # def verify(migration)
21
+ # end
22
+
23
+ def compile(code, force=false)
24
+ raise DBGeni::NotImplemented
25
+ end
26
+
27
+ # def remove(code)
28
+ # end
29
+
30
+ # def code_errors
31
+ # end
32
+
33
+ private
34
+
35
+ def run_in_client(file, force=nil)
36
+ null_device = '/dev/null'
37
+ if Kernel.is_windows?
38
+ null_device = 'NUL:'
39
+ end
40
+
41
+ @logfile = "#{@log_dir}/#{File.basename(file)}"
42
+ IO.popen("sqlite3 #{@connection.database} > #{@logfile} 2>&1", "w") do |p|
43
+ unless force
44
+ p.puts ".bail on"
45
+ end
46
+ p.puts ".echo on"
47
+ p.puts ".read #{file}"
48
+ p.puts ".quit"
49
+ end
50
+ # On OSX sqlite exits with 0 even when the sql script contains errors.
51
+ # On windows when there are errors it exits with 1.
52
+ #
53
+ # The only way to see if the script contained errors consistently is
54
+ # to grep the logfile for lines starting SQL error near line
55
+ # No point in checking if force is on as the errors don't matter anyway.
56
+ has_errors = false
57
+ unless force
58
+ # For empty migrations, sometimes no logfile?
59
+ if File.exists? @logfile
60
+ File.open(@logfile, 'r').each do |l|
61
+ if l =~ /^SQL error near line/
62
+ has_errors = true
63
+ break
64
+ end
65
+ end
66
+ end
67
+ end
68
+ # When the system call ends, ruby sets $? with the exit status. A
69
+ # good exit status is 0 (zero) anything else means it went wrong
70
+ # If $? is anything but zero, raise an exception.
71
+ if $? != 0 or has_errors
72
+ # if there were errors in the migration, SQLITE seems to set a non-zero
73
+ # exit status on **windows only**, depite running the migration to completion.
74
+ # So if the exit status is non-zero AND force is NOT true, raise, otherwise don't.
75
+ unless force
76
+ raise DBGeni::MigrationContainsErrors
77
+ end
78
+ end
79
+ end
80
+
81
+ def ensure_executable_exists
82
+ unless Kernel.executable_exists?('sqlite3')
83
+ raise DBGeni::DBCLINotOnPath
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,118 @@
1
+ module DBGeni
2
+ module Migrator
3
+
4
+ class Sybase < DBGeni::Migrator::MigratorInterface
5
+
6
+ def initialize(config, connection)
7
+ super(config, connection)
8
+ end
9
+
10
+ # Defined in super ...
11
+ # def apply(migration, force=nil)
12
+ # end
13
+
14
+ # def rollback(migration, force=nil)
15
+ # end
16
+
17
+ def migration_errors
18
+ error = ''
19
+ # The first error is the one to report - with sybase isql doesn't stop on errors
20
+ # The error lines tend to look like:
21
+ # Msg 156, Level 15, State 2:
22
+ # Server 'WW637L18714A', Line 2:
23
+ # Incorrect syntax near the keyword 'table'.
24
+ begin
25
+ fh = File.open(@logfile, 'r')
26
+ # search for a line starting Msg or Error and then grab the next 2 lines to make up the error.
27
+ while (l = fh.readline)
28
+ if l =~ /^(Msg|Error)\s\d+/
29
+ error = l
30
+ break
31
+ end
32
+ end
33
+ unless error == ''
34
+ # if an error was found, add the next two lines to the error message
35
+ error << fh.readline
36
+ error << fh.readline
37
+ end
38
+ rescue ::EOFError
39
+ # reached the end of file before a full error message was found ...
40
+ # Just catch and move on ...
41
+ ensure
42
+ fh.close if fh
43
+ end
44
+ error
45
+ end
46
+
47
+ # def verify(migration)
48
+ # end
49
+
50
+ def remove(code, force=false)
51
+ begin
52
+ @connection.execute(drop_command(code))
53
+ rescue Exception => e
54
+ unless e.to_s =~ /Cannot drop the .*(procedure|function|trigger).+exist in the system catalogs/i
55
+ raise DBGeni::CodeRemoveFailed, e.to_s
56
+ end
57
+ end
58
+ end
59
+
60
+ def code_errors
61
+ # In sybase the code errors are just the same as migration errors
62
+ errors = migration_errors
63
+ if errors == ''
64
+ errors = nil
65
+ end
66
+ errors
67
+ end
68
+
69
+ private
70
+
71
+ def run_in_client(file, force, is_proc=false)
72
+ @logfile = "#{@log_dir}/#{File.basename(file)}"
73
+
74
+ z = @config.env
75
+ # -e echos input
76
+ # -w200 - sets line width to 250 from the default of 80
77
+ response = system("isql -U#{z.username} -P#{z.password} -S#{z.sybase_service} -D#{z.database} -e -w 200 -i#{file} -o#{logfile}")
78
+
79
+ has_errors = false
80
+ unless force
81
+ # For empty migrations, sometimes no logfile?
82
+ if File.exists? @logfile
83
+ File.open(@logfile, 'r').each do |l|
84
+ if l =~ /^(Msg|Error)\s\d+/
85
+ has_errors = true
86
+ break
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ if has_errors or !response
93
+ unless force
94
+ raise DBGeni::MigrationContainsErrors
95
+ end
96
+ end
97
+ end
98
+
99
+ def ensure_executable_exists
100
+ unless Kernel.executable_exists?('isql')
101
+ raise DBGeni::DBCLINotOnPath
102
+ end
103
+ end
104
+
105
+ def drop_command(code)
106
+ case code.type
107
+ when DBGeni::Code::TRIGGER
108
+ "drop trigger #{code.name.downcase}"
109
+ when DBGeni::Code::FUNCTION
110
+ "drop function #{code.name.downcase}"
111
+ when DBGeni::Code::PROCEDURE
112
+ "drop procedure #{code.name.downcase}"
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,92 @@
1
+ module DBGeni
2
+ class Plugin
3
+ class << self
4
+ attr_reader :hooks
5
+
6
+ def install_plugin(hook, klass)
7
+ unless @hooks.has_key? hook
8
+ raise DBGeni::InvalidHook
9
+ end
10
+ @hooks[hook].push klass
11
+ end
12
+
13
+ def reset
14
+ @hooks.keys.each do |k|
15
+ @hooks[k] = Array.new
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ @hooks = {
22
+ :before_migration_up => [],
23
+ :after_migration_up => [],
24
+ :before_migration_down => [],
25
+ :after_migration_down => [],
26
+ :before_dml_up => [],
27
+ :after_dml_up => [],
28
+ :before_dml_down => [],
29
+ :after_dml_down => [],
30
+ :before_code_apply => [],
31
+ :after_code_apply => [],
32
+ :before_code_remove => [],
33
+ :after_code_remove => [],
34
+ :before_running_migrations => [],
35
+ :after_running_migrations => [],
36
+ :before_running_dmls => [],
37
+ :after_running_dmls => [],
38
+ :before_modifying_code => [],
39
+ :after_modifying_code => []
40
+ }
41
+
42
+
43
+ def initialize
44
+ end
45
+
46
+ def load_plugins(path)
47
+ begin
48
+ files = Dir.entries(path).grep(/\.rb$/).sort
49
+ rescue Errno::ENOENT, Errno::EACCES => e
50
+ raise DBGeni::PluginDirectoryNotAccessible, e.to_s
51
+ end
52
+ files.each do |f|
53
+ load_plugin File.join(path, f)
54
+ end
55
+ end
56
+
57
+ def load_plugin(filename)
58
+ require filename
59
+ end
60
+
61
+ def run_plugins(hook, attrs)
62
+ klasses = self.class.hooks[hook]
63
+ unless klasses.is_a? Array
64
+ raise DBGeni::InvalidHook, hook
65
+ end
66
+
67
+ klasses.each do |k|
68
+ run_plugin k, hook, attrs
69
+ end
70
+ end
71
+
72
+ def run_plugin(klass, hook, attrs)
73
+ instance = klass.new
74
+ unless instance.respond_to? :run
75
+ raise DBGeni::PluginDoesNotRespondToRun
76
+ end
77
+ instance.run(hook, attrs)
78
+ end
79
+
80
+ end
81
+ end
82
+
83
+ class Class
84
+ DBGeni::Plugin.hooks.keys.each do |k|
85
+ self.class_eval <<-end_eval
86
+ def #{k.to_s}
87
+ DBGeni::Plugin.install_plugin(:#{k.to_s}, self)
88
+ end
89
+ end_eval
90
+ end
91
+ end
92
+
data/lib/dbgeni.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'rbconfig'
2
+ require 'digest/sha1'
3
+
4
+ module Kernel
5
+
6
+ def self.is_windows?
7
+ # Ruby 1.9.3 warns if you use Config (instead of RbConfig) while older Ruby
8
+ # doesn't have RbConfig, only Config :-/
9
+ conf = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG
10
+ conf['host_os'] =~ /mswin|mingw/
11
+ end
12
+
13
+ def suppress_warnings
14
+ original_verbosity = $VERBOSE
15
+ $VERBOSE = nil
16
+ result = yield
17
+ $VERBOSE = original_verbosity
18
+ return result
19
+ end
20
+
21
+ def self.executable_exists?(cmd)
22
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
23
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
24
+ exts.each { |ext|
25
+ exe = "#{path}/#{cmd}#{ext}"
26
+ return exe if File.executable? exe
27
+ }
28
+ end
29
+ return nil
30
+ end
31
+
32
+ end
33
+
34
+ if RUBY_PLATFORM == 'java'
35
+ require 'rubygems'
36
+ require 'java'
37
+
38
+ conf = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG
39
+ if conf['ruby_version'] =~ /1\.8/
40
+ raise "DBGeni requires the --1.9 switch to be passed to jruby (or set env variable JRUBY_OPTS=--1.9)"
41
+ end
42
+
43
+ module JavaLang
44
+ include_package "java.lang"
45
+ end
46
+
47
+ module JavaSql
48
+ include_package 'java.sql'
49
+ end
50
+ end
51
+
52
+ require 'dbgeni/base'
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dbgeni
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephen O'Donnell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Generic installer to manage database migrations for various databases
14
+ email: stephen@betteratoracle.com
15
+ executables:
16
+ - dbgeni
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Rakefile
21
+ - bin/dbgeni
22
+ - lib/dbgeni.rb
23
+ - lib/dbgeni/base.rb
24
+ - lib/dbgeni/base_code.rb
25
+ - lib/dbgeni/blank_slate.rb
26
+ - lib/dbgeni/cli.rb
27
+ - lib/dbgeni/code.rb
28
+ - lib/dbgeni/code_list.rb
29
+ - lib/dbgeni/commands/code.rb
30
+ - lib/dbgeni/commands/commands.rb
31
+ - lib/dbgeni/commands/config.rb
32
+ - lib/dbgeni/commands/dmls.rb
33
+ - lib/dbgeni/commands/generate.rb
34
+ - lib/dbgeni/commands/initialize.rb
35
+ - lib/dbgeni/commands/migrations.rb
36
+ - lib/dbgeni/commands/milestones.rb
37
+ - lib/dbgeni/commands/new.rb
38
+ - lib/dbgeni/config.rb
39
+ - lib/dbgeni/connectors/connector.rb
40
+ - lib/dbgeni/connectors/mysql.rb
41
+ - lib/dbgeni/connectors/oracle.rb
42
+ - lib/dbgeni/connectors/sqlite.rb
43
+ - lib/dbgeni/connectors/sybase.rb
44
+ - lib/dbgeni/dml_cli.rb
45
+ - lib/dbgeni/environment.rb
46
+ - lib/dbgeni/exceptions/exception.rb
47
+ - lib/dbgeni/file_converter.rb
48
+ - lib/dbgeni/initializers/initializer.rb
49
+ - lib/dbgeni/initializers/mysql.rb
50
+ - lib/dbgeni/initializers/oracle.rb
51
+ - lib/dbgeni/initializers/sqlite.rb
52
+ - lib/dbgeni/initializers/sybase.rb
53
+ - lib/dbgeni/logger.rb
54
+ - lib/dbgeni/migration.rb
55
+ - lib/dbgeni/migration_cli.rb
56
+ - lib/dbgeni/migration_list.rb
57
+ - lib/dbgeni/migrators/migrator.rb
58
+ - lib/dbgeni/migrators/migrator_interface.rb
59
+ - lib/dbgeni/migrators/mysql.rb
60
+ - lib/dbgeni/migrators/oracle.rb
61
+ - lib/dbgeni/migrators/sqlite.rb
62
+ - lib/dbgeni/migrators/sybase.rb
63
+ - lib/dbgeni/plugin.rb
64
+ homepage: http://dbgeni.com
65
+ licenses: []
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.2.0
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: A generic database installer
87
+ test_files: []