mysql2 0.3.18 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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