mysql2 0.3.1 → 0.5.2

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 (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
@@ -1,80 +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
- number_of = 100
11
- database = 'test'
12
- sql = "SELECT * FROM mysql2_test LIMIT 100"
13
-
14
- class Mysql
15
- include Enumerable
16
- end
17
-
18
- def mysql_cast(type, value)
19
- case type
20
- when Mysql::Field::TYPE_NULL
21
- nil
22
- when Mysql::Field::TYPE_TINY, Mysql::Field::TYPE_SHORT, Mysql::Field::TYPE_LONG,
23
- Mysql::Field::TYPE_INT24, Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_YEAR
24
- value.to_i
25
- when Mysql::Field::TYPE_DECIMAL, Mysql::Field::TYPE_NEWDECIMAL
26
- BigDecimal.new(value)
27
- when Mysql::Field::TYPE_DOUBLE, Mysql::Field::TYPE_FLOAT
28
- value.to_f
29
- when Mysql::Field::TYPE_DATE
30
- Date.parse(value)
31
- when Mysql::Field::TYPE_TIME, Mysql::Field::TYPE_DATETIME, Mysql::Field::TYPE_TIMESTAMP
32
- Time.parse(value)
33
- when Mysql::Field::TYPE_BLOB, Mysql::Field::TYPE_BIT, Mysql::Field::TYPE_STRING,
34
- Mysql::Field::TYPE_VAR_STRING, Mysql::Field::TYPE_CHAR, Mysql::Field::TYPE_SET
35
- Mysql::Field::TYPE_ENUM
36
- value
37
- else
38
- value
39
- end
40
- end
41
-
42
- Benchmark.bmbm do |x|
43
- mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
44
- mysql2.query "USE #{database}"
45
- x.report "Mysql2" do
46
- number_of.times do
47
- mysql2_result = mysql2.query sql, :symbolize_keys => true
48
- mysql2_result.each do |res|
49
- # puts res.inspect
50
- end
51
- end
52
- end
53
-
54
- mysql = Mysql.new("localhost", "root")
55
- mysql.query "USE #{database}"
56
- x.report "Mysql" do
57
- number_of.times do
58
- mysql_result = mysql.query sql
59
- fields = mysql_result.fetch_fields
60
- mysql_result.each do |row|
61
- row_hash = {}
62
- row.each_with_index do |f, j|
63
- row_hash[fields[j].name.to_sym] = mysql_cast(fields[j].type, row[j])
64
- end
65
- # puts row_hash.inspect
66
- end
67
- end
68
- end
69
-
70
- do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
71
- command = do_mysql.create_command sql
72
- x.report "do_mysql" do
73
- number_of.times do
74
- do_result = command.execute_reader
75
- do_result.each do |res|
76
- # puts res.inspect
77
- end
78
- end
79
- end
80
- end
@@ -1,47 +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
- number_of = 100
11
- database = 'test'
12
- sql = "SELECT * FROM mysql2_test LIMIT 100"
13
-
14
- Benchmark.bmbm do |x|
15
- mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
16
- mysql2.query "USE #{database}"
17
- x.report "Mysql2" do
18
- number_of.times do
19
- mysql2_result = mysql2.query sql, :symbolize_keys => true
20
- mysql2_result.each do |res|
21
- # puts res.inspect
22
- end
23
- end
24
- end
25
-
26
- mysql = Mysql.new("localhost", "root")
27
- mysql.query "USE #{database}"
28
- x.report "Mysql" do
29
- number_of.times do
30
- mysql_result = mysql.query sql
31
- mysql_result.each_hash do |res|
32
- # puts res.inspect
33
- end
34
- end
35
- end
36
-
37
- do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
38
- command = DataObjects::Mysql::Command.new do_mysql, sql
39
- x.report "do_mysql" do
40
- number_of.times do
41
- do_result = command.execute_reader
42
- do_result.each do |res|
43
- # puts res.inspect
44
- end
45
- end
46
- end
47
- end
data/benchmark/sequel.rb DELETED
@@ -1,37 +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 'mysql2'
7
- require 'sequel'
8
- require 'sequel/adapters/do'
9
-
10
- number_of = 10
11
- mysql2_opts = "mysql2://localhost/test"
12
- mysql_opts = "mysql://localhost/test"
13
- do_mysql_opts = "do:mysql://localhost/test"
14
-
15
- class Mysql2Model < Sequel::Model(Sequel.connect(mysql2_opts)[:mysql2_test]); end
16
- class MysqlModel < Sequel::Model(Sequel.connect(mysql_opts)[:mysql2_test]); end
17
- class DOMysqlModel < Sequel::Model(Sequel.connect(do_mysql_opts)[:mysql2_test]); end
18
-
19
- Benchmark.bmbm do |x|
20
- x.report "Mysql2" do
21
- number_of.times do
22
- Mysql2Model.limit(1000).all
23
- end
24
- end
25
-
26
- x.report "do:mysql" do
27
- number_of.times do
28
- DOMysqlModel.limit(1000).all
29
- end
30
- end
31
-
32
- x.report "Mysql" do
33
- number_of.times do
34
- MysqlModel.limit(1000).all
35
- end
36
- end
37
- end
@@ -1,119 +0,0 @@
1
- # encoding: UTF-8
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
-
4
- # This script is for generating psudo-random data into a single table consisting of nearly every
5
- # data type MySQL 5.1 supports.
6
- #
7
- # It's meant to be used with the query.rb benchmark script (or others in the future)
8
-
9
- require 'mysql2'
10
- require 'rubygems'
11
- require 'faker'
12
-
13
- num = ENV['NUM'] && ENV['NUM'].to_i || 10_000
14
-
15
- create_table_sql = %[
16
- CREATE TABLE IF NOT EXISTS mysql2_test (
17
- null_test VARCHAR(10),
18
- bit_test BIT,
19
- tiny_int_test TINYINT,
20
- small_int_test SMALLINT,
21
- medium_int_test MEDIUMINT,
22
- int_test INT,
23
- big_int_test BIGINT,
24
- float_test FLOAT(10,3),
25
- float_zero_test FLOAT(10,3),
26
- double_test DOUBLE(10,3),
27
- decimal_test DECIMAL(10,3),
28
- decimal_zero_test DECIMAL(10,3),
29
- date_test DATE,
30
- date_time_test DATETIME,
31
- timestamp_test TIMESTAMP,
32
- time_test TIME,
33
- year_test YEAR(4),
34
- char_test CHAR(10),
35
- varchar_test VARCHAR(10),
36
- binary_test BINARY(10),
37
- varbinary_test VARBINARY(10),
38
- tiny_blob_test TINYBLOB,
39
- tiny_text_test TINYTEXT,
40
- blob_test BLOB,
41
- text_test TEXT,
42
- medium_blob_test MEDIUMBLOB,
43
- medium_text_test MEDIUMTEXT,
44
- long_blob_test LONGBLOB,
45
- long_text_test LONGTEXT,
46
- enum_test ENUM('val1', 'val2'),
47
- set_test SET('val1', 'val2')
48
- ) DEFAULT CHARSET=utf8
49
- ]
50
-
51
- # connect to localhost by default, pass options as needed
52
- @client = Mysql2::Client.new :host => "localhost", :username => "root", :database => "test"
53
-
54
- @client.query create_table_sql
55
-
56
- def insert_record(args)
57
- insert_sql = "
58
- INSERT INTO mysql2_test (
59
- null_test, bit_test, tiny_int_test, small_int_test, medium_int_test, int_test, big_int_test,
60
- float_test, float_zero_test, double_test, decimal_test, decimal_zero_test, date_test, date_time_test, timestamp_test, time_test,
61
- year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test,
62
- tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test,
63
- long_blob_test, long_text_test, enum_test, set_test
64
- )
65
-
66
- VALUES (
67
- NULL, #{args[:bit_test]}, #{args[:tiny_int_test]}, #{args[:small_int_test]}, #{args[:medium_int_test]}, #{args[:int_test]}, #{args[:big_int_test]},
68
- #{args[:float_test]}, #{args[:float_zero_test]}, #{args[:double_test]}, #{args[:decimal_test]}, #{args[:decimal_zero_test]}, '#{args[:date_test]}', '#{args[:date_time_test]}', '#{args[:timestamp_test]}', '#{args[:time_test]}',
69
- #{args[:year_test]}, '#{args[:char_test]}', '#{args[:varchar_test]}', '#{args[:binary_test]}', '#{args[:varbinary_test]}', '#{args[:tiny_blob_test]}',
70
- '#{args[:tiny_text_test]}', '#{args[:blob_test]}', '#{args[:text_test]}', '#{args[:medium_blob_test]}', '#{args[:medium_text_test]}',
71
- '#{args[:long_blob_test]}', '#{args[:long_text_test]}', '#{args[:enum_test]}', '#{args[:set_test]}'
72
- )
73
- "
74
- @client.query insert_sql
75
- end
76
-
77
- puts "Creating #{num} records"
78
- num.times do |n|
79
- five_words = Faker::Lorem.words(rand(5))
80
- twenty5_paragraphs = Faker::Lorem.paragraphs(rand(25))
81
- insert_record(
82
- :bit_test => 1,
83
- :tiny_int_test => rand(128),
84
- :small_int_test => rand(32767),
85
- :medium_int_test => rand(8388607),
86
- :int_test => rand(2147483647),
87
- :big_int_test => rand(9223372036854775807),
88
- :float_test => rand(32767)/1.87,
89
- :float_zero_test => 0.0,
90
- :double_test => rand(8388607)/1.87,
91
- :decimal_test => rand(8388607)/1.87,
92
- :decimal_zero_test => 0,
93
- :date_test => '2010-4-4',
94
- :date_time_test => '2010-4-4 11:44:00',
95
- :timestamp_test => '2010-4-4 11:44:00',
96
- :time_test => '11:44:00',
97
- :year_test => Time.now.year,
98
- :char_test => five_words,
99
- :varchar_test => five_words,
100
- :binary_test => five_words,
101
- :varbinary_test => five_words,
102
- :tiny_blob_test => five_words,
103
- :tiny_text_test => Faker::Lorem.paragraph(rand(5)),
104
- :blob_test => twenty5_paragraphs,
105
- :text_test => twenty5_paragraphs,
106
- :medium_blob_test => twenty5_paragraphs,
107
- :medium_text_test => twenty5_paragraphs,
108
- :long_blob_test => twenty5_paragraphs,
109
- :long_text_test => twenty5_paragraphs,
110
- :enum_test => ['val1', 'val2'].rand,
111
- :set_test => ['val1', 'val2', 'val1,val2'].rand
112
- )
113
- if n % 100 == 0
114
- $stdout.putc '.'
115
- $stdout.flush
116
- end
117
- end
118
- puts
119
- puts "Done"
@@ -1,44 +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
- mysql2_opts = {
9
- :adapter => 'mysql2',
10
- :database => 'test',
11
- :pool => 25
12
- }
13
- ActiveRecord::Base.establish_connection(mysql2_opts)
14
- x = Benchmark.realtime do
15
- threads = []
16
- 25.times do
17
- threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
18
- end
19
- threads.each {|t| t.join }
20
- end
21
- puts x
22
-
23
- mysql2_opts = {
24
- :adapter => 'mysql',
25
- :database => 'test',
26
- :pool => 25
27
- }
28
- ActiveRecord::Base.establish_connection(mysql2_opts)
29
- x = Benchmark.realtime do
30
- threads = []
31
- 25.times do
32
- threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
33
- end
34
- threads.each {|t| t.join }
35
- end
36
- puts x
37
-
38
- # these results are similar on 1.8.7, 1.9.2 and rbx-head
39
- #
40
- # $ bundle exec ruby benchmarks/threaded.rb
41
- # 1.0774750709533691
42
- #
43
- # and using the mysql gem
44
- # 25.099437952041626
@@ -1,64 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # AR adapter for using a fibered mysql2 connection with EM
4
- # This adapter should be used within Thin or Unicorn with the rack-fiber_pool middleware.
5
- # Just update your database.yml's adapter to be 'em_mysql2'
6
-
7
- module ActiveRecord
8
- class Base
9
- def self.em_mysql2_connection(config)
10
- client = ::Mysql2::Fibered::Client.new(config.symbolize_keys)
11
- options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
12
- ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
13
- end
14
- end
15
- end
16
-
17
- require 'fiber'
18
- require 'eventmachine'
19
- require 'mysql2'
20
- require 'active_record/connection_adapters/mysql2_adapter'
21
- require 'active_record/fiber_patches'
22
-
23
- module Mysql2
24
- module Fibered
25
- class Client < ::Mysql2::Client
26
- module Watcher
27
- def initialize(client, deferable)
28
- @client = client
29
- @deferable = deferable
30
- end
31
-
32
- def notify_readable
33
- begin
34
- detach
35
- results = @client.async_result
36
- @deferable.succeed(results)
37
- rescue Exception => e
38
- @deferable.fail(e)
39
- end
40
- end
41
- end
42
-
43
- def query(sql, opts={})
44
- if ::EM.reactor_running?
45
- super(sql, opts.merge(:async => true))
46
- deferrable = ::EM::DefaultDeferrable.new
47
- ::EM.watch(self.socket, Watcher, self, deferrable).notify_readable = true
48
- fiber = Fiber.current
49
- deferrable.callback do |result|
50
- fiber.resume(result)
51
- end
52
- deferrable.errback do |err|
53
- fiber.resume(err)
54
- end
55
- Fiber.yield.tap do |result|
56
- raise result if result.is_a?(Exception)
57
- end
58
- else
59
- super(sql, opts)
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -1,104 +0,0 @@
1
- # Necessary monkeypatching to make AR fiber-friendly.
2
-
3
- module ActiveRecord
4
- module ConnectionAdapters
5
-
6
- def self.fiber_pools
7
- @fiber_pools ||= []
8
- end
9
- def self.register_fiber_pool(fp)
10
- fiber_pools << fp
11
- end
12
-
13
- class FiberedMonitor
14
- class Queue
15
- def initialize
16
- @queue = []
17
- end
18
-
19
- def wait(timeout)
20
- t = timeout || 5
21
- fiber = Fiber.current
22
- x = EM::Timer.new(t) do
23
- @queue.delete(fiber)
24
- fiber.resume(false)
25
- end
26
- @queue << fiber
27
- Fiber.yield.tap do
28
- x.cancel
29
- end
30
- end
31
-
32
- def signal
33
- fiber = @queue.pop
34
- fiber.resume(true) if fiber
35
- end
36
- end
37
-
38
- def synchronize
39
- yield
40
- end
41
-
42
- def new_cond
43
- Queue.new
44
- end
45
- end
46
-
47
- # ActiveRecord's connection pool is based on threads. Since we are working
48
- # with EM and a single thread, multiple fiber design, we need to provide
49
- # our own connection pool that keys off of Fiber.current so that different
50
- # fibers running in the same thread don't try to use the same connection.
51
- class ConnectionPool
52
- def initialize(spec)
53
- @spec = spec
54
-
55
- # The cache of reserved connections mapped to threads
56
- @reserved_connections = {}
57
-
58
- # The mutex used to synchronize pool access
59
- @connection_mutex = FiberedMonitor.new
60
- @queue = @connection_mutex.new_cond
61
-
62
- # default 5 second timeout unless on ruby 1.9
63
- @timeout = spec.config[:wait_timeout] || 5
64
-
65
- # default max pool size to 5
66
- @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
67
-
68
- @connections = []
69
- @checked_out = []
70
- end
71
-
72
- def clear_stale_cached_connections!
73
- cache = @reserved_connections
74
- keys = Set.new(cache.keys)
75
-
76
- ActiveRecord::ConnectionAdapters.fiber_pools.each do |pool|
77
- pool.busy_fibers.each_pair do |object_id, fiber|
78
- keys.delete(object_id)
79
- end
80
- end
81
-
82
- keys.each do |key|
83
- next unless cache.has_key?(key)
84
- checkin cache[key]
85
- cache.delete(key)
86
- end
87
- end
88
-
89
- private
90
-
91
- def current_connection_id #:nodoc:
92
- Fiber.current.object_id
93
- end
94
-
95
- def checkout_and_verify(c)
96
- @checked_out << c
97
- c.run_callbacks :checkout
98
- c.verify!
99
- c
100
- end
101
- end
102
-
103
- end
104
- end