mysql2_bigint 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/CHANGELOG.md +120 -0
  2. data/Gemfile +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +248 -0
  5. data/Rakefile +5 -0
  6. data/benchmark/active_record.rb +51 -0
  7. data/benchmark/allocations.rb +33 -0
  8. data/benchmark/escape.rb +36 -0
  9. data/benchmark/query_with_mysql_casting.rb +80 -0
  10. data/benchmark/query_without_mysql_casting.rb +47 -0
  11. data/benchmark/sequel.rb +37 -0
  12. data/benchmark/setup_db.rb +119 -0
  13. data/examples/eventmachine.rb +21 -0
  14. data/examples/threaded.rb +20 -0
  15. data/ext/mysql2/client.c +768 -0
  16. data/ext/mysql2/client.h +41 -0
  17. data/ext/mysql2/extconf.rb +69 -0
  18. data/ext/mysql2/mysql2_ext.c +12 -0
  19. data/ext/mysql2/mysql2_ext.h +38 -0
  20. data/ext/mysql2/result.c +488 -0
  21. data/ext/mysql2/result.h +20 -0
  22. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +64 -0
  23. data/lib/active_record/connection_adapters/mysql2_adapter.rb +654 -0
  24. data/lib/active_record/fiber_patches.rb +104 -0
  25. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +11 -0
  26. data/lib/mysql2.rb +16 -0
  27. data/lib/mysql2/client.rb +240 -0
  28. data/lib/mysql2/em.rb +37 -0
  29. data/lib/mysql2/em_fiber.rb +31 -0
  30. data/lib/mysql2/error.rb +15 -0
  31. data/lib/mysql2/result.rb +5 -0
  32. data/lib/mysql2/version.rb +3 -0
  33. data/lib/mysql2_bigint.rb +1 -0
  34. data/mysql2_bigint.gemspec +32 -0
  35. data/spec/em/em_fiber_spec.rb +22 -0
  36. data/spec/em/em_spec.rb +49 -0
  37. data/spec/mysql2/client_spec.rb +385 -0
  38. data/spec/mysql2/error_spec.rb +25 -0
  39. data/spec/mysql2/result_spec.rb +328 -0
  40. data/spec/rcov.opts +3 -0
  41. data/spec/spec_helper.rb +66 -0
  42. data/tasks/benchmarks.rake +20 -0
  43. data/tasks/compile.rake +53 -0
  44. data/tasks/rspec.rake +16 -0
  45. data/tasks/vendor_mysql.rake +41 -0
  46. metadata +199 -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,36 @@
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"
@@ -0,0 +1,80 @@
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
@@ -0,0 +1,47 @@
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
@@ -0,0 +1,37 @@
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
@@ -0,0 +1,119 @@
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"
@@ -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,768 @@
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 rb_mysql_client_thread_id(VALUE self) {
501
+ unsigned long retVal;
502
+ GET_CLIENT(self);
503
+
504
+ REQUIRE_OPEN_DB(wrapper);
505
+ retVal = mysql_thread_id(wrapper->client);
506
+ return ULL2NUM(retVal);
507
+ }
508
+
509
+ static VALUE rb_mysql_client_ping(VALUE self) {
510
+ unsigned long retVal;
511
+ GET_CLIENT(self);
512
+
513
+ retVal = mysql_ping(wrapper->client);
514
+ if (retVal == 0) {
515
+ return Qtrue;
516
+ } else {
517
+ return Qfalse;
518
+ }
519
+ }
520
+
521
+ static VALUE set_reconnect(VALUE self, VALUE value) {
522
+ my_bool reconnect;
523
+ GET_CLIENT(self);
524
+
525
+ if(!NIL_P(value)) {
526
+ reconnect = value == Qfalse ? 0 : 1;
527
+
528
+ /* set default reconnect behavior */
529
+ if (mysql_options(wrapper->client, MYSQL_OPT_RECONNECT, &reconnect)) {
530
+ /* TODO: warning - unable to set reconnect behavior */
531
+ rb_warn("%s\n", mysql_error(wrapper->client));
532
+ }
533
+ }
534
+ return value;
535
+ }
536
+
537
+ static VALUE set_connect_timeout(VALUE self, VALUE value) {
538
+ unsigned int connect_timeout = 0;
539
+ GET_CLIENT(self);
540
+
541
+ if(!NIL_P(value)) {
542
+ connect_timeout = NUM2INT(value);
543
+ if(0 == connect_timeout) return value;
544
+
545
+ /* set default connection timeout behavior */
546
+ if (mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) {
547
+ /* TODO: warning - unable to set connection timeout */
548
+ rb_warn("%s\n", mysql_error(wrapper->client));
549
+ }
550
+ }
551
+ return value;
552
+ }
553
+
554
+ static VALUE set_charset_name(VALUE self, VALUE value) {
555
+ char * charset_name;
556
+ #ifdef HAVE_RUBY_ENCODING_H
557
+ VALUE new_encoding;
558
+ #endif
559
+ GET_CLIENT(self);
560
+
561
+ #ifdef HAVE_RUBY_ENCODING_H
562
+ new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset, 1, value);
563
+ if (new_encoding == Qnil) {
564
+ rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(value));
565
+ } else {
566
+ if (wrapper->encoding == Qnil) {
567
+ wrapper->encoding = new_encoding;
568
+ }
569
+ }
570
+ #endif
571
+
572
+ charset_name = StringValuePtr(value);
573
+
574
+ if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
575
+ /* TODO: warning - unable to set charset */
576
+ rb_warn("%s\n", mysql_error(wrapper->client));
577
+ }
578
+
579
+ return value;
580
+ }
581
+
582
+ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
583
+ GET_CLIENT(self);
584
+
585
+ if(!NIL_P(ca) || !NIL_P(key)) {
586
+ mysql_ssl_set(wrapper->client,
587
+ NIL_P(key) ? NULL : StringValuePtr(key),
588
+ NIL_P(cert) ? NULL : StringValuePtr(cert),
589
+ NIL_P(ca) ? NULL : StringValuePtr(ca),
590
+ NIL_P(capath) ? NULL : StringValuePtr(capath),
591
+ NIL_P(cipher) ? NULL : StringValuePtr(cipher));
592
+ }
593
+
594
+ return self;
595
+ }
596
+
597
+ static VALUE init_connection(VALUE self) {
598
+ GET_CLIENT(self);
599
+
600
+ if (rb_thread_blocking_region(nogvl_init, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
601
+ /* TODO: warning - not enough memory? */
602
+ return rb_raise_mysql2_error(wrapper->client);
603
+ }
604
+
605
+ wrapper->closed = 0;
606
+ return self;
607
+ }
608
+
609
+ void init_mysql2_client() {
610
+ // verify the libmysql we're about to use was the version we were built against
611
+ // https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99
612
+ int i;
613
+ int dots = 0;
614
+ const char *lib = mysql_get_client_info();
615
+ for (i = 0; lib[i] != 0 && MYSQL_SERVER_VERSION[i] != 0; i++) {
616
+ if (lib[i] == '.') {
617
+ dots++;
618
+ // we only compare MAJOR and MINOR
619
+ if (dots == 2) break;
620
+ }
621
+ if (lib[i] != MYSQL_SERVER_VERSION[i]) {
622
+ rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_SERVER_VERSION, lib);
623
+ return;
624
+ }
625
+ }
626
+
627
+ cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
628
+
629
+ rb_define_alloc_func(cMysql2Client, allocate);
630
+
631
+ rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
632
+ rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
633
+ rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
634
+ rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
635
+ rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
636
+ rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
637
+ rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
638
+ rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
639
+ rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
640
+ rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
641
+ rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
642
+
643
+ rb_define_private_method(cMysql2Client, "reconnect=", set_reconnect, 1);
644
+ rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
645
+ rb_define_private_method(cMysql2Client, "charset_name=", set_charset_name, 1);
646
+ rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
647
+ rb_define_private_method(cMysql2Client, "init_connection", init_connection, 0);
648
+ rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
649
+
650
+ intern_encoding_from_charset = rb_intern("encoding_from_charset");
651
+
652
+ sym_id = ID2SYM(rb_intern("id"));
653
+ sym_version = ID2SYM(rb_intern("version"));
654
+ sym_async = ID2SYM(rb_intern("async"));
655
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
656
+ sym_as = ID2SYM(rb_intern("as"));
657
+ sym_array = ID2SYM(rb_intern("array"));
658
+
659
+ intern_merge = rb_intern("merge");
660
+ intern_error_number_eql = rb_intern("error_number=");
661
+ intern_sql_state_eql = rb_intern("sql_state=");
662
+
663
+ #ifdef CLIENT_LONG_PASSWORD
664
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
665
+ INT2NUM(CLIENT_LONG_PASSWORD));
666
+ #endif
667
+
668
+ #ifdef CLIENT_FOUND_ROWS
669
+ rb_const_set(cMysql2Client, rb_intern("FOUND_ROWS"),
670
+ INT2NUM(CLIENT_FOUND_ROWS));
671
+ #endif
672
+
673
+ #ifdef CLIENT_LONG_FLAG
674
+ rb_const_set(cMysql2Client, rb_intern("LONG_FLAG"),
675
+ INT2NUM(CLIENT_LONG_FLAG));
676
+ #endif
677
+
678
+ #ifdef CLIENT_CONNECT_WITH_DB
679
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_WITH_DB"),
680
+ INT2NUM(CLIENT_CONNECT_WITH_DB));
681
+ #endif
682
+
683
+ #ifdef CLIENT_NO_SCHEMA
684
+ rb_const_set(cMysql2Client, rb_intern("NO_SCHEMA"),
685
+ INT2NUM(CLIENT_NO_SCHEMA));
686
+ #endif
687
+
688
+ #ifdef CLIENT_COMPRESS
689
+ rb_const_set(cMysql2Client, rb_intern("COMPRESS"), INT2NUM(CLIENT_COMPRESS));
690
+ #endif
691
+
692
+ #ifdef CLIENT_ODBC
693
+ rb_const_set(cMysql2Client, rb_intern("ODBC"), INT2NUM(CLIENT_ODBC));
694
+ #endif
695
+
696
+ #ifdef CLIENT_LOCAL_FILES
697
+ rb_const_set(cMysql2Client, rb_intern("LOCAL_FILES"),
698
+ INT2NUM(CLIENT_LOCAL_FILES));
699
+ #endif
700
+
701
+ #ifdef CLIENT_IGNORE_SPACE
702
+ rb_const_set(cMysql2Client, rb_intern("IGNORE_SPACE"),
703
+ INT2NUM(CLIENT_IGNORE_SPACE));
704
+ #endif
705
+
706
+ #ifdef CLIENT_PROTOCOL_41
707
+ rb_const_set(cMysql2Client, rb_intern("PROTOCOL_41"),
708
+ INT2NUM(CLIENT_PROTOCOL_41));
709
+ #endif
710
+
711
+ #ifdef CLIENT_INTERACTIVE
712
+ rb_const_set(cMysql2Client, rb_intern("INTERACTIVE"),
713
+ INT2NUM(CLIENT_INTERACTIVE));
714
+ #endif
715
+
716
+ #ifdef CLIENT_SSL
717
+ rb_const_set(cMysql2Client, rb_intern("SSL"), INT2NUM(CLIENT_SSL));
718
+ #endif
719
+
720
+ #ifdef CLIENT_IGNORE_SIGPIPE
721
+ rb_const_set(cMysql2Client, rb_intern("IGNORE_SIGPIPE"),
722
+ INT2NUM(CLIENT_IGNORE_SIGPIPE));
723
+ #endif
724
+
725
+ #ifdef CLIENT_TRANSACTIONS
726
+ rb_const_set(cMysql2Client, rb_intern("TRANSACTIONS"),
727
+ INT2NUM(CLIENT_TRANSACTIONS));
728
+ #endif
729
+
730
+ #ifdef CLIENT_RESERVED
731
+ rb_const_set(cMysql2Client, rb_intern("RESERVED"), INT2NUM(CLIENT_RESERVED));
732
+ #endif
733
+
734
+ #ifdef CLIENT_SECURE_CONNECTION
735
+ rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"),
736
+ INT2NUM(CLIENT_SECURE_CONNECTION));
737
+ #endif
738
+
739
+ #ifdef CLIENT_MULTI_STATEMENTS
740
+ rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
741
+ INT2NUM(CLIENT_MULTI_STATEMENTS));
742
+ #endif
743
+
744
+ #ifdef CLIENT_PS_MULTI_RESULTS
745
+ rb_const_set(cMysql2Client, rb_intern("PS_MULTI_RESULTS"),
746
+ INT2NUM(CLIENT_PS_MULTI_RESULTS));
747
+ #endif
748
+
749
+ #ifdef CLIENT_SSL_VERIFY_SERVER_CERT
750
+ rb_const_set(cMysql2Client, rb_intern("SSL_VERIFY_SERVER_CERT"),
751
+ INT2NUM(CLIENT_SSL_VERIFY_SERVER_CERT));
752
+ #endif
753
+
754
+ #ifdef CLIENT_REMEMBER_OPTIONS
755
+ rb_const_set(cMysql2Client, rb_intern("REMEMBER_OPTIONS"),
756
+ INT2NUM(CLIENT_REMEMBER_OPTIONS));
757
+ #endif
758
+
759
+ #ifdef CLIENT_ALL_FLAGS
760
+ rb_const_set(cMysql2Client, rb_intern("ALL_FLAGS"),
761
+ INT2NUM(CLIENT_ALL_FLAGS));
762
+ #endif
763
+
764
+ #ifdef CLIENT_BASIC_FLAGS
765
+ rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
766
+ INT2NUM(CLIENT_BASIC_FLAGS));
767
+ #endif
768
+ }