mysql2 0.3.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -151
- data/LICENSE +21 -0
- data/README.md +634 -0
- data/examples/eventmachine.rb +1 -3
- data/examples/threaded.rb +5 -9
- data/ext/mysql2/client.c +1154 -342
- data/ext/mysql2/client.h +20 -33
- data/ext/mysql2/extconf.rb +229 -37
- data/ext/mysql2/infile.c +122 -0
- data/ext/mysql2/infile.h +1 -0
- data/ext/mysql2/mysql2_ext.c +3 -1
- data/ext/mysql2/mysql2_ext.h +18 -16
- data/ext/mysql2/mysql_enc_name_to_ruby.h +168 -0
- data/ext/mysql2/mysql_enc_to_ruby.h +259 -0
- data/ext/mysql2/result.c +708 -191
- data/ext/mysql2/result.h +15 -6
- data/ext/mysql2/statement.c +602 -0
- data/ext/mysql2/statement.h +17 -0
- data/ext/mysql2/wait_for_single_fd.h +37 -0
- data/lib/mysql2.rb +69 -7
- data/lib/mysql2/client.rb +126 -211
- data/lib/mysql2/console.rb +5 -0
- data/lib/mysql2/em.rb +24 -8
- data/lib/mysql2/error.rb +93 -8
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +11 -0
- data/lib/mysql2/version.rb +2 -2
- data/spec/configuration.yml.example +11 -0
- data/spec/em/em_spec.rb +101 -15
- data/spec/my.cnf.example +9 -0
- data/spec/mysql2/client_spec.rb +874 -232
- data/spec/mysql2/error_spec.rb +55 -46
- data/spec/mysql2/result_spec.rb +306 -154
- data/spec/mysql2/statement_spec.rb +712 -0
- data/spec/spec_helper.rb +103 -57
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/spec/test_data +1 -0
- data/support/5072E1F5.asc +432 -0
- data/support/libmysql.def +219 -0
- data/support/mysql_enc_to_ruby.rb +81 -0
- data/support/ruby_enc_to_mysql.rb +61 -0
- metadata +82 -188
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rvmrc +0 -1
- data/Gemfile +0 -3
- data/MIT-LICENSE +0 -20
- data/README.rdoc +0 -257
- data/Rakefile +0 -5
- data/benchmark/active_record.rb +0 -51
- data/benchmark/active_record_threaded.rb +0 -42
- data/benchmark/allocations.rb +0 -33
- data/benchmark/escape.rb +0 -36
- data/benchmark/query_with_mysql_casting.rb +0 -80
- data/benchmark/query_without_mysql_casting.rb +0 -47
- data/benchmark/sequel.rb +0 -37
- data/benchmark/setup_db.rb +0 -119
- data/benchmark/threaded.rb +0 -44
- data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +0 -64
- data/lib/active_record/fiber_patches.rb +0 -104
- data/lib/mysql2/em_fiber.rb +0 -31
- data/mysql2.gemspec +0 -32
- data/spec/em/em_fiber_spec.rb +0 -22
- data/tasks/benchmarks.rake +0 -20
- data/tasks/compile.rake +0 -71
- data/tasks/rspec.rake +0 -16
- data/tasks/vendor_mysql.rake +0 -40
data/examples/eventmachine.rb
CHANGED
data/examples/threaded.rb
CHANGED
@@ -1,20 +1,16 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
$LOAD_PATH.unshift 'lib'
|
4
2
|
require 'mysql2'
|
5
3
|
require 'timeout'
|
6
4
|
|
7
|
-
threads = []
|
8
5
|
# Should never exceed worst case 3.5 secs across all 20 threads
|
9
6
|
Timeout.timeout(3.5) do
|
10
|
-
20
|
11
|
-
|
7
|
+
Array.new(20) do
|
8
|
+
Thread.new do
|
12
9
|
overhead = rand(3)
|
13
10
|
puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
|
14
11
|
# 3 second overhead per query
|
15
|
-
Mysql2::Client.new(:
|
12
|
+
Mysql2::Client.new(host: "localhost", username: "root").query("SELECT sleep(#{overhead}) as result")
|
16
13
|
puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
|
17
14
|
end
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
15
|
+
end.each(&:join)
|
16
|
+
end
|
data/ext/mysql2/client.c
CHANGED
@@ -1,28 +1,74 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
|
-
|
2
|
+
|
3
|
+
#include <time.h>
|
3
4
|
#include <errno.h>
|
5
|
+
#ifndef _WIN32
|
6
|
+
#include <sys/types.h>
|
7
|
+
#include <sys/socket.h>
|
8
|
+
#endif
|
9
|
+
#ifndef _MSC_VER
|
10
|
+
#include <unistd.h>
|
11
|
+
#endif
|
12
|
+
#include <fcntl.h>
|
13
|
+
#include "wait_for_single_fd.h"
|
14
|
+
|
15
|
+
#include "mysql_enc_name_to_ruby.h"
|
4
16
|
|
5
17
|
VALUE cMysql2Client;
|
6
|
-
extern VALUE mMysql2, cMysql2Error;
|
7
|
-
static VALUE
|
8
|
-
static VALUE
|
9
|
-
static ID intern_merge,
|
18
|
+
extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
|
19
|
+
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
20
|
+
static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
|
21
|
+
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
|
22
|
+
|
23
|
+
#define REQUIRE_INITIALIZED(wrapper) \
|
24
|
+
if (!wrapper->initialized) { \
|
25
|
+
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
26
|
+
}
|
27
|
+
|
28
|
+
#if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
|
29
|
+
#define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
|
30
|
+
#elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
|
31
|
+
#define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
|
32
|
+
#endif
|
10
33
|
|
11
|
-
#define
|
12
|
-
|
13
|
-
|
34
|
+
#define REQUIRE_CONNECTED(wrapper) \
|
35
|
+
REQUIRE_INITIALIZED(wrapper) \
|
36
|
+
if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
|
37
|
+
rb_raise(cMysql2Error, "MySQL client is not connected"); \
|
14
38
|
}
|
15
39
|
|
16
|
-
#define
|
17
|
-
wrapper
|
40
|
+
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
41
|
+
REQUIRE_INITIALIZED(wrapper) \
|
42
|
+
if (CONNECTED(wrapper)) { \
|
43
|
+
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
44
|
+
}
|
18
45
|
|
19
|
-
|
20
|
-
|
21
|
-
|
46
|
+
/*
|
47
|
+
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
48
|
+
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
49
|
+
* linking against the server itself
|
50
|
+
*/
|
51
|
+
#if defined(MARIADB_CLIENT_VERSION_STR)
|
52
|
+
#define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
|
53
|
+
#elif defined(LIBMYSQL_VERSION)
|
54
|
+
#define MYSQL_LINK_VERSION LIBMYSQL_VERSION
|
55
|
+
#else
|
56
|
+
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
57
|
+
#endif
|
58
|
+
|
59
|
+
/*
|
60
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
61
|
+
*/
|
62
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
63
|
+
#define SSL_MODE_DISABLED 1
|
64
|
+
#define SSL_MODE_REQUIRED 3
|
65
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
66
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
67
|
+
#endif
|
22
68
|
|
23
69
|
/*
|
24
70
|
* used to pass all arguments to mysql_real_connect while inside
|
25
|
-
*
|
71
|
+
* rb_thread_call_without_gvl
|
26
72
|
*/
|
27
73
|
struct nogvl_connect_args {
|
28
74
|
MYSQL *mysql;
|
@@ -37,13 +83,61 @@ struct nogvl_connect_args {
|
|
37
83
|
|
38
84
|
/*
|
39
85
|
* used to pass all arguments to mysql_send_query while inside
|
40
|
-
*
|
86
|
+
* rb_thread_call_without_gvl
|
41
87
|
*/
|
42
88
|
struct nogvl_send_query_args {
|
43
89
|
MYSQL *mysql;
|
44
90
|
VALUE sql;
|
91
|
+
const char *sql_ptr;
|
92
|
+
long sql_len;
|
93
|
+
mysql_client_wrapper *wrapper;
|
45
94
|
};
|
46
95
|
|
96
|
+
/*
|
97
|
+
* used to pass all arguments to mysql_select_db while inside
|
98
|
+
* rb_thread_call_without_gvl
|
99
|
+
*/
|
100
|
+
struct nogvl_select_db_args {
|
101
|
+
MYSQL *mysql;
|
102
|
+
char *db;
|
103
|
+
};
|
104
|
+
|
105
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
106
|
+
unsigned long version = mysql_get_client_version();
|
107
|
+
|
108
|
+
if (version < 50703) {
|
109
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
110
|
+
return Qnil;
|
111
|
+
}
|
112
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
113
|
+
GET_CLIENT(self);
|
114
|
+
int val = NUM2INT( setting );
|
115
|
+
if (version >= 50703 && version < 50711) {
|
116
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
117
|
+
my_bool b = ( val == SSL_MODE_REQUIRED );
|
118
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
119
|
+
return INT2NUM(result);
|
120
|
+
} else {
|
121
|
+
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
122
|
+
return Qnil;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
#endif
|
126
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
127
|
+
GET_CLIENT(self);
|
128
|
+
int val = NUM2INT( setting );
|
129
|
+
|
130
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
131
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
132
|
+
}
|
133
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
134
|
+
|
135
|
+
return INT2NUM(result);
|
136
|
+
#endif
|
137
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
138
|
+
return Qnil;
|
139
|
+
#endif
|
140
|
+
}
|
47
141
|
/*
|
48
142
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
49
143
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -70,94 +164,148 @@ static void rb_mysql_client_mark(void * wrapper) {
|
|
70
164
|
mysql_client_wrapper * w = wrapper;
|
71
165
|
if (w) {
|
72
166
|
rb_gc_mark(w->encoding);
|
167
|
+
rb_gc_mark(w->active_thread);
|
73
168
|
}
|
74
169
|
}
|
75
170
|
|
76
171
|
static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
77
172
|
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
|
78
173
|
VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
|
79
|
-
|
80
|
-
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
81
|
-
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
174
|
+
VALUE e;
|
82
175
|
|
83
|
-
rb_enc_associate(rb_error_msg,
|
84
|
-
rb_enc_associate(rb_sql_state,
|
85
|
-
if (default_internal_enc) {
|
86
|
-
rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
|
87
|
-
rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
|
88
|
-
}
|
89
|
-
#endif
|
176
|
+
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
177
|
+
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
90
178
|
|
91
|
-
|
92
|
-
|
93
|
-
|
179
|
+
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
180
|
+
rb_error_msg,
|
181
|
+
LONG2FIX(wrapper->server_version),
|
182
|
+
UINT2NUM(mysql_errno(wrapper->client)),
|
183
|
+
rb_sql_state);
|
94
184
|
rb_exc_raise(e);
|
95
|
-
return Qnil;
|
96
185
|
}
|
97
186
|
|
98
|
-
static
|
187
|
+
static void *nogvl_init(void *ptr) {
|
99
188
|
MYSQL *client;
|
189
|
+
mysql_client_wrapper *wrapper = ptr;
|
100
190
|
|
101
191
|
/* may initialize embedded server and read /etc/services off disk */
|
102
|
-
client = mysql_init(
|
103
|
-
|
192
|
+
client = mysql_init(wrapper->client);
|
193
|
+
|
194
|
+
if (client) mysql2_set_local_infile(client, wrapper);
|
195
|
+
|
196
|
+
return (void*)(client ? Qtrue : Qfalse);
|
104
197
|
}
|
105
198
|
|
106
|
-
static
|
199
|
+
static void *nogvl_connect(void *ptr) {
|
107
200
|
struct nogvl_connect_args *args = ptr;
|
108
201
|
MYSQL *client;
|
109
202
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
args->client_flag);
|
115
|
-
} while (! client && errno == EINTR && (errno = 0) == 0);
|
203
|
+
client = mysql_real_connect(args->mysql, args->host,
|
204
|
+
args->user, args->passwd,
|
205
|
+
args->db, args->port, args->unix_socket,
|
206
|
+
args->client_flag);
|
116
207
|
|
117
|
-
return client ? Qtrue : Qfalse;
|
208
|
+
return (void *)(client ? Qtrue : Qfalse);
|
118
209
|
}
|
119
210
|
|
120
|
-
static VALUE nogvl_close(void *ptr) {
|
121
|
-
mysql_client_wrapper *wrapper;
|
122
211
|
#ifndef _WIN32
|
123
|
-
|
212
|
+
/*
|
213
|
+
* Redirect clientfd to /dev/null for mysql_close and SSL_close to write,
|
214
|
+
* shutdown, and close. The hack is needed to prevent shutdown() from breaking
|
215
|
+
* a socket that may be in use by the parent or other processes after fork.
|
216
|
+
*
|
217
|
+
* /dev/null is used to absorb writes; previously a dummy socket was used, but
|
218
|
+
* it could not abosrb writes and caused openssl to go into an infinite loop.
|
219
|
+
*
|
220
|
+
* Returns Qtrue or Qfalse (success or failure)
|
221
|
+
*
|
222
|
+
* Note: if this function is needed on Windows, use "nul" instead of "/dev/null"
|
223
|
+
*/
|
224
|
+
static VALUE invalidate_fd(int clientfd)
|
225
|
+
{
|
226
|
+
#ifdef O_CLOEXEC
|
227
|
+
/* Atomically set CLOEXEC on the new FD in case another thread forks */
|
228
|
+
int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
229
|
+
#else
|
230
|
+
/* Well we don't have O_CLOEXEC, trigger the fallback code below */
|
231
|
+
int sockfd = -1;
|
124
232
|
#endif
|
125
|
-
wrapper = ptr;
|
126
|
-
if (!wrapper->closed) {
|
127
|
-
wrapper->closed = 1;
|
128
233
|
|
129
|
-
|
130
|
-
|
131
|
-
*
|
132
|
-
*
|
133
|
-
*
|
134
|
-
*
|
135
|
-
* if the socket is dead we have no chance of blocking,
|
136
|
-
* so ignore any potential fcntl errors since they don't matter
|
234
|
+
if (sockfd < 0) {
|
235
|
+
/* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
|
236
|
+
* compile time, but isn't available at run-time. So we'll just be quick
|
237
|
+
* about setting FD_CLOEXEC now.
|
137
238
|
*/
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
239
|
+
int flags;
|
240
|
+
sockfd = open("/dev/null", O_RDWR);
|
241
|
+
flags = fcntl(sockfd, F_GETFD);
|
242
|
+
/* Do the flags dance in case there are more defined flags in the future */
|
243
|
+
if (flags != -1) {
|
244
|
+
flags |= FD_CLOEXEC;
|
245
|
+
fcntl(sockfd, F_SETFD, flags);
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
if (sockfd < 0) {
|
250
|
+
/* Cannot raise here, because one or both of the following may be true:
|
251
|
+
* a) we have no GVL (in C Ruby)
|
252
|
+
* b) are running as a GC finalizer
|
253
|
+
*/
|
254
|
+
return Qfalse;
|
255
|
+
}
|
256
|
+
|
257
|
+
dup2(sockfd, clientfd);
|
258
|
+
close(sockfd);
|
147
259
|
|
260
|
+
return Qtrue;
|
261
|
+
}
|
262
|
+
#endif /* _WIN32 */
|
263
|
+
|
264
|
+
static void *nogvl_close(void *ptr) {
|
265
|
+
mysql_client_wrapper *wrapper = ptr;
|
266
|
+
|
267
|
+
if (!wrapper->closed) {
|
148
268
|
mysql_close(wrapper->client);
|
149
|
-
|
269
|
+
wrapper->closed = 1;
|
270
|
+
wrapper->reconnect_enabled = 0;
|
271
|
+
wrapper->active_thread = Qnil;
|
150
272
|
}
|
151
273
|
|
152
|
-
return
|
274
|
+
return NULL;
|
153
275
|
}
|
154
276
|
|
155
|
-
|
156
|
-
|
277
|
+
/* this is called during GC */
|
278
|
+
static void rb_mysql_client_free(void *ptr) {
|
279
|
+
mysql_client_wrapper *wrapper = ptr;
|
280
|
+
decr_mysql2_client(wrapper);
|
281
|
+
}
|
282
|
+
|
283
|
+
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
284
|
+
{
|
285
|
+
wrapper->refcount--;
|
157
286
|
|
158
|
-
|
287
|
+
if (wrapper->refcount == 0) {
|
288
|
+
#ifndef _WIN32
|
289
|
+
if (CONNECTED(wrapper) && !wrapper->automatic_close) {
|
290
|
+
/* The client is being garbage collected while connected. Prevent
|
291
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
292
|
+
* the socket by invalidating it. invalidate_fd() will drop this
|
293
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
294
|
+
* would render the underlying connection unusable, interrupting other
|
295
|
+
* processes which share this object across a fork().
|
296
|
+
*/
|
297
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
298
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
299
|
+
close(wrapper->client->net.fd);
|
300
|
+
}
|
301
|
+
wrapper->client->net.fd = -1;
|
302
|
+
}
|
303
|
+
#endif
|
159
304
|
|
160
|
-
|
305
|
+
nogvl_close(wrapper);
|
306
|
+
xfree(wrapper->client);
|
307
|
+
xfree(wrapper);
|
308
|
+
}
|
161
309
|
}
|
162
310
|
|
163
311
|
static VALUE allocate(VALUE klass) {
|
@@ -165,13 +313,27 @@ static VALUE allocate(VALUE klass) {
|
|
165
313
|
mysql_client_wrapper * wrapper;
|
166
314
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
167
315
|
wrapper->encoding = Qnil;
|
168
|
-
wrapper->
|
169
|
-
wrapper->
|
316
|
+
wrapper->active_thread = Qnil;
|
317
|
+
wrapper->automatic_close = 1;
|
318
|
+
wrapper->server_version = 0;
|
319
|
+
wrapper->reconnect_enabled = 0;
|
320
|
+
wrapper->connect_timeout = 0;
|
321
|
+
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
322
|
+
wrapper->refcount = 1;
|
323
|
+
wrapper->closed = 0;
|
170
324
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
325
|
+
|
171
326
|
return obj;
|
172
327
|
}
|
173
328
|
|
174
|
-
|
329
|
+
/* call-seq:
|
330
|
+
* Mysql2::Client.escape(string)
|
331
|
+
*
|
332
|
+
* Escape +string+ so that it may be used in a SQL statement.
|
333
|
+
* Note that this escape method is not connection encoding aware.
|
334
|
+
* If you need encoding support use Mysql2::Client#escape instead.
|
335
|
+
*/
|
336
|
+
static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
175
337
|
unsigned char *newStr;
|
176
338
|
VALUE rb_str;
|
177
339
|
unsigned long newLen, oldLen;
|
@@ -181,72 +343,180 @@ static VALUE rb_mysql_client_escape(VALUE klass, VALUE str) {
|
|
181
343
|
oldLen = RSTRING_LEN(str);
|
182
344
|
newStr = xmalloc(oldLen*2+1);
|
183
345
|
|
184
|
-
newLen = mysql_escape_string((char *)newStr,
|
346
|
+
newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
|
185
347
|
if (newLen == oldLen) {
|
186
|
-
|
348
|
+
/* no need to return a new ruby string if nothing changed */
|
187
349
|
xfree(newStr);
|
188
350
|
return str;
|
189
351
|
} else {
|
190
352
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
191
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
192
353
|
rb_enc_copy(rb_str, str);
|
193
|
-
#endif
|
194
354
|
xfree(newStr);
|
195
355
|
return rb_str;
|
196
356
|
}
|
197
357
|
}
|
198
358
|
|
199
|
-
static VALUE
|
359
|
+
static VALUE rb_mysql_client_warning_count(VALUE self) {
|
360
|
+
unsigned int warning_count;
|
361
|
+
GET_CLIENT(self);
|
362
|
+
|
363
|
+
warning_count = mysql_warning_count(wrapper->client);
|
364
|
+
|
365
|
+
return UINT2NUM(warning_count);
|
366
|
+
}
|
367
|
+
|
368
|
+
static VALUE rb_mysql_info(VALUE self) {
|
369
|
+
const char *info;
|
370
|
+
VALUE rb_str;
|
371
|
+
GET_CLIENT(self);
|
372
|
+
|
373
|
+
info = mysql_info(wrapper->client);
|
374
|
+
|
375
|
+
if (info == NULL) {
|
376
|
+
return Qnil;
|
377
|
+
}
|
378
|
+
|
379
|
+
rb_str = rb_str_new2(info);
|
380
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
381
|
+
|
382
|
+
return rb_str;
|
383
|
+
}
|
384
|
+
|
385
|
+
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
386
|
+
{
|
387
|
+
const char *cipher;
|
388
|
+
VALUE rb_str;
|
389
|
+
GET_CLIENT(self);
|
390
|
+
|
391
|
+
cipher = mysql_get_ssl_cipher(wrapper->client);
|
392
|
+
|
393
|
+
if (cipher == NULL) {
|
394
|
+
return Qnil;
|
395
|
+
}
|
396
|
+
|
397
|
+
rb_str = rb_str_new2(cipher);
|
398
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
399
|
+
|
400
|
+
return rb_str;
|
401
|
+
}
|
402
|
+
|
403
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
404
|
+
static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
|
405
|
+
{
|
406
|
+
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
|
407
|
+
rb_encoding *enc = rb_to_encoding(wrapper->encoding);
|
408
|
+
key = rb_str_export_to_enc(key, enc);
|
409
|
+
value = rb_str_export_to_enc(value, enc);
|
410
|
+
|
411
|
+
mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
|
412
|
+
return ST_CONTINUE;
|
413
|
+
}
|
414
|
+
#endif
|
415
|
+
|
416
|
+
static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
|
200
417
|
struct nogvl_connect_args args;
|
418
|
+
time_t start_time, end_time, elapsed_time, connect_timeout;
|
419
|
+
VALUE rv;
|
201
420
|
GET_CLIENT(self);
|
202
421
|
|
203
|
-
args.host
|
204
|
-
args.unix_socket = NIL_P(socket)
|
205
|
-
args.port
|
206
|
-
args.user
|
207
|
-
args.passwd
|
208
|
-
args.db
|
209
|
-
args.mysql
|
422
|
+
args.host = NIL_P(host) ? NULL : StringValueCStr(host);
|
423
|
+
args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
|
424
|
+
args.port = NIL_P(port) ? 0 : NUM2INT(port);
|
425
|
+
args.user = NIL_P(user) ? NULL : StringValueCStr(user);
|
426
|
+
args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
|
427
|
+
args.db = NIL_P(database) ? NULL : StringValueCStr(database);
|
428
|
+
args.mysql = wrapper->client;
|
210
429
|
args.client_flag = NUM2ULONG(flags);
|
211
430
|
|
212
|
-
|
213
|
-
|
214
|
-
|
431
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
432
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
|
433
|
+
rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
|
434
|
+
#endif
|
435
|
+
|
436
|
+
if (wrapper->connect_timeout)
|
437
|
+
time(&start_time);
|
438
|
+
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
439
|
+
if (rv == Qfalse) {
|
440
|
+
while (rv == Qfalse && errno == EINTR) {
|
441
|
+
if (wrapper->connect_timeout) {
|
442
|
+
time(&end_time);
|
443
|
+
/* avoid long connect timeout from system time changes */
|
444
|
+
if (end_time < start_time)
|
445
|
+
start_time = end_time;
|
446
|
+
elapsed_time = end_time - start_time;
|
447
|
+
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
448
|
+
if (elapsed_time > 0)
|
449
|
+
elapsed_time--;
|
450
|
+
if (elapsed_time >= (time_t)wrapper->connect_timeout)
|
451
|
+
break;
|
452
|
+
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
453
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
454
|
+
}
|
455
|
+
errno = 0;
|
456
|
+
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
457
|
+
}
|
458
|
+
/* restore the connect timeout for reconnecting */
|
459
|
+
if (wrapper->connect_timeout)
|
460
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
461
|
+
if (rv == Qfalse)
|
462
|
+
rb_raise_mysql2_error(wrapper);
|
215
463
|
}
|
216
464
|
|
465
|
+
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
217
466
|
return self;
|
218
467
|
}
|
219
468
|
|
220
469
|
/*
|
221
|
-
* Immediately disconnect from the server
|
470
|
+
* Immediately disconnect from the server; normally the garbage collector
|
222
471
|
* will disconnect automatically when a connection is no longer needed.
|
223
472
|
* Explicitly closing this will free up server resources sooner than waiting
|
224
473
|
* for the garbage collector.
|
474
|
+
*
|
475
|
+
* @return [nil]
|
225
476
|
*/
|
226
477
|
static VALUE rb_mysql_client_close(VALUE self) {
|
227
478
|
GET_CLIENT(self);
|
228
479
|
|
229
|
-
if (
|
230
|
-
|
480
|
+
if (wrapper->client) {
|
481
|
+
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
231
482
|
}
|
232
483
|
|
233
484
|
return Qnil;
|
234
485
|
}
|
235
486
|
|
487
|
+
/* call-seq:
|
488
|
+
* client.closed?
|
489
|
+
*
|
490
|
+
* @return [Boolean]
|
491
|
+
*/
|
492
|
+
static VALUE rb_mysql_client_closed(VALUE self) {
|
493
|
+
GET_CLIENT(self);
|
494
|
+
return CONNECTED(wrapper) ? Qfalse : Qtrue;
|
495
|
+
}
|
496
|
+
|
236
497
|
/*
|
237
498
|
* mysql_send_query is unlikely to block since most queries are small
|
238
499
|
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
239
500
|
* INSERTs will cause the process to block
|
240
501
|
*/
|
241
|
-
static
|
502
|
+
static void *nogvl_send_query(void *ptr) {
|
242
503
|
struct nogvl_send_query_args *args = ptr;
|
243
504
|
int rv;
|
244
|
-
const char *sql = StringValuePtr(args->sql);
|
245
|
-
long sql_len = RSTRING_LEN(args->sql);
|
246
505
|
|
247
|
-
rv = mysql_send_query(args->mysql,
|
506
|
+
rv = mysql_send_query(args->mysql, args->sql_ptr, args->sql_len);
|
248
507
|
|
249
|
-
return rv == 0 ? Qtrue : Qfalse;
|
508
|
+
return (void*)(rv == 0 ? Qtrue : Qfalse);
|
509
|
+
}
|
510
|
+
|
511
|
+
static VALUE do_send_query(void *args) {
|
512
|
+
struct nogvl_send_query_args *query_args = args;
|
513
|
+
mysql_client_wrapper *wrapper = query_args->wrapper;
|
514
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
515
|
+
/* an error occurred, we're not active anymore */
|
516
|
+
wrapper->active_thread = Qnil;
|
517
|
+
rb_raise_mysql2_error(wrapper);
|
518
|
+
}
|
519
|
+
return Qnil;
|
250
520
|
}
|
251
521
|
|
252
522
|
/*
|
@@ -254,117 +524,130 @@ static VALUE nogvl_send_query(void *ptr) {
|
|
254
524
|
* response can overflow the socket buffers and cause us to eventually
|
255
525
|
* block while calling mysql_read_query_result
|
256
526
|
*/
|
257
|
-
static
|
527
|
+
static void *nogvl_read_query_result(void *ptr) {
|
258
528
|
MYSQL * client = ptr;
|
259
529
|
my_bool res = mysql_read_query_result(client);
|
260
530
|
|
261
|
-
return res == 0 ? Qtrue : Qfalse;
|
531
|
+
return (void *)(res == 0 ? Qtrue : Qfalse);
|
532
|
+
}
|
533
|
+
|
534
|
+
static void *nogvl_do_result(void *ptr, char use_result) {
|
535
|
+
mysql_client_wrapper *wrapper = ptr;
|
536
|
+
MYSQL_RES *result;
|
537
|
+
|
538
|
+
if (use_result) {
|
539
|
+
result = mysql_use_result(wrapper->client);
|
540
|
+
} else {
|
541
|
+
result = mysql_store_result(wrapper->client);
|
542
|
+
}
|
543
|
+
|
544
|
+
/* once our result is stored off, this connection is
|
545
|
+
ready for another command to be issued */
|
546
|
+
wrapper->active_thread = Qnil;
|
547
|
+
|
548
|
+
return result;
|
262
549
|
}
|
263
550
|
|
264
551
|
/* mysql_store_result may (unlikely) read rows off the socket */
|
265
|
-
static
|
266
|
-
|
267
|
-
return (VALUE)mysql_store_result(client);
|
552
|
+
static void *nogvl_store_result(void *ptr) {
|
553
|
+
return nogvl_do_result(ptr, 0);
|
268
554
|
}
|
269
555
|
|
556
|
+
static void *nogvl_use_result(void *ptr) {
|
557
|
+
return nogvl_do_result(ptr, 1);
|
558
|
+
}
|
559
|
+
|
560
|
+
/* call-seq:
|
561
|
+
* client.async_result
|
562
|
+
*
|
563
|
+
* Returns the result for the last async issued query.
|
564
|
+
*/
|
270
565
|
static VALUE rb_mysql_client_async_result(VALUE self) {
|
271
566
|
MYSQL_RES * result;
|
272
567
|
VALUE resultObj;
|
273
|
-
|
274
|
-
mysql2_result_wrapper * result_wrapper;
|
275
|
-
#endif
|
568
|
+
VALUE current, is_streaming;
|
276
569
|
GET_CLIENT(self);
|
277
570
|
|
278
|
-
|
279
|
-
if (
|
280
|
-
|
281
|
-
MARK_CONN_INACTIVE(self);
|
282
|
-
return rb_raise_mysql2_error(wrapper);
|
283
|
-
}
|
571
|
+
/* if we're not waiting on a result, do nothing */
|
572
|
+
if (NIL_P(wrapper->active_thread))
|
573
|
+
return Qnil;
|
284
574
|
|
285
|
-
|
575
|
+
REQUIRE_CONNECTED(wrapper);
|
576
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
577
|
+
/* an error occurred, mark this connection inactive */
|
578
|
+
wrapper->active_thread = Qnil;
|
579
|
+
rb_raise_mysql2_error(wrapper);
|
580
|
+
}
|
286
581
|
|
287
|
-
|
288
|
-
|
582
|
+
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
583
|
+
if (is_streaming == Qtrue) {
|
584
|
+
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
|
585
|
+
} else {
|
586
|
+
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
587
|
+
}
|
289
588
|
|
290
589
|
if (result == NULL) {
|
291
|
-
if (
|
590
|
+
if (mysql_errno(wrapper->client) != 0) {
|
591
|
+
wrapper->active_thread = Qnil;
|
292
592
|
rb_raise_mysql2_error(wrapper);
|
293
593
|
}
|
594
|
+
/* no data and no error, so query was not a SELECT */
|
294
595
|
return Qnil;
|
295
596
|
}
|
296
597
|
|
297
|
-
|
298
|
-
|
299
|
-
|
598
|
+
// Duplicate the options hash and put the copy in the Result object
|
599
|
+
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
600
|
+
(void)RB_GC_GUARD(current);
|
601
|
+
Check_Type(current, T_HASH);
|
602
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
603
|
+
|
604
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
300
605
|
|
301
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
302
|
-
GetMysql2Result(resultObj, result_wrapper);
|
303
|
-
result_wrapper->encoding = wrapper->encoding;
|
304
|
-
#endif
|
305
606
|
return resultObj;
|
306
607
|
}
|
307
608
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
VALUE opts, defaults, read_timeout;
|
314
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
315
|
-
rb_encoding *conn_enc;
|
316
|
-
#endif
|
317
|
-
struct timeval tv;
|
318
|
-
struct timeval* tvp;
|
319
|
-
long int sec;
|
320
|
-
VALUE result;
|
321
|
-
GET_CLIENT(self);
|
322
|
-
|
323
|
-
REQUIRE_OPEN_DB(wrapper);
|
324
|
-
args.mysql = wrapper->client;
|
609
|
+
#ifndef _WIN32
|
610
|
+
struct async_query_args {
|
611
|
+
int fd;
|
612
|
+
VALUE self;
|
613
|
+
};
|
325
614
|
|
326
|
-
|
327
|
-
|
328
|
-
// mark this connection active
|
329
|
-
wrapper->active = 1;
|
330
|
-
} else {
|
331
|
-
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
332
|
-
}
|
615
|
+
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
616
|
+
GET_CLIENT(self);
|
333
617
|
|
334
|
-
|
335
|
-
if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
|
336
|
-
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
337
|
-
rb_iv_set(self, "@query_options", opts);
|
618
|
+
wrapper->active_thread = Qnil;
|
338
619
|
|
339
|
-
|
340
|
-
|
620
|
+
/* Invalidate the MySQL socket to prevent further communication.
|
621
|
+
* The GC will come along later and call mysql_close to free it.
|
622
|
+
*/
|
623
|
+
if (CONNECTED(wrapper)) {
|
624
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
625
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
626
|
+
close(wrapper->client->net.fd);
|
341
627
|
}
|
342
|
-
|
343
|
-
opts = defaults;
|
628
|
+
wrapper->client->net.fd = -1;
|
344
629
|
}
|
345
630
|
|
346
|
-
|
347
|
-
|
348
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
349
|
-
// ensure the string is in the encoding the connection is expecting
|
350
|
-
args.sql = rb_str_export_to_enc(args.sql, conn_enc);
|
351
|
-
#endif
|
631
|
+
rb_exc_raise(error);
|
632
|
+
}
|
352
633
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
634
|
+
static VALUE do_query(void *args) {
|
635
|
+
struct async_query_args *async_args = args;
|
636
|
+
struct timeval tv;
|
637
|
+
struct timeval *tvp;
|
638
|
+
long int sec;
|
639
|
+
int retval;
|
640
|
+
VALUE read_timeout;
|
358
641
|
|
359
|
-
read_timeout = rb_iv_get(self, "@read_timeout");
|
642
|
+
read_timeout = rb_iv_get(async_args->self, "@read_timeout");
|
360
643
|
|
361
644
|
tvp = NULL;
|
362
645
|
if (!NIL_P(read_timeout)) {
|
363
646
|
Check_Type(read_timeout, T_FIXNUM);
|
364
647
|
tvp = &tv;
|
365
648
|
sec = FIX2INT(read_timeout);
|
366
|
-
|
367
|
-
|
649
|
+
/* TODO: support partial seconds?
|
650
|
+
also, this check is here for sanity, we also check up in Ruby */
|
368
651
|
if (sec >= 0) {
|
369
652
|
tvp->tv_sec = sec;
|
370
653
|
} else {
|
@@ -373,176 +656,372 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
373
656
|
tvp->tv_usec = 0;
|
374
657
|
}
|
375
658
|
|
376
|
-
|
377
|
-
|
378
|
-
// http://github.com/datamapper/do
|
379
|
-
fd = wrapper->client->net.fd;
|
380
|
-
for(;;) {
|
381
|
-
int fd_set_fd = fd;
|
659
|
+
for(;;) {
|
660
|
+
retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
|
382
661
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
int r = WSADuplicateSocket(fd, GetCurrentProcessId(), &wsa_pi);
|
387
|
-
SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
|
388
|
-
// create the CRT fd so ruby can get back to the SOCKET
|
389
|
-
fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
|
390
|
-
#endif
|
662
|
+
if (retval == 0) {
|
663
|
+
rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
|
664
|
+
}
|
391
665
|
|
392
|
-
|
393
|
-
|
666
|
+
if (retval < 0) {
|
667
|
+
rb_sys_fail(0);
|
668
|
+
}
|
394
669
|
|
395
|
-
|
670
|
+
if (retval > 0) {
|
671
|
+
break;
|
672
|
+
}
|
673
|
+
}
|
396
674
|
|
397
|
-
|
398
|
-
|
399
|
-
_close(fd_set_fd);
|
400
|
-
// cleanup the duplicated SOCKET
|
401
|
-
closesocket(s);
|
675
|
+
return Qnil;
|
676
|
+
}
|
402
677
|
#endif
|
403
678
|
|
404
|
-
|
405
|
-
|
406
|
-
}
|
407
|
-
|
408
|
-
if (retval < 0) {
|
409
|
-
rb_sys_fail(0);
|
410
|
-
}
|
679
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
680
|
+
GET_CLIENT(self);
|
411
681
|
|
412
|
-
|
413
|
-
|
682
|
+
/* Check if execution terminated while result was still being read. */
|
683
|
+
if (!NIL_P(wrapper->active_thread)) {
|
684
|
+
if (CONNECTED(wrapper)) {
|
685
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
686
|
+
#ifndef _WIN32
|
687
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
688
|
+
rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
|
689
|
+
close(wrapper->client->net.fd);
|
414
690
|
}
|
691
|
+
#else
|
692
|
+
close(wrapper->client->net.fd);
|
693
|
+
#endif
|
694
|
+
wrapper->client->net.fd = -1;
|
415
695
|
}
|
696
|
+
/* Skip mysql client check performed before command execution. */
|
697
|
+
wrapper->client->status = MYSQL_STATUS_READY;
|
698
|
+
wrapper->active_thread = Qnil;
|
699
|
+
}
|
416
700
|
|
417
|
-
|
701
|
+
return Qnil;
|
702
|
+
}
|
418
703
|
|
419
|
-
|
704
|
+
void rb_mysql_client_set_active_thread(VALUE self) {
|
705
|
+
VALUE thread_current = rb_thread_current();
|
706
|
+
GET_CLIENT(self);
|
707
|
+
|
708
|
+
// see if this connection is still waiting on a result from a previous query
|
709
|
+
if (NIL_P(wrapper->active_thread)) {
|
710
|
+
// mark this connection active
|
711
|
+
wrapper->active_thread = thread_current;
|
712
|
+
} else if (wrapper->active_thread == thread_current) {
|
713
|
+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
420
714
|
} else {
|
715
|
+
VALUE inspect = rb_inspect(wrapper->active_thread);
|
716
|
+
const char *thr = StringValueCStr(inspect);
|
717
|
+
|
718
|
+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
719
|
+
}
|
720
|
+
}
|
721
|
+
|
722
|
+
/* call-seq:
|
723
|
+
* client.abandon_results!
|
724
|
+
*
|
725
|
+
* When using MULTI_STATEMENTS support, calling this will throw
|
726
|
+
* away any unprocessed results as fast as it can in order to
|
727
|
+
* put the connection back into a state where queries can be issued
|
728
|
+
* again.
|
729
|
+
*/
|
730
|
+
static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
731
|
+
MYSQL_RES *result;
|
732
|
+
int ret;
|
733
|
+
|
734
|
+
GET_CLIENT(self);
|
735
|
+
|
736
|
+
while (mysql_more_results(wrapper->client) == 1) {
|
737
|
+
ret = mysql_next_result(wrapper->client);
|
738
|
+
if (ret > 0) {
|
739
|
+
rb_raise_mysql2_error(wrapper);
|
740
|
+
}
|
741
|
+
|
742
|
+
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
743
|
+
|
744
|
+
if (result != NULL) {
|
745
|
+
mysql_free_result(result);
|
746
|
+
}
|
747
|
+
}
|
748
|
+
|
749
|
+
return Qnil;
|
750
|
+
}
|
751
|
+
|
752
|
+
/* call-seq:
|
753
|
+
* client.query(sql, options = {})
|
754
|
+
*
|
755
|
+
* Query the database with +sql+, with optional +options+. For the possible
|
756
|
+
* options, see default_query_options on the Mysql2::Client class.
|
757
|
+
*/
|
758
|
+
static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
759
|
+
#ifndef _WIN32
|
760
|
+
struct async_query_args async_args;
|
761
|
+
#endif
|
762
|
+
struct nogvl_send_query_args args;
|
763
|
+
GET_CLIENT(self);
|
764
|
+
|
765
|
+
REQUIRE_CONNECTED(wrapper);
|
766
|
+
args.mysql = wrapper->client;
|
767
|
+
|
768
|
+
(void)RB_GC_GUARD(current);
|
769
|
+
Check_Type(current, T_HASH);
|
770
|
+
rb_iv_set(self, "@current_query_options", current);
|
771
|
+
|
772
|
+
Check_Type(sql, T_STRING);
|
773
|
+
/* ensure the string is in the encoding the connection is expecting */
|
774
|
+
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
775
|
+
args.sql_ptr = RSTRING_PTR(args.sql);
|
776
|
+
args.sql_len = RSTRING_LEN(args.sql);
|
777
|
+
args.wrapper = wrapper;
|
778
|
+
|
779
|
+
rb_mysql_client_set_active_thread(self);
|
780
|
+
|
781
|
+
#ifndef _WIN32
|
782
|
+
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
783
|
+
|
784
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
421
785
|
return Qnil;
|
786
|
+
} else {
|
787
|
+
async_args.fd = wrapper->client->net.fd;
|
788
|
+
async_args.self = self;
|
789
|
+
|
790
|
+
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
791
|
+
|
792
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
422
793
|
}
|
794
|
+
#else
|
795
|
+
do_send_query(&args);
|
796
|
+
|
797
|
+
/* this will just block until the result is ready */
|
798
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
799
|
+
#endif
|
423
800
|
}
|
424
801
|
|
802
|
+
/* call-seq:
|
803
|
+
* client.escape(string)
|
804
|
+
*
|
805
|
+
* Escape +string+ so that it may be used in a SQL statement.
|
806
|
+
*/
|
425
807
|
static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
426
808
|
unsigned char *newStr;
|
427
809
|
VALUE rb_str;
|
428
810
|
unsigned long newLen, oldLen;
|
429
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
430
811
|
rb_encoding *default_internal_enc;
|
431
812
|
rb_encoding *conn_enc;
|
432
|
-
#endif
|
433
813
|
GET_CLIENT(self);
|
434
814
|
|
435
|
-
|
815
|
+
REQUIRE_CONNECTED(wrapper);
|
436
816
|
Check_Type(str, T_STRING);
|
437
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
438
817
|
default_internal_enc = rb_default_internal_encoding();
|
439
818
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
440
|
-
|
819
|
+
/* ensure the string is in the encoding the connection is expecting */
|
441
820
|
str = rb_str_export_to_enc(str, conn_enc);
|
442
|
-
#endif
|
443
821
|
|
444
822
|
oldLen = RSTRING_LEN(str);
|
445
823
|
newStr = xmalloc(oldLen*2+1);
|
446
824
|
|
447
|
-
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr,
|
825
|
+
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
448
826
|
if (newLen == oldLen) {
|
449
|
-
|
827
|
+
/* no need to return a new ruby string if nothing changed */
|
828
|
+
if (default_internal_enc) {
|
829
|
+
str = rb_str_export_to_enc(str, default_internal_enc);
|
830
|
+
}
|
450
831
|
xfree(newStr);
|
451
832
|
return str;
|
452
833
|
} else {
|
453
834
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
454
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
455
835
|
rb_enc_associate(rb_str, conn_enc);
|
456
836
|
if (default_internal_enc) {
|
457
837
|
rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
|
458
838
|
}
|
459
|
-
#endif
|
460
839
|
xfree(newStr);
|
461
840
|
return rb_str;
|
462
841
|
}
|
463
842
|
}
|
464
843
|
|
465
|
-
static VALUE
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
844
|
+
static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
845
|
+
int result;
|
846
|
+
const void *retval = NULL;
|
847
|
+
unsigned int intval = 0;
|
848
|
+
const char * charval = NULL;
|
849
|
+
my_bool boolval;
|
850
|
+
|
471
851
|
GET_CLIENT(self);
|
472
|
-
version = rb_hash_new();
|
473
852
|
|
474
|
-
|
475
|
-
|
476
|
-
|
853
|
+
REQUIRE_NOT_CONNECTED(wrapper);
|
854
|
+
|
855
|
+
if (NIL_P(value))
|
856
|
+
return Qfalse;
|
857
|
+
|
858
|
+
switch(opt) {
|
859
|
+
case MYSQL_OPT_CONNECT_TIMEOUT:
|
860
|
+
intval = NUM2UINT(value);
|
861
|
+
retval = &intval;
|
862
|
+
break;
|
863
|
+
|
864
|
+
case MYSQL_OPT_READ_TIMEOUT:
|
865
|
+
intval = NUM2UINT(value);
|
866
|
+
retval = &intval;
|
867
|
+
break;
|
868
|
+
|
869
|
+
case MYSQL_OPT_WRITE_TIMEOUT:
|
870
|
+
intval = NUM2UINT(value);
|
871
|
+
retval = &intval;
|
872
|
+
break;
|
873
|
+
|
874
|
+
case MYSQL_OPT_LOCAL_INFILE:
|
875
|
+
intval = (value == Qfalse ? 0 : 1);
|
876
|
+
retval = &intval;
|
877
|
+
break;
|
878
|
+
|
879
|
+
case MYSQL_OPT_RECONNECT:
|
880
|
+
boolval = (value == Qfalse ? 0 : 1);
|
881
|
+
retval = &boolval;
|
882
|
+
break;
|
883
|
+
|
884
|
+
#ifdef MYSQL_SECURE_AUTH
|
885
|
+
case MYSQL_SECURE_AUTH:
|
886
|
+
boolval = (value == Qfalse ? 0 : 1);
|
887
|
+
retval = &boolval;
|
888
|
+
break;
|
477
889
|
#endif
|
478
890
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
891
|
+
case MYSQL_READ_DEFAULT_FILE:
|
892
|
+
charval = (const char *)StringValueCStr(value);
|
893
|
+
retval = charval;
|
894
|
+
break;
|
895
|
+
|
896
|
+
case MYSQL_READ_DEFAULT_GROUP:
|
897
|
+
charval = (const char *)StringValueCStr(value);
|
898
|
+
retval = charval;
|
899
|
+
break;
|
900
|
+
|
901
|
+
case MYSQL_INIT_COMMAND:
|
902
|
+
charval = (const char *)StringValueCStr(value);
|
903
|
+
retval = charval;
|
904
|
+
break;
|
905
|
+
|
906
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
907
|
+
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
908
|
+
boolval = (value == Qfalse ? 0 : 1);
|
909
|
+
retval = &boolval;
|
910
|
+
break;
|
486
911
|
#endif
|
487
|
-
|
488
|
-
|
912
|
+
|
913
|
+
default:
|
914
|
+
return Qfalse;
|
915
|
+
}
|
916
|
+
|
917
|
+
result = mysql_options(wrapper->client, opt, retval);
|
918
|
+
|
919
|
+
/* Zero means success */
|
920
|
+
if (result != 0) {
|
921
|
+
rb_warn("%s\n", mysql_error(wrapper->client));
|
922
|
+
} else {
|
923
|
+
/* Special case for options that are stored in the wrapper struct */
|
924
|
+
switch (opt) {
|
925
|
+
case MYSQL_OPT_RECONNECT:
|
926
|
+
wrapper->reconnect_enabled = boolval;
|
927
|
+
break;
|
928
|
+
case MYSQL_OPT_CONNECT_TIMEOUT:
|
929
|
+
wrapper->connect_timeout = intval;
|
930
|
+
break;
|
931
|
+
}
|
932
|
+
}
|
933
|
+
|
934
|
+
return (result == 0) ? Qtrue : Qfalse;
|
489
935
|
}
|
490
936
|
|
937
|
+
/* call-seq:
|
938
|
+
* client.info
|
939
|
+
*
|
940
|
+
* Returns a string that represents the client library version.
|
941
|
+
*/
|
942
|
+
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
943
|
+
VALUE version_info, version, header_version;
|
944
|
+
version_info = rb_hash_new();
|
945
|
+
|
946
|
+
version = rb_str_new2(mysql_get_client_info());
|
947
|
+
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
948
|
+
|
949
|
+
rb_enc_associate(version, rb_usascii_encoding());
|
950
|
+
rb_enc_associate(header_version, rb_usascii_encoding());
|
951
|
+
|
952
|
+
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
953
|
+
rb_hash_aset(version_info, sym_version, version);
|
954
|
+
rb_hash_aset(version_info, sym_header_version, header_version);
|
955
|
+
|
956
|
+
return version_info;
|
957
|
+
}
|
958
|
+
|
959
|
+
/* call-seq:
|
960
|
+
* client.server_info
|
961
|
+
*
|
962
|
+
* Returns a string that represents the server version number
|
963
|
+
*/
|
491
964
|
static VALUE rb_mysql_client_server_info(VALUE self) {
|
492
965
|
VALUE version, server_info;
|
493
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
494
966
|
rb_encoding *default_internal_enc;
|
495
967
|
rb_encoding *conn_enc;
|
496
|
-
#endif
|
497
968
|
GET_CLIENT(self);
|
498
969
|
|
499
|
-
|
500
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
970
|
+
REQUIRE_CONNECTED(wrapper);
|
501
971
|
default_internal_enc = rb_default_internal_encoding();
|
502
972
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
503
|
-
#endif
|
504
973
|
|
505
974
|
version = rb_hash_new();
|
506
975
|
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
|
507
976
|
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
|
508
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
509
977
|
rb_enc_associate(server_info, conn_enc);
|
510
978
|
if (default_internal_enc) {
|
511
979
|
server_info = rb_str_export_to_enc(server_info, default_internal_enc);
|
512
980
|
}
|
513
|
-
#endif
|
514
981
|
rb_hash_aset(version, sym_version, server_info);
|
515
982
|
return version;
|
516
983
|
}
|
517
984
|
|
985
|
+
/* call-seq:
|
986
|
+
* client.socket
|
987
|
+
*
|
988
|
+
* Return the file descriptor number for this client.
|
989
|
+
*/
|
990
|
+
#ifndef _WIN32
|
518
991
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
519
992
|
GET_CLIENT(self);
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
WSAPROTOCOL_INFO wsa_pi;
|
524
|
-
// dupicate the SOCKET from libmysql
|
525
|
-
int r = WSADuplicateSocket(wrapper->client->net.fd, GetCurrentProcessId(), &wsa_pi);
|
526
|
-
SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
|
527
|
-
// create the CRT fd so ruby can get back to the SOCKET
|
528
|
-
fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
|
529
|
-
return INT2NUM(fd_set_fd);
|
993
|
+
REQUIRE_CONNECTED(wrapper);
|
994
|
+
return INT2NUM(wrapper->client->net.fd);
|
995
|
+
}
|
530
996
|
#else
|
531
|
-
|
532
|
-
|
997
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
998
|
+
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
533
999
|
}
|
1000
|
+
#endif
|
534
1001
|
|
1002
|
+
/* call-seq:
|
1003
|
+
* client.last_id
|
1004
|
+
*
|
1005
|
+
* Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE
|
1006
|
+
* statement.
|
1007
|
+
*/
|
535
1008
|
static VALUE rb_mysql_client_last_id(VALUE self) {
|
536
1009
|
GET_CLIENT(self);
|
537
|
-
|
1010
|
+
REQUIRE_CONNECTED(wrapper);
|
538
1011
|
return ULL2NUM(mysql_insert_id(wrapper->client));
|
539
1012
|
}
|
540
1013
|
|
1014
|
+
/* call-seq:
|
1015
|
+
* client.affected_rows
|
1016
|
+
*
|
1017
|
+
* returns the number of rows changed, deleted, or inserted by the last statement
|
1018
|
+
* if it was an UPDATE, DELETE, or INSERT.
|
1019
|
+
*/
|
541
1020
|
static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
542
1021
|
my_ulonglong retVal;
|
543
1022
|
GET_CLIENT(self);
|
544
1023
|
|
545
|
-
|
1024
|
+
REQUIRE_CONNECTED(wrapper);
|
546
1025
|
retVal = mysql_affected_rows(wrapper->client);
|
547
1026
|
if (retVal == (my_ulonglong)-1) {
|
548
1027
|
rb_raise_mysql2_error(wrapper);
|
@@ -550,92 +1029,270 @@ static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
|
550
1029
|
return ULL2NUM(retVal);
|
551
1030
|
}
|
552
1031
|
|
1032
|
+
/* call-seq:
|
1033
|
+
* client.thread_id
|
1034
|
+
*
|
1035
|
+
* Returns the thread ID of the current connection.
|
1036
|
+
*/
|
553
1037
|
static VALUE rb_mysql_client_thread_id(VALUE self) {
|
554
1038
|
unsigned long retVal;
|
555
1039
|
GET_CLIENT(self);
|
556
1040
|
|
557
|
-
|
1041
|
+
REQUIRE_CONNECTED(wrapper);
|
558
1042
|
retVal = mysql_thread_id(wrapper->client);
|
559
1043
|
return ULL2NUM(retVal);
|
560
1044
|
}
|
561
1045
|
|
562
|
-
static
|
1046
|
+
static void *nogvl_select_db(void *ptr) {
|
1047
|
+
struct nogvl_select_db_args *args = ptr;
|
1048
|
+
|
1049
|
+
if (mysql_select_db(args->mysql, args->db) == 0)
|
1050
|
+
return (void *)Qtrue;
|
1051
|
+
else
|
1052
|
+
return (void *)Qfalse;
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
/* call-seq:
|
1056
|
+
* client.select_db(name)
|
1057
|
+
*
|
1058
|
+
* Causes the database specified by +name+ to become the default (current)
|
1059
|
+
* database on the connection specified by mysql.
|
1060
|
+
*/
|
1061
|
+
static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
|
563
1062
|
{
|
1063
|
+
struct nogvl_select_db_args args;
|
1064
|
+
|
1065
|
+
GET_CLIENT(self);
|
1066
|
+
REQUIRE_CONNECTED(wrapper);
|
1067
|
+
|
1068
|
+
args.mysql = wrapper->client;
|
1069
|
+
args.db = StringValueCStr(db);
|
1070
|
+
|
1071
|
+
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
|
1072
|
+
rb_raise_mysql2_error(wrapper);
|
1073
|
+
|
1074
|
+
return db;
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
static void *nogvl_ping(void *ptr) {
|
564
1078
|
MYSQL *client = ptr;
|
565
1079
|
|
566
|
-
return mysql_ping(client) == 0 ? Qtrue : Qfalse;
|
1080
|
+
return (void *)(mysql_ping(client) == 0 ? Qtrue : Qfalse);
|
567
1081
|
}
|
568
1082
|
|
1083
|
+
/* call-seq:
|
1084
|
+
* client.ping
|
1085
|
+
*
|
1086
|
+
* Checks whether the connection to the server is working. If the connection
|
1087
|
+
* has gone down and auto-reconnect is enabled an attempt to reconnect is made.
|
1088
|
+
* If the connection is down and auto-reconnect is disabled, ping returns an
|
1089
|
+
* error.
|
1090
|
+
*/
|
569
1091
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
570
1092
|
GET_CLIENT(self);
|
571
1093
|
|
572
|
-
if (wrapper
|
1094
|
+
if (!CONNECTED(wrapper)) {
|
573
1095
|
return Qfalse;
|
574
1096
|
} else {
|
575
|
-
return
|
1097
|
+
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
576
1098
|
}
|
577
1099
|
}
|
578
1100
|
|
579
|
-
|
580
|
-
|
1101
|
+
/* call-seq:
|
1102
|
+
* client.set_server_option(value)
|
1103
|
+
*
|
1104
|
+
* Enables or disables an option for the connection.
|
1105
|
+
* Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
|
1106
|
+
* for more information.
|
1107
|
+
*/
|
1108
|
+
static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
|
581
1109
|
GET_CLIENT(self);
|
582
|
-
|
1110
|
+
|
1111
|
+
if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
|
1112
|
+
return Qtrue;
|
1113
|
+
} else {
|
1114
|
+
return Qfalse;
|
1115
|
+
}
|
583
1116
|
}
|
584
|
-
#endif
|
585
1117
|
|
586
|
-
|
587
|
-
|
1118
|
+
/* call-seq:
|
1119
|
+
* client.more_results?
|
1120
|
+
*
|
1121
|
+
* Returns true or false if there are more results to process.
|
1122
|
+
*/
|
1123
|
+
static VALUE rb_mysql_client_more_results(VALUE self)
|
1124
|
+
{
|
1125
|
+
GET_CLIENT(self);
|
1126
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1127
|
+
return Qfalse;
|
1128
|
+
else
|
1129
|
+
return Qtrue;
|
1130
|
+
}
|
1131
|
+
|
1132
|
+
/* call-seq:
|
1133
|
+
* client.next_result
|
1134
|
+
*
|
1135
|
+
* Fetch the next result set from the server.
|
1136
|
+
* Returns nothing.
|
1137
|
+
*/
|
1138
|
+
static VALUE rb_mysql_client_next_result(VALUE self)
|
1139
|
+
{
|
1140
|
+
int ret;
|
1141
|
+
GET_CLIENT(self);
|
1142
|
+
ret = mysql_next_result(wrapper->client);
|
1143
|
+
if (ret > 0) {
|
1144
|
+
rb_raise_mysql2_error(wrapper);
|
1145
|
+
return Qfalse;
|
1146
|
+
} else if (ret == 0) {
|
1147
|
+
return Qtrue;
|
1148
|
+
} else {
|
1149
|
+
return Qfalse;
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
/* call-seq:
|
1154
|
+
* client.store_result
|
1155
|
+
*
|
1156
|
+
* Return the next result object from a query which
|
1157
|
+
* yielded multiple result sets.
|
1158
|
+
*/
|
1159
|
+
static VALUE rb_mysql_client_store_result(VALUE self)
|
1160
|
+
{
|
1161
|
+
MYSQL_RES * result;
|
1162
|
+
VALUE resultObj;
|
1163
|
+
VALUE current;
|
588
1164
|
GET_CLIENT(self);
|
589
1165
|
|
590
|
-
|
591
|
-
reconnect = value == Qfalse ? 0 : 1;
|
1166
|
+
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
592
1167
|
|
593
|
-
|
594
|
-
if (
|
595
|
-
|
596
|
-
rb_warn("%s\n", mysql_error(wrapper->client));
|
1168
|
+
if (result == NULL) {
|
1169
|
+
if (mysql_errno(wrapper->client) != 0) {
|
1170
|
+
rb_raise_mysql2_error(wrapper);
|
597
1171
|
}
|
1172
|
+
/* no data and no error, so query was not a SELECT */
|
1173
|
+
return Qnil;
|
598
1174
|
}
|
599
|
-
|
1175
|
+
|
1176
|
+
// Duplicate the options hash and put the copy in the Result object
|
1177
|
+
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1178
|
+
(void)RB_GC_GUARD(current);
|
1179
|
+
Check_Type(current, T_HASH);
|
1180
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1181
|
+
|
1182
|
+
return resultObj;
|
600
1183
|
}
|
601
1184
|
|
602
|
-
|
603
|
-
|
1185
|
+
/* call-seq:
|
1186
|
+
* client.encoding
|
1187
|
+
*
|
1188
|
+
* Returns the encoding set on the client.
|
1189
|
+
*/
|
1190
|
+
static VALUE rb_mysql_client_encoding(VALUE self) {
|
604
1191
|
GET_CLIENT(self);
|
1192
|
+
return wrapper->encoding;
|
1193
|
+
}
|
605
1194
|
|
606
|
-
|
607
|
-
|
608
|
-
|
1195
|
+
/* call-seq:
|
1196
|
+
* client.automatic_close?
|
1197
|
+
*
|
1198
|
+
* @return [Boolean]
|
1199
|
+
*/
|
1200
|
+
static VALUE get_automatic_close(VALUE self) {
|
1201
|
+
GET_CLIENT(self);
|
1202
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1203
|
+
}
|
609
1204
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
1205
|
+
/* call-seq:
|
1206
|
+
* client.automatic_close = false
|
1207
|
+
*
|
1208
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1209
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1210
|
+
* call +close+ when the connection is no longer needed.
|
1211
|
+
*
|
1212
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1213
|
+
*/
|
1214
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1215
|
+
GET_CLIENT(self);
|
1216
|
+
if (RTEST(value)) {
|
1217
|
+
wrapper->automatic_close = 1;
|
1218
|
+
} else {
|
1219
|
+
#ifndef _WIN32
|
1220
|
+
wrapper->automatic_close = 0;
|
1221
|
+
#else
|
1222
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1223
|
+
#endif
|
615
1224
|
}
|
616
1225
|
return value;
|
617
1226
|
}
|
618
1227
|
|
1228
|
+
/* call-seq:
|
1229
|
+
* client.reconnect = true
|
1230
|
+
*
|
1231
|
+
* Enable or disable the automatic reconnect behavior of libmysql.
|
1232
|
+
* Read http://dev.mysql.com/doc/refman/5.5/en/auto-reconnect.html
|
1233
|
+
* for more information.
|
1234
|
+
*/
|
1235
|
+
static VALUE set_reconnect(VALUE self, VALUE value) {
|
1236
|
+
return _mysql_client_options(self, MYSQL_OPT_RECONNECT, value);
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
static VALUE set_local_infile(VALUE self, VALUE value) {
|
1240
|
+
return _mysql_client_options(self, MYSQL_OPT_LOCAL_INFILE, value);
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
static VALUE set_connect_timeout(VALUE self, VALUE value) {
|
1244
|
+
long int sec;
|
1245
|
+
Check_Type(value, T_FIXNUM);
|
1246
|
+
sec = FIX2INT(value);
|
1247
|
+
if (sec < 0) {
|
1248
|
+
rb_raise(cMysql2Error, "connect_timeout must be a positive integer, you passed %ld", sec);
|
1249
|
+
}
|
1250
|
+
return _mysql_client_options(self, MYSQL_OPT_CONNECT_TIMEOUT, value);
|
1251
|
+
}
|
1252
|
+
|
1253
|
+
static VALUE set_read_timeout(VALUE self, VALUE value) {
|
1254
|
+
long int sec;
|
1255
|
+
Check_Type(value, T_FIXNUM);
|
1256
|
+
sec = FIX2INT(value);
|
1257
|
+
if (sec < 0) {
|
1258
|
+
rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
|
1259
|
+
}
|
1260
|
+
/* Set the instance variable here even though _mysql_client_options
|
1261
|
+
might not succeed, because the timeout is used in other ways
|
1262
|
+
elsewhere */
|
1263
|
+
rb_iv_set(self, "@read_timeout", value);
|
1264
|
+
return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
static VALUE set_write_timeout(VALUE self, VALUE value) {
|
1268
|
+
long int sec;
|
1269
|
+
Check_Type(value, T_FIXNUM);
|
1270
|
+
sec = FIX2INT(value);
|
1271
|
+
if (sec < 0) {
|
1272
|
+
rb_raise(cMysql2Error, "write_timeout must be a positive integer, you passed %ld", sec);
|
1273
|
+
}
|
1274
|
+
return _mysql_client_options(self, MYSQL_OPT_WRITE_TIMEOUT, value);
|
1275
|
+
}
|
1276
|
+
|
619
1277
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
620
|
-
char *
|
621
|
-
|
622
|
-
|
623
|
-
|
1278
|
+
char *charset_name;
|
1279
|
+
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1280
|
+
rb_encoding *enc;
|
1281
|
+
VALUE rb_enc;
|
624
1282
|
GET_CLIENT(self);
|
625
1283
|
|
626
|
-
|
627
|
-
|
628
|
-
|
1284
|
+
Check_Type(value, T_STRING);
|
1285
|
+
charset_name = RSTRING_PTR(value);
|
1286
|
+
|
1287
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1288
|
+
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
629
1289
|
VALUE inspect = rb_inspect(value);
|
630
1290
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
631
1291
|
} else {
|
632
|
-
|
633
|
-
|
634
|
-
|
1292
|
+
enc = rb_enc_find(mysql2rb->rb_name);
|
1293
|
+
rb_enc = rb_enc_from_encoding(enc);
|
1294
|
+
wrapper->encoding = rb_enc;
|
635
1295
|
}
|
636
|
-
#endif
|
637
|
-
|
638
|
-
charset_name = StringValuePtr(value);
|
639
1296
|
|
640
1297
|
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
641
1298
|
/* TODO: warning - unable to set charset */
|
@@ -648,192 +1305,347 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
648
1305
|
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
|
649
1306
|
GET_CLIENT(self);
|
650
1307
|
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
NIL_P(cipher) ? NULL : StringValuePtr(cipher));
|
658
|
-
}
|
1308
|
+
mysql_ssl_set(wrapper->client,
|
1309
|
+
NIL_P(key) ? NULL : StringValueCStr(key),
|
1310
|
+
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1311
|
+
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1312
|
+
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1313
|
+
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
659
1314
|
|
660
1315
|
return self;
|
661
1316
|
}
|
662
1317
|
|
663
|
-
static VALUE
|
1318
|
+
static VALUE set_secure_auth(VALUE self, VALUE value) {
|
1319
|
+
/* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
|
1320
|
+
#ifdef MYSQL_SECURE_AUTH
|
1321
|
+
return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
|
1322
|
+
#else
|
1323
|
+
return Qfalse;
|
1324
|
+
#endif
|
1325
|
+
}
|
1326
|
+
|
1327
|
+
static VALUE set_read_default_file(VALUE self, VALUE value) {
|
1328
|
+
return _mysql_client_options(self, MYSQL_READ_DEFAULT_FILE, value);
|
1329
|
+
}
|
1330
|
+
|
1331
|
+
static VALUE set_read_default_group(VALUE self, VALUE value) {
|
1332
|
+
return _mysql_client_options(self, MYSQL_READ_DEFAULT_GROUP, value);
|
1333
|
+
}
|
1334
|
+
|
1335
|
+
static VALUE set_init_command(VALUE self, VALUE value) {
|
1336
|
+
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1337
|
+
}
|
1338
|
+
|
1339
|
+
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1340
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1341
|
+
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
1342
|
+
#else
|
1343
|
+
rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
|
1344
|
+
#endif
|
1345
|
+
}
|
1346
|
+
|
1347
|
+
static VALUE initialize_ext(VALUE self) {
|
664
1348
|
GET_CLIENT(self);
|
665
1349
|
|
666
|
-
if (
|
1350
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
667
1351
|
/* TODO: warning - not enough memory? */
|
668
|
-
|
1352
|
+
rb_raise_mysql2_error(wrapper);
|
669
1353
|
}
|
670
1354
|
|
671
|
-
wrapper->
|
1355
|
+
wrapper->initialized = 1;
|
672
1356
|
return self;
|
673
1357
|
}
|
674
1358
|
|
1359
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1360
|
+
*
|
1361
|
+
* Create a new prepared statement.
|
1362
|
+
*/
|
1363
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1364
|
+
GET_CLIENT(self);
|
1365
|
+
REQUIRE_CONNECTED(wrapper);
|
1366
|
+
|
1367
|
+
return rb_mysql_stmt_new(self, sql);
|
1368
|
+
}
|
1369
|
+
|
675
1370
|
void init_mysql2_client() {
|
676
|
-
|
677
|
-
|
1371
|
+
#ifdef _WIN32
|
1372
|
+
/* verify the libmysql we're about to use was the version we were built against
|
1373
|
+
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
678
1374
|
int i;
|
679
1375
|
int dots = 0;
|
680
1376
|
const char *lib = mysql_get_client_info();
|
681
|
-
|
1377
|
+
|
1378
|
+
for (i = 0; lib[i] != 0 && MYSQL_LINK_VERSION[i] != 0; i++) {
|
682
1379
|
if (lib[i] == '.') {
|
683
1380
|
dots++;
|
684
|
-
|
1381
|
+
/* we only compare MAJOR and MINOR */
|
685
1382
|
if (dots == 2) break;
|
686
1383
|
}
|
687
|
-
if (lib[i] !=
|
688
|
-
rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.",
|
689
|
-
return;
|
1384
|
+
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
1385
|
+
rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
|
690
1386
|
}
|
691
1387
|
}
|
1388
|
+
#endif
|
1389
|
+
|
1390
|
+
/* Initializing mysql library, so different threads could call Client.new */
|
1391
|
+
/* without race condition in the library */
|
1392
|
+
if (mysql_library_init(0, NULL, NULL) != 0) {
|
1393
|
+
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
1394
|
+
}
|
692
1395
|
|
1396
|
+
#if 0
|
1397
|
+
mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
|
1398
|
+
#endif
|
693
1399
|
cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
|
694
1400
|
|
695
1401
|
rb_define_alloc_func(cMysql2Client, allocate);
|
696
1402
|
|
697
1403
|
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
1404
|
+
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
698
1405
|
|
699
1406
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
700
|
-
rb_define_method(cMysql2Client, "
|
1407
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1408
|
+
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
701
1409
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
702
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
703
1410
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
704
1411
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
705
1412
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
706
1413
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
707
1414
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1415
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
708
1416
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
709
1417
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
710
|
-
|
1418
|
+
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
1419
|
+
rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
|
1420
|
+
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1421
|
+
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1422
|
+
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1423
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1424
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1425
|
+
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1426
|
+
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1427
|
+
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1428
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
711
1429
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
712
|
-
#endif
|
713
1430
|
|
714
|
-
rb_define_private_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
715
1431
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1432
|
+
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
1433
|
+
rb_define_private_method(cMysql2Client, "write_timeout=", set_write_timeout, 1);
|
1434
|
+
rb_define_private_method(cMysql2Client, "local_infile=", set_local_infile, 1);
|
716
1435
|
rb_define_private_method(cMysql2Client, "charset_name=", set_charset_name, 1);
|
1436
|
+
rb_define_private_method(cMysql2Client, "secure_auth=", set_secure_auth, 1);
|
1437
|
+
rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
|
1438
|
+
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1439
|
+
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
717
1440
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
718
|
-
rb_define_private_method(cMysql2Client, "
|
719
|
-
rb_define_private_method(cMysql2Client, "
|
720
|
-
|
721
|
-
|
1441
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1442
|
+
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1443
|
+
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1444
|
+
rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
|
1445
|
+
rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
|
722
1446
|
|
723
1447
|
sym_id = ID2SYM(rb_intern("id"));
|
724
1448
|
sym_version = ID2SYM(rb_intern("version"));
|
1449
|
+
sym_header_version = ID2SYM(rb_intern("header_version"));
|
725
1450
|
sym_async = ID2SYM(rb_intern("async"));
|
726
1451
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
727
1452
|
sym_as = ID2SYM(rb_intern("as"));
|
728
1453
|
sym_array = ID2SYM(rb_intern("array"));
|
1454
|
+
sym_stream = ID2SYM(rb_intern("stream"));
|
729
1455
|
|
1456
|
+
sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
|
1457
|
+
sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
|
1458
|
+
sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
|
1459
|
+
|
1460
|
+
intern_brackets = rb_intern("[]");
|
730
1461
|
intern_merge = rb_intern("merge");
|
731
|
-
|
732
|
-
|
1462
|
+
intern_merge_bang = rb_intern("merge!");
|
1463
|
+
intern_new_with_args = rb_intern("new_with_args");
|
733
1464
|
|
734
1465
|
#ifdef CLIENT_LONG_PASSWORD
|
735
1466
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
736
|
-
|
1467
|
+
LONG2NUM(CLIENT_LONG_PASSWORD));
|
1468
|
+
#else
|
1469
|
+
/* HACK because MariaDB 10.2 no longer defines this constant,
|
1470
|
+
* but we're using it in our default connection flags. */
|
1471
|
+
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
|
737
1472
|
#endif
|
738
1473
|
|
739
1474
|
#ifdef CLIENT_FOUND_ROWS
|
740
1475
|
rb_const_set(cMysql2Client, rb_intern("FOUND_ROWS"),
|
741
|
-
|
1476
|
+
LONG2NUM(CLIENT_FOUND_ROWS));
|
742
1477
|
#endif
|
743
1478
|
|
744
1479
|
#ifdef CLIENT_LONG_FLAG
|
745
1480
|
rb_const_set(cMysql2Client, rb_intern("LONG_FLAG"),
|
746
|
-
|
1481
|
+
LONG2NUM(CLIENT_LONG_FLAG));
|
747
1482
|
#endif
|
748
1483
|
|
749
1484
|
#ifdef CLIENT_CONNECT_WITH_DB
|
750
1485
|
rb_const_set(cMysql2Client, rb_intern("CONNECT_WITH_DB"),
|
751
|
-
|
1486
|
+
LONG2NUM(CLIENT_CONNECT_WITH_DB));
|
752
1487
|
#endif
|
753
1488
|
|
754
1489
|
#ifdef CLIENT_NO_SCHEMA
|
755
1490
|
rb_const_set(cMysql2Client, rb_intern("NO_SCHEMA"),
|
756
|
-
|
1491
|
+
LONG2NUM(CLIENT_NO_SCHEMA));
|
757
1492
|
#endif
|
758
1493
|
|
759
1494
|
#ifdef CLIENT_COMPRESS
|
760
|
-
rb_const_set(cMysql2Client, rb_intern("COMPRESS"),
|
1495
|
+
rb_const_set(cMysql2Client, rb_intern("COMPRESS"), LONG2NUM(CLIENT_COMPRESS));
|
761
1496
|
#endif
|
762
1497
|
|
763
1498
|
#ifdef CLIENT_ODBC
|
764
|
-
rb_const_set(cMysql2Client, rb_intern("ODBC"),
|
1499
|
+
rb_const_set(cMysql2Client, rb_intern("ODBC"), LONG2NUM(CLIENT_ODBC));
|
765
1500
|
#endif
|
766
1501
|
|
767
1502
|
#ifdef CLIENT_LOCAL_FILES
|
768
1503
|
rb_const_set(cMysql2Client, rb_intern("LOCAL_FILES"),
|
769
|
-
|
1504
|
+
LONG2NUM(CLIENT_LOCAL_FILES));
|
770
1505
|
#endif
|
771
1506
|
|
772
1507
|
#ifdef CLIENT_IGNORE_SPACE
|
773
1508
|
rb_const_set(cMysql2Client, rb_intern("IGNORE_SPACE"),
|
774
|
-
|
1509
|
+
LONG2NUM(CLIENT_IGNORE_SPACE));
|
775
1510
|
#endif
|
776
1511
|
|
777
1512
|
#ifdef CLIENT_PROTOCOL_41
|
778
1513
|
rb_const_set(cMysql2Client, rb_intern("PROTOCOL_41"),
|
779
|
-
|
1514
|
+
LONG2NUM(CLIENT_PROTOCOL_41));
|
780
1515
|
#endif
|
781
1516
|
|
782
1517
|
#ifdef CLIENT_INTERACTIVE
|
783
1518
|
rb_const_set(cMysql2Client, rb_intern("INTERACTIVE"),
|
784
|
-
|
1519
|
+
LONG2NUM(CLIENT_INTERACTIVE));
|
785
1520
|
#endif
|
786
1521
|
|
787
1522
|
#ifdef CLIENT_SSL
|
788
|
-
rb_const_set(cMysql2Client, rb_intern("SSL"),
|
1523
|
+
rb_const_set(cMysql2Client, rb_intern("SSL"), LONG2NUM(CLIENT_SSL));
|
789
1524
|
#endif
|
790
1525
|
|
791
1526
|
#ifdef CLIENT_IGNORE_SIGPIPE
|
792
1527
|
rb_const_set(cMysql2Client, rb_intern("IGNORE_SIGPIPE"),
|
793
|
-
|
1528
|
+
LONG2NUM(CLIENT_IGNORE_SIGPIPE));
|
794
1529
|
#endif
|
795
1530
|
|
796
1531
|
#ifdef CLIENT_TRANSACTIONS
|
797
1532
|
rb_const_set(cMysql2Client, rb_intern("TRANSACTIONS"),
|
798
|
-
|
1533
|
+
LONG2NUM(CLIENT_TRANSACTIONS));
|
799
1534
|
#endif
|
800
1535
|
|
801
1536
|
#ifdef CLIENT_RESERVED
|
802
|
-
rb_const_set(cMysql2Client, rb_intern("RESERVED"),
|
1537
|
+
rb_const_set(cMysql2Client, rb_intern("RESERVED"), LONG2NUM(CLIENT_RESERVED));
|
803
1538
|
#endif
|
804
1539
|
|
805
1540
|
#ifdef CLIENT_SECURE_CONNECTION
|
806
1541
|
rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"),
|
807
|
-
|
1542
|
+
LONG2NUM(CLIENT_SECURE_CONNECTION));
|
1543
|
+
#else
|
1544
|
+
/* HACK because MySQL5.7 no longer defines this constant,
|
1545
|
+
* but we're using it in our default connection flags. */
|
1546
|
+
rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
|
1547
|
+
#endif
|
1548
|
+
|
1549
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
|
1550
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
|
1551
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
|
1552
|
+
#endif
|
1553
|
+
|
1554
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
1555
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
|
1556
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
|
808
1557
|
#endif
|
809
1558
|
|
810
1559
|
#ifdef CLIENT_MULTI_STATEMENTS
|
811
1560
|
rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
|
812
|
-
|
1561
|
+
LONG2NUM(CLIENT_MULTI_STATEMENTS));
|
813
1562
|
#endif
|
814
1563
|
|
815
1564
|
#ifdef CLIENT_PS_MULTI_RESULTS
|
816
1565
|
rb_const_set(cMysql2Client, rb_intern("PS_MULTI_RESULTS"),
|
817
|
-
|
1566
|
+
LONG2NUM(CLIENT_PS_MULTI_RESULTS));
|
818
1567
|
#endif
|
819
1568
|
|
820
1569
|
#ifdef CLIENT_SSL_VERIFY_SERVER_CERT
|
821
1570
|
rb_const_set(cMysql2Client, rb_intern("SSL_VERIFY_SERVER_CERT"),
|
822
|
-
|
1571
|
+
LONG2NUM(CLIENT_SSL_VERIFY_SERVER_CERT));
|
823
1572
|
#endif
|
824
1573
|
|
825
1574
|
#ifdef CLIENT_REMEMBER_OPTIONS
|
826
1575
|
rb_const_set(cMysql2Client, rb_intern("REMEMBER_OPTIONS"),
|
827
|
-
|
1576
|
+
LONG2NUM(CLIENT_REMEMBER_OPTIONS));
|
828
1577
|
#endif
|
829
1578
|
|
830
1579
|
#ifdef CLIENT_ALL_FLAGS
|
831
1580
|
rb_const_set(cMysql2Client, rb_intern("ALL_FLAGS"),
|
832
|
-
|
1581
|
+
LONG2NUM(CLIENT_ALL_FLAGS));
|
833
1582
|
#endif
|
834
1583
|
|
835
1584
|
#ifdef CLIENT_BASIC_FLAGS
|
836
1585
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
837
|
-
|
1586
|
+
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1587
|
+
#endif
|
1588
|
+
|
1589
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
1590
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1591
|
+
LONG2NUM(CLIENT_CONNECT_ATTRS));
|
1592
|
+
#else
|
1593
|
+
/* HACK because MySQL 5.5 and earlier don't define this constant,
|
1594
|
+
* but we're using it in our default connection flags. */
|
1595
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1596
|
+
INT2NUM(0));
|
1597
|
+
#endif
|
1598
|
+
|
1599
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1600
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1601
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1602
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1603
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1604
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1605
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1606
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1607
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1608
|
+
#endif
|
1609
|
+
|
1610
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1611
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1612
|
+
#endif
|
1613
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1614
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1615
|
+
#endif
|
1616
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1617
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1618
|
+
#endif
|
1619
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1620
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
838
1621
|
#endif
|
1622
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1623
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1624
|
+
#endif
|
1625
|
+
}
|
1626
|
+
|
1627
|
+
#define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
|
1628
|
+
|
1629
|
+
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
|
1630
|
+
VALUE server_flags = rb_hash_new();
|
1631
|
+
|
1632
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
|
1633
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
|
1634
|
+
#else
|
1635
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
|
1636
|
+
#endif
|
1637
|
+
|
1638
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
|
1639
|
+
rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
|
1640
|
+
#else
|
1641
|
+
rb_hash_aset(server_flags, sym_no_index_used, Qnil);
|
1642
|
+
#endif
|
1643
|
+
|
1644
|
+
#ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
|
1645
|
+
rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
|
1646
|
+
#else
|
1647
|
+
rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
|
1648
|
+
#endif
|
1649
|
+
|
1650
|
+
rb_iv_set(result, "@server_flags", server_flags);
|
839
1651
|
}
|