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
@@ -0,0 +1,287 @@
1
+ # encoding: utf-8
2
+ require 'skiima/db_adapters/base_mysql_adapter'
3
+
4
+ gem 'mysql', '~> 2.8.1'
5
+ require 'mysql'
6
+
7
+ class Mysql
8
+ class Time
9
+ ###
10
+ # This monkey patch is for test_additional_columns_from_join_table
11
+ def to_date
12
+ Date.new(year, month, day)
13
+ end
14
+ end
15
+ class Stmt; include Enumerable end
16
+ class Result; include Enumerable end
17
+ end
18
+
19
+ module Skiima
20
+ # Establishes a connection to the database that's used by all Active Record objects.
21
+ def self.mysql_connection(logger, config) # :nodoc:
22
+ config = Skiima.symbolize_keys(config)
23
+ host = config[:host]
24
+ port = config[:port]
25
+ socket = config[:socket]
26
+ username = config[:username] ? config[:username].to_s : 'root'
27
+ password = config[:password].to_s
28
+ database = config[:database]
29
+
30
+ mysql = Mysql.init
31
+ mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
32
+
33
+ default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0
34
+ default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
35
+
36
+ options = [host, username, password, database, port, socket, default_flags]
37
+ Skiima::DbAdapters::MysqlAdapter.new(mysql, logger, options, config)
38
+ end
39
+
40
+ module DbAdapters
41
+ class MysqlAdapter < BaseMysqlAdapter
42
+ attr_accessor :client_encoding
43
+
44
+ ADAPTER_NAME = 'MySQL'
45
+
46
+ def initialize(connection, logger, connection_options, config)
47
+ super
48
+ @client_encoding = nil
49
+ connect
50
+ end
51
+
52
+ # # Returns the version of the connected MySQL server.
53
+ def version
54
+ @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
55
+ end
56
+
57
+ # # Returns true, since this connection adapter supports prepared statement
58
+ # # caching.
59
+ def supports_statement_cache?
60
+ true
61
+ end
62
+
63
+ def error_number(exception) # :nodoc:
64
+ exception.errno if exception.respond_to?(:errno)
65
+ end
66
+
67
+ def type_cast(value, column)
68
+ return super unless value == true || value == false
69
+
70
+ value ? 1 : 0
71
+ end
72
+
73
+ def quote_string(string) #:nodoc:
74
+ @connection.quote(string)
75
+ end
76
+
77
+ def active?
78
+ if @connection.respond_to?(:stat)
79
+ @connection.stat
80
+ else
81
+ @connection.query 'select 1'
82
+ end
83
+
84
+ # mysql-ruby doesn't raise an exception when stat fails.
85
+ if @connection.respond_to?(:errno)
86
+ @connection.errno.zero?
87
+ else
88
+ true
89
+ end
90
+ rescue Mysql::Error
91
+ false
92
+ end
93
+
94
+ def reconnect!
95
+ disconnect!
96
+ clear_cache!
97
+ connect
98
+ end
99
+
100
+ # Disconnects from the database if already connected. Otherwise, this
101
+ # method does nothing.
102
+ def disconnect!
103
+ @connection.close rescue nil
104
+ end
105
+
106
+ def reset!
107
+ if @connection.respond_to?(:change_user)
108
+ # See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
109
+ # reset the connection is to change the user to the same user.
110
+ @connection.change_user(@config[:username], @config[:password], @config[:database])
111
+ configure_connection
112
+ end
113
+ end
114
+
115
+ if "<3".respond_to?(:encode)
116
+ # Taken from here:
117
+ # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
118
+ # Author: TOMITA Masahiro <tommy@tmtm.org>
119
+ ENCODINGS = {
120
+ "armscii8" => nil,
121
+ "ascii" => Encoding::US_ASCII,
122
+ "big5" => Encoding::Big5,
123
+ "binary" => Encoding::ASCII_8BIT,
124
+ "cp1250" => Encoding::Windows_1250,
125
+ "cp1251" => Encoding::Windows_1251,
126
+ "cp1256" => Encoding::Windows_1256,
127
+ "cp1257" => Encoding::Windows_1257,
128
+ "cp850" => Encoding::CP850,
129
+ "cp852" => Encoding::CP852,
130
+ "cp866" => Encoding::IBM866,
131
+ "cp932" => Encoding::Windows_31J,
132
+ "dec8" => nil,
133
+ "eucjpms" => Encoding::EucJP_ms,
134
+ "euckr" => Encoding::EUC_KR,
135
+ "gb2312" => Encoding::EUC_CN,
136
+ "gbk" => Encoding::GBK,
137
+ "geostd8" => nil,
138
+ "greek" => Encoding::ISO_8859_7,
139
+ "hebrew" => Encoding::ISO_8859_8,
140
+ "hp8" => nil,
141
+ "keybcs2" => nil,
142
+ "koi8r" => Encoding::KOI8_R,
143
+ "koi8u" => Encoding::KOI8_U,
144
+ "latin1" => Encoding::ISO_8859_1,
145
+ "latin2" => Encoding::ISO_8859_2,
146
+ "latin5" => Encoding::ISO_8859_9,
147
+ "latin7" => Encoding::ISO_8859_13,
148
+ "macce" => Encoding::MacCentEuro,
149
+ "macroman" => Encoding::MacRoman,
150
+ "sjis" => Encoding::SHIFT_JIS,
151
+ "swe7" => nil,
152
+ "tis620" => Encoding::TIS_620,
153
+ "ucs2" => Encoding::UTF_16BE,
154
+ "ujis" => Encoding::EucJP_ms,
155
+ "utf8" => Encoding::UTF_8,
156
+ "utf8mb4" => Encoding::UTF_8,
157
+ }
158
+ else
159
+ ENCODINGS = Hash.new { |h,k| h[k] = k }
160
+ end
161
+
162
+ # Get the client encoding for this database
163
+ def client_encoding
164
+ return @client_encoding if @client_encoding
165
+
166
+ result = exec_query(
167
+ "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
168
+ 'SCHEMA')
169
+ @client_encoding = ENCODINGS[result.last.last]
170
+ end
171
+
172
+ def execute(sql, name = nil)
173
+ # this is nauseating. i can't believe it's an issue to run multiple statements
174
+ # tried using OPTION_MULTI_STATEMENTS_ON, but ruby segfaulted. womp womp.
175
+ # my workaround is messy for now
176
+
177
+ # relying on formatting inside the file is precisely what i wanted to avoid...
178
+ results = sql.split(/^--={4,}/).map do |spider_monkey|
179
+ super(spider_monkey)
180
+ end
181
+
182
+ results.first
183
+ end
184
+
185
+ def exec_query(sql, name = 'SQL')
186
+ log(sql, name) do
187
+ exec_stmt(sql, name) do |cols, stmt|
188
+ stmt.to_a
189
+ end
190
+ end
191
+ end
192
+
193
+ # def exec_without_stmt(sql, name = 'SQL') # :nodoc:
194
+ # # Some queries, like SHOW CREATE TABLE don't work through the prepared
195
+ # # statement API. For those queries, we need to use this method. :'(
196
+ # log(sql, name) do
197
+ # result = @connection.query(sql)
198
+ # cols = []
199
+ # rows = []
200
+
201
+ # if result
202
+ # cols = result.fetch_fields.map { |field| field.name }
203
+ # rows = result.to_a
204
+ # result.free
205
+ # end
206
+ # ActiveRecord::Result.new(cols, rows)
207
+ # end
208
+ # end
209
+
210
+ def execute_and_free(sql, name = nil)
211
+ result = execute(sql, name)
212
+ ret = yield result
213
+ result.free
214
+ ret
215
+ end
216
+
217
+ def begin_db_transaction #:nodoc:
218
+ exec_without_stmt "BEGIN"
219
+ rescue Mysql::Error
220
+ # Transactions aren't supported
221
+ end
222
+
223
+ private
224
+
225
+ def exec_stmt(sql, name)
226
+ stmt = @connection.prepare(sql)
227
+
228
+ begin
229
+ stmt.execute
230
+ rescue Mysql::Error => e
231
+ # Older versions of MySQL leave the prepared statement in a bad
232
+ # place when an error occurs. To support older mysql versions, we
233
+ # need to close the statement and delete the statement from the
234
+ # cache.
235
+ stmt.close
236
+ raise e
237
+ end
238
+
239
+ cols = nil
240
+ if metadata = stmt.result_metadata
241
+ cols = metadata.fetch_fields.map { |field| field.name }
242
+ end
243
+
244
+ result = yield [cols, stmt]
245
+
246
+ stmt.result_metadata.free if cols
247
+ stmt.free_result
248
+ stmt.close
249
+
250
+ result
251
+ end
252
+
253
+ def connect
254
+ encoding = @config[:encoding]
255
+ if encoding
256
+ @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
257
+ end
258
+
259
+ if @config[:sslca] || @config[:sslkey]
260
+ @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
261
+ end
262
+
263
+ @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
264
+ @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
265
+ @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
266
+
267
+ @connection.real_connect(*@connection_options)
268
+
269
+ # reconnect must be set after real_connect is called, because real_connect sets it to false internally
270
+ @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
271
+
272
+ version #gets version
273
+
274
+ configure_connection
275
+ end
276
+
277
+ def configure_connection
278
+ encoding = @config[:encoding]
279
+ execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
280
+
281
+ # By default, MySQL 'where id is null' selects the last inserted id.
282
+ # Turn this off. http://dev.rubyonrails.org/ticket/6778
283
+ execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
284
+ end
285
+ end
286
+ end
287
+ end