mysql2 0.3.18 → 0.4.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +63 -12
  5. data/examples/eventmachine.rb +1 -1
  6. data/examples/threaded.rb +4 -6
  7. data/ext/mysql2/client.c +170 -175
  8. data/ext/mysql2/client.h +21 -1
  9. data/ext/mysql2/extconf.rb +95 -35
  10. data/ext/mysql2/infile.c +2 -2
  11. data/ext/mysql2/mysql2_ext.c +1 -0
  12. data/ext/mysql2/mysql2_ext.h +5 -6
  13. data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
  14. data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
  15. data/ext/mysql2/result.c +494 -132
  16. data/ext/mysql2/result.h +12 -6
  17. data/ext/mysql2/statement.c +494 -0
  18. data/ext/mysql2/statement.h +19 -0
  19. data/lib/mysql2/client.rb +68 -22
  20. data/lib/mysql2/console.rb +1 -1
  21. data/lib/mysql2/em.rb +5 -6
  22. data/lib/mysql2/error.rb +18 -27
  23. data/lib/mysql2/field.rb +3 -0
  24. data/lib/mysql2/statement.rb +17 -0
  25. data/lib/mysql2/version.rb +1 -1
  26. data/lib/mysql2.rb +38 -18
  27. data/spec/em/em_spec.rb +21 -21
  28. data/spec/mysql2/client_spec.rb +393 -351
  29. data/spec/mysql2/error_spec.rb +37 -36
  30. data/spec/mysql2/result_spec.rb +213 -208
  31. data/spec/mysql2/statement_spec.rb +684 -0
  32. data/spec/spec_helper.rb +7 -0
  33. data/spec/ssl/ca-cert.pem +17 -0
  34. data/spec/ssl/ca-key.pem +27 -0
  35. data/spec/ssl/ca.cnf +22 -0
  36. data/spec/ssl/cert.cnf +22 -0
  37. data/spec/ssl/client-cert.pem +17 -0
  38. data/spec/ssl/client-key.pem +27 -0
  39. data/spec/ssl/client-req.pem +15 -0
  40. data/spec/ssl/gen_certs.sh +48 -0
  41. data/spec/ssl/pkcs8-client-key.pem +28 -0
  42. data/spec/ssl/pkcs8-server-key.pem +28 -0
  43. data/spec/ssl/server-cert.pem +17 -0
  44. data/spec/ssl/server-key.pem +27 -0
  45. data/spec/ssl/server-req.pem +15 -0
  46. data/support/mysql_enc_to_ruby.rb +7 -8
  47. data/support/ruby_enc_to_mysql.rb +1 -1
  48. metadata +41 -46
data/lib/mysql2/em.rb CHANGED
@@ -17,7 +17,7 @@ module Mysql2
17
17
  detach
18
18
  begin
19
19
  result = @client.async_result
20
- rescue Exception => e
20
+ rescue => e
21
21
  @deferable.fail(e)
22
22
  else
23
23
  @deferable.succeed(result)
@@ -34,17 +34,16 @@ module Mysql2
34
34
  end
35
35
 
36
36
  def close(*args)
37
- if @watch
38
- @watch.detach if @watch.watching?
39
- end
37
+ @watch.detach if @watch && @watch.watching?
38
+
40
39
  super(*args)
41
40
  end
42
41
 
43
- def query(sql, opts={})
42
+ def query(sql, opts = {})
44
43
  if ::EM.reactor_running?
45
44
  super(sql, opts.merge(:async => true))
46
45
  deferable = ::EM::DefaultDeferrable.new
47
- @watch = ::EM.watch(self.socket, Watcher, self, deferable)
46
+ @watch = ::EM.watch(socket, Watcher, self, deferable)
48
47
  @watch.notify_readable = true
49
48
  deferable
50
49
  else
data/lib/mysql2/error.rb CHANGED
@@ -2,25 +2,31 @@
2
2
 
3
3
  module Mysql2
4
4
  class Error < StandardError
5
- REPLACEMENT_CHAR = '?'
6
- ENCODE_OPTS = {:undef => :replace, :invalid => :replace, :replace => REPLACEMENT_CHAR}
5
+ ENCODE_OPTS = {
6
+ :undef => :replace,
7
+ :invalid => :replace,
8
+ :replace => '?'.freeze,
9
+ }.freeze
7
10
 
8
- attr_accessor :error_number
9
- attr_reader :sql_state
10
- attr_writer :server_version
11
+ attr_reader :error_number, :sql_state
11
12
 
12
13
  # Mysql gem compatibility
13
14
  alias_method :errno, :error_number
14
15
  alias_method :error, :message
15
16
 
16
- def initialize(msg, server_version=nil)
17
- self.server_version = server_version
17
+ def initialize(msg)
18
+ @server_version ||= nil
18
19
 
19
20
  super(clean_message(msg))
20
21
  end
21
22
 
22
- def sql_state=(state)
23
- @sql_state = ''.respond_to?(:encode) ? state.encode(ENCODE_OPTS) : state
23
+ def self.new_with_args(msg, server_version, error_number, sql_state)
24
+ err = allocate
25
+ err.instance_variable_set('@server_version', server_version)
26
+ err.instance_variable_set('@error_number', error_number)
27
+ err.instance_variable_set('@sql_state', sql_state.respond_to?(:encode) ? sql_state.encode(ENCODE_OPTS) : sql_state)
28
+ err.send(:initialize, msg)
29
+ err
24
30
  end
25
31
 
26
32
  private
@@ -30,7 +36,7 @@ module Mysql2
30
36
  # variable.
31
37
  #
32
38
  # See http://dev.mysql.com/doc/refman/5.5/en/charset-errors.html for
33
- # more contetx.
39
+ # more context.
34
40
  #
35
41
  # Before MySQL 5.5 error message template strings are in whatever encoding
36
42
  # is associated with the error message language.
@@ -53,27 +59,12 @@ module Mysql2
53
59
  #
54
60
  # Returns a valid UTF-8 string in Ruby 1.9+, the original string on Ruby 1.8
55
61
  def clean_message(message)
56
- return message if !message.respond_to?(:encoding)
62
+ return message unless message.respond_to?(:encode)
57
63
 
58
64
  if @server_version && @server_version > 50500
59
65
  message.encode(ENCODE_OPTS)
60
66
  else
61
- if message.respond_to? :scrub
62
- message.scrub(REPLACEMENT_CHAR).encode(ENCODE_OPTS)
63
- else
64
- # This is ugly as hell but Ruby 1.9 doesn't provide a way to clean a string
65
- # and retain it's valid UTF-8 characters, that I know of.
66
-
67
- new_message = "".force_encoding(Encoding::UTF_8)
68
- message.chars.each do |char|
69
- if char.valid_encoding?
70
- new_message << char
71
- else
72
- new_message << REPLACEMENT_CHAR
73
- end
74
- end
75
- new_message.encode(ENCODE_OPTS)
76
- end
67
+ message.encode(Encoding::UTF_8, ENCODE_OPTS)
77
68
  end
78
69
  end
79
70
  end
@@ -0,0 +1,3 @@
1
+ module Mysql2
2
+ Field = Struct.new(:name, :type)
3
+ end
@@ -0,0 +1,17 @@
1
+ module Mysql2
2
+ class Statement
3
+ include Enumerable
4
+
5
+ if Thread.respond_to?(:handle_interrupt)
6
+ def execute(*args)
7
+ Thread.handle_interrupt(::Mysql2::Util::TimeoutError => :never) do
8
+ _execute(*args)
9
+ end
10
+ end
11
+ else
12
+ def execute(*args)
13
+ _execute(*args)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.3.18"
2
+ VERSION = "0.4.2"
3
3
  end
data/lib/mysql2.rb CHANGED
@@ -8,16 +8,16 @@ require 'rational' unless RUBY_VERSION >= '1.9.2'
8
8
  # Or to bomb out with a clear error message instead of a linker crash
9
9
  if RUBY_PLATFORM =~ /mswin|mingw/
10
10
  dll_path = if ENV['RUBY_MYSQL2_LIBMYSQL_DLL']
11
- # If this environment variable is set, it overrides any other paths
12
- # The user is advised to use backslashes not forward slashes
13
- ENV['RUBY_MYSQL2_LIBMYSQL_DLL'].dup
14
- elsif File.exist?(File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)))
15
- # Use vendor/libmysql.dll if it exists, convert slashes for Win32 LoadLibrary
16
- File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)).gsub('/', '\\')
17
- else
18
- # This will use default / system library paths
19
- 'libmysql.dll'
20
- end
11
+ # If this environment variable is set, it overrides any other paths
12
+ # The user is advised to use backslashes not forward slashes
13
+ ENV['RUBY_MYSQL2_LIBMYSQL_DLL']
14
+ elsif File.exist?(File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)))
15
+ # Use vendor/libmysql.dll if it exists, convert slashes for Win32 LoadLibrary
16
+ File.expand_path('../vendor/libmysql.dll', File.dirname(__FILE__)).tr('/', '\\')
17
+ else
18
+ # This will use default / system library paths
19
+ 'libmysql.dll'
20
+ end
21
21
 
22
22
  require 'Win32API'
23
23
  LoadLibrary = Win32API.new('Kernel32', 'LoadLibrary', ['P'], 'I')
@@ -31,6 +31,8 @@ require 'mysql2/error'
31
31
  require 'mysql2/mysql2'
32
32
  require 'mysql2/result'
33
33
  require 'mysql2/client'
34
+ require 'mysql2/field'
35
+ require 'mysql2/statement'
34
36
 
35
37
  # = Mysql2
36
38
  #
@@ -51,14 +53,32 @@ if defined?(ActiveRecord::VERSION::STRING) && ActiveRecord::VERSION::STRING < "3
51
53
  end
52
54
 
53
55
  # For holding utility methods
54
- module Mysql2::Util
56
+ module Mysql2
57
+ module Util
58
+ #
59
+ # Rekey a string-keyed hash with equivalent symbols.
60
+ #
61
+ def self.key_hash_as_symbols(hash)
62
+ return nil unless hash
63
+ Hash[hash.map { |k, v| [k.to_sym, v] }]
64
+ end
55
65
 
56
- #
57
- # Rekey a string-keyed hash with equivalent symbols.
58
- #
59
- def self.key_hash_as_symbols(hash)
60
- return nil unless hash
61
- Hash[hash.map { |k,v| [k.to_sym, v] }]
66
+ #
67
+ # In Mysql2::Client#query and Mysql2::Statement#execute,
68
+ # Thread#handle_interrupt is used to prevent Timeout#timeout
69
+ # from interrupting query execution.
70
+ #
71
+ # Timeout::ExitException was removed in Ruby 2.3.0, 2.2.3, and 2.1.8,
72
+ # but is present in earlier 2.1.x and 2.2.x, so we provide a shim.
73
+ #
74
+ if Thread.respond_to?(:handle_interrupt)
75
+ require 'timeout'
76
+ # rubocop:disable Style/ConstantName
77
+ TimeoutError = if defined?(::Timeout::ExitException)
78
+ ::Timeout::ExitException
79
+ else
80
+ ::Timeout::Error
81
+ end
82
+ end
62
83
  end
63
-
64
84
  end
data/spec/em/em_spec.rb CHANGED
@@ -4,7 +4,7 @@ begin
4
4
  require 'eventmachine'
5
5
  require 'mysql2/em'
6
6
 
7
- describe Mysql2::EM::Client do
7
+ RSpec.describe Mysql2::EM::Client do
8
8
  it "should support async queries" do
9
9
  results = []
10
10
  EM.run do
@@ -24,8 +24,8 @@ begin
24
24
  end
25
25
  end
26
26
 
27
- results[0].keys.should include("second_query")
28
- results[1].keys.should include("first_query")
27
+ expect(results[0].keys).to include("second_query")
28
+ expect(results[1].keys).to include("first_query")
29
29
  end
30
30
 
31
31
  it "should support queries in callbacks" do
@@ -44,38 +44,38 @@ begin
44
44
  end
45
45
  end
46
46
 
47
- results[0].keys.should include("first_query")
48
- results[1].keys.should include("second_query")
47
+ expect(results[0].keys).to include("first_query")
48
+ expect(results[1].keys).to include("second_query")
49
49
  end
50
50
 
51
51
  it "should not swallow exceptions raised in callbacks" do
52
- lambda {
52
+ expect {
53
53
  EM.run do
54
54
  client = Mysql2::EM::Client.new DatabaseCredentials['root']
55
55
  defer = client.query "SELECT sleep(0.1) as first_query"
56
- defer.callback do |result|
56
+ defer.callback do
57
57
  client.close
58
- raise 'some error'
58
+ fail 'some error'
59
59
  end
60
- defer.errback do |err|
60
+ defer.errback do
61
61
  # This _shouldn't_ be run, but it needed to prevent the specs from
62
62
  # freezing if this test fails.
63
63
  EM.stop_event_loop
64
64
  end
65
65
  end
66
- }.should raise_error
66
+ }.to raise_error('some error')
67
67
  end
68
68
 
69
69
  context 'when an exception is raised by the client' do
70
70
  let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
71
71
  let(:error) { StandardError.new('some error') }
72
- before { client.stub(:async_result).and_raise(error) }
72
+ before { allow(client).to receive(:async_result).and_raise(error) }
73
73
 
74
74
  it "should swallow exceptions raised in by the client" do
75
75
  errors = []
76
76
  EM.run do
77
77
  defer = client.query "SELECT sleep(0.1) as first_query"
78
- defer.callback do |result|
78
+ defer.callback do
79
79
  # This _shouldn't_ be run, but it is needed to prevent the specs from
80
80
  # freezing if this test fails.
81
81
  EM.stop_event_loop
@@ -85,7 +85,7 @@ begin
85
85
  EM.stop_event_loop
86
86
  end
87
87
  end
88
- errors.should == [error]
88
+ expect(errors).to eq([error])
89
89
  end
90
90
 
91
91
  it "should fail the deferrable" do
@@ -93,19 +93,19 @@ begin
93
93
  EM.run do
94
94
  defer = client.query "SELECT sleep(0.025) as first_query"
95
95
  EM.add_timer(0.1) do
96
- defer.callback do |result|
96
+ defer.callback do
97
97
  callbacks_run << :callback
98
98
  # This _shouldn't_ be run, but it is needed to prevent the specs from
99
99
  # freezing if this test fails.
100
100
  EM.stop_event_loop
101
101
  end
102
- defer.errback do |err|
102
+ defer.errback do
103
103
  callbacks_run << :errback
104
104
  EM.stop_event_loop
105
105
  end
106
106
  end
107
107
  end
108
- callbacks_run.should == [:errback]
108
+ expect(callbacks_run).to eq([:errback])
109
109
  end
110
110
  end
111
111
 
@@ -114,17 +114,17 @@ begin
114
114
  EM.run do
115
115
  client = Mysql2::EM::Client.new DatabaseCredentials['root']
116
116
  defer = client.query("select sleep(0.025)")
117
- defer.callback do |result|
117
+ defer.callback do
118
118
  callbacks_run << :callback
119
119
  end
120
- defer.errback do |err|
120
+ defer.errback do
121
121
  callbacks_run << :errback
122
122
  end
123
123
  EM.add_timer(0.1) do
124
- callbacks_run.should == [:callback]
125
- lambda {
124
+ expect(callbacks_run).to eq([:callback])
125
+ expect {
126
126
  client.close
127
- }.should_not raise_error(/invalid binding to detach/)
127
+ }.not_to raise_error
128
128
  EM.stop_event_loop
129
129
  end
130
130
  end