mysql2 0.1.9 → 0.2.0

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.
@@ -0,0 +1,40 @@
1
+ #ifndef MYSQL2_CLIENT_H
2
+ #define MYSQL2_CLIENT_H
3
+
4
+ /*
5
+ * partial emulation of the 1.9 rb_thread_blocking_region under 1.8,
6
+ * this is enough for dealing with blocking I/O functions in the
7
+ * presence of threads.
8
+ */
9
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
10
+
11
+ #include <rubysig.h>
12
+ #define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
13
+ typedef void rb_unblock_function_t(void *);
14
+ typedef VALUE rb_blocking_function_t(void *);
15
+ static VALUE
16
+ rb_thread_blocking_region(
17
+ rb_blocking_function_t *func, void *data1,
18
+ RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
19
+ RB_MYSQL_UNUSED void *data2)
20
+ {
21
+ VALUE rv;
22
+
23
+ TRAP_BEG;
24
+ rv = func(data1);
25
+ TRAP_END;
26
+
27
+ return rv;
28
+ }
29
+
30
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
31
+
32
+ void init_mysql2_client();
33
+
34
+ typedef struct {
35
+ VALUE encoding;
36
+ short int active;
37
+ MYSQL client;
38
+ } mysql_client_wrapper;
39
+
40
+ #endif
@@ -10,7 +10,7 @@ have_func('rb_thread_blocking_region')
10
10
 
11
11
  # borrowed from mysqlplus
12
12
  # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
13
- dirs = ENV['PATH'].split(':') + %w[
13
+ dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
14
14
  /opt
15
15
  /opt/local
16
16
  /opt/local/mysql
@@ -24,7 +24,7 @@ dirs = ENV['PATH'].split(':') + %w[
24
24
 
25
25
  GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
26
26
 
27
- if /mswin32/ =~ RUBY_PLATFORM
27
+ if RUBY_PLATFORM =~ /mswin|mingw/
28
28
  inc, lib = dir_config('mysql')
29
29
  exit 1 unless have_library("libmysql")
30
30
  elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
@@ -57,7 +57,9 @@ end
57
57
  asplode h unless have_header h
58
58
  end
59
59
 
60
- $CFLAGS << ' -Wall -Wextra -funroll-loops'
61
- # $CFLAGS << ' -O0 -ggdb3'
60
+ unless RUBY_PLATFORM =~ /mswin/
61
+ $CFLAGS << ' -Wall -funroll-loops'
62
+ end
63
+ # $CFLAGS << ' -O0 -ggdb3 -Wextra'
62
64
 
63
65
  create_makefile('mysql2/mysql2')
@@ -1,496 +1,12 @@
1
1
  #include <mysql2_ext.h>
2
2
 
3
- VALUE mMysql2, cMysql2Client;
4
- VALUE cMysql2Error, intern_encoding_from_charset;
5
- ID sym_id, sym_version, sym_async;
6
-
7
- #define REQUIRE_OPEN_DB(_ctxt) \
8
- if(!_ctxt->net.vio) { \
9
- rb_raise(cMysql2Error, "closed MySQL connection"); \
10
- return Qnil; \
11
- }
12
-
13
- /*
14
- * non-blocking mysql_*() functions that we won't be wrapping since
15
- * they do not appear to hit the network nor issue any interruptible
16
- * or blocking system calls.
17
- *
18
- * - mysql_affected_rows()
19
- * - mysql_error()
20
- * - mysql_fetch_fields()
21
- * - mysql_fetch_lengths() - calls cli_fetch_lengths or emb_fetch_lengths
22
- * - mysql_field_count()
23
- * - mysql_get_client_info()
24
- * - mysql_get_client_version()
25
- * - mysql_get_server_info()
26
- * - mysql_get_server_version()
27
- * - mysql_insert_id()
28
- * - mysql_num_fields()
29
- * - mysql_num_rows()
30
- * - mysql_options()
31
- * - mysql_real_escape_string()
32
- * - mysql_ssl_set()
33
- */
34
-
35
- static VALUE rb_raise_mysql2_error(MYSQL *client) {
36
- VALUE e = rb_exc_new2(cMysql2Error, mysql_error(client));
37
- rb_funcall(e, rb_intern("error_number="), 1, INT2NUM(mysql_errno(client)));
38
- rb_funcall(e, rb_intern("sql_state="), 1, rb_tainted_str_new2(mysql_sqlstate(client)));
39
- rb_exc_raise(e);
40
- return Qnil;
41
- }
42
-
43
- static VALUE nogvl_init(void *ptr) {
44
- MYSQL * client = (MYSQL *)ptr;
45
-
46
- /* may initialize embedded server and read /etc/services off disk */
47
- mysql_init(client);
48
-
49
- return client ? Qtrue : Qfalse;
50
- }
51
-
52
- static VALUE nogvl_connect(void *ptr) {
53
- struct nogvl_connect_args *args = ptr;
54
- MYSQL *client;
55
-
56
- client = mysql_real_connect(args->mysql, args->host,
57
- args->user, args->passwd,
58
- args->db, args->port, args->unix_socket,
59
- args->client_flag);
60
-
61
- return client ? Qtrue : Qfalse;
62
- }
63
-
64
- static void rb_mysql_client_free(void * ptr) {
65
- MYSQL * client = (MYSQL *)ptr;
66
-
67
- /*
68
- * we'll send a QUIT message to the server, but that message is more of a
69
- * formality than a hard requirement since the socket is getting shutdown
70
- * anyways, so ensure the socket write does not block our interpreter
71
- */
72
- int fd = client->net.fd;
73
- int flags;
74
-
75
- if (fd >= 0) {
76
- /*
77
- * if the socket is dead we have no chance of blocking,
78
- * so ignore any potential fcntl errors since they don't matter
79
- */
80
- flags = fcntl(fd, F_GETFL);
81
- if (flags > 0 && !(flags & O_NONBLOCK))
82
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
83
- }
84
-
85
- /* It's safe to call mysql_close() on an already closed connection. */
86
- mysql_close(client);
87
- xfree(ptr);
88
- }
89
-
90
- static VALUE nogvl_close(void * ptr) {
91
- mysql_close((MYSQL *)ptr);
92
- return Qnil;
93
- }
94
-
95
- static VALUE allocate(VALUE klass)
96
- {
97
- MYSQL * client;
98
-
99
- return Data_Make_Struct(
100
- klass,
101
- MYSQL,
102
- NULL,
103
- rb_mysql_client_free,
104
- client
105
- );
106
- }
107
-
108
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket)
109
- {
110
- MYSQL * client;
111
- struct nogvl_connect_args args;
112
-
113
- Data_Get_Struct(self, MYSQL, client);
114
-
115
- args.host = NIL_P(host) ? "localhost" : StringValuePtr(host);
116
- args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
117
- args.port = NIL_P(port) ? 3306 : NUM2INT(port);
118
- args.user = NIL_P(user) ? NULL : StringValuePtr(user);
119
- args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
120
- args.db = NIL_P(database) ? NULL : StringValuePtr(database);
121
- args.mysql = client;
122
- args.client_flag = 0;
123
-
124
- if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse)
125
- {
126
- // unable to connect
127
- return rb_raise_mysql2_error(client);
128
- }
129
-
130
- return self;
131
- }
132
-
133
- /*
134
- * Immediately disconnect from the server, normally the garbage collector
135
- * will disconnect automatically when a connection is no longer needed.
136
- * Explicitly closing this will free up server resources sooner than waiting
137
- * for the garbage collector.
138
- */
139
- static VALUE rb_mysql_client_close(VALUE self) {
140
- MYSQL *client;
141
-
142
- Data_Get_Struct(self, MYSQL, client);
143
-
144
- REQUIRE_OPEN_DB(client);
145
- rb_thread_blocking_region(nogvl_close, client, RUBY_UBF_IO, 0);
146
-
147
- return Qnil;
148
- }
149
-
150
- /*
151
- * mysql_send_query is unlikely to block since most queries are small
152
- * enough to fit in a socket buffer, but sometimes large UPDATE and
153
- * INSERTs will cause the process to block
154
- */
155
- static VALUE nogvl_send_query(void *ptr) {
156
- struct nogvl_send_query_args *args = ptr;
157
- int rv;
158
- const char *sql = StringValuePtr(args->sql);
159
- long sql_len = RSTRING_LEN(args->sql);
160
-
161
- rv = mysql_send_query(args->mysql, sql, sql_len);
162
-
163
- return rv == 0 ? Qtrue : Qfalse;
164
- }
165
-
166
- /*
167
- * even though we did rb_thread_select before calling this, a large
168
- * response can overflow the socket buffers and cause us to eventually
169
- * block while calling mysql_read_query_result
170
- */
171
- static VALUE nogvl_read_query_result(void *ptr) {
172
- MYSQL * client = ptr;
173
- my_bool res = mysql_read_query_result(client);
174
-
175
- return res == 0 ? Qtrue : Qfalse;
176
- }
177
-
178
- /* mysql_store_result may (unlikely) read rows off the socket */
179
- static VALUE nogvl_store_result(void *ptr) {
180
- MYSQL * client = ptr;
181
- return (VALUE)mysql_store_result(client);
182
- }
183
-
184
- static VALUE rb_mysql_client_async_result(VALUE self) {
185
- MYSQL * client;
186
- MYSQL_RES * result;
187
-
188
- Data_Get_Struct(self, MYSQL, client);
189
-
190
- REQUIRE_OPEN_DB(client);
191
- if (rb_thread_blocking_region(nogvl_read_query_result, client, RUBY_UBF_IO, 0) == Qfalse) {
192
- return rb_raise_mysql2_error(client);
193
- }
194
-
195
- result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0);
196
- if (result == NULL) {
197
- if (mysql_field_count(client) != 0) {
198
- rb_raise_mysql2_error(client);
199
- }
200
- return Qnil;
201
- }
202
-
203
- VALUE resultObj = rb_mysql_result_to_obj(result);
204
- rb_iv_set(resultObj, "@encoding", rb_iv_get(self, "@encoding"));
205
- return resultObj;
206
- }
207
-
208
- static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
209
- struct nogvl_send_query_args args;
210
- fd_set fdset;
211
- int fd, retval;
212
- int async = 0;
213
- VALUE opts;
214
- VALUE rb_async;
215
-
216
- MYSQL * client;
217
-
218
- if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
219
- if ((rb_async = rb_hash_aref(opts, sym_async)) != Qnil) {
220
- async = rb_async == Qtrue ? 1 : 0;
221
- }
222
- }
223
-
224
- Check_Type(args.sql, T_STRING);
225
- #ifdef HAVE_RUBY_ENCODING_H
226
- rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
227
- // ensure the string is in the encoding the connection is expecting
228
- args.sql = rb_str_export_to_enc(args.sql, conn_enc);
229
- #endif
230
-
231
- Data_Get_Struct(self, MYSQL, client);
232
-
233
- REQUIRE_OPEN_DB(client);
234
-
235
- args.mysql = client;
236
- if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
237
- return rb_raise_mysql2_error(client);
238
- }
239
-
240
- if (!async) {
241
- // the below code is largely from do_mysql
242
- // http://github.com/datamapper/do
243
- fd = client->net.fd;
244
- for(;;) {
245
- FD_ZERO(&fdset);
246
- FD_SET(fd, &fdset);
247
-
248
- retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
249
-
250
- if (retval < 0) {
251
- rb_sys_fail(0);
252
- }
253
-
254
- if (retval > 0) {
255
- break;
256
- }
257
- }
258
-
259
- return rb_mysql_client_async_result(self);
260
- } else {
261
- return Qnil;
262
- }
263
- }
264
-
265
- static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
266
- MYSQL * client;
267
- VALUE newStr;
268
- unsigned long newLen, oldLen;
269
-
270
- Check_Type(str, T_STRING);
271
- #ifdef HAVE_RUBY_ENCODING_H
272
- rb_encoding *default_internal_enc = rb_default_internal_encoding();
273
- rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
274
- // ensure the string is in the encoding the connection is expecting
275
- str = rb_str_export_to_enc(str, conn_enc);
276
- #endif
277
-
278
- oldLen = RSTRING_LEN(str);
279
- char escaped[(oldLen*2)+1];
280
-
281
- Data_Get_Struct(self, MYSQL, client);
282
-
283
- REQUIRE_OPEN_DB(client);
284
- newLen = mysql_real_escape_string(client, escaped, StringValuePtr(str), RSTRING_LEN(str));
285
- if (newLen == oldLen) {
286
- // no need to return a new ruby string if nothing changed
287
- return str;
288
- } else {
289
- newStr = rb_str_new(escaped, newLen);
290
- #ifdef HAVE_RUBY_ENCODING_H
291
- rb_enc_associate(newStr, conn_enc);
292
- if (default_internal_enc) {
293
- newStr = rb_str_export_to_enc(newStr, default_internal_enc);
294
- }
295
- #endif
296
- return newStr;
297
- }
298
- }
299
-
300
- static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE self) {
301
- VALUE version = rb_hash_new(), client_info;
302
- #ifdef HAVE_RUBY_ENCODING_H
303
- rb_encoding *default_internal_enc = rb_default_internal_encoding();
304
- rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
305
- #endif
306
-
307
- rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
308
- client_info = rb_str_new2(mysql_get_client_info());
309
- #ifdef HAVE_RUBY_ENCODING_H
310
- rb_enc_associate(client_info, conn_enc);
311
- if (default_internal_enc) {
312
- client_info = rb_str_export_to_enc(client_info, default_internal_enc);
313
- }
314
- #endif
315
- rb_hash_aset(version, sym_version, client_info);
316
- return version;
317
- }
318
-
319
- static VALUE rb_mysql_client_server_info(VALUE self) {
320
- MYSQL * client;
321
- VALUE version, server_info;
322
- #ifdef HAVE_RUBY_ENCODING_H
323
- rb_encoding *default_internal_enc = rb_default_internal_encoding();
324
- rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
325
- #endif
326
-
327
- Data_Get_Struct(self, MYSQL, client);
328
- REQUIRE_OPEN_DB(client);
329
-
330
- version = rb_hash_new();
331
- rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(client)));
332
- server_info = rb_str_new2(mysql_get_server_info(client));
333
- #ifdef HAVE_RUBY_ENCODING_H
334
- rb_enc_associate(server_info, conn_enc);
335
- if (default_internal_enc) {
336
- server_info = rb_str_export_to_enc(server_info, default_internal_enc);
337
- }
338
- #endif
339
- rb_hash_aset(version, sym_version, server_info);
340
- return version;
341
- }
342
-
343
- static VALUE rb_mysql_client_socket(VALUE self) {
344
- MYSQL * client;
345
- Data_Get_Struct(self, MYSQL, client);
346
- REQUIRE_OPEN_DB(client);
347
- return INT2NUM(client->net.fd);
348
- }
349
-
350
- static VALUE rb_mysql_client_last_id(VALUE self) {
351
- MYSQL * client;
352
- Data_Get_Struct(self, MYSQL, client);
353
- REQUIRE_OPEN_DB(client);
354
- return ULL2NUM(mysql_insert_id(client));
355
- }
356
-
357
- static VALUE rb_mysql_client_affected_rows(VALUE self) {
358
- MYSQL * client;
359
- Data_Get_Struct(self, MYSQL, client);
360
- REQUIRE_OPEN_DB(client);
361
- return ULL2NUM(mysql_affected_rows(client));
362
- }
363
-
364
- static VALUE set_reconnect(VALUE self, VALUE value)
365
- {
366
- my_bool reconnect;
367
- MYSQL * client;
368
-
369
- Data_Get_Struct(self, MYSQL, client);
370
-
371
- if(!NIL_P(value)) {
372
- reconnect = value == Qfalse ? 0 : 1;
373
-
374
- /* set default reconnect behavior */
375
- if (mysql_options(client, MYSQL_OPT_RECONNECT, &reconnect)) {
376
- /* TODO: warning - unable to set reconnect behavior */
377
- rb_warn("%s\n", mysql_error(client));
378
- }
379
- }
380
- return value;
381
- }
382
-
383
- static VALUE set_connect_timeout(VALUE self, VALUE value)
384
- {
385
- unsigned int connect_timeout = 0;
386
- MYSQL * client;
387
-
388
- Data_Get_Struct(self, MYSQL, client);
389
-
390
- if(!NIL_P(value)) {
391
- connect_timeout = NUM2INT(value);
392
- if(0 == connect_timeout) return value;
393
-
394
- /* set default connection timeout behavior */
395
- if (mysql_options(client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) {
396
- /* TODO: warning - unable to set connection timeout */
397
- rb_warn("%s\n", mysql_error(client));
398
- }
399
- }
400
- return value;
401
- }
402
-
403
- static VALUE set_charset_name(VALUE self, VALUE value)
404
- {
405
- char * charset_name;
406
- MYSQL * client;
407
-
408
- Data_Get_Struct(self, MYSQL, client);
409
-
410
- #ifdef HAVE_RUBY_ENCODING_H
411
- VALUE new_encoding, old_encoding;
412
- new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset, 1, value);
413
- if (new_encoding == Qnil) {
414
- rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(value));
415
- } else {
416
- old_encoding = rb_iv_get(self, "@encoding");
417
- if (old_encoding == Qnil) {
418
- rb_iv_set(self, "@encoding", new_encoding);
419
- }
420
- }
421
- #endif
422
-
423
- charset_name = StringValuePtr(value);
424
-
425
- if (mysql_options(client, MYSQL_SET_CHARSET_NAME, charset_name)) {
426
- /* TODO: warning - unable to set charset */
427
- rb_warn("%s\n", mysql_error(client));
428
- }
429
-
430
- return value;
431
- }
432
-
433
- static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher)
434
- {
435
- MYSQL * client;
436
- Data_Get_Struct(self, MYSQL, client);
437
-
438
- if(!NIL_P(ca) || !NIL_P(key)) {
439
- mysql_ssl_set(client,
440
- NIL_P(key) ? NULL : StringValuePtr(key),
441
- NIL_P(cert) ? NULL : StringValuePtr(cert),
442
- NIL_P(ca) ? NULL : StringValuePtr(ca),
443
- NIL_P(capath) ? NULL : StringValuePtr(capath),
444
- NIL_P(cipher) ? NULL : StringValuePtr(cipher));
445
- }
446
-
447
- return self;
448
- }
449
-
450
- static VALUE init_connection(VALUE self)
451
- {
452
- MYSQL * client;
453
- Data_Get_Struct(self, MYSQL, client);
454
-
455
- if (rb_thread_blocking_region(nogvl_init, client, RUBY_UBF_IO, 0) == Qfalse) {
456
- /* TODO: warning - not enough memory? */
457
- return rb_raise_mysql2_error(client);
458
- }
459
-
460
- return self;
461
- }
3
+ VALUE mMysql2, cMysql2Error;
462
4
 
463
5
  /* Ruby Extension initializer */
464
6
  void Init_mysql2() {
465
- mMysql2 = rb_define_module("Mysql2");
466
- cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
467
-
468
- rb_define_alloc_func(cMysql2Client, allocate);
469
-
470
- rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
471
- rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
472
- rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
473
- rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
474
- rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
475
- rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
476
- rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
477
- rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
478
- rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
479
-
480
- rb_define_private_method(cMysql2Client, "reconnect=", set_reconnect, 1);
481
- rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
482
- rb_define_private_method(cMysql2Client, "charset_name=", set_charset_name, 1);
483
- rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
484
- rb_define_private_method(cMysql2Client, "init_connection", init_connection, 0);
485
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 6);
486
-
7
+ mMysql2 = rb_define_module("Mysql2");
487
8
  cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
488
9
 
10
+ init_mysql2_client();
489
11
  init_mysql2_result();
490
-
491
- sym_id = ID2SYM(rb_intern("id"));
492
- sym_version = ID2SYM(rb_intern("version"));
493
- sym_async = ID2SYM(rb_intern("async"));
494
-
495
- intern_encoding_from_charset = rb_intern("encoding_from_charset");
496
12
  }