skiima 0.1.000

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 (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