ruby-mysql-ext 2.9.8 → 2.9.9

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.
data/ext/mysql/ext.c ADDED
@@ -0,0 +1,597 @@
1
+ #include "ruby.h"
2
+
3
+ typedef struct {
4
+ unsigned char *ptr;
5
+ unsigned char *endp;
6
+ } packet_data_t;
7
+
8
+ static VALUE cMysql;
9
+ static VALUE cPacket;
10
+ static VALUE cMysqlTime;
11
+ static VALUE cProtocol;
12
+ static VALUE cStmtRawRecord;
13
+ static VALUE cExecutePacket;
14
+ static VALUE cCharset;
15
+ static VALUE eProtocolError;
16
+
17
+ static VALUE packet_s_lcb(VALUE klass, VALUE val)
18
+ {
19
+ unsigned long long n;
20
+ unsigned char buf[9];
21
+ int i;
22
+
23
+ if (val == Qnil)
24
+ return rb_str_new("\xfb", 1);
25
+ n = NUM2ULL(val);
26
+ if (n < 251) {
27
+ buf[0] = n;
28
+ return rb_str_new(buf, 1);
29
+ }
30
+ if (n < 65536) {
31
+ buf[0] = '\xfc';
32
+ buf[1] = n % 256;
33
+ buf[2] = n / 256;
34
+ return rb_str_new(buf, 3);
35
+ }
36
+ if (n < 16777216) {
37
+ buf[0] = '\xfd';
38
+ buf[1] = n % 256;
39
+ n /= 256;
40
+ buf[2] = n % 256;
41
+ buf[3] = n / 256;
42
+ return rb_str_new(buf, 4);
43
+ }
44
+ buf[0] = '\xfe';
45
+ #ifdef WORDS_BIGENDIAN
46
+ for (i = 0; i < 8; i++) {
47
+ buf[i+1] = *((char *)&n + 7-i);
48
+ }
49
+ #else
50
+ memcpy(&buf[1], (char *)&n, 8);
51
+ #endif
52
+ return rb_str_new(buf, 9);
53
+ }
54
+
55
+ static VALUE packet_s_lcs(VALUE klass, VALUE val)
56
+ {
57
+ VALUE ret = packet_s_lcb(klass, ULONG2NUM(RSTRING_LEN(val)));
58
+ return rb_str_cat(ret, RSTRING_PTR(val), RSTRING_LEN(val));
59
+ }
60
+
61
+ static VALUE packet_allocate(VALUE klass)
62
+ {
63
+ packet_data_t *data;
64
+
65
+ data = xmalloc(sizeof *data);
66
+ data->ptr = NULL;
67
+ data->endp = NULL;
68
+ return Data_Wrap_Struct(klass, 0, xfree, data);
69
+ }
70
+
71
+ static VALUE packet_initialize(VALUE obj, VALUE buf)
72
+ {
73
+ packet_data_t *data;
74
+
75
+ Data_Get_Struct(obj, packet_data_t, data);
76
+ rb_ivar_set(obj, rb_intern("buf"), buf);
77
+ data->ptr = RSTRING_PTR(buf);
78
+ data->endp = data->ptr + RSTRING_LEN(buf);
79
+ }
80
+
81
+ #define NIL_VALUE 0xFFFFFFFFFFFFFFFF
82
+
83
+ static unsigned long long _packet_lcb(packet_data_t *data)
84
+ {
85
+ unsigned char v;
86
+ unsigned long long n = 0;
87
+
88
+ if (data->ptr >= data->endp)
89
+ return NIL_VALUE;
90
+
91
+ v = *data->ptr++;
92
+ switch (v) {
93
+ case 0xfb:
94
+ return NIL_VALUE;
95
+ case 0xfc:
96
+ n = *data->ptr++;
97
+ n |= ((unsigned int)*data->ptr++) << 8;
98
+ return n;
99
+ case 0xfd:
100
+ n = *data->ptr++;
101
+ n |= ((unsigned int)*data->ptr++) << 8;
102
+ n |= ((unsigned int)*data->ptr++) << 16;
103
+ return n;
104
+ case 0xfe:
105
+ n = *data->ptr++;
106
+ n |= ((unsigned long long)*data->ptr++) << 8;
107
+ n |= ((unsigned long long)*data->ptr++) << 16;
108
+ n |= ((unsigned long long)*data->ptr++) << 24;
109
+ n |= ((unsigned long long)*data->ptr++) << 32;
110
+ n |= ((unsigned long long)*data->ptr++) << 40;
111
+ n |= ((unsigned long long)*data->ptr++) << 48;
112
+ n |= ((unsigned long long)*data->ptr++) << 56;
113
+ return n;
114
+ default:
115
+ return v;
116
+ }
117
+ }
118
+
119
+ static VALUE packet_lcb(VALUE obj)
120
+ {
121
+ packet_data_t *data;
122
+ unsigned char v;
123
+ unsigned long long n;
124
+
125
+ Data_Get_Struct(obj, packet_data_t, data);
126
+ n = _packet_lcb(data);
127
+ if (n == NIL_VALUE)
128
+ return Qnil;
129
+ return ULL2NUM(n);
130
+ }
131
+
132
+ static VALUE _packet_lcs(packet_data_t *data)
133
+ {
134
+ unsigned long long l;
135
+ VALUE ret;
136
+
137
+ l = _packet_lcb(data);
138
+ if (l == NIL_VALUE)
139
+ return Qnil;
140
+ if (data->ptr+l > data->endp)
141
+ l = data->endp - data->ptr;
142
+ ret = rb_str_new(data->ptr, l);
143
+ data->ptr += l;
144
+ return ret;
145
+ }
146
+
147
+ static VALUE packet_lcs(VALUE obj)
148
+ {
149
+ packet_data_t *data;
150
+ unsigned long long l;
151
+ VALUE ret;
152
+
153
+ Data_Get_Struct(obj, packet_data_t, data);
154
+ return _packet_lcs(data);
155
+ }
156
+
157
+ static VALUE packet_read(VALUE obj, VALUE len)
158
+ {
159
+ packet_data_t *data;
160
+ unsigned long long l = NUM2ULL(len);
161
+ VALUE ret;
162
+
163
+ Data_Get_Struct(obj, packet_data_t, data);
164
+ if (data->ptr+l > data->endp)
165
+ l = data->endp - data->ptr;
166
+ ret = rb_str_new(data->ptr, l);
167
+ data->ptr += l;
168
+ return ret;
169
+ }
170
+
171
+ static VALUE packet_string(VALUE obj)
172
+ {
173
+ packet_data_t *data;
174
+ unsigned char *p;
175
+ VALUE ret;
176
+
177
+ Data_Get_Struct(obj, packet_data_t, data);
178
+ p = data->ptr;
179
+ while (p < data->endp && *p++ != '\0')
180
+ ;
181
+ ret = rb_str_new(data->ptr, (p - data->ptr)-1);
182
+ data->ptr = p;
183
+ return ret;
184
+ }
185
+
186
+ static VALUE packet_utiny(VALUE obj)
187
+ {
188
+ packet_data_t *data;
189
+
190
+ Data_Get_Struct(obj, packet_data_t, data);
191
+ return UINT2NUM(*data->ptr++);
192
+ }
193
+
194
+ static VALUE packet_ushort(VALUE obj)
195
+ {
196
+ packet_data_t *data;
197
+ unsigned short n;
198
+
199
+ Data_Get_Struct(obj, packet_data_t, data);
200
+ n = *data->ptr++;
201
+ n |= *data->ptr++ * 0x100;
202
+ return UINT2NUM(n);
203
+ }
204
+
205
+ static VALUE packet_ulong(VALUE obj)
206
+ {
207
+ packet_data_t *data;
208
+ unsigned long n;
209
+
210
+ Data_Get_Struct(obj, packet_data_t, data);
211
+ n = *data->ptr++;
212
+ n |= *data->ptr++ * 0x100;
213
+ n |= *data->ptr++ * 0x10000;
214
+ n |= *data->ptr++ * 0x1000000;
215
+ return UINT2NUM(n);
216
+ }
217
+
218
+ static VALUE packet_eofQ(VALUE obj)
219
+ {
220
+ packet_data_t *data;
221
+
222
+ Data_Get_Struct(obj, packet_data_t, data);
223
+ if (*data->ptr == 0xfe && data->endp - data->ptr == 5)
224
+ return Qtrue;
225
+ else
226
+ return Qfalse;
227
+ }
228
+
229
+ static VALUE packet_to_s(VALUE obj)
230
+ {
231
+ packet_data_t *data;
232
+
233
+ Data_Get_Struct(obj, packet_data_t, data);
234
+ return rb_str_new(data->ptr, data->endp-data->ptr);
235
+ }
236
+
237
+ enum {
238
+ TYPE_DECIMAL = 0,
239
+ TYPE_TINY = 1,
240
+ TYPE_SHORT = 2,
241
+ TYPE_LONG = 3,
242
+ TYPE_FLOAT = 4,
243
+ TYPE_DOUBLE = 5,
244
+ TYPE_NULL = 6,
245
+ TYPE_TIMESTAMP = 7,
246
+ TYPE_LONGLONG = 8,
247
+ TYPE_INT24 = 9,
248
+ TYPE_DATE = 10,
249
+ TYPE_TIME = 11,
250
+ TYPE_DATETIME = 12,
251
+ TYPE_YEAR = 13,
252
+ TYPE_NEWDATE = 14,
253
+ TYPE_VARCHAR = 15,
254
+ TYPE_BIT = 16,
255
+ TYPE_NEWDECIMAL = 246,
256
+ TYPE_ENUM = 247,
257
+ TYPE_SET = 248,
258
+ TYPE_TINY_BLOB = 249,
259
+ TYPE_MEDIUM_BLOB = 250,
260
+ TYPE_LONG_BLOB = 251,
261
+ TYPE_BLOB = 252,
262
+ TYPE_VAR_STRING = 253,
263
+ TYPE_STRING = 254,
264
+ TYPE_GEOMETRY = 255
265
+ };
266
+
267
+ #define UNSIGNED_FLAG 32
268
+ #define BINARY_CHARSET_NUMBER 63
269
+
270
+ static VALUE _protocol_net2value(packet_data_t *data, int type, int uflag)
271
+ {
272
+ unsigned long n;
273
+ unsigned long long ll;
274
+ float f;
275
+ double fd;
276
+ int len;
277
+ int sign;
278
+ unsigned long y, m, d, h, mi, s, bs;
279
+ unsigned char buf[12];
280
+
281
+ switch (type) {
282
+ case TYPE_STRING:
283
+ case TYPE_VAR_STRING:
284
+ case TYPE_NEWDECIMAL:
285
+ case TYPE_BLOB:
286
+ case TYPE_BIT:
287
+ return _packet_lcs(data);
288
+ case TYPE_TINY:
289
+ n = *data->ptr++;
290
+ return uflag ? INT2FIX(n) : INT2FIX((char)n);
291
+ case TYPE_SHORT:
292
+ case TYPE_YEAR:
293
+ n = *data->ptr++;
294
+ n |= *data->ptr++ * 0x100;
295
+ return uflag ? INT2FIX(n) : INT2FIX((short)n);
296
+ case TYPE_INT24:
297
+ case TYPE_LONG:
298
+ n = *data->ptr++;
299
+ n |= *data->ptr++ * 0x100;
300
+ n |= *data->ptr++ * 0x10000;
301
+ n |= *data->ptr++ * 0x1000000;
302
+ return uflag ? UINT2NUM(n) : INT2NUM((long)n);
303
+ case TYPE_LONGLONG:
304
+ n = *data->ptr++;
305
+ n |= *data->ptr++ * 0x100;
306
+ n |= *data->ptr++ * 0x10000;
307
+ n |= *data->ptr++ * 0x1000000;
308
+ ll = *data->ptr++;
309
+ ll |= *data->ptr++ * 0x100;
310
+ ll |= *data->ptr++ * 0x10000;
311
+ ll |= *data->ptr++ * 0x1000000;
312
+ ll = (ll<<32) + n;
313
+ return uflag ? ULL2NUM(ll) : LL2NUM((long long)(ll));
314
+ case TYPE_FLOAT:
315
+ memcpy(&f, data->ptr, 4);
316
+ data->ptr += 4;
317
+ return rb_float_new(f);
318
+ case TYPE_DOUBLE:
319
+ memcpy(&fd, data->ptr, 8);
320
+ data->ptr += 8;
321
+ return rb_float_new(fd);
322
+ case TYPE_DATE:
323
+ len = *data->ptr++;
324
+ memset(buf, 0, sizeof(buf));
325
+ memcpy(buf, data->ptr, len);
326
+ data->ptr += len;
327
+ y = buf[0] | buf[1]<<8;
328
+ m = buf[2];
329
+ d = buf[3];
330
+ return rb_funcall(cMysqlTime, rb_intern("new"), 6, ULONG2NUM(y), ULONG2NUM(m), ULONG2NUM(d), Qnil, Qnil, Qnil);
331
+ case TYPE_DATETIME:
332
+ case TYPE_TIMESTAMP:
333
+ len = *data->ptr++;
334
+ memset(buf, 0, sizeof(buf));
335
+ memcpy(buf, data->ptr, len);
336
+ data->ptr += len;
337
+ y = buf[0] | buf[1]<<8;
338
+ m = buf[2];
339
+ d = buf[3];
340
+ h = buf[4];
341
+ mi = buf[5];
342
+ s = buf[6];
343
+ bs = buf[7] | buf[8]<<8 | buf[9]<<16 | buf[10]<<24;
344
+ return rb_funcall(cMysqlTime, rb_intern("new"), 8, ULONG2NUM(y), ULONG2NUM(m), ULONG2NUM(d), ULONG2NUM(h), ULONG2NUM(mi), ULONG2NUM(s), Qfalse, ULONG2NUM(bs));
345
+ case TYPE_TIME:
346
+ len = *data->ptr++;
347
+ memset(buf, 0, sizeof(buf));
348
+ memcpy(buf, data->ptr, len);
349
+ data->ptr += len;
350
+ sign = buf[0];
351
+ d = buf[1] | buf[2]<<8 | buf[3]<<16 | buf[4]<<24;;
352
+ h = buf[5];
353
+ mi = buf[6];
354
+ s = buf[7];
355
+ bs = buf[8] | buf[9]<<8 | buf[10]<<16 | buf[11]<<24;;
356
+ h += d * 24;
357
+ return rb_funcall(cMysqlTime, rb_intern("new"), 8, ULONG2NUM(0), ULONG2NUM(0), ULONG2NUM(0), ULONG2NUM(h), ULONG2NUM(mi), ULONG2NUM(s), (sign != 0 ? Qtrue : Qfalse), ULONG2NUM(bs));
358
+ default:
359
+ rb_raise(rb_eRuntimeError, "%s", "not implemented: type=#{%d}", type);
360
+ }
361
+ }
362
+
363
+ static VALUE _protocol_value2net(VALUE obj, VALUE netval, VALUE types)
364
+ {
365
+ int type;
366
+ char *ptr;
367
+ int len;
368
+ unsigned char buf[8];
369
+ long n;
370
+ long long ll;
371
+ unsigned long long ull;
372
+ double dbl;
373
+
374
+ if (obj == Qnil) {
375
+ type = TYPE_NULL;
376
+ ptr = NULL;
377
+ len = 0;
378
+ } else if (FIXNUM_P(obj)) {
379
+ int flag = 0;
380
+
381
+ n = FIX2LONG(obj);
382
+ if (n >= 0) {
383
+ flag = 0x8000;
384
+ }
385
+ ptr = (char *)&n;
386
+ #ifdef WORDS_BIGENDIAN
387
+ for (i=0; i<sizeof(n); i++) {
388
+ buf[i] = n % 0x100;
389
+ n /= 0x100;
390
+ }
391
+ ptr = buf;
392
+ #endif
393
+ if (-0x80 <= n && n < 0x100) {
394
+ type = TYPE_TINY | flag;
395
+ len = 1;
396
+ } else if (-0x8000 <= n && n < 0x10000) {
397
+ type = TYPE_SHORT | flag;
398
+ len = 2;
399
+ #if SIZEOF_LONG == 4
400
+ } else {
401
+ type = TYPE_LONG | flag;
402
+ len = 4;
403
+ #else
404
+ } else if (-0x80000000 <= n && n < 0x100000000) {
405
+ type = TYPE_LONG | flag;
406
+ len = 4;
407
+ } else {
408
+ type = TYPE_LONGLONG | flag;
409
+ len = 8;
410
+ #endif
411
+ }
412
+ } else if (TYPE(obj) == T_BIGNUM) {
413
+ if (RBIGNUM_SIGN(obj)) {
414
+ ull = NUM2ULL(obj);
415
+ ptr = (char *)&ull;
416
+ type = TYPE_LONGLONG | 0x8000;
417
+ len = sizeof(ull);
418
+ #ifdef WORDS_BIGENDIAN
419
+ for (i=0; i<len; i++) {
420
+ buf[i] = ull % 0x100;
421
+ ull /= 0x100;
422
+ }
423
+ ptr = buf;
424
+ #endif
425
+ } else {
426
+ ll = NUM2LL(obj);
427
+ ptr = (char *)&ll;
428
+ type = TYPE_LONGLONG;
429
+ len = sizeof(ll);
430
+ #ifdef WORDS_BIGENDIAN
431
+ for (i=0; i<len; i++) {
432
+ buf[i] = ll % 0x100;
433
+ ll /= 0x100;
434
+ }
435
+ ptr = buf;
436
+ #endif
437
+ }
438
+ } else if (rb_obj_is_kind_of(obj, rb_cFloat)) {
439
+ dbl = NUM2DBL(obj);
440
+ type = TYPE_DOUBLE;
441
+ ptr = (char *)&dbl;
442
+ len = sizeof(dbl);
443
+ } else if (rb_obj_is_kind_of(obj, rb_cString)) {
444
+ VALUE val;
445
+
446
+ type = TYPE_STRING;
447
+ val = packet_s_lcs(0, obj);
448
+ ptr = RSTRING_PTR(val);
449
+ len = RSTRING_LEN(val);
450
+ } else if (rb_obj_is_kind_of(obj, cMysqlTime) || rb_obj_is_kind_of(obj, rb_cTime)) {
451
+ int year, month, day, hour, min, sec;
452
+
453
+ year = FIX2INT(rb_funcall(obj, rb_intern("year"), 0));
454
+ month = FIX2INT(rb_funcall(obj, rb_intern("month"), 0));
455
+ day = FIX2INT(rb_funcall(obj, rb_intern("day"), 0));
456
+ hour = FIX2INT(rb_funcall(obj, rb_intern("hour"), 0));
457
+ min = FIX2INT(rb_funcall(obj, rb_intern("min"), 0));
458
+ sec = FIX2INT(rb_funcall(obj, rb_intern("sec"), 0));
459
+ type = TYPE_DATETIME;
460
+ buf[0] = 7;
461
+ buf[1] = year & 0xff;
462
+ buf[2] = (year >> 8) & 0xff;
463
+ buf[3] = month;
464
+ buf[4] = day;
465
+ buf[5] = hour;
466
+ buf[6] = min;
467
+ buf[7] = sec;
468
+ ptr = buf;
469
+ len = 8;
470
+ } else {
471
+ rb_raise(eProtocolError, "class %s is not supported", rb_class2name(rb_obj_class(obj)));
472
+ }
473
+ rb_str_cat(netval, ptr, len);
474
+ buf[0] = type % 256;
475
+ buf[1] = type / 256;
476
+ rb_str_cat(types, buf, 2);
477
+ return Qnil;
478
+ }
479
+
480
+ VALUE stmt_raw_record_parse_record_packet(VALUE obj)
481
+ {
482
+ VALUE packet;
483
+ VALUE fields;
484
+ packet_data_t *data;
485
+ int nfields;
486
+ int bitmap_length;
487
+ char *bitmap;
488
+ int i;
489
+ VALUE rec;
490
+
491
+ packet = rb_iv_get(obj, "@packet");
492
+ fields = rb_iv_get(obj, "@fields");
493
+ Data_Get_Struct(packet, packet_data_t, data);
494
+ data->ptr++;
495
+ nfields = RARRAY_LEN(fields);
496
+ bitmap_length = (nfields+7+2)/8;
497
+ bitmap = data->ptr;
498
+ data->ptr += bitmap_length;
499
+ rec = rb_ary_new2(nfields);
500
+ for (i = 0; i < nfields; i++) {
501
+ if ((bitmap[(i+2)/8] >> (i+2)%8) & 1) {
502
+ rb_ary_push(rec, Qnil);
503
+ } else {
504
+ VALUE field, u_flag, value;
505
+ field = RARRAY_PTR(fields)[i];
506
+ u_flag = FIX2INT(rb_iv_get(field, "@flags")) & UNSIGNED_FLAG;
507
+ value = _protocol_net2value(data, FIX2INT(rb_iv_get(field, "@type")), u_flag);
508
+ if (rb_obj_is_kind_of(value, rb_cNumeric) || rb_obj_is_kind_of(value, cMysqlTime)) {
509
+ rb_ary_push(rec, value);
510
+ } else if (FIX2INT(rb_iv_get(field, "@type")) == TYPE_BIT || FIX2INT(rb_iv_get(field, "@charsetnr")) == BINARY_CHARSET_NUMBER) {
511
+ rb_ary_push(rec, rb_funcall(cCharset, rb_intern("to_binary"), 1, value));
512
+ } else {
513
+ rb_ary_push(rec, rb_funcall(cCharset, rb_intern("convert_encoding"), 2, value, rb_iv_get(obj, "@encoding")));
514
+ }
515
+ }
516
+ }
517
+ return rec;
518
+ }
519
+
520
+ #define COM_STMT_EXECUTE 23
521
+
522
+ VALUE execute_packet_serialize(VALUE obj, VALUE stmt_id, VALUE cursor_type, VALUE values)
523
+ {
524
+ VALUE *ary = RARRAY_PTR(values);
525
+ int len = RARRAY_LEN(values);
526
+ char *null_bitmap;
527
+ int i;
528
+ int null_bitmap_len = 0;
529
+ unsigned long int_stmt_id = NUM2ULONG(stmt_id);
530
+ unsigned char buf[10];
531
+ VALUE netval;
532
+ VALUE types;
533
+ VALUE ret;
534
+
535
+ buf[0] = COM_STMT_EXECUTE;
536
+ buf[1] = int_stmt_id % 0x100;
537
+ buf[2] = (int_stmt_id / 0x100) % 0x100;
538
+ buf[3] = (int_stmt_id / 0x10000) % 0x100;
539
+ buf[4] = (int_stmt_id / 0x1000000) % 0x100;
540
+ buf[5] = FIX2INT(cursor_type);
541
+ buf[6] = 1;
542
+ buf[7] = 0;
543
+ buf[8] = 0;
544
+ buf[9] = 0;
545
+ ret = rb_str_new(buf, 10);
546
+
547
+ if (len == 0) {
548
+ return rb_str_cat(ret, "\x01", 1);
549
+ }
550
+ null_bitmap_len = (len - 1) / 8 + 1;
551
+ null_bitmap = xmalloc(null_bitmap_len);
552
+ memset(null_bitmap, 0, null_bitmap_len);
553
+ netval = rb_str_new("", 0);
554
+ types = rb_str_new("", 0);
555
+ for (i = 0; i < RARRAY_LEN(values); i++) {
556
+ if (ary[i] == Qnil) {
557
+ null_bitmap[i/8] |= 1 << (i%8);
558
+ }
559
+ _protocol_value2net(ary[i], netval, types);
560
+ }
561
+ rb_str_cat(ret, null_bitmap, null_bitmap_len);
562
+ rb_str_cat(ret, "\x01", 1);
563
+ rb_str_concat(ret, types);
564
+ rb_str_concat(ret, netval);
565
+ return ret;
566
+ }
567
+
568
+ void Init_ext(void)
569
+ {
570
+ cMysql = rb_const_get(rb_cObject, rb_intern("Mysql"));
571
+ cPacket = rb_const_get(cMysql, rb_intern("Packet"));
572
+ cMysqlTime = rb_define_class_under(cMysql, "Time", rb_cObject);
573
+ cProtocol = rb_const_get(cMysql, rb_intern("Protocol"));
574
+ cStmtRawRecord = rb_const_get(cMysql, rb_intern("StmtRawRecord"));
575
+ cExecutePacket = rb_const_get(cProtocol, rb_intern("ExecutePacket"));
576
+ cCharset = rb_const_get(cMysql, rb_intern("Charset"));
577
+ eProtocolError = rb_const_get(cMysql, rb_intern("ProtocolError"));
578
+
579
+ rb_define_alloc_func(cPacket, packet_allocate);
580
+ rb_define_singleton_method(cPacket, "lcb", packet_s_lcb, 1);
581
+ rb_define_singleton_method(cPacket, "lcs", packet_s_lcs, 1);
582
+ rb_define_method(cPacket, "initialize", packet_initialize, 1);
583
+ rb_define_method(cPacket, "lcb", packet_lcb, 0);
584
+ rb_define_method(cPacket, "lcs", packet_lcs, 0);
585
+ rb_define_method(cPacket, "read", packet_read, 1);
586
+ rb_define_method(cPacket, "string", packet_string, 0);
587
+ rb_define_method(cPacket, "utiny", packet_utiny, 0);
588
+ rb_define_method(cPacket, "ushort", packet_ushort, 0);
589
+ rb_define_method(cPacket, "ulong", packet_ulong, 0);
590
+ rb_define_method(cPacket, "eof?", packet_eofQ, 0);
591
+ rb_define_method(cPacket, "to_s", packet_to_s, 0);
592
+
593
+ rb_define_method(cStmtRawRecord, "parse_record_packet", stmt_raw_record_parse_record_packet, 0);
594
+ rb_define_alias(cStmtRawRecord, "to_a", "parse_record_packet");
595
+
596
+ rb_define_singleton_method(cExecutePacket, "serialize", execute_packet_serialize, 3);
597
+ }
data/ext/mysql/extconf.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  require 'mkmf'
2
2
 
3
- create_makefile 'mysql/packet'
3
+ create_makefile 'mysql/ext'
data/lib/mysql.rb CHANGED
@@ -14,13 +14,13 @@ class Mysql
14
14
  require "mysql/error"
15
15
  require "mysql/charset"
16
16
  require "mysql/protocol"
17
+ require "mysql/packet.rb"
17
18
  begin
18
- require "mysql/packet.so"
19
+ require "mysql/ext.so"
19
20
  rescue LoadError
20
- require "mysql/packet.rb"
21
21
  end
22
22
 
23
- VERSION = 20908 # Version number of this library
23
+ VERSION = 20909 # Version number of this library
24
24
  MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename
25
25
  MYSQL_TCP_PORT = 3306 # TCP socket port number
26
26
 
@@ -1054,6 +1054,7 @@ class Mysql
1054
1054
  # @param [Boolean] neg negative flag
1055
1055
  # @param [Integer] second_part
1056
1056
  def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)
1057
+ @date_flag = !(hour && minute && second)
1057
1058
  @year, @month, @day, @hour, @minute, @second, @neg, @second_part =
1058
1059
  year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i, neg, second_part.to_i
1059
1060
  end
@@ -1077,7 +1078,9 @@ class Mysql
1077
1078
 
1078
1079
  # @return [String] "yyyy-mm-dd HH:MM:SS"
1079
1080
  def to_s
1080
- if year == 0 and mon == 0 and day == 0
1081
+ if @date_flag
1082
+ sprintf "%04d-%02d-%02d", year, mon, day
1083
+ elsif year == 0 and mon == 0 and day == 0
1081
1084
  h = neg ? hour * -1 : hour
1082
1085
  sprintf "%02d:%02d:%02d", h, min, sec
1083
1086
  else
@@ -0,0 +1,77 @@
1
+ class Mysql
2
+ class Packet
3
+ # convert Numeric to LengthCodedBinary
4
+ def self.lcb(num)
5
+ return "\xfb" if num.nil?
6
+ return [num].pack("C") if num < 251
7
+ return [252, num].pack("Cv") if num < 65536
8
+ return [253, num&0xffff, num>>16].pack("CvC") if num < 16777216
9
+ return [254, num&0xffffffff, num>>32].pack("CVV")
10
+ end
11
+
12
+ # convert String to LengthCodedString
13
+ def self.lcs(str)
14
+ str = Charset.to_binary str
15
+ lcb(str.length)+str
16
+ end
17
+
18
+ def initialize(data)
19
+ @data = data
20
+ end
21
+
22
+ def lcb
23
+ return nil if @data.empty?
24
+ case v = utiny
25
+ when 0xfb
26
+ return nil
27
+ when 0xfc
28
+ return ushort
29
+ when 0xfd
30
+ c, v = utiny, ushort
31
+ return (v << 8)+c
32
+ when 0xfe
33
+ v1, v2 = ulong, ulong
34
+ return (v2 << 32)+v1
35
+ else
36
+ return v
37
+ end
38
+ end
39
+
40
+ def lcs
41
+ len = self.lcb
42
+ return nil unless len
43
+ @data.slice!(0, len)
44
+ end
45
+
46
+ def read(len)
47
+ @data.slice!(0, len)
48
+ end
49
+
50
+ def string
51
+ str = @data.unpack('Z*').first
52
+ @data.slice!(0, str.length+1)
53
+ str
54
+ end
55
+
56
+ def utiny
57
+ @data.slice!(0, 1).unpack('C').first
58
+ end
59
+
60
+ def ushort
61
+ @data.slice!(0, 2).unpack('v').first
62
+ end
63
+
64
+ def ulong
65
+ @data.slice!(0, 4).unpack('V').first
66
+ end
67
+
68
+ def eof?
69
+ @data[0] == ?\xfe && @data.length == 5
70
+ end
71
+
72
+ def to_s
73
+ @data
74
+ end
75
+
76
+ end
77
+ end
@@ -44,15 +44,12 @@ class Mysql
44
44
  when Field::TYPE_DATE
45
45
  len = pkt.utiny
46
46
  y, m, d = pkt.read(len).unpack("vCC")
47
- t = Mysql::Time.new(y, m, d)
48
- def t.to_s
49
- sprintf "%04d-%02d-%02d", year, mon ,day
50
- end
47
+ t = Mysql::Time.new(y, m, d, nil, nil, nil)
51
48
  return t
52
49
  when Field::TYPE_DATETIME, Field::TYPE_TIMESTAMP
53
50
  len = pkt.utiny
54
- y, m, d, h, mi, s, bs = pkt.read(len).unpack("vCCCCCV")
55
- return Mysql::Time.new(y, m, d, h, mi, s, bs)
51
+ y, m, d, h, mi, s, sp = pkt.read(len).unpack("vCCCCCV")
52
+ return Mysql::Time.new(y, m, d, h, mi, s, false, sp)
56
53
  when Field::TYPE_TIME
57
54
  len = pkt.utiny
58
55
  sign, d, h, mi, s, sp = pkt.read(len).unpack("CVCCCV")
@@ -423,10 +420,11 @@ class Mysql
423
420
  # [Array of Array of Object] all records
424
421
  def stmt_retr_all_records(fields, charset)
425
422
  check_state :RESULT
423
+ enc = charset.encoding
426
424
  begin
427
425
  all_recs = []
428
- until (data = read).eof?
429
- all_recs.push StmtRawRecord.new(data, fields, charset.encoding)
426
+ until (pkt = read).eof?
427
+ all_recs.push StmtRawRecord.new(pkt, fields, enc)
430
428
  end
431
429
  all_recs
432
430
  ensure
@@ -494,13 +492,13 @@ class Mysql
494
492
  begin
495
493
  Timeout.timeout @read_timeout do
496
494
  header = @sock.read(4)
497
- raise EOFError unless header.length == 4
495
+ raise EOFError unless header && header.length == 4
498
496
  len1, len2, seq = header.unpack("CvC")
499
497
  len = (len2 << 8) + len1
500
498
  raise ProtocolError, "invalid packet: sequence number mismatch(#{seq} != #{@seq}(expected))" if @seq != seq
501
499
  @seq = (@seq + 1) % 256
502
500
  ret = @sock.read(len)
503
- raise EOFError unless ret.length == len
501
+ raise EOFError unless ret && ret.length == len
504
502
  end
505
503
  rescue EOFError
506
504
  raise ClientError::ServerGoneError, 'The MySQL server has gone away'
data/spec/mysql_spec.rb CHANGED
@@ -13,7 +13,7 @@ MYSQL_SOCKET = ENV['MYSQL_SOCKET']
13
13
 
14
14
  describe 'Mysql::VERSION' do
15
15
  it 'returns client version' do
16
- Mysql::VERSION.should == 20908
16
+ Mysql::VERSION.should == 20909
17
17
  end
18
18
  end
19
19
 
@@ -649,6 +649,7 @@ end
649
649
  describe 'Mysql::Result' do
650
650
  before do
651
651
  @m = Mysql.new(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, MYSQL_PORT, MYSQL_SOCKET)
652
+ @m.charset = 'latin1'
652
653
  @m.query 'create temporary table t (id int, str char(10), primary key (id))'
653
654
  @m.query "insert into t values (1,'abc'),(2,'defg'),(3,'hi'),(4,null)"
654
655
  @res = @m.query 'select * from t'
@@ -808,6 +809,7 @@ end
808
809
  describe 'Mysql::Field' do
809
810
  before do
810
811
  @m = Mysql.new(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, MYSQL_PORT, MYSQL_SOCKET)
812
+ @m.charset = 'latin1'
811
813
  @m.query 'create temporary table t (id int default 0, str char(10), primary key (id))'
812
814
  @m.query "insert into t values (1,'abc'),(2,'defg'),(3,'hi'),(4,null)"
813
815
  @res = @m.query 'select * from t'
@@ -1065,6 +1067,99 @@ describe 'Mysql::Stmt' do
1065
1067
  end
1066
1068
  end
1067
1069
 
1070
+ describe '#execute with various integer value:' do
1071
+ before do
1072
+ @m.query('create temporary table t (i bigint)')
1073
+ end
1074
+ [
1075
+ -9223372036854775808,
1076
+ -9223372036854775807,
1077
+ -4294967297,
1078
+ -4294967296,
1079
+ -4294967295,
1080
+ -2147483649,
1081
+ -2147483648,
1082
+ -2147483647,
1083
+ -65537,
1084
+ -65536,
1085
+ -65535,
1086
+ -32769,
1087
+ -32768,
1088
+ -32767,
1089
+ -257,
1090
+ -256,
1091
+ -255,
1092
+ -129,
1093
+ -128,
1094
+ -127,
1095
+ 0,
1096
+ 126,
1097
+ 127,
1098
+ 128,
1099
+ 254,
1100
+ 255,
1101
+ 256,
1102
+ 32766,
1103
+ 32767,
1104
+ 32768,
1105
+ 65534,
1106
+ 65535,
1107
+ 65536,
1108
+ 2147483646,
1109
+ 2147483647,
1110
+ 2147483648,
1111
+ 4294967294,
1112
+ 4294967295,
1113
+ 4294967296,
1114
+ 9223372036854775806,
1115
+ 9223372036854775807,
1116
+ ].each do |n|
1117
+ it "#{n} is #{n}" do
1118
+ @s.prepare 'insert into t values (?)'
1119
+ @s.execute n
1120
+ @m.query('select i from t').fetch.should == ["#{n}"]
1121
+ end
1122
+ end
1123
+ end
1124
+
1125
+ describe '#execute with various unsigned integer value:' do
1126
+ before do
1127
+ @m.query('create temporary table t (i bigint unsigned)')
1128
+ end
1129
+ [
1130
+ 0,
1131
+ 126,
1132
+ 127,
1133
+ 128,
1134
+ 254,
1135
+ 255,
1136
+ 256,
1137
+ 32766,
1138
+ 32767,
1139
+ 32768,
1140
+ 65534,
1141
+ 65535,
1142
+ 65536,
1143
+ 2147483646,
1144
+ 2147483647,
1145
+ 2147483648,
1146
+ 4294967294,
1147
+ 4294967295,
1148
+ 4294967296,
1149
+ 9223372036854775806,
1150
+ 9223372036854775807,
1151
+ 9223372036854775808,
1152
+ 18446744073709551614,
1153
+ 18446744073709551615,
1154
+ ].each do |n|
1155
+ it "#{n} is #{n}" do
1156
+ @s.prepare 'insert into t values (?)'
1157
+ @s.execute n
1158
+ @m.query('select i from t').fetch.should == ["#{n}"]
1159
+ end
1160
+ end
1161
+ end
1162
+
1068
1163
  it '#fetch returns result-record' do
1069
1164
  @s.prepare 'select 123, "abc", null'
1070
1165
  @s.execute
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mysql-ext
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.8
4
+ version: 2.9.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-08 00:00:00.000000000 Z
12
+ date: 2012-06-17 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: This is MySQL connector with C extension.
15
15
  email: tommy@tmtm.org
@@ -25,7 +25,8 @@ files:
25
25
  - lib/mysql/protocol.rb
26
26
  - lib/mysql/charset.rb
27
27
  - lib/mysql/error.rb
28
- - ext/mysql/packet.c
28
+ - lib/mysql/packet.rb
29
+ - ext/mysql/ext.c
29
30
  - spec/mysql_spec.rb
30
31
  - spec/mysql/packet_spec.rb
31
32
  - ext/mysql/extconf.rb
data/ext/mysql/packet.c DELETED
@@ -1,246 +0,0 @@
1
- #include "ruby.h"
2
-
3
- typedef struct {
4
- unsigned char *ptr;
5
- unsigned char *endp;
6
- } data_t;
7
-
8
- static VALUE s_lcb(VALUE klass, VALUE val)
9
- {
10
- unsigned long long n;
11
- unsigned char buf[9];
12
-
13
- if (val == Qnil)
14
- return rb_str_new("\xfb", 1);
15
- n = NUM2ULL(val);
16
- if (n < 251) {
17
- buf[0] = n;
18
- return rb_str_new(buf, 1);
19
- }
20
- if (n < 65536) {
21
- buf[0] = '\xfc';
22
- buf[1] = n % 256;
23
- buf[2] = n / 256;
24
- return rb_str_new(buf, 3);
25
- }
26
- if (n < 16777216) {
27
- buf[0] = '\xfd';
28
- buf[1] = n % 256;
29
- n /= 256;
30
- buf[2] = n % 256;
31
- buf[3] = n / 256;
32
- return rb_str_new(buf, 4);
33
- }
34
- buf[0] = '\xfe';
35
- buf[1] = n % 256;
36
- n /= 256;
37
- buf[2] = n % 256;
38
- n /= 256;
39
- buf[3] = n % 256;
40
- n /= 256;
41
- buf[4] = n % 256;
42
- n /= 256;
43
- buf[5] = n % 256;
44
- n /= 256;
45
- buf[6] = n % 256;
46
- n /= 256;
47
- buf[7] = n % 256;
48
- buf[8] = n / 256;
49
- return rb_str_new(buf, 9);
50
- }
51
-
52
- static VALUE s_lcs(VALUE klass, VALUE val)
53
- {
54
- VALUE ret = s_lcb(klass, ULONG2NUM(RSTRING_LEN(val)));
55
- return rb_str_cat(ret, RSTRING_PTR(val), RSTRING_LEN(val));
56
- }
57
-
58
- static VALUE allocate(VALUE klass)
59
- {
60
- data_t *data;
61
-
62
- data = xmalloc(sizeof *data);
63
- data->ptr = NULL;
64
- data->endp = NULL;
65
- return Data_Wrap_Struct(klass, 0, xfree, data);
66
- }
67
-
68
- static VALUE initialize(VALUE obj, VALUE buf)
69
- {
70
- data_t *data;
71
-
72
- Data_Get_Struct(obj, data_t, data);
73
- rb_ivar_set(obj, rb_intern("buf"), buf);
74
- data->ptr = RSTRING_PTR(buf);
75
- data->endp = data->ptr + RSTRING_LEN(buf);
76
- }
77
-
78
- #define NIL_VALUE 0xFFFFFFFFFFFFFFFF
79
-
80
- static unsigned long long _lcb(data_t *data)
81
- {
82
- unsigned char v;
83
- unsigned long long n;
84
-
85
- if (data->ptr >= data->endp)
86
- return NIL_VALUE;
87
-
88
- v = *data->ptr++;
89
- switch (v) {
90
- case 0xfb:
91
- return NIL_VALUE;
92
- case 0xfc:
93
- n = *data->ptr++;
94
- n |= ((unsigned int)*data->ptr++) << 8;
95
- return n;
96
- case 0xfd:
97
- n = *data->ptr++;
98
- n |= ((unsigned int)*data->ptr++) << 8;
99
- n |= ((unsigned int)*data->ptr++) << 16;
100
- return n;
101
- case 0xfe:
102
- n = *data->ptr++;
103
- n |= ((unsigned long long)*data->ptr++) << 8;
104
- n |= ((unsigned long long)*data->ptr++) << 16;
105
- n |= ((unsigned long long)*data->ptr++) << 24;
106
- n |= ((unsigned long long)*data->ptr++) << 32;
107
- n |= ((unsigned long long)*data->ptr++) << 40;
108
- n |= ((unsigned long long)*data->ptr++) << 48;
109
- n |= ((unsigned long long)*data->ptr++) << 56;
110
- return n;
111
- default:
112
- return v;
113
- }
114
- }
115
-
116
- static VALUE lcb(VALUE obj)
117
- {
118
- data_t *data;
119
- unsigned char v;
120
- unsigned long long n;
121
-
122
- Data_Get_Struct(obj, data_t, data);
123
- n = _lcb(data);
124
- if (n == NIL_VALUE)
125
- return Qnil;
126
- return ULL2NUM(n);
127
- }
128
-
129
- static VALUE lcs(VALUE obj)
130
- {
131
- data_t *data;
132
- unsigned long long l;
133
- VALUE ret;
134
-
135
- Data_Get_Struct(obj, data_t, data);
136
- l = _lcb(data);
137
- if (l == NIL_VALUE)
138
- return Qnil;
139
- if (data->ptr+l > data->endp)
140
- l = data->endp - data->ptr;
141
- ret = rb_str_new(data->ptr, l);
142
- data->ptr += l;
143
- return ret;
144
- }
145
-
146
- static VALUE read(VALUE obj, VALUE len)
147
- {
148
- data_t *data;
149
- unsigned long long l = NUM2ULL(len);
150
- VALUE ret;
151
-
152
- Data_Get_Struct(obj, data_t, data);
153
- if (data->ptr+l > data->endp)
154
- l = data->endp - data->ptr;
155
- ret = rb_str_new(data->ptr, l);
156
- data->ptr += l;
157
- return ret;
158
- }
159
-
160
- static VALUE string(VALUE obj)
161
- {
162
- data_t *data;
163
- unsigned char *p;
164
- VALUE ret;
165
-
166
- Data_Get_Struct(obj, data_t, data);
167
- p = data->ptr;
168
- while (p < data->endp && *p++ != '\0')
169
- ;
170
- ret = rb_str_new(data->ptr, (p - data->ptr)-1);
171
- data->ptr = p;
172
- return ret;
173
- }
174
-
175
- static VALUE utiny(VALUE obj)
176
- {
177
- data_t *data;
178
-
179
- Data_Get_Struct(obj, data_t, data);
180
- return UINT2NUM(*data->ptr++);
181
- }
182
-
183
- static VALUE _ushort(VALUE obj)
184
- {
185
- data_t *data;
186
- unsigned short n;
187
-
188
- Data_Get_Struct(obj, data_t, data);
189
- n = *data->ptr++;
190
- n |= *data->ptr++ * 0x100;
191
- return UINT2NUM(n);
192
- }
193
-
194
- static VALUE _ulong(VALUE obj)
195
- {
196
- data_t *data;
197
- unsigned long n;
198
-
199
- Data_Get_Struct(obj, data_t, data);
200
- n = *data->ptr++;
201
- n |= *data->ptr++ * 0x100;
202
- n |= *data->ptr++ * 0x10000;
203
- n |= *data->ptr++ * 0x1000000;
204
- return UINT2NUM(n);
205
- }
206
-
207
- static VALUE eofQ(VALUE obj)
208
- {
209
- data_t *data;
210
-
211
- Data_Get_Struct(obj, data_t, data);
212
- if (*data->ptr == 0xfe && data->endp - data->ptr == 5)
213
- return Qtrue;
214
- else
215
- return Qfalse;
216
- }
217
-
218
- static VALUE to_s(VALUE obj)
219
- {
220
- data_t *data;
221
-
222
- Data_Get_Struct(obj, data_t, data);
223
- return rb_str_new(data->ptr, data->endp-data->ptr);
224
- }
225
-
226
- void Init_packet(void)
227
- {
228
- VALUE cMysql;
229
- VALUE cPacket;
230
-
231
- cMysql = rb_define_class("Mysql", rb_cObject);
232
- cPacket = rb_define_class_under(cMysql, "Packet", rb_cObject);
233
- rb_define_alloc_func(cPacket, allocate);
234
- rb_define_singleton_method(cPacket, "lcb", s_lcb, 1);
235
- rb_define_singleton_method(cPacket, "lcs", s_lcs, 1);
236
- rb_define_method(cPacket, "initialize", initialize, 1);
237
- rb_define_method(cPacket, "lcb", lcb, 0);
238
- rb_define_method(cPacket, "lcs", lcs, 0);
239
- rb_define_method(cPacket, "read", read, 1);
240
- rb_define_method(cPacket, "string", string, 0);
241
- rb_define_method(cPacket, "utiny", utiny, 0);
242
- rb_define_method(cPacket, "ushort", _ushort, 0);
243
- rb_define_method(cPacket, "ulong", _ulong, 0);
244
- rb_define_method(cPacket, "eof?", eofQ, 0);
245
- rb_define_method(cPacket, "to_s", to_s, 0);
246
- }