ghazel-mysql2 0.2.6.1

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 (46) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG.md +117 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +240 -0
  6. data/Rakefile +5 -0
  7. data/VERSION +1 -0
  8. data/benchmark/active_record.rb +53 -0
  9. data/benchmark/allocations.rb +33 -0
  10. data/benchmark/escape.rb +39 -0
  11. data/benchmark/query_with_mysql_casting.rb +83 -0
  12. data/benchmark/query_without_mysql_casting.rb +50 -0
  13. data/benchmark/sequel.rb +39 -0
  14. data/benchmark/setup_db.rb +115 -0
  15. data/examples/eventmachine.rb +21 -0
  16. data/examples/threaded.rb +20 -0
  17. data/ext/mysql2/client.c +728 -0
  18. data/ext/mysql2/client.h +41 -0
  19. data/ext/mysql2/extconf.rb +69 -0
  20. data/ext/mysql2/mysql2_ext.c +12 -0
  21. data/ext/mysql2/mysql2_ext.h +38 -0
  22. data/ext/mysql2/result.c +478 -0
  23. data/ext/mysql2/result.h +20 -0
  24. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +62 -0
  25. data/lib/active_record/connection_adapters/mysql2_adapter.rb +657 -0
  26. data/lib/active_record/fiber_patches.rb +104 -0
  27. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +11 -0
  28. data/lib/mysql2.rb +16 -0
  29. data/lib/mysql2/client.rb +240 -0
  30. data/lib/mysql2/em.rb +37 -0
  31. data/lib/mysql2/em_fiber.rb +29 -0
  32. data/lib/mysql2/error.rb +15 -0
  33. data/lib/mysql2/result.rb +5 -0
  34. data/mysql2.gemspec +90 -0
  35. data/spec/em/em_spec.rb +49 -0
  36. data/spec/mysql2/client_spec.rb +367 -0
  37. data/spec/mysql2/error_spec.rb +25 -0
  38. data/spec/mysql2/result_spec.rb +318 -0
  39. data/spec/rcov.opts +3 -0
  40. data/spec/spec_helper.rb +67 -0
  41. data/tasks/benchmarks.rake +8 -0
  42. data/tasks/compile.rake +54 -0
  43. data/tasks/jeweler.rake +17 -0
  44. data/tasks/rspec.rake +16 -0
  45. data/tasks/vendor_mysql.rake +41 -0
  46. metadata +119 -0
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,39 @@
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 do
14
+ puts "Mysql #{str.inspect}"
15
+ number_of.times do
16
+ mysql.quote str
17
+ end
18
+ end
19
+
20
+ mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
21
+ x.report do
22
+ puts "Mysql2 #{str.inspect}"
23
+ number_of.times do
24
+ mysql2.escape str
25
+ end
26
+ end
27
+
28
+ do_mysql = DataObjects::Connection.new("mysql://localhost/test")
29
+ x.report do
30
+ puts "do_mysql #{str.inspect}"
31
+ number_of.times do
32
+ do_mysql.quote_string str
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ run_escape_benchmarks "abc'def\"ghi\0jkl%mno"
39
+ run_escape_benchmarks "clean string"
@@ -0,0 +1,83 @@
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 do
46
+ puts "Mysql2"
47
+ number_of.times do
48
+ mysql2_result = mysql2.query sql, :symbolize_keys => true
49
+ mysql2_result.each do |res|
50
+ # puts res.inspect
51
+ end
52
+ end
53
+ end
54
+
55
+ mysql = Mysql.new("localhost", "root")
56
+ mysql.query "USE #{database}"
57
+ x.report do
58
+ puts "Mysql"
59
+ number_of.times do
60
+ mysql_result = mysql.query sql
61
+ fields = mysql_result.fetch_fields
62
+ mysql_result.each do |row|
63
+ row_hash = {}
64
+ row.each_with_index do |f, j|
65
+ row_hash[fields[j].name.to_sym] = mysql_cast(fields[j].type, row[j])
66
+ end
67
+ # puts row_hash.inspect
68
+ end
69
+ end
70
+ end
71
+
72
+ do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
73
+ command = do_mysql.create_command sql
74
+ x.report do
75
+ puts "do_mysql"
76
+ number_of.times do
77
+ do_result = command.execute_reader
78
+ do_result.each do |res|
79
+ # puts res.inspect
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,50 @@
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 do
18
+ puts "Mysql2"
19
+ number_of.times do
20
+ mysql2_result = mysql2.query sql, :symbolize_keys => true
21
+ mysql2_result.each do |res|
22
+ # puts res.inspect
23
+ end
24
+ end
25
+ end
26
+
27
+ mysql = Mysql.new("localhost", "root")
28
+ mysql.query "USE #{database}"
29
+ x.report do
30
+ puts "Mysql"
31
+ number_of.times do
32
+ mysql_result = mysql.query sql
33
+ mysql_result.each_hash do |res|
34
+ # puts res.inspect
35
+ end
36
+ end
37
+ end
38
+
39
+ do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
40
+ command = DataObjects::Mysql::Command.new do_mysql, sql
41
+ x.report do
42
+ puts "do_mysql"
43
+ number_of.times do
44
+ do_result = command.execute_reader
45
+ do_result.each do |res|
46
+ # puts res.inspect
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'rubygems'
5
+ require 'benchmark'
6
+ require 'sequel'
7
+ require 'sequel/adapters/do'
8
+
9
+ number_of = 10
10
+ mysql2_opts = "mysql2://localhost/test"
11
+ mysql_opts = "mysql://localhost/test"
12
+ do_mysql_opts = "do:mysql://localhost/test"
13
+
14
+ class Mysql2Model < Sequel::Model(Sequel.connect(mysql2_opts)[:mysql2_test]); end
15
+ class MysqlModel < Sequel::Model(Sequel.connect(mysql_opts)[:mysql2_test]); end
16
+ class DOMysqlModel < Sequel::Model(Sequel.connect(do_mysql_opts)[:mysql2_test]); end
17
+
18
+ Benchmark.bmbm do |x|
19
+ x.report do
20
+ puts "Mysql2"
21
+ number_of.times do
22
+ Mysql2Model.limit(1000).all
23
+ end
24
+ end
25
+
26
+ x.report do
27
+ puts "do:mysql"
28
+ number_of.times do
29
+ DOMysqlModel.limit(1000).all
30
+ end
31
+ end
32
+
33
+ x.report do
34
+ puts "Mysql"
35
+ number_of.times do
36
+ MysqlModel.limit(1000).all
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,115 @@
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
+ insert_record(
80
+ :bit_test => 1,
81
+ :tiny_int_test => rand(128),
82
+ :small_int_test => rand(32767),
83
+ :medium_int_test => rand(8388607),
84
+ :int_test => rand(2147483647),
85
+ :big_int_test => rand(9223372036854775807),
86
+ :float_test => rand(32767)/1.87,
87
+ :float_zero_test => 0.0,
88
+ :double_test => rand(8388607)/1.87,
89
+ :decimal_test => rand(8388607)/1.87,
90
+ :decimal_zero_test => 0,
91
+ :date_test => '2010-4-4',
92
+ :date_time_test => '2010-4-4 11:44:00',
93
+ :timestamp_test => '2010-4-4 11:44:00',
94
+ :time_test => '11:44:00',
95
+ :year_test => Time.now.year,
96
+ :char_test => Faker::Lorem.words(rand(5)),
97
+ :varchar_test => Faker::Lorem.words(rand(5)),
98
+ :binary_test => Faker::Lorem.words(rand(5)),
99
+ :varbinary_test => Faker::Lorem.words(rand(5)),
100
+ :tiny_blob_test => Faker::Lorem.words(rand(5)),
101
+ :tiny_text_test => Faker::Lorem.paragraph(rand(5)),
102
+ :blob_test => Faker::Lorem.paragraphs(rand(25)),
103
+ :text_test => Faker::Lorem.paragraphs(rand(25)),
104
+ :medium_blob_test => Faker::Lorem.paragraphs(rand(25)),
105
+ :medium_text_test => Faker::Lorem.paragraphs(rand(25)),
106
+ :long_blob_test => Faker::Lorem.paragraphs(rand(25)),
107
+ :long_text_test => Faker::Lorem.paragraphs(rand(25)),
108
+ :enum_test => ['val1', 'val2'].rand,
109
+ :set_test => ['val1', 'val2', 'val1,val2'].rand
110
+ )
111
+ $stdout.putc '.'
112
+ $stdout.flush
113
+ end
114
+ puts
115
+ puts "Done"
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+
5
+ require 'rubygems'
6
+ require 'eventmachine'
7
+ require 'mysql2/em'
8
+
9
+ EM.run do
10
+ client1 = Mysql2::EM::Client.new
11
+ defer1 = client1.query "SELECT sleep(3) as first_query"
12
+ defer1.callback do |result|
13
+ puts "Result: #{result.to_a.inspect}"
14
+ end
15
+
16
+ client2 = Mysql2::EM::Client.new
17
+ defer2 = client2.query "SELECT sleep(1) second_query"
18
+ defer2.callback do |result|
19
+ puts "Result: #{result.to_a.inspect}"
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+ require 'mysql2'
5
+ require 'timeout'
6
+
7
+ threads = []
8
+ # Should never exceed worst case 3.5 secs across all 20 threads
9
+ Timeout.timeout(3.5) do
10
+ 20.times do
11
+ threads << Thread.new do
12
+ overhead = rand(3)
13
+ puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
14
+ # 3 second overhead per query
15
+ Mysql2::Client.new(:host => "localhost", :username => "root").query("SELECT sleep(#{overhead}) as result")
16
+ puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
17
+ end
18
+ end
19
+ threads.each{|t| t.join }
20
+ end
@@ -0,0 +1,728 @@
1
+ #include <mysql2_ext.h>
2
+ #include <client.h>
3
+ #include <errno.h>
4
+
5
+ VALUE cMysql2Client;
6
+ extern VALUE mMysql2, cMysql2Error;
7
+ static VALUE intern_encoding_from_charset;
8
+ static ID sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array;
9
+ static ID intern_merge, intern_error_number_eql, intern_sql_state_eql;
10
+
11
+ #define REQUIRE_OPEN_DB(wrapper) \
12
+ if(wrapper->closed) { \
13
+ rb_raise(cMysql2Error, "closed MySQL connection"); \
14
+ }
15
+
16
+ #define MARK_CONN_INACTIVE(conn) \
17
+ wrapper->active = 0
18
+
19
+ #define GET_CLIENT(self) \
20
+ mysql_client_wrapper *wrapper; \
21
+ Data_Get_Struct(self, mysql_client_wrapper, wrapper)
22
+
23
+ /*
24
+ * used to pass all arguments to mysql_real_connect while inside
25
+ * rb_thread_blocking_region
26
+ */
27
+ struct nogvl_connect_args {
28
+ MYSQL *mysql;
29
+ const char *host;
30
+ const char *user;
31
+ const char *passwd;
32
+ const char *db;
33
+ unsigned int port;
34
+ const char *unix_socket;
35
+ unsigned long client_flag;
36
+ };
37
+
38
+ /*
39
+ * used to pass all arguments to mysql_send_query while inside
40
+ * rb_thread_blocking_region
41
+ */
42
+ struct nogvl_send_query_args {
43
+ MYSQL *mysql;
44
+ VALUE sql;
45
+ };
46
+
47
+ /*
48
+ * non-blocking mysql_*() functions that we won't be wrapping since
49
+ * they do not appear to hit the network nor issue any interruptible
50
+ * or blocking system calls.
51
+ *
52
+ * - mysql_affected_rows()
53
+ * - mysql_error()
54
+ * - mysql_fetch_fields()
55
+ * - mysql_fetch_lengths() - calls cli_fetch_lengths or emb_fetch_lengths
56
+ * - mysql_field_count()
57
+ * - mysql_get_client_info()
58
+ * - mysql_get_client_version()
59
+ * - mysql_get_server_info()
60
+ * - mysql_get_server_version()
61
+ * - mysql_insert_id()
62
+ * - mysql_num_fields()
63
+ * - mysql_num_rows()
64
+ * - mysql_options()
65
+ * - mysql_real_escape_string()
66
+ * - mysql_ssl_set()
67
+ */
68
+
69
+ static void rb_mysql_client_mark(void * wrapper) {
70
+ mysql_client_wrapper * w = wrapper;
71
+ if (w) {
72
+ rb_gc_mark(w->encoding);
73
+ }
74
+ }
75
+
76
+ static VALUE rb_raise_mysql2_error(MYSQL *client) {
77
+ VALUE e = rb_exc_new2(cMysql2Error, mysql_error(client));
78
+ rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(client)));
79
+ rb_funcall(e, intern_sql_state_eql, 1, rb_tainted_str_new2(mysql_sqlstate(client)));
80
+ rb_exc_raise(e);
81
+ return Qnil;
82
+ }
83
+
84
+ static VALUE nogvl_init(void *ptr) {
85
+ MYSQL *client;
86
+
87
+ /* may initialize embedded server and read /etc/services off disk */
88
+ client = mysql_init((MYSQL *)ptr);
89
+ return client ? Qtrue : Qfalse;
90
+ }
91
+
92
+ static VALUE nogvl_connect(void *ptr) {
93
+ struct nogvl_connect_args *args = ptr;
94
+ MYSQL *client;
95
+
96
+ do {
97
+ client = mysql_real_connect(args->mysql, args->host,
98
+ args->user, args->passwd,
99
+ args->db, args->port, args->unix_socket,
100
+ args->client_flag);
101
+ } while (! client && errno == EINTR && (errno = 0) == 0);
102
+
103
+ return client ? Qtrue : Qfalse;
104
+ }
105
+
106
+ static VALUE nogvl_close(void *ptr) {
107
+ mysql_client_wrapper *wrapper;
108
+ #ifndef _WIN32
109
+ int flags;
110
+ #endif
111
+ wrapper = ptr;
112
+ if (!wrapper->closed) {
113
+ wrapper->closed = 1;
114
+
115
+ /*
116
+ * we'll send a QUIT message to the server, but that message is more of a
117
+ * formality than a hard requirement since the socket is getting shutdown
118
+ * anyways, so ensure the socket write does not block our interpreter
119
+ *
120
+ *
121
+ * if the socket is dead we have no chance of blocking,
122
+ * so ignore any potential fcntl errors since they don't matter
123
+ */
124
+ #ifndef _WIN32
125
+ flags = fcntl(wrapper->client->net.fd, F_GETFL);
126
+ if (flags > 0 && !(flags & O_NONBLOCK))
127
+ fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK);
128
+ #else
129
+ u_long iMode;
130
+ iMode = 1;
131
+ ioctlsocket(wrapper->client->net.fd, FIONBIO, &iMode);
132
+ #endif
133
+
134
+ mysql_close(wrapper->client);
135
+ free(wrapper->client);
136
+ }
137
+
138
+ return Qnil;
139
+ }
140
+
141
+ static void rb_mysql_client_free(void * ptr) {
142
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
143
+
144
+ nogvl_close(wrapper);
145
+
146
+ xfree(ptr);
147
+ }
148
+
149
+ static VALUE allocate(VALUE klass) {
150
+ VALUE obj;
151
+ mysql_client_wrapper * wrapper;
152
+ obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
153
+ wrapper->encoding = Qnil;
154
+ wrapper->active = 0;
155
+ wrapper->closed = 1;
156
+ wrapper->client = (MYSQL*)malloc(sizeof(MYSQL));
157
+ return obj;
158
+ }
159
+
160
+ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
161
+ struct nogvl_connect_args args;
162
+ GET_CLIENT(self);
163
+
164
+ args.host = NIL_P(host) ? "localhost" : StringValuePtr(host);
165
+ args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
166
+ args.port = NIL_P(port) ? 3306 : NUM2INT(port);
167
+ args.user = NIL_P(user) ? NULL : StringValuePtr(user);
168
+ args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
169
+ args.db = NIL_P(database) ? NULL : StringValuePtr(database);
170
+ args.mysql = wrapper->client;
171
+ args.client_flag = NUM2ULONG(flags);
172
+
173
+ if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
174
+ // unable to connect
175
+ return rb_raise_mysql2_error(wrapper->client);
176
+ }
177
+
178
+ return self;
179
+ }
180
+
181
+ /*
182
+ * Immediately disconnect from the server, normally the garbage collector
183
+ * will disconnect automatically when a connection is no longer needed.
184
+ * Explicitly closing this will free up server resources sooner than waiting
185
+ * for the garbage collector.
186
+ */
187
+ static VALUE rb_mysql_client_close(VALUE self) {
188
+ GET_CLIENT(self);
189
+
190
+ if (!wrapper->closed) {
191
+ rb_thread_blocking_region(nogvl_close, wrapper, RUBY_UBF_IO, 0);
192
+ }
193
+
194
+ return Qnil;
195
+ }
196
+
197
+ /*
198
+ * mysql_send_query is unlikely to block since most queries are small
199
+ * enough to fit in a socket buffer, but sometimes large UPDATE and
200
+ * INSERTs will cause the process to block
201
+ */
202
+ static VALUE nogvl_send_query(void *ptr) {
203
+ struct nogvl_send_query_args *args = ptr;
204
+ int rv;
205
+ const char *sql = StringValuePtr(args->sql);
206
+ long sql_len = RSTRING_LEN(args->sql);
207
+
208
+ rv = mysql_send_query(args->mysql, sql, sql_len);
209
+
210
+ return rv == 0 ? Qtrue : Qfalse;
211
+ }
212
+
213
+ /*
214
+ * even though we did rb_thread_select before calling this, a large
215
+ * response can overflow the socket buffers and cause us to eventually
216
+ * block while calling mysql_read_query_result
217
+ */
218
+ static VALUE nogvl_read_query_result(void *ptr) {
219
+ MYSQL * client = ptr;
220
+ my_bool res = mysql_read_query_result(client);
221
+
222
+ return res == 0 ? Qtrue : Qfalse;
223
+ }
224
+
225
+ /* mysql_store_result may (unlikely) read rows off the socket */
226
+ static VALUE nogvl_store_result(void *ptr) {
227
+ MYSQL * client = ptr;
228
+ return (VALUE)mysql_store_result(client);
229
+ }
230
+
231
+ static VALUE rb_mysql_client_async_result(VALUE self) {
232
+ MYSQL_RES * result;
233
+ VALUE resultObj;
234
+ #ifdef HAVE_RUBY_ENCODING_H
235
+ mysql2_result_wrapper * result_wrapper;
236
+ #endif
237
+ GET_CLIENT(self);
238
+
239
+ REQUIRE_OPEN_DB(wrapper);
240
+ if (rb_thread_blocking_region(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
241
+ // an error occurred, mark this connection inactive
242
+ MARK_CONN_INACTIVE(self);
243
+ return rb_raise_mysql2_error(wrapper->client);
244
+ }
245
+
246
+ result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper->client, RUBY_UBF_IO, 0);
247
+
248
+ // we have our result, mark this connection inactive
249
+ MARK_CONN_INACTIVE(self);
250
+
251
+ if (result == NULL) {
252
+ if (mysql_field_count(wrapper->client) != 0) {
253
+ rb_raise_mysql2_error(wrapper->client);
254
+ }
255
+ return Qnil;
256
+ }
257
+
258
+ resultObj = rb_mysql_result_to_obj(result);
259
+ // pass-through query options for result construction later
260
+ rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0));
261
+
262
+ #ifdef HAVE_RUBY_ENCODING_H
263
+ GetMysql2Result(resultObj, result_wrapper);
264
+ result_wrapper->encoding = wrapper->encoding;
265
+ #endif
266
+ return resultObj;
267
+ }
268
+
269
+ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
270
+ struct nogvl_send_query_args args;
271
+ fd_set fdset;
272
+ int fd, retval;
273
+ int async = 0;
274
+ VALUE opts, defaults, read_timeout;
275
+ #ifdef HAVE_RUBY_ENCODING_H
276
+ rb_encoding *conn_enc;
277
+ #endif
278
+ struct timeval tv;
279
+ struct timeval* tvp;
280
+ long int sec;
281
+ VALUE result;
282
+ GET_CLIENT(self);
283
+
284
+ REQUIRE_OPEN_DB(wrapper);
285
+ args.mysql = wrapper->client;
286
+
287
+ // see if this connection is still waiting on a result from a previous query
288
+ if (wrapper->active == 0) {
289
+ // mark this connection active
290
+ wrapper->active = 1;
291
+ } else {
292
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
293
+ }
294
+
295
+ defaults = rb_iv_get(self, "@query_options");
296
+ if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
297
+ opts = rb_funcall(defaults, intern_merge, 1, opts);
298
+ rb_iv_set(self, "@query_options", opts);
299
+
300
+ if (rb_hash_aref(opts, sym_async) == Qtrue) {
301
+ async = 1;
302
+ }
303
+ } else {
304
+ opts = defaults;
305
+ }
306
+
307
+ Check_Type(args.sql, T_STRING);
308
+ #ifdef HAVE_RUBY_ENCODING_H
309
+ conn_enc = rb_to_encoding(wrapper->encoding);
310
+ // ensure the string is in the encoding the connection is expecting
311
+ args.sql = rb_str_export_to_enc(args.sql, conn_enc);
312
+ #endif
313
+
314
+ if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
315
+ // an error occurred, we're not active anymore
316
+ MARK_CONN_INACTIVE(self);
317
+ return rb_raise_mysql2_error(wrapper->client);
318
+ }
319
+
320
+ read_timeout = rb_iv_get(self, "@read_timeout");
321
+
322
+ tvp = NULL;
323
+ if (!NIL_P(read_timeout)) {
324
+ Check_Type(read_timeout, T_FIXNUM);
325
+ tvp = &tv;
326
+ sec = FIX2INT(read_timeout);
327
+ // TODO: support partial seconds?
328
+ // also, this check is here for sanity, we also check up in Ruby
329
+ if (sec >= 0) {
330
+ tvp->tv_sec = sec;
331
+ } else {
332
+ rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
333
+ }
334
+ tvp->tv_usec = 0;
335
+ }
336
+
337
+ if (!async) {
338
+ // the below code is largely from do_mysql
339
+ // http://github.com/datamapper/do
340
+ fd = wrapper->client->net.fd;
341
+ for(;;) {
342
+ int fd_set_fd = fd;
343
+
344
+ #ifdef _WIN32
345
+ WSAPROTOCOL_INFO wsa_pi;
346
+ // dupicate the SOCKET from libmysql
347
+ int r = WSADuplicateSocket(fd, GetCurrentProcessId(), &wsa_pi);
348
+ SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
349
+ // create the CRT fd so ruby can get back to the SOCKET
350
+ fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
351
+ #endif
352
+
353
+ FD_ZERO(&fdset);
354
+ FD_SET(fd_set_fd, &fdset);
355
+
356
+ retval = rb_thread_select(fd_set_fd + 1, &fdset, NULL, NULL, tvp);
357
+
358
+ #ifdef _WIN32
359
+ // cleanup the CRT fd
360
+ _close(fd_set_fd);
361
+ // cleanup the duplicated SOCKET
362
+ closesocket(s);
363
+ #endif
364
+
365
+ if (retval == 0) {
366
+ rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
367
+ }
368
+
369
+ if (retval < 0) {
370
+ rb_sys_fail(0);
371
+ }
372
+
373
+ if (retval > 0) {
374
+ break;
375
+ }
376
+ }
377
+
378
+ result = rb_mysql_client_async_result(self);
379
+
380
+ return result;
381
+ } else {
382
+ return Qnil;
383
+ }
384
+ }
385
+
386
+ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
387
+ VALUE newStr;
388
+ unsigned long newLen, oldLen;
389
+ #ifdef HAVE_RUBY_ENCODING_H
390
+ rb_encoding *default_internal_enc;
391
+ rb_encoding *conn_enc;
392
+ #endif
393
+ GET_CLIENT(self);
394
+
395
+ REQUIRE_OPEN_DB(wrapper);
396
+ Check_Type(str, T_STRING);
397
+ #ifdef HAVE_RUBY_ENCODING_H
398
+ default_internal_enc = rb_default_internal_encoding();
399
+ conn_enc = rb_to_encoding(wrapper->encoding);
400
+ // ensure the string is in the encoding the connection is expecting
401
+ str = rb_str_export_to_enc(str, conn_enc);
402
+ #endif
403
+
404
+ oldLen = RSTRING_LEN(str);
405
+ newStr = rb_str_new(0, oldLen*2+1);
406
+
407
+ newLen = mysql_real_escape_string(wrapper->client, RSTRING_PTR(newStr), StringValuePtr(str), oldLen);
408
+ if (newLen == oldLen) {
409
+ // no need to return a new ruby string if nothing changed
410
+ return str;
411
+ } else {
412
+ rb_str_resize(newStr, newLen);
413
+ #ifdef HAVE_RUBY_ENCODING_H
414
+ rb_enc_associate(newStr, conn_enc);
415
+ if (default_internal_enc) {
416
+ newStr = rb_str_export_to_enc(newStr, default_internal_enc);
417
+ }
418
+ #endif
419
+ return newStr;
420
+ }
421
+ }
422
+
423
+ static VALUE rb_mysql_client_info(VALUE self) {
424
+ VALUE version, client_info;
425
+ #ifdef HAVE_RUBY_ENCODING_H
426
+ rb_encoding *default_internal_enc;
427
+ rb_encoding *conn_enc;
428
+ #endif
429
+ GET_CLIENT(self);
430
+ version = rb_hash_new();
431
+
432
+ #ifdef HAVE_RUBY_ENCODING_H
433
+ default_internal_enc = rb_default_internal_encoding();
434
+ conn_enc = rb_to_encoding(wrapper->encoding);
435
+ #endif
436
+
437
+ rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
438
+ client_info = rb_str_new2(mysql_get_client_info());
439
+ #ifdef HAVE_RUBY_ENCODING_H
440
+ rb_enc_associate(client_info, conn_enc);
441
+ if (default_internal_enc) {
442
+ client_info = rb_str_export_to_enc(client_info, default_internal_enc);
443
+ }
444
+ #endif
445
+ rb_hash_aset(version, sym_version, client_info);
446
+ return version;
447
+ }
448
+
449
+ static VALUE rb_mysql_client_server_info(VALUE self) {
450
+ VALUE version, server_info;
451
+ #ifdef HAVE_RUBY_ENCODING_H
452
+ rb_encoding *default_internal_enc;
453
+ rb_encoding *conn_enc;
454
+ #endif
455
+ GET_CLIENT(self);
456
+
457
+ REQUIRE_OPEN_DB(wrapper);
458
+ #ifdef HAVE_RUBY_ENCODING_H
459
+ default_internal_enc = rb_default_internal_encoding();
460
+ conn_enc = rb_to_encoding(wrapper->encoding);
461
+ #endif
462
+
463
+ version = rb_hash_new();
464
+ rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
465
+ server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
466
+ #ifdef HAVE_RUBY_ENCODING_H
467
+ rb_enc_associate(server_info, conn_enc);
468
+ if (default_internal_enc) {
469
+ server_info = rb_str_export_to_enc(server_info, default_internal_enc);
470
+ }
471
+ #endif
472
+ rb_hash_aset(version, sym_version, server_info);
473
+ return version;
474
+ }
475
+
476
+ static VALUE rb_mysql_client_socket(VALUE self) {
477
+ GET_CLIENT(self);
478
+ REQUIRE_OPEN_DB(wrapper);
479
+ return INT2NUM(wrapper->client->net.fd);
480
+ }
481
+
482
+ static VALUE rb_mysql_client_last_id(VALUE self) {
483
+ GET_CLIENT(self);
484
+ REQUIRE_OPEN_DB(wrapper);
485
+ return ULL2NUM(mysql_insert_id(wrapper->client));
486
+ }
487
+
488
+ static VALUE rb_mysql_client_affected_rows(VALUE self) {
489
+ my_ulonglong retVal;
490
+ GET_CLIENT(self);
491
+
492
+ REQUIRE_OPEN_DB(wrapper);
493
+ retVal = mysql_affected_rows(wrapper->client);
494
+ if (retVal == (my_ulonglong)-1) {
495
+ rb_raise_mysql2_error(wrapper->client);
496
+ }
497
+ return ULL2NUM(retVal);
498
+ }
499
+
500
+ static VALUE set_reconnect(VALUE self, VALUE value) {
501
+ my_bool reconnect;
502
+ GET_CLIENT(self);
503
+
504
+ if(!NIL_P(value)) {
505
+ reconnect = value == Qfalse ? 0 : 1;
506
+
507
+ /* set default reconnect behavior */
508
+ if (mysql_options(wrapper->client, MYSQL_OPT_RECONNECT, &reconnect)) {
509
+ /* TODO: warning - unable to set reconnect behavior */
510
+ rb_warn("%s\n", mysql_error(wrapper->client));
511
+ }
512
+ }
513
+ return value;
514
+ }
515
+
516
+ static VALUE set_connect_timeout(VALUE self, VALUE value) {
517
+ unsigned int connect_timeout = 0;
518
+ GET_CLIENT(self);
519
+
520
+ if(!NIL_P(value)) {
521
+ connect_timeout = NUM2INT(value);
522
+ if(0 == connect_timeout) return value;
523
+
524
+ /* set default connection timeout behavior */
525
+ if (mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) {
526
+ /* TODO: warning - unable to set connection timeout */
527
+ rb_warn("%s\n", mysql_error(wrapper->client));
528
+ }
529
+ }
530
+ return value;
531
+ }
532
+
533
+ static VALUE set_charset_name(VALUE self, VALUE value) {
534
+ char * charset_name;
535
+ #ifdef HAVE_RUBY_ENCODING_H
536
+ VALUE new_encoding;
537
+ #endif
538
+ GET_CLIENT(self);
539
+
540
+ #ifdef HAVE_RUBY_ENCODING_H
541
+ new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset, 1, value);
542
+ if (new_encoding == Qnil) {
543
+ rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(value));
544
+ } else {
545
+ if (wrapper->encoding == Qnil) {
546
+ wrapper->encoding = new_encoding;
547
+ }
548
+ }
549
+ #endif
550
+
551
+ charset_name = StringValuePtr(value);
552
+
553
+ if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
554
+ /* TODO: warning - unable to set charset */
555
+ rb_warn("%s\n", mysql_error(wrapper->client));
556
+ }
557
+
558
+ return value;
559
+ }
560
+
561
+ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
562
+ GET_CLIENT(self);
563
+
564
+ if(!NIL_P(ca) || !NIL_P(key)) {
565
+ mysql_ssl_set(wrapper->client,
566
+ NIL_P(key) ? NULL : StringValuePtr(key),
567
+ NIL_P(cert) ? NULL : StringValuePtr(cert),
568
+ NIL_P(ca) ? NULL : StringValuePtr(ca),
569
+ NIL_P(capath) ? NULL : StringValuePtr(capath),
570
+ NIL_P(cipher) ? NULL : StringValuePtr(cipher));
571
+ }
572
+
573
+ return self;
574
+ }
575
+
576
+ static VALUE init_connection(VALUE self) {
577
+ GET_CLIENT(self);
578
+
579
+ if (rb_thread_blocking_region(nogvl_init, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
580
+ /* TODO: warning - not enough memory? */
581
+ return rb_raise_mysql2_error(wrapper->client);
582
+ }
583
+
584
+ wrapper->closed = 0;
585
+ return self;
586
+ }
587
+
588
+ void init_mysql2_client() {
589
+ cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
590
+
591
+ rb_define_alloc_func(cMysql2Client, allocate);
592
+
593
+ rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
594
+ rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
595
+ rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
596
+ rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
597
+ rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
598
+ rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
599
+ rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
600
+ rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
601
+ rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
602
+
603
+ rb_define_private_method(cMysql2Client, "reconnect=", set_reconnect, 1);
604
+ rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
605
+ rb_define_private_method(cMysql2Client, "charset_name=", set_charset_name, 1);
606
+ rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
607
+ rb_define_private_method(cMysql2Client, "init_connection", init_connection, 0);
608
+ rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
609
+
610
+ intern_encoding_from_charset = rb_intern("encoding_from_charset");
611
+
612
+ sym_id = ID2SYM(rb_intern("id"));
613
+ sym_version = ID2SYM(rb_intern("version"));
614
+ sym_async = ID2SYM(rb_intern("async"));
615
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
616
+ sym_as = ID2SYM(rb_intern("as"));
617
+ sym_array = ID2SYM(rb_intern("array"));
618
+
619
+ intern_merge = rb_intern("merge");
620
+ intern_error_number_eql = rb_intern("error_number=");
621
+ intern_sql_state_eql = rb_intern("sql_state=");
622
+
623
+ #ifdef CLIENT_LONG_PASSWORD
624
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
625
+ INT2NUM(CLIENT_LONG_PASSWORD));
626
+ #endif
627
+
628
+ #ifdef CLIENT_FOUND_ROWS
629
+ rb_const_set(cMysql2Client, rb_intern("FOUND_ROWS"),
630
+ INT2NUM(CLIENT_FOUND_ROWS));
631
+ #endif
632
+
633
+ #ifdef CLIENT_LONG_FLAG
634
+ rb_const_set(cMysql2Client, rb_intern("LONG_FLAG"),
635
+ INT2NUM(CLIENT_LONG_FLAG));
636
+ #endif
637
+
638
+ #ifdef CLIENT_CONNECT_WITH_DB
639
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_WITH_DB"),
640
+ INT2NUM(CLIENT_CONNECT_WITH_DB));
641
+ #endif
642
+
643
+ #ifdef CLIENT_NO_SCHEMA
644
+ rb_const_set(cMysql2Client, rb_intern("NO_SCHEMA"),
645
+ INT2NUM(CLIENT_NO_SCHEMA));
646
+ #endif
647
+
648
+ #ifdef CLIENT_COMPRESS
649
+ rb_const_set(cMysql2Client, rb_intern("COMPRESS"), INT2NUM(CLIENT_COMPRESS));
650
+ #endif
651
+
652
+ #ifdef CLIENT_ODBC
653
+ rb_const_set(cMysql2Client, rb_intern("ODBC"), INT2NUM(CLIENT_ODBC));
654
+ #endif
655
+
656
+ #ifdef CLIENT_LOCAL_FILES
657
+ rb_const_set(cMysql2Client, rb_intern("LOCAL_FILES"),
658
+ INT2NUM(CLIENT_LOCAL_FILES));
659
+ #endif
660
+
661
+ #ifdef CLIENT_IGNORE_SPACE
662
+ rb_const_set(cMysql2Client, rb_intern("IGNORE_SPACE"),
663
+ INT2NUM(CLIENT_IGNORE_SPACE));
664
+ #endif
665
+
666
+ #ifdef CLIENT_PROTOCOL_41
667
+ rb_const_set(cMysql2Client, rb_intern("PROTOCOL_41"),
668
+ INT2NUM(CLIENT_PROTOCOL_41));
669
+ #endif
670
+
671
+ #ifdef CLIENT_INTERACTIVE
672
+ rb_const_set(cMysql2Client, rb_intern("INTERACTIVE"),
673
+ INT2NUM(CLIENT_INTERACTIVE));
674
+ #endif
675
+
676
+ #ifdef CLIENT_SSL
677
+ rb_const_set(cMysql2Client, rb_intern("SSL"), INT2NUM(CLIENT_SSL));
678
+ #endif
679
+
680
+ #ifdef CLIENT_IGNORE_SIGPIPE
681
+ rb_const_set(cMysql2Client, rb_intern("IGNORE_SIGPIPE"),
682
+ INT2NUM(CLIENT_IGNORE_SIGPIPE));
683
+ #endif
684
+
685
+ #ifdef CLIENT_TRANSACTIONS
686
+ rb_const_set(cMysql2Client, rb_intern("TRANSACTIONS"),
687
+ INT2NUM(CLIENT_TRANSACTIONS));
688
+ #endif
689
+
690
+ #ifdef CLIENT_RESERVED
691
+ rb_const_set(cMysql2Client, rb_intern("RESERVED"), INT2NUM(CLIENT_RESERVED));
692
+ #endif
693
+
694
+ #ifdef CLIENT_SECURE_CONNECTION
695
+ rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"),
696
+ INT2NUM(CLIENT_SECURE_CONNECTION));
697
+ #endif
698
+
699
+ #ifdef CLIENT_MULTI_STATEMENTS
700
+ rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
701
+ INT2NUM(CLIENT_MULTI_STATEMENTS));
702
+ #endif
703
+
704
+ #ifdef CLIENT_PS_MULTI_RESULTS
705
+ rb_const_set(cMysql2Client, rb_intern("PS_MULTI_RESULTS"),
706
+ INT2NUM(CLIENT_PS_MULTI_RESULTS));
707
+ #endif
708
+
709
+ #ifdef CLIENT_SSL_VERIFY_SERVER_CERT
710
+ rb_const_set(cMysql2Client, rb_intern("SSL_VERIFY_SERVER_CERT"),
711
+ INT2NUM(CLIENT_SSL_VERIFY_SERVER_CERT));
712
+ #endif
713
+
714
+ #ifdef CLIENT_REMEMBER_OPTIONS
715
+ rb_const_set(cMysql2Client, rb_intern("REMEMBER_OPTIONS"),
716
+ INT2NUM(CLIENT_REMEMBER_OPTIONS));
717
+ #endif
718
+
719
+ #ifdef CLIENT_ALL_FLAGS
720
+ rb_const_set(cMysql2Client, rb_intern("ALL_FLAGS"),
721
+ INT2NUM(CLIENT_ALL_FLAGS));
722
+ #endif
723
+
724
+ #ifdef CLIENT_BASIC_FLAGS
725
+ rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
726
+ INT2NUM(CLIENT_BASIC_FLAGS));
727
+ #endif
728
+ }