mysql2_bigint 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/CHANGELOG.md +120 -0
  2. data/Gemfile +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +248 -0
  5. data/Rakefile +5 -0
  6. data/benchmark/active_record.rb +51 -0
  7. data/benchmark/allocations.rb +33 -0
  8. data/benchmark/escape.rb +36 -0
  9. data/benchmark/query_with_mysql_casting.rb +80 -0
  10. data/benchmark/query_without_mysql_casting.rb +47 -0
  11. data/benchmark/sequel.rb +37 -0
  12. data/benchmark/setup_db.rb +119 -0
  13. data/examples/eventmachine.rb +21 -0
  14. data/examples/threaded.rb +20 -0
  15. data/ext/mysql2/client.c +768 -0
  16. data/ext/mysql2/client.h +41 -0
  17. data/ext/mysql2/extconf.rb +69 -0
  18. data/ext/mysql2/mysql2_ext.c +12 -0
  19. data/ext/mysql2/mysql2_ext.h +38 -0
  20. data/ext/mysql2/result.c +488 -0
  21. data/ext/mysql2/result.h +20 -0
  22. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +64 -0
  23. data/lib/active_record/connection_adapters/mysql2_adapter.rb +654 -0
  24. data/lib/active_record/fiber_patches.rb +104 -0
  25. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +11 -0
  26. data/lib/mysql2.rb +16 -0
  27. data/lib/mysql2/client.rb +240 -0
  28. data/lib/mysql2/em.rb +37 -0
  29. data/lib/mysql2/em_fiber.rb +31 -0
  30. data/lib/mysql2/error.rb +15 -0
  31. data/lib/mysql2/result.rb +5 -0
  32. data/lib/mysql2/version.rb +3 -0
  33. data/lib/mysql2_bigint.rb +1 -0
  34. data/mysql2_bigint.gemspec +32 -0
  35. data/spec/em/em_fiber_spec.rb +22 -0
  36. data/spec/em/em_spec.rb +49 -0
  37. data/spec/mysql2/client_spec.rb +385 -0
  38. data/spec/mysql2/error_spec.rb +25 -0
  39. data/spec/mysql2/result_spec.rb +328 -0
  40. data/spec/rcov.opts +3 -0
  41. data/spec/spec_helper.rb +66 -0
  42. data/tasks/benchmarks.rake +20 -0
  43. data/tasks/compile.rake +53 -0
  44. data/tasks/rspec.rake +16 -0
  45. data/tasks/vendor_mysql.rake +41 -0
  46. metadata +199 -0
@@ -0,0 +1,5 @@
1
+ module Mysql2
2
+ class Result
3
+ include Enumerable
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Mysql2
2
+ VERSION = "0.2.6.1"
3
+ end
@@ -0,0 +1 @@
1
+ require 'mysql2'
@@ -0,0 +1,32 @@
1
+ require File.expand_path('../lib/mysql2/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{mysql2_bigint}
5
+ s.version = Mysql2::VERSION
6
+ s.authors = ["Brian Lopez", "Julien Dumas"]
7
+ s.date = Time.now.utc.strftime("%Y-%m-%d")
8
+ s.email = [ "seniorlopez@gmail.com", "julien7.dumas@gmail.com" ]
9
+ s.extensions = ["ext/mysql2/extconf.rb"]
10
+ s.extra_rdoc_files = [
11
+ "README.rdoc"
12
+ ]
13
+ s.files = `git ls-files`.split("\n")
14
+ s.homepage = %q{http://github.com/saluzafa/mysql2_bigint}
15
+ s.rdoc_options = ["--charset=UTF-8"]
16
+ s.require_paths = ["lib", "ext"]
17
+ s.rubygems_version = %q{1.4.2}
18
+ s.summary = %q{A simple, fast Mysql library for Ruby, binding to libmysql}
19
+ s.test_files = `git ls-files spec examples`.split("\n")
20
+
21
+ # tests
22
+ s.add_development_dependency 'eventmachine'
23
+ s.add_development_dependency 'rake-compiler', "~> 0.7.1"
24
+ s.add_development_dependency 'rspec'
25
+ # benchmarks
26
+ s.add_development_dependency 'activerecord'
27
+ s.add_development_dependency 'mysql'
28
+ s.add_development_dependency 'do_mysql'
29
+ s.add_development_dependency 'sequel'
30
+ s.add_development_dependency 'faker'
31
+ end
32
+
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ if defined? EventMachine && defined? Fiber
3
+ require 'spec_helper'
4
+ require 'mysql2/em_fiber'
5
+
6
+ describe Mysql2::EM::Fiber::Client do
7
+ it 'should support queries' do
8
+ results = []
9
+ EM.run do
10
+ Fiber.new {
11
+ client1 = Mysql2::EM::Fiber::Client.new
12
+ results = client1.query "SELECT sleep(0.1) as first_query"
13
+ EM.stop_event_loop
14
+ }.resume
15
+ end
16
+
17
+ results.first.keys.should include("first_query")
18
+ end
19
+ end
20
+ else
21
+ puts "Either EventMachine or Fibers not available. Skipping tests that use them."
22
+ end
@@ -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,385 @@
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 => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42'
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
+
368
+ it "should respond to #thread_id" do
369
+ @client.should respond_to(:thread_id)
370
+ end
371
+
372
+ it "#thread_id should be a Fixnum" do
373
+ @client.thread_id.class.should eql(Fixnum)
374
+ end
375
+
376
+ it "should respond to #ping" do
377
+ @client.should respond_to(:ping)
378
+ end
379
+
380
+ it "#thread_id should return a boolean" do
381
+ @client.ping.should eql(true)
382
+ @client.close
383
+ @client.ping.should eql(false)
384
+ end
385
+ end