mysql2 0.5.0 → 0.5.5
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 +5 -5
- data/README.md +150 -44
- data/ext/mysql2/client.c +217 -58
- data/ext/mysql2/client.h +9 -2
- data/ext/mysql2/extconf.rb +60 -7
- data/ext/mysql2/mysql2_ext.c +6 -1
- data/ext/mysql2/mysql2_ext.h +13 -0
- data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
- data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
- data/ext/mysql2/result.c +287 -21
- data/ext/mysql2/result.h +1 -0
- data/ext/mysql2/statement.c +95 -24
- data/lib/mysql2/client.rb +24 -3
- data/lib/mysql2/error.rb +4 -3
- data/lib/mysql2/statement.rb +1 -3
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +8 -3
- data/support/3A79BD29.asc +49 -0
- data/support/5072E1F5.asc +5 -5
- data/support/C74CD1D8.asc +104 -0
- data/support/mysql_enc_to_ruby.rb +6 -1
- data/support/ruby_enc_to_mysql.rb +2 -0
- metadata +11 -55
- data/examples/eventmachine.rb +0 -19
- data/examples/threaded.rb +0 -16
- data/spec/configuration.yml.example +0 -11
- data/spec/em/em_spec.rb +0 -135
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -1072
- data/spec/mysql2/error_spec.rb +0 -78
- data/spec/mysql2/result_spec.rb +0 -485
- data/spec/mysql2/statement_spec.rb +0 -708
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -112
- data/spec/ssl/ca-cert.pem +0 -17
- data/spec/ssl/ca-key.pem +0 -27
- data/spec/ssl/ca.cnf +0 -22
- data/spec/ssl/cert.cnf +0 -22
- data/spec/ssl/client-cert.pem +0 -17
- data/spec/ssl/client-key.pem +0 -27
- data/spec/ssl/client-req.pem +0 -15
- data/spec/ssl/gen_certs.sh +0 -48
- data/spec/ssl/pkcs8-client-key.pem +0 -28
- data/spec/ssl/pkcs8-server-key.pem +0 -28
- data/spec/ssl/server-cert.pem +0 -17
- data/spec/ssl/server-key.pem +0 -27
- data/spec/ssl/server-req.pem +0 -15
- data/spec/test_data +0 -1
data/ext/mysql2/result.c
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
3
|
#include "mysql_enc_to_ruby.h"
|
4
|
+
#define MYSQL2_CHARSETNR_SIZE (sizeof(mysql2_mysql_enc_to_rb)/sizeof(mysql2_mysql_enc_to_rb[0]))
|
4
5
|
|
5
6
|
static rb_encoding *binaryEncoding;
|
6
7
|
|
@@ -16,9 +17,27 @@ static rb_encoding *binaryEncoding;
|
|
16
17
|
*/
|
17
18
|
#define MYSQL2_MIN_TIME 2678400ULL
|
18
19
|
|
20
|
+
#define MYSQL2_MAX_BYTES_PER_CHAR 3
|
21
|
+
|
22
|
+
/* From Mysql documentations:
|
23
|
+
* To distinguish between binary and nonbinary data for string data types,
|
24
|
+
* check whether the charsetnr value is 63. If so, the character set is binary,
|
25
|
+
* which indicates binary rather than nonbinary data. This enables you to distinguish BINARY
|
26
|
+
* from CHAR, VARBINARY from VARCHAR, and the BLOB types from the TEXT types.
|
27
|
+
*/
|
28
|
+
#define MYSQL2_BINARY_CHARSET 63
|
29
|
+
|
30
|
+
#ifndef MYSQL_TYPE_JSON
|
31
|
+
#define MYSQL_TYPE_JSON 245
|
32
|
+
#endif
|
33
|
+
|
34
|
+
#ifndef NEW_TYPEDDATA_WRAPPER
|
35
|
+
#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
|
36
|
+
#endif
|
37
|
+
|
19
38
|
#define GET_RESULT(self) \
|
20
39
|
mysql2_result_wrapper *wrapper; \
|
21
|
-
|
40
|
+
TypedData_Get_Struct(self, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
|
22
41
|
|
23
42
|
typedef struct {
|
24
43
|
int symbolizeKeys;
|
@@ -29,14 +48,15 @@ typedef struct {
|
|
29
48
|
int streaming;
|
30
49
|
ID db_timezone;
|
31
50
|
ID app_timezone;
|
32
|
-
|
51
|
+
int block_given; /* boolean */
|
33
52
|
} result_each_args;
|
34
53
|
|
35
54
|
extern VALUE mMysql2, cMysql2Client, cMysql2Error;
|
36
55
|
static VALUE cMysql2Result, cDateTime, cDate;
|
37
56
|
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
38
57
|
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
|
39
|
-
intern_civil, intern_new_offset, intern_merge, intern_BigDecimal
|
58
|
+
intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
|
59
|
+
intern_query_options;
|
40
60
|
static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
|
41
61
|
sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
|
42
62
|
sym_cache_rows, sym_cast, sym_stream, sym_name;
|
@@ -45,11 +65,11 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
|
|
45
65
|
static void rb_mysql_result_mark(void * wrapper) {
|
46
66
|
mysql2_result_wrapper * w = wrapper;
|
47
67
|
if (w) {
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
68
|
+
rb_gc_mark_movable(w->fields);
|
69
|
+
rb_gc_mark_movable(w->rows);
|
70
|
+
rb_gc_mark_movable(w->encoding);
|
71
|
+
rb_gc_mark_movable(w->client);
|
72
|
+
rb_gc_mark_movable(w->statement);
|
53
73
|
}
|
54
74
|
}
|
55
75
|
|
@@ -111,6 +131,48 @@ static void rb_mysql_result_free(void *ptr) {
|
|
111
131
|
xfree(wrapper);
|
112
132
|
}
|
113
133
|
|
134
|
+
static size_t rb_mysql_result_memsize(const void * wrapper) {
|
135
|
+
const mysql2_result_wrapper * w = wrapper;
|
136
|
+
size_t memsize = sizeof(*w);
|
137
|
+
if (w->stmt_wrapper) {
|
138
|
+
memsize += sizeof(*w->stmt_wrapper);
|
139
|
+
}
|
140
|
+
if (w->client_wrapper) {
|
141
|
+
memsize += sizeof(*w->client_wrapper);
|
142
|
+
}
|
143
|
+
return memsize;
|
144
|
+
}
|
145
|
+
|
146
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
147
|
+
static void rb_mysql_result_compact(void * wrapper) {
|
148
|
+
mysql2_result_wrapper * w = wrapper;
|
149
|
+
if (w) {
|
150
|
+
rb_mysql2_gc_location(w->fields);
|
151
|
+
rb_mysql2_gc_location(w->rows);
|
152
|
+
rb_mysql2_gc_location(w->encoding);
|
153
|
+
rb_mysql2_gc_location(w->client);
|
154
|
+
rb_mysql2_gc_location(w->statement);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
#endif
|
158
|
+
|
159
|
+
static const rb_data_type_t rb_mysql_result_type = {
|
160
|
+
"rb_mysql_result",
|
161
|
+
{
|
162
|
+
rb_mysql_result_mark,
|
163
|
+
rb_mysql_result_free,
|
164
|
+
rb_mysql_result_memsize,
|
165
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
166
|
+
rb_mysql_result_compact,
|
167
|
+
#endif
|
168
|
+
},
|
169
|
+
0,
|
170
|
+
0,
|
171
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
172
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
173
|
+
#endif
|
174
|
+
};
|
175
|
+
|
114
176
|
static VALUE rb_mysql_result_free_(VALUE self) {
|
115
177
|
GET_RESULT(self);
|
116
178
|
rb_mysql_result_free_result(wrapper);
|
@@ -155,11 +217,18 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
|
|
155
217
|
rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
|
156
218
|
rb_field = ID2SYM(rb_field);
|
157
219
|
} else {
|
158
|
-
|
159
|
-
|
160
|
-
if (default_internal_enc) {
|
220
|
+
#ifdef HAVE_RB_ENC_INTERNED_STR
|
221
|
+
rb_field = rb_enc_interned_str(field->name, field->name_length, conn_enc);
|
222
|
+
if (default_internal_enc && default_internal_enc != conn_enc) {
|
223
|
+
rb_field = rb_str_to_interned_str(rb_str_export_to_enc(rb_field, default_internal_enc));
|
224
|
+
}
|
225
|
+
#else
|
226
|
+
rb_field = rb_enc_str_new(field->name, field->name_length, conn_enc);
|
227
|
+
if (default_internal_enc && default_internal_enc != conn_enc) {
|
161
228
|
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
162
229
|
}
|
230
|
+
rb_obj_freeze(rb_field);
|
231
|
+
#endif
|
163
232
|
}
|
164
233
|
rb_ary_store(wrapper->fields, idx, rb_field);
|
165
234
|
}
|
@@ -167,9 +236,171 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
|
|
167
236
|
return rb_field;
|
168
237
|
}
|
169
238
|
|
239
|
+
static VALUE rb_mysql_result_fetch_field_type(VALUE self, unsigned int idx) {
|
240
|
+
VALUE rb_field_type;
|
241
|
+
GET_RESULT(self);
|
242
|
+
|
243
|
+
if (wrapper->fieldTypes == Qnil) {
|
244
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
245
|
+
wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
|
246
|
+
}
|
247
|
+
|
248
|
+
rb_field_type = rb_ary_entry(wrapper->fieldTypes, idx);
|
249
|
+
if (rb_field_type == Qnil) {
|
250
|
+
MYSQL_FIELD *field = NULL;
|
251
|
+
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
252
|
+
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
253
|
+
int precision;
|
254
|
+
|
255
|
+
field = mysql_fetch_field_direct(wrapper->result, idx);
|
256
|
+
|
257
|
+
switch(field->type) {
|
258
|
+
case MYSQL_TYPE_NULL: // NULL
|
259
|
+
rb_field_type = rb_str_new_cstr("null");
|
260
|
+
break;
|
261
|
+
case MYSQL_TYPE_TINY: // signed char
|
262
|
+
rb_field_type = rb_sprintf("tinyint(%ld)", field->length);
|
263
|
+
break;
|
264
|
+
case MYSQL_TYPE_SHORT: // short int
|
265
|
+
rb_field_type = rb_sprintf("smallint(%ld)", field->length);
|
266
|
+
break;
|
267
|
+
case MYSQL_TYPE_YEAR: // short int
|
268
|
+
rb_field_type = rb_sprintf("year(%ld)", field->length);
|
269
|
+
break;
|
270
|
+
case MYSQL_TYPE_INT24: // int
|
271
|
+
rb_field_type = rb_sprintf("mediumint(%ld)", field->length);
|
272
|
+
break;
|
273
|
+
case MYSQL_TYPE_LONG: // int
|
274
|
+
rb_field_type = rb_sprintf("int(%ld)", field->length);
|
275
|
+
break;
|
276
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
277
|
+
rb_field_type = rb_sprintf("bigint(%ld)", field->length);
|
278
|
+
break;
|
279
|
+
case MYSQL_TYPE_FLOAT: // float
|
280
|
+
rb_field_type = rb_sprintf("float(%ld,%d)", field->length, field->decimals);
|
281
|
+
break;
|
282
|
+
case MYSQL_TYPE_DOUBLE: // double
|
283
|
+
rb_field_type = rb_sprintf("double(%ld,%d)", field->length, field->decimals);
|
284
|
+
break;
|
285
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
286
|
+
rb_field_type = rb_str_new_cstr("time");
|
287
|
+
break;
|
288
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
289
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
290
|
+
rb_field_type = rb_str_new_cstr("date");
|
291
|
+
break;
|
292
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
293
|
+
rb_field_type = rb_str_new_cstr("datetime");
|
294
|
+
break;
|
295
|
+
case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
|
296
|
+
rb_field_type = rb_str_new_cstr("timestamp");
|
297
|
+
break;
|
298
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
299
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
300
|
+
/*
|
301
|
+
Handle precision similar to this line from mysql's code:
|
302
|
+
https://github.com/mysql/mysql-server/blob/ea7d2e2d16ac03afdd9cb72a972a95981107bf51/sql/field.cc#L2246
|
303
|
+
*/
|
304
|
+
precision = field->length - (field->decimals > 0 ? 2 : 1);
|
305
|
+
rb_field_type = rb_sprintf("decimal(%d,%d)", precision, field->decimals);
|
306
|
+
break;
|
307
|
+
case MYSQL_TYPE_STRING: // char[]
|
308
|
+
if (field->flags & ENUM_FLAG) {
|
309
|
+
rb_field_type = rb_str_new_cstr("enum");
|
310
|
+
} else if (field->flags & SET_FLAG) {
|
311
|
+
rb_field_type = rb_str_new_cstr("set");
|
312
|
+
} else {
|
313
|
+
if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
|
314
|
+
rb_field_type = rb_sprintf("binary(%ld)", field->length);
|
315
|
+
} else {
|
316
|
+
rb_field_type = rb_sprintf("char(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
|
317
|
+
}
|
318
|
+
}
|
319
|
+
break;
|
320
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
321
|
+
if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
|
322
|
+
rb_field_type = rb_sprintf("varbinary(%ld)", field->length);
|
323
|
+
} else {
|
324
|
+
rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
|
325
|
+
}
|
326
|
+
break;
|
327
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
328
|
+
rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
|
329
|
+
break;
|
330
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
331
|
+
rb_field_type = rb_str_new_cstr("tinyblob");
|
332
|
+
break;
|
333
|
+
case MYSQL_TYPE_BLOB: // char[]
|
334
|
+
if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
|
335
|
+
switch(field->length) {
|
336
|
+
case 255:
|
337
|
+
rb_field_type = rb_str_new_cstr("tinyblob");
|
338
|
+
break;
|
339
|
+
case 65535:
|
340
|
+
rb_field_type = rb_str_new_cstr("blob");
|
341
|
+
break;
|
342
|
+
case 16777215:
|
343
|
+
rb_field_type = rb_str_new_cstr("mediumblob");
|
344
|
+
break;
|
345
|
+
case 4294967295:
|
346
|
+
rb_field_type = rb_str_new_cstr("longblob");
|
347
|
+
default:
|
348
|
+
break;
|
349
|
+
}
|
350
|
+
} else {
|
351
|
+
if (field->length == (255 * MYSQL2_MAX_BYTES_PER_CHAR)) {
|
352
|
+
rb_field_type = rb_str_new_cstr("tinytext");
|
353
|
+
} else if (field->length == (65535 * MYSQL2_MAX_BYTES_PER_CHAR)) {
|
354
|
+
rb_field_type = rb_str_new_cstr("text");
|
355
|
+
} else if (field->length == (16777215 * MYSQL2_MAX_BYTES_PER_CHAR)) {
|
356
|
+
rb_field_type = rb_str_new_cstr("mediumtext");
|
357
|
+
} else if (field->length == 4294967295) {
|
358
|
+
rb_field_type = rb_str_new_cstr("longtext");
|
359
|
+
} else {
|
360
|
+
rb_field_type = rb_sprintf("text(%ld)", field->length);
|
361
|
+
}
|
362
|
+
}
|
363
|
+
break;
|
364
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
365
|
+
rb_field_type = rb_str_new_cstr("mediumblob");
|
366
|
+
break;
|
367
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
368
|
+
rb_field_type = rb_str_new_cstr("longblob");
|
369
|
+
break;
|
370
|
+
case MYSQL_TYPE_BIT: // char[]
|
371
|
+
rb_field_type = rb_sprintf("bit(%ld)", field->length);
|
372
|
+
break;
|
373
|
+
case MYSQL_TYPE_SET: // char[]
|
374
|
+
rb_field_type = rb_str_new_cstr("set");
|
375
|
+
break;
|
376
|
+
case MYSQL_TYPE_ENUM: // char[]
|
377
|
+
rb_field_type = rb_str_new_cstr("enum");
|
378
|
+
break;
|
379
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
380
|
+
rb_field_type = rb_str_new_cstr("geometry");
|
381
|
+
break;
|
382
|
+
case MYSQL_TYPE_JSON: // json
|
383
|
+
rb_field_type = rb_str_new_cstr("json");
|
384
|
+
break;
|
385
|
+
default:
|
386
|
+
rb_field_type = rb_str_new_cstr("unknown");
|
387
|
+
break;
|
388
|
+
}
|
389
|
+
|
390
|
+
rb_enc_associate(rb_field_type, conn_enc);
|
391
|
+
if (default_internal_enc) {
|
392
|
+
rb_field_type = rb_str_export_to_enc(rb_field_type, default_internal_enc);
|
393
|
+
}
|
394
|
+
|
395
|
+
rb_ary_store(wrapper->fieldTypes, idx, rb_field_type);
|
396
|
+
}
|
397
|
+
|
398
|
+
return rb_field_type;
|
399
|
+
}
|
400
|
+
|
170
401
|
static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
|
171
402
|
/* if binary flag is set, respect its wishes */
|
172
|
-
if (field.flags & BINARY_FLAG && field.charsetnr ==
|
403
|
+
if (field.flags & BINARY_FLAG && field.charsetnr == MYSQL2_BINARY_CHARSET) {
|
173
404
|
rb_enc_associate(val, binaryEncoding);
|
174
405
|
} else if (!field.charsetnr) {
|
175
406
|
/* MySQL 4.x may not provide an encoding, binary will get the bytes through */
|
@@ -179,7 +410,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
179
410
|
const char *enc_name;
|
180
411
|
int enc_index;
|
181
412
|
|
182
|
-
enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
|
413
|
+
enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
|
414
|
+
|
183
415
|
if (enc_name != NULL) {
|
184
416
|
/* use the field encoding we were able to match */
|
185
417
|
enc_index = rb_enc_find_index(enc_name);
|
@@ -693,7 +925,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
693
925
|
|
694
926
|
GET_RESULT(self);
|
695
927
|
|
696
|
-
defaults =
|
928
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
697
929
|
Check_Type(defaults, T_HASH);
|
698
930
|
if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
|
699
931
|
symbolizeKeys = 1;
|
@@ -713,6 +945,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
713
945
|
return wrapper->fields;
|
714
946
|
}
|
715
947
|
|
948
|
+
static VALUE rb_mysql_result_fetch_field_types(VALUE self) {
|
949
|
+
unsigned int i = 0;
|
950
|
+
|
951
|
+
GET_RESULT(self);
|
952
|
+
|
953
|
+
if (wrapper->fieldTypes == Qnil) {
|
954
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
955
|
+
wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
|
956
|
+
}
|
957
|
+
|
958
|
+
if ((my_ulonglong)RARRAY_LEN(wrapper->fieldTypes) != wrapper->numberOfFields) {
|
959
|
+
for (i=0; i<wrapper->numberOfFields; i++) {
|
960
|
+
rb_mysql_result_fetch_field_type(self, i);
|
961
|
+
}
|
962
|
+
}
|
963
|
+
|
964
|
+
return wrapper->fieldTypes;
|
965
|
+
}
|
966
|
+
|
716
967
|
static VALUE rb_mysql_result_each_(VALUE self,
|
717
968
|
VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
|
718
969
|
const result_each_args *args)
|
@@ -738,7 +989,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
738
989
|
row = fetch_row_func(self, fields, args);
|
739
990
|
if (row != Qnil) {
|
740
991
|
wrapper->numberOfRows++;
|
741
|
-
if (args->block_given
|
992
|
+
if (args->block_given) {
|
742
993
|
rb_yield(row);
|
743
994
|
}
|
744
995
|
}
|
@@ -788,7 +1039,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
788
1039
|
return Qnil;
|
789
1040
|
}
|
790
1041
|
|
791
|
-
if (args->block_given
|
1042
|
+
if (args->block_given) {
|
792
1043
|
rb_yield(row);
|
793
1044
|
}
|
794
1045
|
}
|
@@ -806,7 +1057,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
806
1057
|
|
807
1058
|
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
808
1059
|
result_each_args args;
|
809
|
-
VALUE defaults, opts,
|
1060
|
+
VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
|
810
1061
|
ID db_timezone, app_timezone, dbTz, appTz;
|
811
1062
|
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
812
1063
|
|
@@ -816,9 +1067,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
816
1067
|
rb_raise(cMysql2Error, "Statement handle already closed");
|
817
1068
|
}
|
818
1069
|
|
819
|
-
defaults =
|
1070
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
820
1071
|
Check_Type(defaults, T_HASH);
|
821
|
-
|
1072
|
+
|
1073
|
+
// A block can be passed to this method, but since we don't call the block directly from C,
|
1074
|
+
// we don't need to capture it into a variable here with the "&" scan arg.
|
1075
|
+
if (rb_scan_args(argc, argv, "01", &opts) == 1) {
|
822
1076
|
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
823
1077
|
} else {
|
824
1078
|
opts = defaults;
|
@@ -884,7 +1138,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
884
1138
|
args.cast = cast;
|
885
1139
|
args.db_timezone = db_timezone;
|
886
1140
|
args.app_timezone = app_timezone;
|
887
|
-
args.block_given =
|
1141
|
+
args.block_given = rb_block_given_p();
|
888
1142
|
|
889
1143
|
if (wrapper->stmt_wrapper) {
|
890
1144
|
fetch_row_func = rb_mysql_result_fetch_row_stmt;
|
@@ -921,13 +1175,18 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
921
1175
|
VALUE obj;
|
922
1176
|
mysql2_result_wrapper * wrapper;
|
923
1177
|
|
1178
|
+
#ifdef NEW_TYPEDDATA_WRAPPER
|
1179
|
+
obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
|
1180
|
+
#else
|
924
1181
|
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
1182
|
+
#endif
|
925
1183
|
wrapper->numberOfFields = 0;
|
926
1184
|
wrapper->numberOfRows = 0;
|
927
1185
|
wrapper->lastRowProcessed = 0;
|
928
1186
|
wrapper->resultFreed = 0;
|
929
1187
|
wrapper->result = r;
|
930
1188
|
wrapper->fields = Qnil;
|
1189
|
+
wrapper->fieldTypes = Qnil;
|
931
1190
|
wrapper->rows = Qnil;
|
932
1191
|
wrapper->encoding = encoding;
|
933
1192
|
wrapper->streamingComplete = 0;
|
@@ -949,7 +1208,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
949
1208
|
}
|
950
1209
|
|
951
1210
|
rb_obj_call_init(obj, 0, NULL);
|
952
|
-
|
1211
|
+
rb_ivar_set(obj, intern_query_options, options);
|
953
1212
|
|
954
1213
|
/* Options that cannot be changed in results.each(...) { |row| }
|
955
1214
|
* should be processed here. */
|
@@ -960,11 +1219,17 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
960
1219
|
|
961
1220
|
void init_mysql2_result() {
|
962
1221
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
1222
|
+
rb_global_variable(&cDate);
|
963
1223
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
1224
|
+
rb_global_variable(&cDateTime);
|
964
1225
|
|
965
1226
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
1227
|
+
rb_undef_alloc_func(cMysql2Result);
|
1228
|
+
rb_global_variable(&cMysql2Result);
|
1229
|
+
|
966
1230
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
967
1231
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
1232
|
+
rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);
|
968
1233
|
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
969
1234
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
970
1235
|
rb_define_alias(cMysql2Result, "size", "count");
|
@@ -978,6 +1243,7 @@ void init_mysql2_result() {
|
|
978
1243
|
intern_civil = rb_intern("civil");
|
979
1244
|
intern_new_offset = rb_intern("new_offset");
|
980
1245
|
intern_BigDecimal = rb_intern("BigDecimal");
|
1246
|
+
intern_query_options = rb_intern("@query_options");
|
981
1247
|
|
982
1248
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
983
1249
|
sym_as = ID2SYM(rb_intern("as"));
|
data/ext/mysql2/result.h
CHANGED
data/ext/mysql2/statement.c
CHANGED
@@ -3,11 +3,16 @@
|
|
3
3
|
extern VALUE mMysql2, cMysql2Error;
|
4
4
|
static VALUE cMysql2Statement, cBigDecimal, cDateTime, cDate;
|
5
5
|
static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_merge_bang;
|
6
|
-
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year
|
6
|
+
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year,
|
7
|
+
intern_query_options;
|
8
|
+
|
9
|
+
#ifndef NEW_TYPEDDATA_WRAPPER
|
10
|
+
#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
|
11
|
+
#endif
|
7
12
|
|
8
13
|
#define GET_STATEMENT(self) \
|
9
14
|
mysql_stmt_wrapper *stmt_wrapper; \
|
10
|
-
|
15
|
+
TypedData_Get_Struct(self, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); \
|
11
16
|
if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
|
12
17
|
if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
|
13
18
|
|
@@ -15,8 +20,44 @@ static void rb_mysql_stmt_mark(void * ptr) {
|
|
15
20
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
16
21
|
if (!stmt_wrapper) return;
|
17
22
|
|
18
|
-
|
23
|
+
rb_gc_mark_movable(stmt_wrapper->client);
|
24
|
+
}
|
25
|
+
|
26
|
+
static void rb_mysql_stmt_free(void *ptr) {
|
27
|
+
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
28
|
+
decr_mysql2_stmt(stmt_wrapper);
|
29
|
+
}
|
30
|
+
|
31
|
+
static size_t rb_mysql_stmt_memsize(const void * ptr) {
|
32
|
+
const mysql_stmt_wrapper *stmt_wrapper = ptr;
|
33
|
+
return sizeof(*stmt_wrapper);
|
34
|
+
}
|
35
|
+
|
36
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
37
|
+
static void rb_mysql_stmt_compact(void * ptr) {
|
38
|
+
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
39
|
+
if (!stmt_wrapper) return;
|
40
|
+
|
41
|
+
rb_mysql2_gc_location(stmt_wrapper->client);
|
19
42
|
}
|
43
|
+
#endif
|
44
|
+
|
45
|
+
static const rb_data_type_t rb_mysql_statement_type = {
|
46
|
+
"rb_mysql_statement",
|
47
|
+
{
|
48
|
+
rb_mysql_stmt_mark,
|
49
|
+
rb_mysql_stmt_free,
|
50
|
+
rb_mysql_stmt_memsize,
|
51
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
52
|
+
rb_mysql_stmt_compact,
|
53
|
+
#endif
|
54
|
+
},
|
55
|
+
0,
|
56
|
+
0,
|
57
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
58
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
59
|
+
#endif
|
60
|
+
};
|
20
61
|
|
21
62
|
static void *nogvl_stmt_close(void *ptr) {
|
22
63
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
@@ -27,11 +68,6 @@ static void *nogvl_stmt_close(void *ptr) {
|
|
27
68
|
return NULL;
|
28
69
|
}
|
29
70
|
|
30
|
-
static void rb_mysql_stmt_free(void *ptr) {
|
31
|
-
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
32
|
-
decr_mysql2_stmt(stmt_wrapper);
|
33
|
-
}
|
34
|
-
|
35
71
|
void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
|
36
72
|
stmt_wrapper->refcount--;
|
37
73
|
|
@@ -45,7 +81,7 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
45
81
|
VALUE e;
|
46
82
|
GET_CLIENT(stmt_wrapper->client);
|
47
83
|
VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
|
48
|
-
VALUE rb_sql_state =
|
84
|
+
VALUE rb_sql_state = rb_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
|
49
85
|
|
50
86
|
rb_encoding *conn_enc;
|
51
87
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
@@ -95,7 +131,11 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
95
131
|
|
96
132
|
Check_Type(sql, T_STRING);
|
97
133
|
|
134
|
+
#ifdef NEW_TYPEDDATA_WRAPPER
|
135
|
+
rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper);
|
136
|
+
#else
|
98
137
|
rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
|
138
|
+
#endif
|
99
139
|
{
|
100
140
|
stmt_wrapper->client = rb_client;
|
101
141
|
stmt_wrapper->refcount = 1;
|
@@ -403,6 +443,39 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
|
403
443
|
}
|
404
444
|
}
|
405
445
|
|
446
|
+
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
447
|
+
current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
|
448
|
+
(void)RB_GC_GUARD(current);
|
449
|
+
Check_Type(current, T_HASH);
|
450
|
+
|
451
|
+
// Merge in hash opts/keyword arguments
|
452
|
+
if (!NIL_P(opts)) {
|
453
|
+
rb_funcall(current, intern_merge_bang, 1, opts);
|
454
|
+
}
|
455
|
+
|
456
|
+
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
457
|
+
|
458
|
+
// From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no
|
459
|
+
// Ruby API calls are allowed so that GC is not invoked. If the connection is
|
460
|
+
// in results-streaming-mode for Statement A, and in the middle Statement B
|
461
|
+
// gets garbage collected, a message will be sent to the server notifying it
|
462
|
+
// to release Statement B, resulting in the following error:
|
463
|
+
// Commands out of sync; you can't run this command now
|
464
|
+
//
|
465
|
+
// In streaming mode, statement execute must return a cursor because we
|
466
|
+
// cannot prevent other Statement objects from being garbage collected
|
467
|
+
// between fetches of each row of the result set. The following error
|
468
|
+
// occurs if cursor mode is not set:
|
469
|
+
// Row retrieval was canceled by mysql_stmt_close
|
470
|
+
|
471
|
+
if (is_streaming) {
|
472
|
+
unsigned long type = CURSOR_TYPE_READ_ONLY;
|
473
|
+
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) {
|
474
|
+
FREE_BINDS;
|
475
|
+
rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY");
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
406
479
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
|
407
480
|
FREE_BINDS;
|
408
481
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
@@ -414,31 +487,20 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
|
414
487
|
if (metadata == NULL) {
|
415
488
|
if (mysql_stmt_errno(stmt) != 0) {
|
416
489
|
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
417
|
-
wrapper->
|
490
|
+
wrapper->active_fiber = Qnil;
|
418
491
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
419
492
|
}
|
420
493
|
// no data and no error, so query was not a SELECT
|
421
494
|
return Qnil;
|
422
495
|
}
|
423
496
|
|
424
|
-
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
425
|
-
current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
|
426
|
-
(void)RB_GC_GUARD(current);
|
427
|
-
Check_Type(current, T_HASH);
|
428
|
-
|
429
|
-
// Merge in hash opts/keyword arguments
|
430
|
-
if (!NIL_P(opts)) {
|
431
|
-
rb_funcall(current, intern_merge_bang, 1, opts);
|
432
|
-
}
|
433
|
-
|
434
|
-
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
435
497
|
if (!is_streaming) {
|
436
|
-
//
|
498
|
+
// receive the whole result set from the server
|
437
499
|
if (mysql_stmt_store_result(stmt)) {
|
438
500
|
mysql_free_result(metadata);
|
439
501
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
440
502
|
}
|
441
|
-
wrapper->
|
503
|
+
wrapper->active_fiber = Qnil;
|
442
504
|
}
|
443
505
|
|
444
506
|
resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
|
@@ -479,7 +541,7 @@ static VALUE rb_mysql_stmt_fields(VALUE self) {
|
|
479
541
|
if (metadata == NULL) {
|
480
542
|
if (mysql_stmt_errno(stmt) != 0) {
|
481
543
|
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
482
|
-
wrapper->
|
544
|
+
wrapper->active_fiber = Qnil;
|
483
545
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
484
546
|
}
|
485
547
|
// no data and no error, so query was not a SELECT
|
@@ -549,10 +611,18 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
|
|
549
611
|
|
550
612
|
void init_mysql2_statement() {
|
551
613
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
614
|
+
rb_global_variable(&cDate);
|
615
|
+
|
552
616
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
617
|
+
rb_global_variable(&cDateTime);
|
618
|
+
|
553
619
|
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
620
|
+
rb_global_variable(&cBigDecimal);
|
554
621
|
|
555
622
|
cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
|
623
|
+
rb_undef_alloc_func(cMysql2Statement);
|
624
|
+
rb_global_variable(&cMysql2Statement);
|
625
|
+
|
556
626
|
rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
|
557
627
|
rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
|
558
628
|
rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
|
@@ -577,4 +647,5 @@ void init_mysql2_statement() {
|
|
577
647
|
|
578
648
|
intern_to_s = rb_intern("to_s");
|
579
649
|
intern_merge_bang = rb_intern("merge!");
|
650
|
+
intern_query_options = rb_intern("@query_options");
|
580
651
|
}
|