skiima 0.1.000
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.travis.yml +8 -0
- data/CHANGELOG +8 -0
- data/Gemfile +30 -0
- data/Guardfile +14 -0
- data/README.md +87 -0
- data/Rakefile +56 -0
- data/lib/skiima/db_adapters/base_mysql_adapter.rb +308 -0
- data/lib/skiima/db_adapters/mysql2_adapter.rb +114 -0
- data/lib/skiima/db_adapters/mysql_adapter.rb +287 -0
- data/lib/skiima/db_adapters/postgresql_adapter.rb +509 -0
- data/lib/skiima/db_adapters.rb +187 -0
- data/lib/skiima/dependency.rb +84 -0
- data/lib/skiima/locales/en.yml +20 -0
- data/lib/skiima/locales/fr.yml +2 -0
- data/lib/skiima/version.rb +4 -0
- data/lib/skiima.rb +270 -0
- data/lib/skiima_helpers.rb +49 -0
- data/skiima.gemspec +53 -0
- data/spec/config/database.yml +56 -0
- data/spec/db/skiima/depends.yml +61 -0
- data/spec/db/skiima/empty_depends.yml +0 -0
- data/spec/db/skiima/init_test_db/database.skiima_test.mysql.current.sql +2 -0
- data/spec/db/skiima/init_test_db/database.skiima_test.postgresql.current.sql +2 -0
- data/spec/db/skiima/test_column_names/table.test_column_names.mysql.current.sql +8 -0
- data/spec/db/skiima/test_column_names/table.test_column_names.postgresql.current.sql +18 -0
- data/spec/db/skiima/test_index/index.test_index.mysql.current.sql +2 -0
- data/spec/db/skiima/test_index/index.test_index.postgresql.current.sql +2 -0
- data/spec/db/skiima/test_proc/proc.test_proc.mysql.current.sql +8 -0
- data/spec/db/skiima/test_proc/proc.test_proc_drop.mysql.current.drop.sql +1 -0
- data/spec/db/skiima/test_proc/proc.test_proc_drop.mysql.current.sql +8 -0
- data/spec/db/skiima/test_rule/rule.test_rule.postgresql.current.sql +6 -0
- data/spec/db/skiima/test_schema/schema.test_schema.postgresql.current.sql +2 -0
- data/spec/db/skiima/test_table/table.test_table.mysql.current.sql +7 -0
- data/spec/db/skiima/test_table/table.test_table.postgresql.current.sql +4 -0
- data/spec/db/skiima/test_view/view.test_view.mysql.current.sql +5 -0
- data/spec/db/skiima/test_view/view.test_view.postgresql.current.sql +6 -0
- data/spec/helpers/mysql_spec_helper.rb +0 -0
- data/spec/helpers/postgresql_spec_helper.rb +11 -0
- data/spec/mysql2_spec.rb +57 -0
- data/spec/mysql_spec.rb +100 -0
- data/spec/postgresql_spec.rb +138 -0
- data/spec/skiima/db_adapters/mysql_adapter_spec.rb +38 -0
- data/spec/skiima/db_adapters/postgresql_adapter_spec.rb +20 -0
- data/spec/skiima/db_adapters_spec.rb +31 -0
- data/spec/skiima/dependency_spec.rb +94 -0
- data/spec/skiima_spec.rb +97 -0
- data/spec/spec_helper.rb +35 -0
- 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
|