ruby-mysql-ext 2.9.8 → 2.9.9

Sign up to get free protection for your applications and to get access to all the features.
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
- }