ghazel-mysql2 0.2.6.1

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