skiima 0.1.000

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +20 -0
  2. data/.travis.yml +8 -0
  3. data/CHANGELOG +8 -0
  4. data/Gemfile +30 -0
  5. data/Guardfile +14 -0
  6. data/README.md +87 -0
  7. data/Rakefile +56 -0
  8. data/lib/skiima/db_adapters/base_mysql_adapter.rb +308 -0
  9. data/lib/skiima/db_adapters/mysql2_adapter.rb +114 -0
  10. data/lib/skiima/db_adapters/mysql_adapter.rb +287 -0
  11. data/lib/skiima/db_adapters/postgresql_adapter.rb +509 -0
  12. data/lib/skiima/db_adapters.rb +187 -0
  13. data/lib/skiima/dependency.rb +84 -0
  14. data/lib/skiima/locales/en.yml +20 -0
  15. data/lib/skiima/locales/fr.yml +2 -0
  16. data/lib/skiima/version.rb +4 -0
  17. data/lib/skiima.rb +270 -0
  18. data/lib/skiima_helpers.rb +49 -0
  19. data/skiima.gemspec +53 -0
  20. data/spec/config/database.yml +56 -0
  21. data/spec/db/skiima/depends.yml +61 -0
  22. data/spec/db/skiima/empty_depends.yml +0 -0
  23. data/spec/db/skiima/init_test_db/database.skiima_test.mysql.current.sql +2 -0
  24. data/spec/db/skiima/init_test_db/database.skiima_test.postgresql.current.sql +2 -0
  25. data/spec/db/skiima/test_column_names/table.test_column_names.mysql.current.sql +8 -0
  26. data/spec/db/skiima/test_column_names/table.test_column_names.postgresql.current.sql +18 -0
  27. data/spec/db/skiima/test_index/index.test_index.mysql.current.sql +2 -0
  28. data/spec/db/skiima/test_index/index.test_index.postgresql.current.sql +2 -0
  29. data/spec/db/skiima/test_proc/proc.test_proc.mysql.current.sql +8 -0
  30. data/spec/db/skiima/test_proc/proc.test_proc_drop.mysql.current.drop.sql +1 -0
  31. data/spec/db/skiima/test_proc/proc.test_proc_drop.mysql.current.sql +8 -0
  32. data/spec/db/skiima/test_rule/rule.test_rule.postgresql.current.sql +6 -0
  33. data/spec/db/skiima/test_schema/schema.test_schema.postgresql.current.sql +2 -0
  34. data/spec/db/skiima/test_table/table.test_table.mysql.current.sql +7 -0
  35. data/spec/db/skiima/test_table/table.test_table.postgresql.current.sql +4 -0
  36. data/spec/db/skiima/test_view/view.test_view.mysql.current.sql +5 -0
  37. data/spec/db/skiima/test_view/view.test_view.postgresql.current.sql +6 -0
  38. data/spec/helpers/mysql_spec_helper.rb +0 -0
  39. data/spec/helpers/postgresql_spec_helper.rb +11 -0
  40. data/spec/mysql2_spec.rb +57 -0
  41. data/spec/mysql_spec.rb +100 -0
  42. data/spec/postgresql_spec.rb +138 -0
  43. data/spec/skiima/db_adapters/mysql_adapter_spec.rb +38 -0
  44. data/spec/skiima/db_adapters/postgresql_adapter_spec.rb +20 -0
  45. data/spec/skiima/db_adapters_spec.rb +31 -0
  46. data/spec/skiima/dependency_spec.rb +94 -0
  47. data/spec/skiima_spec.rb +97 -0
  48. data/spec/spec_helper.rb +35 -0
  49. metadata +195 -0
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ .rvmrc
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+
7
+ ## Rubymine
8
+ .idea/*
9
+ .idea
10
+
11
+ ## MAC OS
12
+ .DS_Store
13
+ .Trashes
14
+ .com.apple.timemachine.supported
15
+ .fseventsd
16
+ Desktop DB
17
+ Desktop DF
18
+
19
+ ## Spec Logging
20
+ spec/test.log
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby
6
+ - rbx-19mode
7
+ # uncomment this line if your project needs to run something other than `rake`:
8
+ # script: bundle exec rspec spec
data/CHANGELOG ADDED
@@ -0,0 +1,8 @@
1
+ ** PRERELEASE 0.0.001
2
+
3
+ * Determining the layout for modules, classes and tasks
4
+
5
+ ** PRERELEASE 0.0.010
6
+
7
+ * Major changes to interface. Added version subgroup. Removed skiima.yml. Removed ActiveRecord.
8
+ * Added support for Postgres: databases, schemas, tables, indexes, views, rules.
data/Gemfile ADDED
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ source "http://rubygems.org"
3
+
4
+ # Specify your gem's dependencies in skiima.gemspec
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'guard'
9
+ gem 'guard-minitest'
10
+ gem 'pry'
11
+ gem 'rake'
12
+
13
+ gem 'pg'
14
+ gem 'mysql'
15
+ gem 'mysql2'
16
+ end
17
+
18
+ if Config::CONFIG['target_os'] =~ /darwin/i
19
+ gem 'rb-fsevent', '>= 0.3.2'
20
+ gem 'growl', '~> 1.0.3'
21
+ end
22
+ if Config::CONFIG['target_os'] =~ /linux/i
23
+ gem 'rb-inotify', '>= 0.5.1'
24
+ gem 'libnotify', '~> 0.1.3'
25
+ end
26
+ if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
27
+ gem 'win32console', :require => false
28
+ gem 'rb-fchange', '~> 0.0.2', :require => false
29
+ gem 'rb-notifu', '~> 0.0.4', :require => false
30
+ end
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'minitest', :notify => true do
5
+ # with Minitest::Unit
6
+ # watch(%r|^test/test_(.*)\.rb|)
7
+ # watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
8
+ # watch(%r|^test/test_helper\.rb|) { "test" }
9
+
10
+ # with Minitest::Spec
11
+ watch(%r|^spec/(.*)_spec\.rb|)
12
+ watch(%r|^lib/(.*)\.rb|) { |m| "spec/#{m[1]}_spec.rb" }
13
+ watch(%r|^spec/spec_helper\.rb|) { "spec" }
14
+ end
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ Skiima: an ORM-Agnostic, Rails-Independent alternative to migrations.
2
+ ------------------------
3
+
4
+ #### Goals:
5
+ Skiima is a work in progress with the following goals:
6
+
7
+ * Provide a better way to integrate views and functions into Ruby applications.
8
+ * Embeddable in gems to create DB independent extensions.
9
+ * Avoid any unnecessary dependencies or complexity.
10
+
11
+ I was working on another project and added some cool features that relied on postgres views, rules, etc. And basically, I needed a place to put these scripts and a way to execute them in a specified order.
12
+
13
+ There are alot of cool tricks to use in ActiveRecord with sql objects other than tables. Not to mention there are performance benefits with the right schema. Not everything needs to happen at the application layer.
14
+
15
+ If you have any questions about how things work, look at the tests. If you have any suggestions, don't hesitate to contact me.
16
+
17
+ #### Supported Databases:
18
+ ##### Postgres
19
+
20
+ - Schemas
21
+ - Tables
22
+ - Indexes
23
+ - Views
24
+ - Rules
25
+ - Triggers(soon)
26
+ - Functions(soon)
27
+
28
+ ##### Mysql/Mysql2
29
+
30
+ - Tables
31
+ - Views
32
+ - Indexes
33
+ - Triggers(soon)
34
+ - Functions(soon)
35
+ - Procs(soon)
36
+
37
+ #### Interface:
38
+ ##### Config Files
39
+ Skiima reads two yaml files: database.yml and depends.yml.
40
+
41
+ - By default, `database.yml` goes in your `APP_ROOT/config directory`.
42
+ - And similarly, `depends.yml` goes in `APP_ROOT/db/skiima`.
43
+
44
+ ##### Groups
45
+ Skiima allows you to create groups of sql scripts to be executed during migrations. Each group of sql scripts requires its own folder inside `db/skiima`.
46
+
47
+ ##### Adapter and Version
48
+ Since different databases have different capabilities and object types, you can trigger different scripts to run per adapter. Furthermore, you can execute different scripts for different versions of a database. You can also use this version tag if you want to package different sets of functionality.
49
+
50
+ ##### Depends.yml Format
51
+ In the `depends.yml` configuration, add lines for each script in the format.
52
+
53
+ * `type.name.attr1.attr2`
54
+
55
+ `attr1` and `attr2` allow you to define the target SQL object and other attributes that need to be passed on a per type basis. This is only used when Skiima drops your objects without a matching 'drop' script.
56
+
57
+ ##### Filename Format
58
+ Inside each group folder, create sql files with the following format. These need to match the `depends.yml` configuration.
59
+
60
+ * `type.name.adapter.version.sql`
61
+ * `type.name.adapter.version.drop.sql` to override the default drop behavior.
62
+
63
+ ##### Interpolation in Scripts
64
+ You can interpolate variables in your SQL Scripts and then pass in values at runtime with the `:vars` hash. The default character to use for interpolation is `&`, but this can be changed in the Module initialization. There are default vars that are substituted, such as `&database`.
65
+
66
+ ##### Module Initialization
67
+ If you're using Rails, you can add a Skiima.setup block in an intializer file to set defaults. There are other ways to integrate Skiima into your project as well.
68
+
69
+ Skiima.setup do |config|
70
+ config.root_path = Rails.root # changing
71
+ config.config_path = 'config'
72
+ config.scripts_path = 'db/skiima'
73
+ config.locale = :en
74
+ end
75
+
76
+ ##### Finally, in your Migrations
77
+ Skiima reads the specified groups from depends.yml and compiles a list of scripts to run. If you're using Rails, substitute `:development` with `Rails.env`
78
+
79
+ def up
80
+ Skiima.up(:development, :group_one, :group_n, :vars => {:var_one => 'db_name'})
81
+ end
82
+
83
+ def down
84
+ Skiima.down(:development, :group_one, :group_n, :vars => {:var_one => 'db_name'})
85
+ end
86
+
87
+ #### yes, i know i am shamelessly biting activerecord code.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'rake/testtask'
4
+ require 'bundler/gem_tasks'
5
+ require 'skiima'
6
+
7
+ SPEC_ROOT = File.join(File.dirname(__FILE__), 'spec')
8
+
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs << "spec"
12
+ t.test_files = FileList['spec/**/*_spec.rb']
13
+ t.verbose = true
14
+ end
15
+
16
+ task :default => :test
17
+
18
+ namespace :test do
19
+ namespace :prepare do
20
+
21
+ task :postgresql do
22
+ Skiima.setup do |config|
23
+ config.root_path = SPEC_ROOT
24
+ config.config_path = 'config'
25
+ config.scripts_path = 'db/skiima'
26
+ config.locale = :en
27
+ end
28
+
29
+ db_config = Skiima.read_db_yaml(Skiima.full_database_path)[:development]
30
+ Skiima.new(:development)
31
+ Skiima.read_sql_file('init_test_db', 'database.skiima_test.postgresql.current.sql')
32
+ end
33
+
34
+ task :mysql do
35
+ Skiima.setup do |config|
36
+ config.root_path = SPEC_ROOT
37
+ config.config_path = 'config'
38
+ config.scripts_path = 'db/skiima'
39
+ config.locale = :en
40
+ end
41
+
42
+ db_config = Skiima.read_db_yaml(Skiima.full_database_path)
43
+ Skiima.read_sql_file('init_test_db', 'database.skiima_test.mysql.current.sql')
44
+ end
45
+
46
+ end
47
+ end
48
+
49
+ #desc 'Generate documentation for Skiima.'
50
+ #Rake::RDocTask.new(:rdoc) do |rdoc|
51
+ # rdoc.rdoc_dir = 'rdoc'
52
+ # rdoc.title = 'Skiima'
53
+ # rdoc.options << '--line-numbers' << '--inline-source'
54
+ # rdoc.rdoc_files.include('README.rdoc')
55
+ # rdoc.rdoc_files.include('lib/**/*.rb')
56
+ #end
@@ -0,0 +1,308 @@
1
+ # encoding: utf-8
2
+ module Skiima
3
+ module DbAdapters
4
+ class BaseMysqlAdapter < Base
5
+ attr_accessor :version
6
+
7
+ LOST_CONNECTION_ERROR_MESSAGES = [
8
+ "Server shutdown in progress",
9
+ "Broken pipe",
10
+ "Lost connection to MySQL server during query",
11
+ "MySQL server has gone away" ]
12
+
13
+ # FIXME: Make the first parameter more similar for the two adapters
14
+ def initialize(connection, logger, connection_options, config)
15
+ super(connection, logger)
16
+ @connection_options, @config = connection_options, config
17
+ @quoted_column_names, @quoted_table_names = {}, {}
18
+ end
19
+
20
+ def version
21
+ @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
22
+ end
23
+
24
+ def adapter_name #:nodoc:
25
+ self.class::ADAPTER_NAME
26
+ end
27
+
28
+ # Returns true, since this connection adapter supports migrations.
29
+ def supports_migrations?
30
+ true
31
+ end
32
+
33
+ def supports_primary_key?
34
+ true
35
+ end
36
+
37
+ # Returns true, since this connection adapter supports savepoints.
38
+ def supports_savepoints?
39
+ true
40
+ end
41
+
42
+ # Must return the Mysql error number from the exception, if the exception has an
43
+ # error number.
44
+ def error_number(exception) # :nodoc:
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def disable_referential_integrity(&block) #:nodoc:
49
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
50
+
51
+ begin
52
+ update("SET FOREIGN_KEY_CHECKS = 0")
53
+ yield
54
+ ensure
55
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
56
+ end
57
+ end
58
+
59
+ # MysqlAdapter has to free a result after using it, so we use this method to write
60
+ # stuff in a abstract way without concerning ourselves about whether it needs to be
61
+ # explicitly freed or not.
62
+ def execute_and_free(sql, name = nil) #:nodoc:
63
+ yield execute(sql, name)
64
+ end
65
+
66
+ # Executes the SQL statement in the context of this connection.
67
+ def execute(sql, name = nil)
68
+ if name == :skip_logging
69
+ @connection.query(sql)
70
+ else
71
+ log(sql, name) { @connection.query(sql) }
72
+ end
73
+ rescue StatementInvalid => exception
74
+ if exception.message.split(":").first =~ /Packets out of order/
75
+ raise StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
76
+ else
77
+ raise
78
+ end
79
+ end
80
+
81
+ def begin_db_transaction
82
+ execute "BEGIN"
83
+ rescue Exception
84
+ # Transactions aren't supported
85
+ end
86
+
87
+ def commit_db_transaction #:nodoc:
88
+ execute "COMMIT"
89
+ rescue Exception
90
+ # Transactions aren't supported
91
+ end
92
+
93
+ def rollback_db_transaction #:nodoc:
94
+ execute "ROLLBACK"
95
+ rescue Exception
96
+ # Transactions aren't supported
97
+ end
98
+
99
+ def create_savepoint
100
+ execute("SAVEPOINT #{current_savepoint_name}")
101
+ end
102
+
103
+ def rollback_to_savepoint
104
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
105
+ end
106
+
107
+ def release_savepoint
108
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
109
+ end
110
+
111
+ def supported_objects
112
+ [:database, :table, :view, :index]
113
+ end
114
+
115
+ def tables(name = nil, database = nil, like = nil)
116
+ sql = "SHOW FULL TABLES "
117
+ sql << "IN #{database} " if database
118
+ sql << "WHERE table_type = 'BASE TABLE' "
119
+ sql << "LIKE '#{like}' " if like
120
+
121
+ execute_and_free(sql, 'SCHEMA') do |result|
122
+ result.collect { |field| field.first }
123
+ end
124
+ end
125
+
126
+ def views(name = nil, database = nil, like = nil)
127
+ sql = "SHOW FULL TABLES "
128
+ sql << "IN #{database} " if database
129
+ sql << "WHERE table_type = 'VIEW' "
130
+ sql << "LIKE '#{like}' " if like
131
+
132
+ execute_and_free(sql, 'SCHEMA') do |result|
133
+ result.collect { |field| field.first }
134
+ end
135
+ end
136
+
137
+ def indexes(name = nil, database = nil, table = nil)
138
+ sql = "SHOW INDEX "
139
+ sql << "IN #{table} "
140
+ sql << "IN #{database} " if database
141
+ sql << "WHERE key_name = '#{name}'" if name
142
+
143
+ execute_and_free(sql, 'SCHEMA') do |result|
144
+ result.collect { |field| field[2] }
145
+ end
146
+ end
147
+
148
+ "select routine_schema, routine_name, routine_type from routines;"
149
+
150
+ def procs(name = nil, database = nil, like = nil)
151
+ sql = "SELECT r.routine_name "
152
+ sql << "FROM information_schema.routines r "
153
+ sql << "WHERE r.routine_type = 'PROCEDURE' "
154
+ sql << "AND r.routine_name LIKE '#{like}' " if like
155
+ sql << "AND r.routine_schema = #{database} " if database
156
+
157
+ execute_and_free(sql, 'SCHEMA') do |result|
158
+ result.collect { |field| field.first }
159
+ end
160
+ end
161
+
162
+ def database_exists?(name)
163
+ #stub
164
+ end
165
+
166
+ def table_exists?(name)
167
+ return false unless name
168
+ return true if tables(nil, nil, name).any?
169
+
170
+ name = name.to_s
171
+ schema, table = name.split('.', 2)
172
+
173
+ unless table # A table was provided without a schema
174
+ table = schema
175
+ schema = nil
176
+ end
177
+
178
+ tables(nil, schema, table).any?
179
+ end
180
+
181
+ def view_exists?(name)
182
+ return false unless name
183
+ return true if views(nil, nil, name).any?
184
+
185
+ name = name.to_s
186
+ schema, view = name.split('.', 2)
187
+
188
+ unless view # A table was provided without a schema
189
+ view = schema
190
+ schema = nil
191
+ end
192
+
193
+ views(nil, schema, view).any?
194
+ end
195
+
196
+ def index_exists?(name, opts = {})
197
+ target = opts[:attr] ? opts[:attr][0] : nil
198
+ raise "requires target object" unless target
199
+
200
+ return false unless table_exists?(target) #mysql blows up when table doesn't exist
201
+ return false unless name
202
+ return true if indexes(name, nil, target).any?
203
+
204
+ name = name.to_s
205
+ schema, target = name.split('.', 2)
206
+
207
+ unless target # A table was provided without a schema
208
+ target = schema
209
+ schema = nil
210
+ end
211
+
212
+ indexes(name, schema, target).any?
213
+ end
214
+
215
+ def proc_exists?(name, opts = {})
216
+ return false unless name
217
+ return true if procs(nil, nil, name).any?
218
+
219
+ name = name.to_s
220
+ schema, proc = name.split('.', 2)
221
+
222
+ unless proc # A table was provided without a schema
223
+ proc = schema
224
+ schema = nil
225
+ end
226
+
227
+ procs(name, schema, proc).any?
228
+ end
229
+
230
+ def drop_database(name, opts = {})
231
+ "DROP DATABASE IF EXISTS #{name}"
232
+ end
233
+
234
+ def drop_table(name, opts = {})
235
+ "DROP TABLE IF EXISTS #{name}"
236
+ end
237
+
238
+ def drop_view(name, opts = {})
239
+ "DROP VIEW IF EXISTS #{name}"
240
+ end
241
+
242
+ def drop_index(name, opts = {})
243
+ target = opts[:attr].first if opts[:attr]
244
+ raise "requires target object" unless target
245
+
246
+ "DROP INDEX #{name} ON #{target}"
247
+ end
248
+
249
+ def column_definitions(table_name)
250
+ # "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
251
+ end
252
+
253
+ def column_names(table_name)
254
+ sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
255
+ execute_and_free(sql, 'SCHEMA') do |result|
256
+ result.collect { |field| field.first }
257
+ end
258
+ end
259
+
260
+ def quote_column_name(name) #:nodoc:
261
+ @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
262
+ end
263
+
264
+ def quote_table_name(name) #:nodoc:
265
+ @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
266
+ end
267
+
268
+ def current_database
269
+ select_value 'SELECT DATABASE() as db'
270
+ end
271
+
272
+ # Returns the database character set.
273
+ def charset
274
+ show_variable 'character_set_database'
275
+ end
276
+
277
+ # Returns the database collation strategy.
278
+ def collation
279
+ show_variable 'collation_database'
280
+ end
281
+
282
+ def show_variable(name)
283
+ # variables = select_all("SHOW VARIABLES LIKE '#{name}'")
284
+ # variables.first['Value'] unless variables.empty?
285
+ end
286
+
287
+ protected
288
+
289
+ def translate_exception(exception, message)
290
+ exception
291
+ # case error_number(exception)
292
+ # when 1062
293
+ # RecordNotUnique.new(message, exception)
294
+ # when 1452
295
+ # InvalidForeignKey.new(message, exception)
296
+ # else
297
+ # super
298
+ # end
299
+ end
300
+
301
+ private
302
+
303
+ def supports_views?
304
+ version[0] >= 5
305
+ end
306
+ end
307
+ end
308
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ require 'skiima/db_adapters/base_mysql_adapter'
3
+
4
+ gem 'mysql2', '~> 0.3.10'
5
+ require 'mysql2'
6
+
7
+ module Skiima
8
+ def self.mysql2_connection(logger, config)
9
+ config[:username] = 'root' if config[:username].nil?
10
+
11
+ if Mysql2::Client.const_defined? :FOUND_ROWS
12
+ config[:flags] = Mysql2::Client::FOUND_ROWS
13
+ end
14
+
15
+ client = Mysql2::Client.new(Skiima.symbolize_keys(config))
16
+ options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
17
+ Skiima::DbAdapters::Mysql2Adapter.new(client, logger, options, config)
18
+ end
19
+
20
+ module DbAdapters
21
+ class Mysql2Adapter < BaseMysqlAdapter
22
+ ADAPTER_NAME = 'Mysql2'
23
+
24
+ def initialize(connection, logger, connection_options, config)
25
+ super
26
+ configure_connection
27
+ end
28
+
29
+ def supports_explain?
30
+ true
31
+ end
32
+
33
+ def error_number(exception)
34
+ exception.error_number if exception.respond_to?(:error_number)
35
+ end
36
+
37
+ def active?
38
+ return false unless @connection
39
+ @connection.ping
40
+ end
41
+
42
+ def reconnect!
43
+ disconnect!
44
+ connect
45
+ end
46
+
47
+ # Disconnects from the database if already connected.
48
+ # Otherwise, this method does nothing.
49
+ def disconnect!
50
+ unless @connection.nil?
51
+ @connection.close
52
+ @connection = nil
53
+ end
54
+ end
55
+
56
+ def reset!
57
+ disconnect!
58
+ connect
59
+ end
60
+
61
+ def execute(sql, name = nil)
62
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
63
+ # made since we established the connection
64
+ # @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
65
+
66
+ # relying on formatting inside the file is precisely what i wanted to avoid...
67
+ results = sql.split(/^--={4,}/).map do |spider_monkey|
68
+ super(spider_monkey)
69
+ end
70
+
71
+ results.first
72
+ end
73
+
74
+ def exec_query(sql, name = 'SQL', binds = [])
75
+ result = execute(sql, name)
76
+ ActiveRecord::Result.new(result.fields, result.to_a)
77
+ end
78
+
79
+ alias exec_without_stmt exec_query
80
+
81
+ private
82
+
83
+ def connect
84
+ @connection = Mysql2::Client.new(@config)
85
+ configure_connection
86
+ end
87
+
88
+ def configure_connection
89
+ @connection.query_options.merge!(:as => :array)
90
+
91
+ variable_assignments = get_var_assignments
92
+ execute("SET #{variable_assignments.join(', ')}", :skip_logging)
93
+
94
+ version
95
+ end
96
+
97
+ def get_var_assignments
98
+ # By default, MySQL 'where id is null' selects the last inserted id.
99
+ # Turn this off. http://dev.rubyonrails.org/ticket/6778
100
+ variable_assignments = ['SQL_AUTO_IS_NULL=0']
101
+ encoding = @config[:encoding]
102
+
103
+ # make sure we set the encoding
104
+ variable_assignments << "NAMES '#{encoding}'" if encoding
105
+
106
+ # increase timeout so mysql server doesn't disconnect us
107
+ wait_timeout = @config[:wait_timeout]
108
+ wait_timeout = 2592000 unless wait_timeout.is_a?(Fixnum)
109
+ variable_assignments << "@@wait_timeout = #{wait_timeout}"
110
+ end
111
+
112
+ end
113
+ end
114
+ end