ghazel-mysql2 0.2.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG.md +117 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +240 -0
  6. data/Rakefile +5 -0
  7. data/VERSION +1 -0
  8. data/benchmark/active_record.rb +53 -0
  9. data/benchmark/allocations.rb +33 -0
  10. data/benchmark/escape.rb +39 -0
  11. data/benchmark/query_with_mysql_casting.rb +83 -0
  12. data/benchmark/query_without_mysql_casting.rb +50 -0
  13. data/benchmark/sequel.rb +39 -0
  14. data/benchmark/setup_db.rb +115 -0
  15. data/examples/eventmachine.rb +21 -0
  16. data/examples/threaded.rb +20 -0
  17. data/ext/mysql2/client.c +728 -0
  18. data/ext/mysql2/client.h +41 -0
  19. data/ext/mysql2/extconf.rb +69 -0
  20. data/ext/mysql2/mysql2_ext.c +12 -0
  21. data/ext/mysql2/mysql2_ext.h +38 -0
  22. data/ext/mysql2/result.c +478 -0
  23. data/ext/mysql2/result.h +20 -0
  24. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +62 -0
  25. data/lib/active_record/connection_adapters/mysql2_adapter.rb +657 -0
  26. data/lib/active_record/fiber_patches.rb +104 -0
  27. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +11 -0
  28. data/lib/mysql2.rb +16 -0
  29. data/lib/mysql2/client.rb +240 -0
  30. data/lib/mysql2/em.rb +37 -0
  31. data/lib/mysql2/em_fiber.rb +29 -0
  32. data/lib/mysql2/error.rb +15 -0
  33. data/lib/mysql2/result.rb +5 -0
  34. data/mysql2.gemspec +90 -0
  35. data/spec/em/em_spec.rb +49 -0
  36. data/spec/mysql2/client_spec.rb +367 -0
  37. data/spec/mysql2/error_spec.rb +25 -0
  38. data/spec/mysql2/result_spec.rb +318 -0
  39. data/spec/rcov.opts +3 -0
  40. data/spec/spec_helper.rb +67 -0
  41. data/tasks/benchmarks.rake +8 -0
  42. data/tasks/compile.rake +54 -0
  43. data/tasks/jeweler.rake +17 -0
  44. data/tasks/rspec.rake +16 -0
  45. data/tasks/vendor_mysql.rake +41 -0
  46. metadata +119 -0
@@ -0,0 +1,5 @@
1
+ module Mysql2
2
+ class Result
3
+ include Enumerable
4
+ end
5
+ end
data/mysql2.gemspec ADDED
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ghazel-mysql2}
8
+ s.version = "0.2.6.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Lopez"]
12
+ s.date = %q{2010-10-19}
13
+ s.email = %q{seniorlopez@gmail.com}
14
+ s.extensions = ["ext/mysql2/extconf.rb"]
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ ".rspec",
21
+ "CHANGELOG.md",
22
+ "MIT-LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "benchmark/active_record.rb",
27
+ "benchmark/allocations.rb",
28
+ "benchmark/escape.rb",
29
+ "benchmark/query_with_mysql_casting.rb",
30
+ "benchmark/query_without_mysql_casting.rb",
31
+ "benchmark/sequel.rb",
32
+ "benchmark/setup_db.rb",
33
+ "examples/eventmachine.rb",
34
+ "examples/threaded.rb",
35
+ "ext/mysql2/client.c",
36
+ "ext/mysql2/client.h",
37
+ "ext/mysql2/extconf.rb",
38
+ "ext/mysql2/mysql2_ext.c",
39
+ "ext/mysql2/mysql2_ext.h",
40
+ "ext/mysql2/result.c",
41
+ "ext/mysql2/result.h",
42
+ "lib/active_record/connection_adapters/em_mysql2_adapter.rb",
43
+ "lib/active_record/connection_adapters/mysql2_adapter.rb",
44
+ "lib/active_record/fiber_patches.rb",
45
+ "lib/arel/engines/sql/compilers/mysql2_compiler.rb",
46
+ "lib/mysql2.rb",
47
+ "lib/mysql2/client.rb",
48
+ "lib/mysql2/em.rb",
49
+ "lib/mysql2/em_fiber.rb",
50
+ "lib/mysql2/error.rb",
51
+ "lib/mysql2/result.rb",
52
+ "mysql2.gemspec",
53
+ "spec/em/em_spec.rb",
54
+ "spec/mysql2/client_spec.rb",
55
+ "spec/mysql2/error_spec.rb",
56
+ "spec/mysql2/result_spec.rb",
57
+ "spec/rcov.opts",
58
+ "spec/spec_helper.rb",
59
+ "tasks/benchmarks.rake",
60
+ "tasks/compile.rake",
61
+ "tasks/jeweler.rake",
62
+ "tasks/rspec.rake",
63
+ "tasks/vendor_mysql.rake"
64
+ ]
65
+ s.homepage = %q{http://github.com/brianmario/mysql2}
66
+ s.rdoc_options = ["--charset=UTF-8"]
67
+ s.require_paths = ["lib", "ext"]
68
+ s.rubygems_version = %q{1.3.7}
69
+ s.summary = %q{A simple, fast Mysql library for Ruby, binding to libmysql}
70
+ s.test_files = [
71
+ "spec/em/em_spec.rb",
72
+ "spec/mysql2/client_spec.rb",
73
+ "spec/mysql2/error_spec.rb",
74
+ "spec/mysql2/result_spec.rb",
75
+ "spec/spec_helper.rb",
76
+ "examples/eventmachine.rb",
77
+ "examples/threaded.rb"
78
+ ]
79
+
80
+ if s.respond_to? :specification_version then
81
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
82
+ s.specification_version = 3
83
+
84
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
85
+ else
86
+ end
87
+ else
88
+ end
89
+ end
90
+
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+ if defined? EventMachine
3
+ require 'spec_helper'
4
+ require 'mysql2/em'
5
+
6
+ describe Mysql2::EM::Client do
7
+ it "should support async queries" do
8
+ results = []
9
+ EM.run do
10
+ client1 = Mysql2::EM::Client.new
11
+ defer1 = client1.query "SELECT sleep(0.1) as first_query"
12
+ defer1.callback do |result|
13
+ results << result.first
14
+ EM.stop_event_loop
15
+ end
16
+
17
+ client2 = Mysql2::EM::Client.new
18
+ defer2 = client2.query "SELECT sleep(0.025) second_query"
19
+ defer2.callback do |result|
20
+ results << result.first
21
+ end
22
+ end
23
+
24
+ results[0].keys.should include("second_query")
25
+ results[1].keys.should include("first_query")
26
+ end
27
+
28
+ it "should support queries in callbacks" do
29
+ results = []
30
+ EM.run do
31
+ client = Mysql2::EM::Client.new
32
+ defer1 = client.query "SELECT sleep(0.025) as first_query"
33
+ defer1.callback do |result|
34
+ results << result.first
35
+ defer2 = client.query "SELECT sleep(0.025) as second_query"
36
+ defer2.callback do |result|
37
+ results << result.first
38
+ EM.stop_event_loop
39
+ end
40
+ end
41
+ end
42
+
43
+ results[0].keys.should include("first_query")
44
+ results[1].keys.should include("second_query")
45
+ end
46
+ end
47
+ else
48
+ puts "EventMachine not installed, skipping the specs that use it"
49
+ end
@@ -0,0 +1,367 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Mysql2::Client do
5
+ before(:each) do
6
+ @client = Mysql2::Client.new
7
+ end
8
+
9
+ if defined? Encoding
10
+ it "should raise an exception on create for invalid encodings" do
11
+ lambda {
12
+ c = Mysql2::Client.new(:encoding => "fake")
13
+ }.should raise_error(Mysql2::Error)
14
+ end
15
+ end
16
+
17
+ it "should accept connect flags and pass them to #connect" do
18
+ klient = Class.new(Mysql2::Client) do
19
+ attr_reader :connect_args
20
+ def connect *args
21
+ @connect_args ||= []
22
+ @connect_args << args
23
+ end
24
+ end
25
+ client = klient.new :flags => Mysql2::Client::FOUND_ROWS
26
+ (client.connect_args.last.last & Mysql2::Client::FOUND_ROWS).should be_true
27
+ end
28
+
29
+ it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
30
+ klient = Class.new(Mysql2::Client) do
31
+ attr_reader :connect_args
32
+ def connect *args
33
+ @connect_args ||= []
34
+ @connect_args << args
35
+ end
36
+ end
37
+ client = klient.new
38
+ (client.connect_args.last.last & (Mysql2::Client::REMEMBER_OPTIONS |
39
+ Mysql2::Client::LONG_PASSWORD |
40
+ Mysql2::Client::LONG_FLAG |
41
+ Mysql2::Client::TRANSACTIONS |
42
+ Mysql2::Client::PROTOCOL_41 |
43
+ Mysql2::Client::SECURE_CONNECTION)).should be_true
44
+ end
45
+
46
+ it "should have a global default_query_options hash" do
47
+ Mysql2::Client.should respond_to(:default_query_options)
48
+ end
49
+
50
+ it "should be able to connect via SSL options" do
51
+ pending("DON'T WORRY, THIS TEST PASSES :) - but is machine-specific. You need to have MySQL running with SSL configured and enabled. Then update the paths in this test to your needs and remove the pending state.")
52
+ ssl_client = nil
53
+ lambda {
54
+ ssl_client = Mysql2::Client.new(
55
+ :sslkey => '/path/to/client-key.pem',
56
+ :sslcert => '/path/to/client-cert.pem',
57
+ :sslca => '/path/to/ca-cert.pem',
58
+ :sslcapath => '/path/to/newcerts/',
59
+ :sslcipher => 'DHE-RSA-AES256-SHA'
60
+ )
61
+ }.should_not raise_error(Mysql2::Error)
62
+
63
+ results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
64
+ results[0]['Variable_name'].should eql('Ssl_cipher')
65
+ results[0]['Value'].should_not be_nil
66
+ results[0]['Value'].class.should eql(String)
67
+
68
+ results[1]['Variable_name'].should eql('Ssl_version')
69
+ results[1]['Value'].should_not be_nil
70
+ results[1]['Value'].class.should eql(String)
71
+ end
72
+
73
+ it "should respond to #close" do
74
+ @client.should respond_to(:close)
75
+ end
76
+
77
+ it "should be able to close properly" do
78
+ @client.close.should be_nil
79
+ lambda {
80
+ @client.query "SELECT 1"
81
+ }.should raise_error(Mysql2::Error)
82
+ end
83
+
84
+ it "should respond to #query" do
85
+ @client.should respond_to(:query)
86
+ end
87
+
88
+ it "should expect read_timeout to be a positive integer" do
89
+ lambda {
90
+ Mysql2::Client.new(:read_timeout => -1)
91
+ }.should raise_error(Mysql2::Error)
92
+ end
93
+
94
+ context "#query" do
95
+ it "should only accept strings as the query parameter" do
96
+ lambda {
97
+ @client.query ["SELECT 'not right'"]
98
+ }.should raise_error(TypeError)
99
+ end
100
+
101
+ it "should accept an options hash that inherits from Mysql2::Client.default_query_options" do
102
+ @client.query "SELECT 1", :something => :else
103
+ @client.query_options.should eql(@client.query_options.merge(:something => :else))
104
+ end
105
+
106
+ it "should return results as a hash by default" do
107
+ @client.query("SELECT 1").first.class.should eql(Hash)
108
+ end
109
+
110
+ it "should be able to return results as an array" do
111
+ @client.query("SELECT 1", :as => :array).first.class.should eql(Array)
112
+ @client.query("SELECT 1").each(:as => :array)
113
+ end
114
+
115
+ it "should be able to return results with symbolized keys" do
116
+ @client.query("SELECT 1", :symbolize_keys => true).first.keys[0].class.should eql(Symbol)
117
+ end
118
+
119
+ it "should not allow another query to be sent without fetching a result first" do
120
+ @client.query("SELECT 1", :async => true)
121
+ lambda {
122
+ @client.query("SELECT 1")
123
+ }.should raise_error(Mysql2::Error)
124
+ end
125
+
126
+ it "should require an open connection" do
127
+ @client.close
128
+ lambda {
129
+ @client.query "SELECT 1"
130
+ }.should raise_error(Mysql2::Error)
131
+ end
132
+
133
+ it "should timeout if we wait longer than :read_timeout" do
134
+ client = Mysql2::Client.new(:read_timeout => 1)
135
+ lambda {
136
+ client.query("SELECT sleep(2)")
137
+ }.should raise_error(Mysql2::Error)
138
+ end
139
+
140
+ # XXX this test is not deterministic (because Unix signal handling is not)
141
+ # and may fail on a loaded system
142
+ if RUBY_PLATFORM !~ /mingw|mswin/
143
+ it "should run signal handlers while waiting for a response" do
144
+ mark = {}
145
+ trap(:USR1) { mark[:USR1] = Time.now }
146
+ begin
147
+ mark[:START] = Time.now
148
+ pid = fork do
149
+ sleep 1 # wait for client "SELECT sleep(2)" query to start
150
+ Process.kill(:USR1, Process.ppid)
151
+ sleep # wait for explicit kill to prevent GC disconnect
152
+ end
153
+ @client.query("SELECT sleep(2)")
154
+ mark[:END] = Time.now
155
+ mark.include?(:USR1).should be_true
156
+ (mark[:USR1] - mark[:START]).should >= 1
157
+ (mark[:USR1] - mark[:START]).should < 1.1
158
+ (mark[:END] - mark[:USR1]).should > 0.9
159
+ (mark[:END] - mark[:START]).should >= 2
160
+ (mark[:END] - mark[:START]).should < 2.1
161
+ Process.kill(:TERM, pid)
162
+ Process.waitpid2(pid)
163
+ ensure
164
+ trap(:USR1, 'DEFAULT')
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ it "should respond to #escape" do
171
+ @client.should respond_to(:escape)
172
+ end
173
+
174
+ context "#escape" do
175
+ it "should return a new SQL-escape version of the passed string" do
176
+ @client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
177
+ end
178
+
179
+ it "should return the passed string if nothing was escaped" do
180
+ str = "plain"
181
+ @client.escape(str).object_id.should eql(str.object_id)
182
+ end
183
+
184
+ it "should not overflow the thread stack" do
185
+ lambda {
186
+ Thread.new { @client.escape("'" * 256 * 1024) }.join
187
+ }.should_not raise_error(SystemStackError)
188
+ end
189
+
190
+ it "should not overflow the process stack" do
191
+ lambda {
192
+ Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
193
+ }.should_not raise_error(SystemStackError)
194
+ end
195
+
196
+ it "should require an open connection" do
197
+ @client.close
198
+ lambda {
199
+ @client.escape ""
200
+ }.should raise_error(Mysql2::Error)
201
+ end
202
+ end
203
+
204
+ it "should respond to #info" do
205
+ @client.should respond_to(:info)
206
+ end
207
+
208
+ it "#info should return a hash containing the client version ID and String" do
209
+ info = @client.info
210
+ info.class.should eql(Hash)
211
+ info.should have_key(:id)
212
+ info[:id].class.should eql(Fixnum)
213
+ info.should have_key(:version)
214
+ info[:version].class.should eql(String)
215
+ end
216
+
217
+ if defined? Encoding
218
+ context "strings returned by #info" do
219
+ it "should default to the connection's encoding if Encoding.default_internal is nil" do
220
+ Encoding.default_internal = nil
221
+ @client.info[:version].encoding.should eql(Encoding.find('utf-8'))
222
+
223
+ client2 = Mysql2::Client.new :encoding => 'ascii'
224
+ client2.info[:version].encoding.should eql(Encoding.find('us-ascii'))
225
+ end
226
+
227
+ it "should use Encoding.default_internal" do
228
+ Encoding.default_internal = Encoding.find('utf-8')
229
+ @client.info[:version].encoding.should eql(Encoding.default_internal)
230
+ Encoding.default_internal = Encoding.find('us-ascii')
231
+ @client.info[:version].encoding.should eql(Encoding.default_internal)
232
+ end
233
+ end
234
+ end
235
+
236
+ it "should respond to #server_info" do
237
+ @client.should respond_to(:server_info)
238
+ end
239
+
240
+ it "#server_info should return a hash containing the client version ID and String" do
241
+ server_info = @client.server_info
242
+ server_info.class.should eql(Hash)
243
+ server_info.should have_key(:id)
244
+ server_info[:id].class.should eql(Fixnum)
245
+ server_info.should have_key(:version)
246
+ server_info[:version].class.should eql(String)
247
+ end
248
+
249
+ it "#server_info should require an open connection" do
250
+ @client.close
251
+ lambda {
252
+ @client.server_info
253
+ }.should raise_error(Mysql2::Error)
254
+ end
255
+
256
+ if defined? Encoding
257
+ context "strings returned by #server_info" do
258
+ it "should default to the connection's encoding if Encoding.default_internal is nil" do
259
+ Encoding.default_internal = nil
260
+ @client.server_info[:version].encoding.should eql(Encoding.find('utf-8'))
261
+
262
+ client2 = Mysql2::Client.new :encoding => 'ascii'
263
+ client2.server_info[:version].encoding.should eql(Encoding.find('us-ascii'))
264
+ end
265
+
266
+ it "should use Encoding.default_internal" do
267
+ Encoding.default_internal = Encoding.find('utf-8')
268
+ @client.server_info[:version].encoding.should eql(Encoding.default_internal)
269
+ Encoding.default_internal = Encoding.find('us-ascii')
270
+ @client.server_info[:version].encoding.should eql(Encoding.default_internal)
271
+ end
272
+ end
273
+ end
274
+
275
+ it "should respond to #socket" do
276
+ @client.should respond_to(:socket)
277
+ end
278
+
279
+ it "#socket should return a Fixnum (file descriptor from C)" do
280
+ @client.socket.class.should eql(Fixnum)
281
+ @client.socket.should_not eql(0)
282
+ end
283
+
284
+ it "#socket should require an open connection" do
285
+ @client.close
286
+ lambda {
287
+ @client.socket
288
+ }.should raise_error(Mysql2::Error)
289
+ end
290
+
291
+ it "should raise a Mysql2::Error exception upon connection failure" do
292
+ lambda {
293
+ bad_client = Mysql2::Client.new :host => "dfjhdi9wrhw", :username => 'asdfasdf8d2h'
294
+ }.should raise_error(Mysql2::Error)
295
+
296
+ lambda {
297
+ good_client = Mysql2::Client.new
298
+ }.should_not raise_error(Mysql2::Error)
299
+ end
300
+
301
+ it "threaded queries should be supported" do
302
+ threads, results = [], {}
303
+ connect = lambda{ Mysql2::Client.new(:host => "localhost", :username => "root") }
304
+ Timeout.timeout(0.7) do
305
+ 5.times {
306
+ threads << Thread.new do
307
+ results[Thread.current.object_id] = connect.call.query("SELECT sleep(0.5) as result")
308
+ end
309
+ }
310
+ end
311
+ threads.each{|t| t.join }
312
+ results.keys.sort.should eql(threads.map{|t| t.object_id }.sort)
313
+ end
314
+
315
+ it "evented async queries should be supported" do
316
+ # should immediately return nil
317
+ @client.query("SELECT sleep(0.1)", :async => true).should eql(nil)
318
+
319
+ io_wrapper = IO.for_fd(@client.socket)
320
+ loops = 0
321
+ loop do
322
+ if IO.select([io_wrapper], nil, nil, 0.05)
323
+ break
324
+ else
325
+ loops += 1
326
+ end
327
+ end
328
+
329
+ # make sure we waited some period of time
330
+ (loops >= 1).should be_true
331
+
332
+ result = @client.async_result
333
+ result.class.should eql(Mysql2::Result)
334
+ end
335
+
336
+ context 'write operations api' do
337
+ before(:each) do
338
+ @client.query "USE test"
339
+ @client.query "CREATE TABLE lastIdTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
340
+ end
341
+
342
+ after(:each) do
343
+ @client.query "DROP TABLE lastIdTest"
344
+ end
345
+
346
+ it "should respond to #last_id" do
347
+ @client.should respond_to(:last_id)
348
+ end
349
+
350
+ it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
351
+ @client.last_id.should eql(0)
352
+ @client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
353
+ @client.last_id.should eql(1)
354
+ end
355
+
356
+ it "should respond to #last_id" do
357
+ @client.should respond_to(:last_id)
358
+ end
359
+
360
+ it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
361
+ @client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
362
+ @client.affected_rows.should eql(1)
363
+ @client.query "UPDATE lastIdTest SET blah=4321 WHERE id=1"
364
+ @client.affected_rows.should eql(1)
365
+ end
366
+ end
367
+ end