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
@@ -0,0 +1,149 @@
1
+ module DBGeni
2
+
3
+ module Connector
4
+
5
+ class Oracle
6
+
7
+ if RUBY_PLATFORM == 'java'
8
+ require 'java'
9
+ java_import 'oracle.jdbc.OracleDriver'
10
+ java_import 'java.sql.DriverManager'
11
+ else
12
+ require 'oci8'
13
+ end
14
+
15
+ attr_reader :connection
16
+ attr_reader :database
17
+
18
+ def self.connect(user, password, database, host=nil, port=nil)
19
+ self.new(user, password, database, host, port)
20
+ end
21
+
22
+ def disconnect
23
+ @connection.logoff
24
+ end
25
+
26
+ def execute(sql, *binds)
27
+ if RUBY_PLATFORM == 'java'
28
+ return execute_jdbc(sql, *binds)
29
+ end
30
+
31
+ begin
32
+ # OCI doesn't like the ? bind variables, so need to convert them
33
+ index = 0
34
+ new_sql = sql.gsub(/\?/) do |m|
35
+ index += 1
36
+ ":b#{index.to_s}"
37
+ end
38
+
39
+ query = @connection.parse(new_sql)
40
+ query.exec(*binds)
41
+
42
+ results = nil
43
+ if query.type == OCI8::STMT_SELECT
44
+ results = Array.new
45
+ while r = query.fetch()
46
+ results.push r
47
+ end
48
+ else
49
+ # everthing is auto commit right now ...
50
+ @connection.commit
51
+ end
52
+ ensure
53
+ begin
54
+ query.close()
55
+ rescue
56
+ end
57
+ end
58
+ results
59
+ end
60
+
61
+ def ping
62
+ results = self.execute('select dummy from dual')
63
+ if results.length == 1
64
+ true
65
+ else
66
+ false
67
+ end
68
+ end
69
+
70
+ def commit
71
+ @connection.commit
72
+ end
73
+
74
+ def rollback
75
+ @connection.rollback
76
+ end
77
+
78
+ def date_placeholder(bind_var)
79
+ "to_date(:#{bind_var}, 'YYYYMMDDHH24MISS')"
80
+ end
81
+
82
+ def date_as_string(dtm)
83
+ dtm.strftime '%Y%m%d%H%M%S'
84
+ end
85
+
86
+ private
87
+
88
+ def initialize(user, password, database, host=nil, port=1521)
89
+ @database = database
90
+
91
+ if RUBY_PLATFORM == 'java'
92
+ oradriver = OracleDriver.new
93
+ DriverManager.registerDriver oradriver
94
+ begin
95
+ @connection = DriverManager.get_connection("jdbc:oracle:thin:@#{host}:#{port}/#{database}", user, password)
96
+ @connection.auto_commit = true
97
+ rescue Exception => e
98
+ raise DBGeni::DBConnectionError, e.to_s
99
+ end
100
+ else
101
+ begin
102
+ @connection = OCI8.new(user, password, database)
103
+ rescue Exception => e
104
+ raise DBGeni::DBConnectionError, e.to_s
105
+ end
106
+ end
107
+ end
108
+
109
+ def execute_jdbc(sql, *binds)
110
+ begin
111
+ query = @connection.prepare_statement(sql)
112
+ binds.each_with_index do |b, i|
113
+ if b.is_a?(String)
114
+ query.setString(i+1, b)
115
+ elsif b.is_a?(Fixnum)
116
+ query.setInt(i+1, b)
117
+ end
118
+ end
119
+ results = nil
120
+ unless sql =~ /^\s*select/i
121
+ query.execute()
122
+ else
123
+ results = Array.new
124
+ rset = query.execute_query()
125
+ cols = rset.get_meta_data.get_column_count
126
+ while(r = rset.next) do
127
+ a = Array.new
128
+ 1.upto(cols) do |i|
129
+ if rset.get_meta_data.get_column_type_name(i) == 'NUMBER'
130
+ a.push rset.get_float(i)
131
+ else
132
+ a.push rset.get_object(i)
133
+ end
134
+ end
135
+ results.push a
136
+ end
137
+ end
138
+ results
139
+ ensure
140
+ begin
141
+ query.close()
142
+ rescue Exception => e
143
+ end
144
+ end
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,166 @@
1
+ module DBGeni
2
+
3
+ module Connector
4
+ class Sqlite
5
+
6
+ if RUBY_PLATFORM == 'java'
7
+ require 'rubygems'
8
+ require 'jdbc/sqlite3'
9
+ Java::org.sqlite.JDBC #initialize the driver
10
+ else
11
+ require 'sqlite3'
12
+ end
13
+
14
+ attr_reader :connection
15
+ attr_reader :database
16
+
17
+ def self.db_file_path(base, file)
18
+ # starts with a ., eg ./filename, or does not
19
+ # contain any forward or backslashes, eg db.sqlite3
20
+ if file =~ /^\./ || file !~ /\/|\\/
21
+ # it is a relative path, so join to base directory
22
+ File.join(base, file)
23
+ else
24
+ file
25
+ end
26
+ end
27
+
28
+ def self.connect(user, password, database)
29
+ raise DBGeni::DatabaseNotSpecified unless database
30
+ self.new(database)
31
+ end
32
+
33
+ def disconnect
34
+ @connection.close
35
+ end
36
+
37
+ def execute(sql, *binds)
38
+ # unless @connection.transaction_active?
39
+ # @connection.transaction
40
+ # end
41
+ if RUBY_PLATFORM == 'java'
42
+ return execute_jdbc(sql, *binds)
43
+ end
44
+ begin
45
+ # Why am I re-establishing the database connection? Well, something has changed
46
+ # somewhere in the sqlite3 application or ruby drivers, and without this line
47
+ # I get lots of errors in the test suite:
48
+ #
49
+ # sqlite 3.7.8 QLite3::SQLException: SQL logic error or missing database
50
+ #
51
+ # This line fixes it be reconnecting. I am not sure if this is a non-reentrant
52
+ # issue, but it used to work just fine :-/
53
+ # SQLITE + DBGeni is not really intended as a production combo, so this is probably
54
+ # ok.
55
+ @connection = SQLite3::Database.new(@database)
56
+ query = @connection.prepare(sql)
57
+ binds.each_with_index do |b, i|
58
+ query.bind_param(i+1, b)
59
+ end
60
+ results = query.execute!
61
+ # This shouldn't even be needed, as there are never transactions started.
62
+ # by default everthing in sqlite is autocommit
63
+ if @connection.transaction_active?
64
+ @connection.commit
65
+ end
66
+ results
67
+ ensure
68
+ begin
69
+ query.close
70
+ rescue Exception => e
71
+ end
72
+ end
73
+ end
74
+
75
+ def execute_jdbc(sql, *binds)
76
+ query = @connection.prepare_statement(sql)
77
+ binds.each_with_index do |b, i|
78
+ if b.is_a?(String)
79
+ query.setString(i+1, b)
80
+ elsif b.is_a?(Fixnum)
81
+ query.setInt(i+1, b)
82
+ end
83
+ end
84
+ results = Array.new
85
+ unless sql =~ /^\s*select/i
86
+ query.execute()
87
+ else
88
+ rset = query.execute_query()
89
+ cols = rset.get_meta_data.get_column_count
90
+ while(r = rset.next) do
91
+ a = Array.new
92
+ 1.upto(cols) do |i|
93
+ a.push rset.get_object(i)
94
+ end
95
+ results.push a
96
+ end
97
+ end
98
+ query.close
99
+ results
100
+ end
101
+
102
+ def ping
103
+ ! @connection.closed?
104
+ end
105
+
106
+ def commit
107
+ @connection.commit
108
+ end
109
+
110
+ def rollback
111
+ @connection.rollback
112
+ end
113
+
114
+ def date_placeholder(bind_var)
115
+ "time(:#{bind_var})"
116
+ end
117
+
118
+ def date_as_string(dtm)
119
+ dtm.strftime '%Y-%m-%d %H:%M:%S'
120
+ end
121
+
122
+ private
123
+
124
+ def initialize(database)
125
+ @database = database
126
+ begin
127
+ if RUBY_PLATFORM == 'java'
128
+ @connection = ::JavaSql::DriverManager.getConnection("jdbc:sqlite:#{database}")
129
+ else
130
+ @connection = SQLite3::Database.new(database)
131
+ end
132
+ rescue Exception => e
133
+ raise DBGeni::DBConnectionError, e.to_s
134
+ end
135
+ end
136
+
137
+ def execute_jdbc(sql, *binds)
138
+ query = @connection.prepare_statement(sql)
139
+ binds.each_with_index do |b, i|
140
+ if b.is_a?(String)
141
+ query.setString(i+1, b)
142
+ elsif b.is_a?(Fixnum)
143
+ query.setInt(i+1, b)
144
+ end
145
+ end
146
+ results = Array.new
147
+ unless sql =~ /^\s*select/i
148
+ query.execute()
149
+ else
150
+ rset = query.execute_query()
151
+ cols = rset.get_meta_data.get_column_count
152
+ while(r = rset.next) do
153
+ a = Array.new
154
+ 1.upto(cols) do |i|
155
+ a.push rset.get_object(i)
156
+ end
157
+ results.push a
158
+ end
159
+ end
160
+ query.close
161
+ results
162
+ end
163
+
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,97 @@
1
+ module DBGeni
2
+
3
+ module Connector
4
+
5
+ class Sybase
6
+
7
+ unless RUBY_PLATFORM == 'java'
8
+ raise DBGeni::UnsupportedRubyPlatform
9
+ end
10
+
11
+ require 'java'
12
+ java_import 'net.sourceforge.jtds.jdbc.Driver'
13
+ java_import 'java.sql.DriverManager'
14
+
15
+ attr_reader :connection
16
+ attr_reader :database
17
+
18
+ def self.connect(user, password, database, host=nil, port=nil)
19
+ self.new(user, password, database, host, port)
20
+ end
21
+
22
+ def disconnect
23
+ @connection.close
24
+ end
25
+
26
+ def execute(sql, *binds)
27
+ query = @connection.prepare_statement(sql)
28
+ binds.each_with_index do |b, i|
29
+ if b.is_a?(String)
30
+ query.setString(i+1, b)
31
+ elsif b.is_a?(Fixnum)
32
+ query.setInt(i+1, b)
33
+ end
34
+ end
35
+ results = Array.new
36
+ unless sql =~ /^\s*select/i
37
+ query.execute()
38
+ else
39
+ rset = query.execute_query()
40
+ cols = rset.get_meta_data.get_column_count
41
+ while(r = rset.next) do
42
+ a = Array.new
43
+ 1.upto(cols) do |i|
44
+ a.push rset.get_object(i)
45
+ end
46
+ results.push a
47
+ end
48
+ end
49
+ query.close
50
+ results
51
+ end
52
+
53
+ def ping
54
+ results = self.execute('select 1')
55
+ if results.length == 1
56
+ true
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ def commit
63
+ @connection.commit
64
+ end
65
+
66
+ def rollback
67
+ @connection.rollback
68
+ end
69
+
70
+ def date_placeholder(bind_var)
71
+ "cast(? as datetime)"
72
+ end
73
+
74
+ def date_as_string(dtm)
75
+ dtm.strftime '%Y-%m-%d %H:%M:%S'
76
+ end
77
+
78
+ private
79
+
80
+
81
+ #
82
+ def initialize(user, password, database, host=nil, port=nil)
83
+ @database = database
84
+
85
+ sybdriver = Driver.new
86
+ DriverManager.registerDriver sybdriver
87
+ begin
88
+ @connection = DriverManager.get_connection("jdbc:jtds:sybase://#{host}:#{port}/#{database}", user, password)
89
+ @connection.auto_commit = true
90
+ rescue Exception => e
91
+ raise DBGeni::DBConnectionError, e.to_s
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,35 @@
1
+ module DBGeni
2
+
3
+ class DmlCLI < MigrationCLI
4
+
5
+ BEFORE_UP_RUN_PLUGIN = :before_dml_up
6
+ AFTER_UP_RUN_PLUGIN = :after_dml_up
7
+ BEFORE_DOWN_RUN_PLUGIN = :before_dml_down
8
+ AFTER_DOWN_RUN_PLUGIN = :after_dml_down
9
+ BEFORE_RUNNING_PLUGIN = :before_running_dml
10
+ AFTER_RUNNING_PLUGIN = :after_running_dml
11
+
12
+ def migrations
13
+ @migration_list ||= DBGeni::MigrationList.new_dml_migrations(@config.dml_directory) unless @migration_list
14
+ @migration_list.migrations
15
+ end
16
+
17
+ private
18
+
19
+ def find_migration(migration_name)
20
+ m = Migration.initialize_from_internal_name(@config.dml_directory, migration_name)
21
+ end
22
+
23
+ def set_plugin_hooks
24
+ @before_up_run_plugin = :before_dml_up
25
+ @after_up_run_plugin = :after_dml_up
26
+ @before_down_run_plugin = :before_dml_down
27
+ @after_down_run_plugin = :after_dml_down
28
+ @before_running_plugin = :before_running_dmls
29
+ @after_running_plugin = :after_running_dmls
30
+ end
31
+
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,161 @@
1
+ module DBGeni
2
+
3
+ class Environment < BlankSlate
4
+
5
+ def initialize(environment_name)
6
+ @environment_name = environment_name
7
+ @params = Hash.new
8
+ @loading = true
9
+ end
10
+
11
+
12
+ def __merge_environment(new_env)
13
+ @params.merge!(new_env.__params)
14
+ end
15
+
16
+ def __enable_loading
17
+ @loading = true
18
+ end
19
+
20
+ def __completed_loading
21
+ @loading = false
22
+ end
23
+
24
+ def __is_loadable?
25
+ @loading
26
+ end
27
+
28
+ def __environment_name
29
+ @environment_name
30
+ end
31
+
32
+ def __params
33
+ @params
34
+ end
35
+
36
+ def __merge_defaults(default_params)
37
+ all_params = default_params.merge(@params)
38
+ @params = all_params
39
+ end
40
+
41
+ def method_missing(name, *args, &block)
42
+ str_name = name.to_s.gsub(/=$/, '')
43
+ if __is_loadable?
44
+ __load_parameter(str_name, *args)
45
+ else
46
+ __get_parameter(str_name, *args)
47
+ end
48
+ end
49
+
50
+ def __to_s
51
+ str = ''
52
+ # get the longest key length to pad the keys
53
+ max_length = @params.keys.map{ |k| k.length }.sort.last
54
+ @params.keys.sort.each do |k|
55
+ str << "#{k.ljust(max_length, ' ')} => #{@params[k]}\n"
56
+ end
57
+ str
58
+ end
59
+
60
+ private
61
+
62
+ def __load_parameter(name, *args)
63
+ if @params.has_key?(name)
64
+ warn "Parameter #{name} was previously defined"
65
+ end
66
+ # TODO - Not sure whether to raise or just let it be nil
67
+ #if nil == args[0]
68
+ # raise ""
69
+ #end
70
+ @params[name] = args[0] unless args[0] == ''
71
+ end
72
+
73
+ def __get_parameter(name, *args)
74
+ unless @params.has_key?(name)
75
+ # Password is a special case - if it is not defined it should be prompted for
76
+ if name == 'password'
77
+ puts "Please enter the password for #{@params['database']} in the #{@environment_name} environment\n"
78
+ password = ''
79
+ begin
80
+ while (password == '')
81
+ print "password: "
82
+ unless is_windows?
83
+ system "stty -echo"
84
+ end
85
+ password = gets.chomp
86
+ unless is_windows?
87
+ system "stty echo"
88
+ end
89
+ print "\n"
90
+ end
91
+ rescue Interrupt
92
+ unless is_windows?
93
+ system "stty echo"
94
+ end
95
+ exit(1)
96
+ end
97
+ @params[name] = password
98
+ password
99
+ end
100
+ else
101
+ @params[name]
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ # class Environment < BlankSlate
110
+
111
+ # def initialize(name)
112
+ # @__environment_name = name
113
+ # end
114
+
115
+ # def metaclass
116
+ # class << self
117
+ # self
118
+ # end
119
+ # end
120
+
121
+ # def lock
122
+ # metaclass.send(:alias_method, :__method_missing, :method_missing)
123
+ # metaclass.send(:undef_method, :method_missing)
124
+ # end
125
+
126
+ # def unlock
127
+ # metaclass.send(:alias_method, :method_missing, :__method_missing)
128
+ # end
129
+
130
+ # def method_missing(name, *args, &block)
131
+ # # TODO - add list of illegal param names (ie the list of kept methods
132
+ # #
133
+ # # if the method was a meth= type one, we need to get rid of the = sign
134
+ # #
135
+ # # This allows variables to be defined as
136
+ # # param 'value'
137
+ # # or
138
+ # # param = 'value'
139
+ # str_name = name.to_s.gsub(/=$/, '')
140
+ # method_name = ('@'+str_name).intern
141
+ # self.instance_variable_set(method_name, args[0])
142
+ # instance_eval("
143
+ # def #{str_name}(__param = nil)
144
+ # return @#{str_name} unless __param
145
+ # @#{str_name} = __param
146
+ # end
147
+ # ")
148
+ # metaclass.send(:alias_method, "#{str_name}=".intern, str_name.intern)
149
+ # end
150
+
151
+
152
+
153
+ # Method to define a generic getter setter.
154
+ #def font_size(size = nil)
155
+ # return @font_size unless size
156
+ # @font_size = size
157
+ # end
158
+ #alias_method :font_size=, :font_size
159
+
160
+ # end
161
+ #end
@@ -0,0 +1,69 @@
1
+ module DBGeni
2
+
3
+ class DBConnectionError < Exception; end
4
+ class NoLoggerLocation < Exception; end
5
+ class MigrationDirectoryNotExist < Exception; end
6
+ class MigrationFilenameInvalid < Exception; end
7
+ class MigrationFileNotExist < Exception; end
8
+ class MigrationAlreadyApplied < Exception; end
9
+ class MigrationNotApplied < Exception; end
10
+ class MigrationApplyFailed < Exception; end
11
+ class MigrationContainsErrors < Exception; end
12
+ class MigrationNotOutstanding < Exception; end
13
+
14
+ class EnvironmentNotExist < Exception; end
15
+
16
+ class ConfigFileNotExist < Exception; end
17
+ class ConfigAmbiguousEnvironment < Exception; end
18
+ class ConfigFileNotSpecified < Exception; end
19
+ class ConfigSyntaxError < Exception; end
20
+
21
+ class NoEnvironmentSelected < Exception; end
22
+
23
+ ## Initializer
24
+
25
+ # If an attempt is made to load an initializer that doesn't exist
26
+ class NoInitializerForDBType < Exception; end
27
+ # If the initializer is not corretly defined, this will be raise.
28
+ class InvalidInitializerForDBType < Exception; end
29
+
30
+ ## Connectors
31
+ class NoConnectorForDBType < Exception; end
32
+ class InvalidConnectorForDBType < Exception; end
33
+
34
+ ## Migrators
35
+ class NoMigratorForDBType < Exception; end
36
+ class InvalidMigratorForDBType < Exception; end
37
+
38
+ class DatabaseAlreadyInitialized < Exception; end
39
+ class DatabaseNotInitialized < Exception; end
40
+ class DBGeni::DatabaseNotSpecified < Exception; end
41
+ class NoOutstandingMigrations < Exception; end
42
+ class NoAppliedMigrations < Exception; end
43
+ class MigratorCouldNotConnect < Exception; end
44
+
45
+ ## code
46
+ class UnknownCodeType < Exception; end
47
+ class CodeModuleCurrent < Exception; end
48
+ class CodeApplyFailed < Exception; end
49
+ class CodeFileNotExist < Exception; end
50
+ class CodeDirectoryNotExist < Exception; end
51
+ class NoOutstandingCode < Exception; end
52
+ class NoCodeFilesExist < Exception; end
53
+ class CannotRemoveUnknownObject < Exception; end
54
+
55
+ class DBCLINotOnPath < Exception; end
56
+ class CodeRemoveFailed < Exception; end
57
+ class NotImplemented < Exception; end
58
+
59
+ class MilestoneHasNoMigration < Exception; end
60
+ class UnsupportedRubyPlatform < Exception; end
61
+ class ConnectionError < Exception; end
62
+
63
+ # Plugins
64
+ class InvalidHook < Exception; end
65
+ class PluginDoesNotRespondToRun < Exception; end
66
+ class PluginDirectoryNotAccessible < Exception; end
67
+ class PluginException < Exception; end
68
+
69
+ end