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,178 @@
1
+ # Expects only one parameter, the directory of the new dbgeni directory structure
2
+
3
+ ARGV << '--help' if ARGV.empty?
4
+
5
+ if ARGV.length > 1 or %w(-h --help).include? ARGV[0]
6
+ puts "Error: only one parameter is allowed for the new command" if ARGV.length > 1
7
+ puts <<-EOF
8
+ Usage: dbgeni new <path/to/directory/for/new/installer/structure>
9
+ EOF
10
+ exit
11
+ end
12
+
13
+ require 'fileutils'
14
+
15
+ directory = ARGV.shift
16
+
17
+ # There can be two commands here - first is to create the directory
18
+ # structure, the second is new-conf to just create the file, but only if
19
+ # the directory exists.
20
+ if %w(n new).include? $initial_command
21
+ if File.directory?(directory)
22
+ puts "Error: The directory already exists"
23
+ exit(1)
24
+ end
25
+
26
+ # create the base directory
27
+ begin
28
+ puts "creating directory: #{directory}"
29
+ FileUtils.mkdir_p(directory)
30
+ rescue Exception => e
31
+ puts "error: failed to create #{directory} - #{e.to_s}"
32
+ exit(1)
33
+ end
34
+
35
+ # create the directory to hold migrations
36
+ begin
37
+ puts "creating directory: #{directory}/migrations"
38
+ FileUtils.mkdir_p(directory+'/migrations')
39
+ rescue Exception => e
40
+ puts "error: failed to create #{directory}/migrations - #{e.to_s}"
41
+ exit(1)
42
+ end
43
+ # create the directory to hold code
44
+ begin
45
+ puts "creating directory: #{directory}/code"
46
+ FileUtils.mkdir_p(directory+'/code')
47
+ rescue Exception => e
48
+ puts "error: failed to create #{directory}/code - #{e.to_s}"
49
+ exit(1)
50
+ end
51
+
52
+ # create the directory to hold dml
53
+ begin
54
+ puts "creating directory: #{directory}/dml"
55
+ FileUtils.mkdir_p(directory+'/dml')
56
+ rescue Exception => e
57
+ puts "error: failed to create #{directory}/dml - #{e.to_s}"
58
+ exit(1)
59
+ end
60
+
61
+ else
62
+ unless File.directory?(directory)
63
+ puts "Error: The directory #{directory} does not exist"
64
+ exit(1)
65
+ end
66
+ end
67
+
68
+
69
+ # Create the initial version of the configuration file
70
+ begin
71
+ puts "creating file: #{directory}/.dbgeni"
72
+ conf = File.open("#{directory}/.dbgeni", "w")
73
+ conf.puts <<-EOF
74
+
75
+ # This directory specifies the location of the migrations directory
76
+ migrations_directory "./migrations"
77
+
78
+ # This directory specifies the location of the DML files directory
79
+ dml_directory "./dml"
80
+
81
+ # This directory specifies the location of any code modules
82
+ code_directory "./code"
83
+
84
+ # This specifes the location of the plugin directory. Specifying this parameter
85
+ # enables plugins and the directory must exist
86
+ # plugin_directory "./dbgeni_plugins"
87
+
88
+ # This specifies the type of database this installer is applied against
89
+ # Valid values are oracle, mysql, sqlite, sybase however this is not validated
90
+ # to enable different database plugins to easily be added.
91
+ database_type "sqlite"
92
+
93
+ # This is the table the installer logs applied migrations in the database
94
+ # The default is dbgeni_migrations
95
+ database_table "dbgeni_migrations"
96
+
97
+ # Use the include_file option to load another config file, perhaps
98
+ # containing environment details for many different environments in one place
99
+ #
100
+ # include_file '/path/to/include/file'
101
+
102
+
103
+ # Environment Section
104
+ #
105
+ # There must be at least one environment, and at a minimum each environment
106
+ # should define a username, database and password (except sqlite which only
107
+ # requires a database to be specified)
108
+ #
109
+ # Typically there will be more than one enviroment block detailling development,
110
+ # test and production but any number of environments are valid provided there is at least one.
111
+ #
112
+ # The environments can be defined here or in an included file.
113
+
114
+ # Defaults - this section is optional and can be used to define parameters that must
115
+ # appear in every environment, for example install_schema. If a parameter is defined
116
+ # here, and also defined in the environment, then the value in the environment block
117
+ # will override the default. If many default blocks are specified, in included files
118
+ # for example, the parameters are merged. If any parameters are duplicated, the last to
119
+ # be loaded will used.
120
+ #
121
+ # defaults {
122
+ # username 'scott'
123
+ # }
124
+
125
+ environment('development') {
126
+
127
+ ### SQLITE
128
+ database 'testdb.sqlite' # This is the only required connection parameter for sqlite
129
+
130
+ ### ORACLE
131
+ #
132
+ # database 'DEV1' # This is the name of an entry in the tns_names.ora file
133
+ # username 'scott' # This is the username to connect as, and also the default schema
134
+ # password 'tiger' # This is the password for the username
135
+ #
136
+ # install_schema 'other' # Optional: If dbgeni connects as a database user, but the application is owned
137
+ # by another user, set the application schema here
138
+
139
+ ### MYSQL
140
+ #
141
+ # database 'DEV1' # This is the database to use after connection
142
+ # username 'scott' # This is the username to connect as
143
+ # password 'tiger' # This is the password for the username
144
+ # hostname '127.0.0.1' # This is the hostname or IP mysql is running on
145
+ # For localhost use the IP 127.0.0.1 or it will not work.
146
+ # port '3306' # This the port of the mysql service
147
+
148
+ ### SYBASE
149
+ #
150
+ # database 'DEV1' # This is the database to use after connection
151
+ # username 'scott' # This is the username to connect as
152
+ # password 'tiger' # This is the password for the username
153
+ # hostname '127.0.0.1' # This is the hostname or IP sybase is running on
154
+ # port '3306' # This the port of the sybase service
155
+ # sybase_service 'dev1' # THis is the sybase service name defined in the sql.ini file
156
+
157
+
158
+ # Other parameters can be defined here to be used as replacement parameters in scripts
159
+ # or in plugins in future versions of dbgeni.
160
+ # param_name 'value'
161
+ }
162
+
163
+ #
164
+ # environment('test') {
165
+ # username 'user'
166
+ # database 'TEST.WORLD'
167
+ # password ''
168
+ # }
169
+ #
170
+
171
+
172
+ EOF
173
+ rescue Exception => e
174
+ puts "error: failed to create #{directory}/.dbgeni - #{e.to_s}"
175
+ exit(1)
176
+ end
177
+
178
+ exit(0)
@@ -0,0 +1,325 @@
1
+ module DBGeni
2
+
3
+ # Config understands the following:
4
+
5
+ # migrations_directory 'value' # This will be checked to ensure its a valid dir
6
+ # # and defaults to 'migrations'
7
+ # environment('development') {
8
+ # username 'user' # this must be here, or it will error
9
+ # database 'MYDB.WORLD' # this must be here, or it will error. For Oracle, this is the TNS Name
10
+ # password '' # If this value is missing, it will be promoted for if the env is used.
11
+ # }
12
+ #
13
+ # environment('other environment') {
14
+ # username '' # the environment block can be repeated for many environments
15
+ # }
16
+ #
17
+ # global_parameters { # These are common parameters to all environments, but they can be
18
+ # # overriden. Basically take global, merge in environment
19
+ # param_name 'value'
20
+ # }
21
+
22
+ class Config
23
+ attr_reader :environments
24
+ attr_reader :current_environment
25
+ attr_reader :migration_directory
26
+ attr_reader :dml_directory
27
+ # ideally wnat code_dir to be code_directory, but then it clashes with the
28
+ # setter used in the config file, so need to change it. Probably more sensible
29
+ # like this than the migrations_directory vs migration_directory
30
+ # _-_
31
+ #
32
+ attr_reader :code_dir
33
+ attr_reader :db_type # oracle, mysql, sqlite etc, default sqlite
34
+ attr_reader :db_table # defaults to dbgeni_migrations
35
+ attr_reader :config_file
36
+ attr_reader :base_directory
37
+
38
+ DEFAULTS_ENV = '__defaults__'
39
+
40
+
41
+ def initialize
42
+ @migration_directory = 'migrations'
43
+ @dml_directory = 'dml'
44
+ @code_dir = 'code'
45
+ @plugin_dir = nil
46
+ @db_type = 'sqlite'
47
+ @db_table = 'dbgeni_migrations'
48
+ @base_dir = '.'
49
+ @environments = Hash.new
50
+ end
51
+
52
+
53
+ def self.load_from_file(filename)
54
+ cfg = self.new
55
+ cfg.load_from_file(filename)
56
+ end
57
+
58
+
59
+ def load_from_file(filename)
60
+ if filename == nil
61
+ raise DBGeni::ConfigFileNotSpecified
62
+ end
63
+ raw_config = ''
64
+ self.base_directory = File.expand_path(File.dirname(filename))
65
+ @config_file = File.expand_path(filename)
66
+ begin
67
+ File.open(@config_file) do |f|
68
+ raw_config = f.read
69
+ end
70
+ rescue Errno::ENOENT
71
+ raise DBGeni::ConfigFileNotExist, "#{@config_location} (expanded from #{filename}) does not exist"
72
+ end
73
+ load(raw_config)
74
+ end
75
+
76
+
77
+ # Normally this is assumed to be the location the config file is in.
78
+ # All relative file operations come from this directory
79
+ def base_directory=(dir)
80
+ @base_directory = dir
81
+ # If change the base directory, then unless migration dir is
82
+ # an absolute path, it will need to change too.
83
+ @migration_directory = File.join(@base_directory, @migration_directory) unless is_absolute_path?(@migration_directory)
84
+ @dml_directory = File.join(@base_directory, @dml_directory) unless is_absolute_path?(@dml_directory)
85
+ @code_dir = File.join(@base_directory, @code_dir) unless is_absolute_path?(@code_dir)
86
+ if @plugin_dir
87
+ @plugin_dir = File.join(@base_directory, @plugin_dir) unless is_absolute_path?(@plugin_dir)
88
+ end
89
+ end
90
+
91
+
92
+ # def get_environment(name)
93
+ # if name == nil
94
+ # # if there is only a single environment defined, then return it
95
+ # if @environments.keys.length == 1
96
+ # @environments[@environments.keys.first]
97
+ # else
98
+ # raise DBGeni::ConfigAmbiguousEnvironment, "More than one environment is defined"
99
+ # end
100
+ # else
101
+ # unless @environments.has_key?(name)
102
+ # raise DBGeni::EnvironmentNotExist
103
+ # end
104
+ # @environments[name]
105
+ # end
106
+ # end
107
+
108
+
109
+ def env
110
+ current_env
111
+ end
112
+
113
+
114
+ def current_env
115
+ if @current_environment
116
+ @environments[@current_environment]
117
+ elsif @environments.keys.reject{|i| i == DEFAULTS_ENV}.length == 1
118
+ @environments[@environments.keys.reject{|i| i == DEFAULTS_ENV}.first]
119
+ else
120
+ raise DBGeni::ConfigAmbiguousEnvironment, "More than one environment is defined"
121
+ end
122
+ end
123
+
124
+
125
+ def set_env(name=nil)
126
+ if name == nil
127
+ valid_envs = @environments.keys.reject{|i| i == DEFAULTS_ENV}
128
+ if valid_envs.length == 1
129
+ @current_environment = valid_envs.first
130
+ else
131
+ raise DBGeni::ConfigAmbiguousEnvironment, "More than one environment is defined"
132
+ end
133
+ elsif @environments.has_key?(name)
134
+ @current_environment = name
135
+ else
136
+ raise DBGeni::EnvironmentNotExist
137
+ end
138
+ end
139
+
140
+
141
+ def load(raw_config, recursed=false)
142
+ begin
143
+ self.instance_eval(raw_config)
144
+ rescue Exception => e
145
+ raise DBGeni::ConfigSyntaxError, e.to_s
146
+ end
147
+ merge_defaults unless recursed
148
+ self
149
+ end
150
+
151
+ def merge_defaults
152
+ if @environments.has_key? DEFAULTS_ENV
153
+ @environments.keys.each do |k|
154
+ next if k == DEFAULTS_ENV
155
+ @environments[k].__merge_defaults(@environments[DEFAULTS_ENV].__params)
156
+ end
157
+ end
158
+ end
159
+
160
+ def to_s
161
+ str = ''
162
+ str << "migrations_directory => #{@migration_directory}\n"
163
+ @environments.keys.sort.each do |k|
164
+ str << "\n\nEnvironment: #{k}\n"
165
+ str << "=============#{(1..k.length).map do "=" end.join}\n\n"
166
+ str << @environments[k].__to_s
167
+ end
168
+ str
169
+ end
170
+
171
+ ######################################
172
+ # Methods below here are for the DSL #
173
+ ######################################
174
+
175
+ # mig_dir could be defined as a file in current directory 'migrations'
176
+ # or it could be a relative directory './somedir/migrations'
177
+ # or it could be a full path '/somedir/migrations' or in windows 'C:\somedir\migrations'
178
+ # To use the migrations it needs to be expanded to a full path *somehow*.
179
+ # If it begins / or <LETTER>:\ then assume its full path, otherwise concatenate
180
+ # to the full path of the config file.
181
+ def migrations_directory(*p)
182
+ if p.length == 0
183
+ @migration_directory
184
+ else
185
+ if is_absolute_path?(p[0])
186
+ # it looks like an absolute path
187
+ @migration_directory = p[0]
188
+ else
189
+ # it looks like a relative path
190
+ if @base_directory
191
+ @migration_directory = File.join(@base_directory, p[0])
192
+ else
193
+ @migration_directory = p[0]
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ def dml_directory(*p)
200
+ if p.length == 0
201
+ @dml_directory
202
+ else
203
+ if is_absolute_path?(p[0])
204
+ # it looks like an absolute path
205
+ @dml_directory = p[0]
206
+ else
207
+ # it looks like a relative path
208
+ if @base_directory
209
+ @dml_directory = File.join(@base_directory, p[0])
210
+ else
211
+ @dml_directory = p[0]
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ def code_directory(*p)
218
+ if p.length == 0
219
+ @code_dir
220
+ else
221
+ if is_absolute_path?(p[0])
222
+ # it looks like an absolute path
223
+ @code_dir = p[0]
224
+ else
225
+ # it looks like a relative path
226
+ if @base_directory
227
+ @code_dir = File.join(@base_directory, p[0])
228
+ else
229
+ @code_dir = p[0]
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ def plugin_directory(*p)
236
+ if p.length == 0
237
+ @plugin_dir
238
+ else
239
+ if is_absolute_path?(p[0])
240
+ # it looks like an absolute path
241
+ @plugin_dir = p[0]
242
+ else
243
+ # it looks like a relative path
244
+ if @base_directory
245
+ @plugin_dir = File.join(@base_directory, p[0])
246
+ else
247
+ @plugin_dir = p[0]
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+
254
+ def database_type(*p)
255
+ # TODO - consider putting validation here
256
+ @db_type = p[0]
257
+ end
258
+
259
+ def database_table(*p)
260
+ # TODO - consider putting validation here
261
+ @db_table = p[0]
262
+ end
263
+
264
+ # For some reason I cannot work out, this method is never getting
265
+ # call in the evaluated block. Ideally wanted to have migrations_directory
266
+ # and migrations_directory= so that it doesn't matter which is called.
267
+ # def migrations_directory=(value)
268
+ # puts "called the equals one"
269
+ # @migration_directory = value
270
+ # end
271
+
272
+
273
+ # Given a block of environment details, generate a new environment
274
+ # object. eg
275
+ # environment('some_name') do
276
+ # database ''
277
+ # user ''
278
+ # password prompt
279
+ #
280
+ # some_dir_path '/path_to_directory'
281
+ def environment(name, &block)
282
+ env = Environment.new(name)
283
+ block.arity < 1 ? env.instance_eval(&block) : block.call(env)
284
+ env.__completed_loading
285
+ if @environments.has_key?(name)
286
+ @environments[name].__merge_environment(env)
287
+ else
288
+ @environments[name] = env
289
+ end
290
+ end
291
+
292
+ def defaults(&block)
293
+ environment(DEFAULTS_ENV, &block)
294
+ end
295
+
296
+
297
+ def include_file(*p)
298
+ file = p[0]
299
+ if !is_absolute_path?(file)
300
+ file = File.join(@base_directory, file)
301
+ end
302
+ begin
303
+ raw_config = ''
304
+ File.open(file) do |f|
305
+ raw_config = f.read
306
+ end
307
+ self.load(raw_config)
308
+ rescue Errno::ENOENT
309
+ raise DBGeni::ConfigFileNotExist, "Included config #{file} does not exist"
310
+ rescue DBGeni::ConfigSyntaxError
311
+ raise DBGeni::ConfigSyntaxError, "Included config #{file} contains errors: #{e.to_s}"
312
+ end
313
+ end
314
+
315
+ private
316
+
317
+ def is_absolute_path?(path)
318
+ if path =~ /^\/|[a-zA-Z]:\\/
319
+ true
320
+ else
321
+ false
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,59 @@
1
+ module DBGeni
2
+
3
+ module Connector
4
+
5
+ def self.initialize(config)
6
+ required_class = setup(config.db_type)
7
+ conn = nil
8
+ begin
9
+ required_method = required_class.method("connect")
10
+ rescue NameError
11
+ raise DBGeni::InvalidConnectorForDBType, config.db_type
12
+ end
13
+ if config.db_type == 'sqlite' or (config.db_type == 'oracle' and RUBY_PLATFORM != 'java')
14
+ # don't need a host or port here, so make this a seperate call block
15
+ conn = required_method.call(config.env.username,
16
+ # SQLITE doesn't need a password, so prevent asking for it
17
+ # or it may be promoted for
18
+ config.db_type == 'sqlite' ? '' : config.env.password,
19
+ config.env.database)
20
+ else
21
+ conn = required_method.call(config.env.username,
22
+ config.env.password,
23
+ config.env.database,
24
+ config.env.hostname,
25
+ config.env.port)
26
+ end
27
+ if config.db_type == 'oracle'
28
+ if config.env.install_schema
29
+ if config.env.username != config.env.install_schema
30
+ conn.execute("alter session set current_schema=#{config.env.install_schema}")
31
+ end
32
+ end
33
+ end
34
+ conn
35
+ end
36
+
37
+ private
38
+
39
+ def self.setup(db_type)
40
+ begin
41
+ require "dbgeni/connectors/#{db_type}"
42
+ rescue Exception => e
43
+ puts "Error requiring connector: #{e.to_s}"
44
+ raise DBGeni::NoConnectorForDBType, db_type
45
+ end
46
+
47
+ required_class = nil
48
+ if Connector.const_defined?(db_type.capitalize)
49
+ required_class = Connector.const_get(db_type.capitalize)
50
+ else
51
+ raise DBGeni::NoConnectorForDBType, db_type
52
+ end
53
+ required_class
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
@@ -0,0 +1,146 @@
1
+ module DBGeni
2
+
3
+ module Connector
4
+
5
+ class Mysql
6
+ if RUBY_PLATFORM == 'java'
7
+ require 'java'
8
+ java_import 'com.mysql.jdbc.Driver'
9
+ java_import 'java.sql.DriverManager'
10
+ else
11
+ require 'mysql'
12
+ end
13
+
14
+ attr_reader :connection
15
+ attr_reader :database
16
+
17
+ def self.connect(user, password, database, host, port)
18
+ self.new(user, password, database, host, port=nil)
19
+ end
20
+
21
+ def disconnect
22
+ @connection.close
23
+ end
24
+
25
+ def execute(sql, *binds)
26
+ if RUBY_PLATFORM == 'java'
27
+ execute_jdbc(sql, *binds)
28
+ else
29
+ execute_native(sql, *binds)
30
+ end
31
+ end
32
+
33
+
34
+ def ping
35
+ results = self.execute('select 1 + 1 from dual')
36
+ if results.length == 1
37
+ true
38
+ else
39
+
40
+ false
41
+ end
42
+ end
43
+
44
+ def commit
45
+ @connection.commit
46
+ end
47
+
48
+ def rollback
49
+ @connection.rollback
50
+ end
51
+
52
+ def date_placeholder(bind_var)
53
+ "?" # ":#{bind_var}"
54
+ end
55
+
56
+ def date_as_string(dtm)
57
+ dtm.strftime '%Y-%m-%d %H:%M:%S'
58
+ end
59
+
60
+ private
61
+
62
+ def initialize(user, password, database, host, port=3306)
63
+ @database = database
64
+ begin
65
+ if RUBY_PLATFORM == 'java'
66
+ @connection = DriverManager.get_connection("jdbc:mysql://#{host}:#{port||3306}/#{database}", user, password)
67
+ else
68
+ @connection = ::Mysql.connect(host, user, password, database, port)
69
+ end
70
+ rescue Exception => e
71
+ raise DBGeni::DBConnectionError, e.to_s
72
+ end
73
+ end
74
+
75
+ def execute_native(sql, *binds)
76
+ # strange exception handling here. If you issue a select, the fetch works fine.
77
+ # However, something like create table blows up when fetch is called with noMethodError
78
+ # so it is being caught and thrown away ... hackish, but the mysql drive seems to be
79
+ # at fault, is badly documented and seems to be a bit rubbish. Ideall uses DBD::Mysql + DBI.
80
+
81
+ results = Array.new
82
+ if sql =~ /^drop\s+(procedure|function|trigger|table)/i
83
+ # cannot prepare these statements, need to just execute
84
+ @connection.query(sql)
85
+ return results
86
+ end
87
+
88
+ begin
89
+ query = @connection.prepare(sql)
90
+ query.execute(*binds)
91
+
92
+ results = Array.new
93
+ if query.num_rows > 0
94
+ while r = query.fetch()
95
+ results.push r
96
+ end
97
+ end
98
+ # everthing is auto commit right now ...
99
+ @connection.commit
100
+ rescue NoMethodError
101
+ ensure
102
+ begin
103
+ query.close()
104
+ rescue
105
+ end
106
+ end
107
+ results
108
+ end
109
+
110
+ def execute_jdbc(sql, *binds)
111
+ begin
112
+ query = @connection.prepare_statement(sql)
113
+ binds.each_with_index do |b, i|
114
+ if b.is_a?(String)
115
+ query.setString(i+1, b)
116
+ elsif b.is_a?(Fixnum)
117
+ query.setInt(i+1, b)
118
+ end
119
+ end
120
+ results = Array.new
121
+ unless sql =~ /^\s*(select|show)/i
122
+ query.execute()
123
+ else
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
+ a.push rset.get_object(i)
130
+ end
131
+ results.push a
132
+ end
133
+ end
134
+ results
135
+ ensure
136
+ begin
137
+ query.close
138
+ rescue Exception => e
139
+ end
140
+ end
141
+ end
142
+
143
+ end
144
+ end
145
+ end
146
+