mysql2 0.3.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -151
  3. data/LICENSE +21 -0
  4. data/README.md +634 -0
  5. data/examples/eventmachine.rb +1 -3
  6. data/examples/threaded.rb +5 -9
  7. data/ext/mysql2/client.c +1154 -342
  8. data/ext/mysql2/client.h +20 -33
  9. data/ext/mysql2/extconf.rb +229 -37
  10. data/ext/mysql2/infile.c +122 -0
  11. data/ext/mysql2/infile.h +1 -0
  12. data/ext/mysql2/mysql2_ext.c +3 -1
  13. data/ext/mysql2/mysql2_ext.h +18 -16
  14. data/ext/mysql2/mysql_enc_name_to_ruby.h +168 -0
  15. data/ext/mysql2/mysql_enc_to_ruby.h +259 -0
  16. data/ext/mysql2/result.c +708 -191
  17. data/ext/mysql2/result.h +15 -6
  18. data/ext/mysql2/statement.c +602 -0
  19. data/ext/mysql2/statement.h +17 -0
  20. data/ext/mysql2/wait_for_single_fd.h +37 -0
  21. data/lib/mysql2.rb +69 -7
  22. data/lib/mysql2/client.rb +126 -211
  23. data/lib/mysql2/console.rb +5 -0
  24. data/lib/mysql2/em.rb +24 -8
  25. data/lib/mysql2/error.rb +93 -8
  26. data/lib/mysql2/field.rb +3 -0
  27. data/lib/mysql2/result.rb +2 -0
  28. data/lib/mysql2/statement.rb +11 -0
  29. data/lib/mysql2/version.rb +2 -2
  30. data/spec/configuration.yml.example +11 -0
  31. data/spec/em/em_spec.rb +101 -15
  32. data/spec/my.cnf.example +9 -0
  33. data/spec/mysql2/client_spec.rb +874 -232
  34. data/spec/mysql2/error_spec.rb +55 -46
  35. data/spec/mysql2/result_spec.rb +306 -154
  36. data/spec/mysql2/statement_spec.rb +712 -0
  37. data/spec/spec_helper.rb +103 -57
  38. data/spec/ssl/ca-cert.pem +17 -0
  39. data/spec/ssl/ca-key.pem +27 -0
  40. data/spec/ssl/ca.cnf +22 -0
  41. data/spec/ssl/cert.cnf +22 -0
  42. data/spec/ssl/client-cert.pem +17 -0
  43. data/spec/ssl/client-key.pem +27 -0
  44. data/spec/ssl/client-req.pem +15 -0
  45. data/spec/ssl/gen_certs.sh +48 -0
  46. data/spec/ssl/pkcs8-client-key.pem +28 -0
  47. data/spec/ssl/pkcs8-server-key.pem +28 -0
  48. data/spec/ssl/server-cert.pem +17 -0
  49. data/spec/ssl/server-key.pem +27 -0
  50. data/spec/ssl/server-req.pem +15 -0
  51. data/spec/test_data +1 -0
  52. data/support/5072E1F5.asc +432 -0
  53. data/support/libmysql.def +219 -0
  54. data/support/mysql_enc_to_ruby.rb +81 -0
  55. data/support/ruby_enc_to_mysql.rb +61 -0
  56. metadata +82 -188
  57. data/.gitignore +0 -12
  58. data/.rspec +0 -2
  59. data/.rvmrc +0 -1
  60. data/Gemfile +0 -3
  61. data/MIT-LICENSE +0 -20
  62. data/README.rdoc +0 -257
  63. data/Rakefile +0 -5
  64. data/benchmark/active_record.rb +0 -51
  65. data/benchmark/active_record_threaded.rb +0 -42
  66. data/benchmark/allocations.rb +0 -33
  67. data/benchmark/escape.rb +0 -36
  68. data/benchmark/query_with_mysql_casting.rb +0 -80
  69. data/benchmark/query_without_mysql_casting.rb +0 -47
  70. data/benchmark/sequel.rb +0 -37
  71. data/benchmark/setup_db.rb +0 -119
  72. data/benchmark/threaded.rb +0 -44
  73. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +0 -64
  74. data/lib/active_record/fiber_patches.rb +0 -104
  75. data/lib/mysql2/em_fiber.rb +0 -31
  76. data/mysql2.gemspec +0 -32
  77. data/spec/em/em_fiber_spec.rb +0 -22
  78. data/tasks/benchmarks.rake +0 -20
  79. data/tasks/compile.rake +0 -71
  80. data/tasks/rspec.rake +0 -16
  81. data/tasks/vendor_mysql.rake +0 -40
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- Makefile
2
- *.dSYM
3
- *.o
4
- *.bundle
5
- *.so
6
- *.a
7
- *.rbc
8
- mkmf.log
9
- pkg/
10
- tmp
11
- vendor
12
- lib/mysql2/mysql2.rb
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --colour
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm use 1.9.2@mysql2 --create
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source :rubygems
2
-
3
- gemspec
data/MIT-LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2010-2011 Brian Lopez - http://github.com/brianmario
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc DELETED
@@ -1,257 +0,0 @@
1
- = Mysql2 - A modern, simple and very fast Mysql library for Ruby - binding to libmysql
2
-
3
- The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
4
- Some database libraries out there serve as direct 1:1 mappings of the already complex C API's available.
5
- This one is not.
6
-
7
- It also forces the use of UTF-8 [or binary] for the connection [and all strings in 1.9, unless Encoding.default_internal is set then it'll convert from UTF-8 to that encoding] and uses encoding-aware MySQL API calls where it can.
8
-
9
- The API consists of two clases:
10
-
11
- Mysql2::Client - your connection to the database
12
-
13
- Mysql2::Result - returned from issuing a #query on the connection. It includes Enumerable.
14
-
15
- == Installing
16
-
17
- gem install mysql2
18
-
19
- You may have to specify --with-mysql-config=/some/random/path/bin/mysql_config
20
-
21
- == Usage
22
-
23
- Connect to a database:
24
-
25
- # this takes a hash of options, almost all of which map directly
26
- # to the familiar database.yml in rails
27
- # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html
28
- client = Mysql2::Client.new(:host => "localhost", :username => "root")
29
-
30
- Then query it:
31
-
32
- results = client.query("SELECT * FROM users WHERE group='githubbers'")
33
-
34
- Need to escape something first?
35
-
36
- escaped = client.escape("gi'thu\"bbe\0r's")
37
- results = client.query("SELECT * FROM users WHERE group='#{escaped}'")
38
-
39
- Finally, iterate over the results:
40
-
41
- results.each do |row|
42
- # conveniently, row is a hash
43
- # the keys are the fields, as you'd expect
44
- # the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
45
- # Here's an otter: http://farm1.static.flickr.com/130/398077070_b8795d0ef3_b.jpg
46
- end
47
-
48
- Or, you might just keep it simple:
49
-
50
- client.query("SELECT * FROM users WHERE group='githubbers'").each do |row|
51
- # do something with row, it's ready to rock
52
- end
53
-
54
- How about with symbolized keys?
55
-
56
- # NOTE: the :symbolize_keys and future options will likely move to the #query method soon
57
- client.query("SELECT * FROM users WHERE group='githubbers'").each(:symbolize_keys => true) do |row|
58
- # do something with row, it's ready to rock
59
- end
60
-
61
- You can get the headers and the columns in the order that they were returned
62
- by the query like this:
63
-
64
- headers = results.fields # <= that's an array of field names, in order
65
- results.each(:as => :array) do |row|
66
- # Each row is an array, ordered the same as the query results
67
- # An otter's den is called a "holt" or "couch"
68
- end
69
-
70
- == Cascading config
71
-
72
- The default config hash is at:
73
-
74
- Mysql2::Client.default_query_options
75
-
76
- which defaults to:
77
-
78
- {:async => false, :as => :hash, :symbolize_keys => false}
79
-
80
- that can be used as so:
81
-
82
- # these are the defaults all Mysql2::Client instances inherit
83
- Mysql2::Client.default_query_options.merge!(:as => :array)
84
-
85
- or
86
-
87
- # this will change the defaults for all future results returned by the #query method _for this connection only_
88
- c = Mysql2::Client.new
89
- c.query_options.merge!(:symbolize_keys => true)
90
-
91
- or
92
-
93
- # this will set the options for the Mysql2::Result instance returned from the #query method
94
- c = Mysql2::Client.new
95
- c.query(sql, :symbolize_keys => true)
96
-
97
- == Result types
98
-
99
- === Array of Arrays
100
-
101
- Pass the :as => :array option to any of the above methods of configuration
102
-
103
- === Array of Hashes
104
-
105
- The default result type is set to :hash, but you can override a previous setting to something else with :as => :hash
106
-
107
- === Others...
108
-
109
- I may add support for :as => :csv or even :as => :json to allow for *much* more efficient generation of those data types from result sets.
110
- If you'd like to see either of these (or others), open an issue and start bugging me about it ;)
111
-
112
- === Timezones
113
-
114
- Mysql2 now supports two timezone options:
115
-
116
- :database_timezone - this is the timezone Mysql2 will assume fields are already stored as, and will use this when creating the initial Time objects in ruby
117
- :application_timezone - this is the timezone Mysql2 will convert to before finally handing back to the caller
118
-
119
- In other words, if :database_timezone is set to :utc - Mysql2 will create the Time objects using Time.utc(...) from the raw value libmysql hands over initially.
120
- Then, if :application_timezone is set to say - :local - Mysql2 will then convert the just-created UTC Time object to local time.
121
-
122
- Both options only allow two values - :local or :utc - with the exception that :application_timezone can be [and defaults to] nil
123
-
124
- === Casting "boolean" columns
125
-
126
- You can now tell Mysql2 to cast tinyint(1) fields to boolean values in Ruby with the :cast_booleans option.
127
-
128
- client = Mysql2::Client.new
129
- result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
130
-
131
- === Async
132
-
133
- Mysql2::Client takes advantage of the MySQL C API's (undocumented) non-blocking function mysql_send_query for *all* queries.
134
- But, in order to take full advantage of it in your Ruby code, you can do:
135
-
136
- client.query("SELECT sleep(5)", :async => true)
137
-
138
- Which will return nil immediately. At this point you'll probably want to use some socket monitoring mechanism
139
- like EventMachine or even IO.select. Once the socket becomes readable, you can do:
140
-
141
- # result will be a Mysql2::Result instance
142
- result = client.async_result
143
-
144
- NOTE: Because of the way MySQL's query API works, this method will block until the result is ready.
145
- So if you really need things to stay async, it's best to just monitor the socket with something like EventMachine.
146
- If you need multiple query concurrency take a look at using a connection pool.
147
-
148
- === Row Caching
149
-
150
- By default, Mysql2 will cache rows that have been created in Ruby (since this happens lazily).
151
- This is especially helpful since it saves the cost of creating the row in Ruby if you were to iterate over the collection again.
152
-
153
- If you only plan on using each row once, then it's much more efficient to disable this behavior by setting the :cache_rows option to false.
154
- This would be helpful if you wanted to iterate over the results in a streaming manner. Meaning the GC would cleanup rows you don't need anymore as you're iterating over the result set.
155
-
156
- == ActiveRecord
157
-
158
- To use the ActiveRecord driver (with our without rails), all you should need to do is have this gem installed and set the adapter in your database.yml to "mysql2".
159
- That was easy right? :)
160
-
161
- == Asynchronous ActiveRecord
162
-
163
- You can also use Mysql2 with asynchronous Rails (first introduced at http://www.mikeperham.com/2010/04/03/introducing-phat-an-asynchronous-rails-app/) by
164
- setting the adapter in your database.yml to "em_mysql2". You must be running Ruby 1.9, thin and the rack-fiber_pool middleware for it to work.
165
-
166
- == Sequel
167
-
168
- The Sequel adapter was pulled out into Sequel core (will be part of the next release) and can be used by specifying the "mysql2://" prefix to your connection specification.
169
-
170
- == EventMachine
171
-
172
- The mysql2 EventMachine deferrable api allows you to make async queries using EventMachine,
173
- while specifying callbacks for success for failure. Here's a simple example:
174
-
175
- require 'mysql2/em'
176
-
177
- EM.run do
178
- client1 = Mysql2::EM::Client.new
179
- defer1 = client1.query "SELECT sleep(3) as first_query"
180
- defer1.callback do |result|
181
- puts "Result: #{result.to_a.inspect}"
182
- end
183
-
184
- client2 = Mysql2::EM::Client.new
185
- defer2 = client2.query "SELECT sleep(1) second_query"
186
- defer2.callback do |result|
187
- puts "Result: #{result.to_a.inspect}"
188
- end
189
- end
190
-
191
- == Lazy Everything
192
-
193
- Well... almost ;)
194
-
195
- Field name strings/symbols are shared across all the rows so only one object is ever created to represent the field name for an entire dataset.
196
-
197
- Rows themselves are lazily created in ruby-land when an attempt to yield it is made via #each.
198
- For example, if you were to yield 4 rows from a 100 row dataset, only 4 hashes will be created. The rest will sit and wait in C-land until you want them (or when the GC goes to cleanup your Mysql2::Result instance).
199
- Now say you were to iterate over that same collection again, this time yielding 15 rows - the 4 previous rows that had already been turned into ruby hashes would be pulled from an internal cache, then 11 more would be created and stored in that cache.
200
- Once the entire dataset has been converted into ruby objects, Mysql2::Result will free the Mysql C result object as it's no longer needed.
201
-
202
- This caching behavior can be disabled by setting the :cache_rows option to false.
203
-
204
- As for field values themselves, I'm workin on it - but expect that soon.
205
-
206
- == Compatibility
207
-
208
- The specs pass on my system (SL 10.6.3, x86_64) in these rubies:
209
-
210
- * 1.8.7-p249
211
- * ree-1.8.7-2010.01
212
- * 1.9.1-p378
213
- * ruby-trunk
214
- * rbx-head - broken at the moment, working with the rbx team for a solution
215
-
216
- The ActiveRecord driver should work on 2.3.5 and 3.0
217
-
218
- == Yeah... but why?
219
-
220
- Someone: Dude, the Mysql gem works fiiiiiine.
221
-
222
- Me: It sure does, but it only hands you nil and strings for field values. Leaving you to convert
223
- them into proper Ruby types in Ruby-land - which is slow as balls.
224
-
225
-
226
- Someone: OK fine, but do_mysql can already give me back values with Ruby objects mapped to MySQL types.
227
-
228
- Me: Yep, but it's API is considerably more complex *and* can be ~2x slower.
229
-
230
- == Benchmarks
231
-
232
- Performing a basic "SELECT * FROM" query on a table with 30k rows and fields of nearly every Ruby-representable data type,
233
- then iterating over every row using an #each like method yielding a block:
234
-
235
- # These results are from the query_with_mysql_casting.rb script in the benchmarks folder
236
- user system total real
237
- Mysql2
238
- 0.750000 0.180000 0.930000 ( 1.821655)
239
- do_mysql
240
- 1.650000 0.200000 1.850000 ( 2.811357)
241
- Mysql
242
- 7.500000 0.210000 7.710000 ( 8.065871)
243
-
244
- == Development
245
-
246
- To run the tests, you can use RVM and Bundler to create a pristine environment for mysql2 development/hacking.
247
- Use 'bundle install' to install the necessary development and testing gems:
248
-
249
- bundle install
250
- rake
251
-
252
- == Special Thanks
253
-
254
- * Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
255
- * Yury Korolev (http://github.com/yury) - for TONS of help testing the ActiveRecord adapter
256
- * Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
257
- * Mike Perham (http://github.com/mperham) - Async ActiveRecord adapter (uses Fibers and EventMachine)
data/Rakefile DELETED
@@ -1,5 +0,0 @@
1
- # encoding: UTF-8
2
- require 'rake'
3
-
4
- # Load custom tasks
5
- Dir['tasks/*.rake'].sort.each { |f| load f }
@@ -1,51 +0,0 @@
1
- # encoding: UTF-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
-
4
- require 'rubygems'
5
- require 'benchmark'
6
- require 'active_record'
7
-
8
- ActiveRecord::Base.default_timezone = :local
9
- ActiveRecord::Base.time_zone_aware_attributes = true
10
-
11
- number_of = 10
12
- mysql2_opts = {
13
- :adapter => 'mysql2',
14
- :database => 'test'
15
- }
16
- mysql_opts = {
17
- :adapter => 'mysql',
18
- :database => 'test'
19
- }
20
-
21
- class Mysql2Model < ActiveRecord::Base
22
- set_table_name :mysql2_test
23
- end
24
-
25
- class MysqlModel < ActiveRecord::Base
26
- set_table_name :mysql2_test
27
- end
28
-
29
- Benchmark.bmbm do |x|
30
- x.report "Mysql2" do
31
- Mysql2Model.establish_connection(mysql2_opts)
32
- number_of.times do
33
- Mysql2Model.all(:limit => 1000).each{ |r|
34
- r.attributes.keys.each{ |k|
35
- r.send(k.to_sym)
36
- }
37
- }
38
- end
39
- end
40
-
41
- x.report "Mysql" do
42
- MysqlModel.establish_connection(mysql_opts)
43
- number_of.times do
44
- MysqlModel.all(:limit => 1000).each{ |r|
45
- r.attributes.keys.each{ |k|
46
- r.send(k.to_sym)
47
- }
48
- }
49
- end
50
- end
51
- end
@@ -1,42 +0,0 @@
1
- # encoding: UTF-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
-
4
- require 'rubygems'
5
- require 'benchmark'
6
- require 'active_record'
7
-
8
- times = 25
9
-
10
-
11
- # mysql2
12
- mysql2_opts = {
13
- :adapter => 'mysql2',
14
- :database => 'test',
15
- :pool => times
16
- }
17
- ActiveRecord::Base.establish_connection(mysql2_opts)
18
- x = Benchmark.realtime do
19
- threads = []
20
- times.times do
21
- threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
22
- end
23
- threads.each {|t| t.join }
24
- end
25
- puts "mysql2: #{x} seconds"
26
-
27
-
28
- # mysql
29
- mysql2_opts = {
30
- :adapter => 'mysql',
31
- :database => 'test',
32
- :pool => times
33
- }
34
- ActiveRecord::Base.establish_connection(mysql2_opts)
35
- x = Benchmark.realtime do
36
- threads = []
37
- times.times do
38
- threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
39
- end
40
- threads.each {|t| t.join }
41
- end
42
- puts "mysql: #{x} seconds"
@@ -1,33 +0,0 @@
1
- # encoding: UTF-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
-
4
- raise Mysql2::Mysql2Error.new("GC allocation benchmarks only supported on Ruby 1.9!") unless RUBY_VERSION =~ /1\.9/
5
-
6
- require 'rubygems'
7
- require 'benchmark'
8
- require 'active_record'
9
-
10
- ActiveRecord::Base.default_timezone = :local
11
- ActiveRecord::Base.time_zone_aware_attributes = true
12
-
13
- class Mysql2Model < ActiveRecord::Base
14
- set_table_name :mysql2_test
15
- end
16
-
17
- def bench_allocations(feature, iterations = 10, &blk)
18
- puts "GC overhead for #{feature}"
19
- Mysql2Model.establish_connection(:adapter => 'mysql2', :database => 'test')
20
- GC::Profiler.clear
21
- GC::Profiler.enable
22
- iterations.times{ blk.call }
23
- GC::Profiler.report(STDOUT)
24
- GC::Profiler.disable
25
- end
26
-
27
- bench_allocations('coercion') do
28
- Mysql2Model.all(:limit => 1000).each{ |r|
29
- r.attributes.keys.each{ |k|
30
- r.send(k.to_sym)
31
- }
32
- }
33
- end
data/benchmark/escape.rb DELETED
@@ -1,36 +0,0 @@
1
- # encoding: UTF-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
-
4
- require 'rubygems'
5
- require 'benchmark'
6
- require 'mysql'
7
- require 'mysql2'
8
- require 'do_mysql'
9
-
10
- def run_escape_benchmarks(str, number_of = 1000)
11
- Benchmark.bmbm do |x|
12
- mysql = Mysql.new("localhost", "root")
13
- x.report "Mysql #{str.inspect}" do
14
- number_of.times do
15
- mysql.quote str
16
- end
17
- end
18
-
19
- mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
20
- x.report "Mysql2 #{str.inspect}" do
21
- number_of.times do
22
- mysql2.escape str
23
- end
24
- end
25
-
26
- do_mysql = DataObjects::Connection.new("mysql://localhost/test")
27
- x.report "do_mysql #{str.inspect}" do
28
- number_of.times do
29
- do_mysql.quote_string str
30
- end
31
- end
32
- end
33
- end
34
-
35
- run_escape_benchmarks "abc'def\"ghi\0jkl%mno"
36
- run_escape_benchmarks "clean string"