mysql2-replication 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1427 @@
1
+ #include <ruby.h>
2
+ #include <ruby/encoding.h>
3
+ #include <ruby/thread.h>
4
+
5
+ /* libmariadb */
6
+ #include <mysql.h>
7
+ #include <mariadb_com.h>
8
+ #include <mariadb_rpl.h>
9
+
10
+ /* mysql2 */
11
+ #include <client.h>
12
+
13
+ void Init_mysql2_replication(void);
14
+
15
+ static VALUE rb_cDate;
16
+
17
+ static VALUE rb_cMysql2Error;
18
+
19
+ static VALUE rb_cMysql2ReplicationEvent;
20
+ static VALUE rb_cMysql2ReplicationRotateEvent;
21
+ static VALUE rb_cMysql2ReplicationFormatDescriptionEvent;
22
+ static VALUE rb_cMysql2ReplicationTableMapEvent;
23
+ static VALUE rb_cMysql2ReplicationWriteRowsEvent;
24
+ static VALUE rb_cMysql2ReplicationUpdateRowsEvent;
25
+ static VALUE rb_cMysql2ReplicationDeleteRowsEvent;
26
+
27
+ static VALUE
28
+ rbm2_replication_rows_event_statement_end_p(VALUE self)
29
+ {
30
+ return (NUM2USHORT(rb_iv_get(self, "@rows_flags")) & FL_STMT_END) ?
31
+ RUBY_Qtrue : RUBY_Qfalse;
32
+ }
33
+
34
+ static inline int8_t
35
+ rbm2_read_int8(const uint8_t *data)
36
+ {
37
+ return *((const int8_t *)data);
38
+ }
39
+
40
+ static inline uint8_t
41
+ rbm2_read_uint8(const uint8_t *data)
42
+ {
43
+ return *data;
44
+ }
45
+
46
+ static inline int16_t
47
+ rbm2_read_int16(const uint8_t *data)
48
+ {
49
+ return *((const int16_t *)data);
50
+ }
51
+
52
+ static inline uint16_t
53
+ rbm2_read_uint16(const uint8_t *data)
54
+ {
55
+ return *((const uint16_t *)data);
56
+ }
57
+
58
+ static inline uint16_t
59
+ rbm2_read_uint16_bigendian(const uint8_t *data)
60
+ {
61
+ return (((uint32_t)(data[0]) << 8) +
62
+ ((uint32_t)(data[1])));
63
+ }
64
+
65
+ static inline int32_t
66
+ rbm2_read_int24(const uint8_t *data)
67
+ {
68
+ uint32_t value = (((uint32_t)(data[0])) |
69
+ ((uint32_t)(data[1]) << 8) |
70
+ ((uint32_t)(data[2]) << 16));
71
+ if (data[2] & 128) {
72
+ return (int32_t)(value | (255 << 24));
73
+ } else {
74
+ return value;
75
+ }
76
+ }
77
+
78
+ static inline uint32_t
79
+ rbm2_read_uint24(const uint8_t *data)
80
+ {
81
+ return (((uint32_t)(data[0])) +
82
+ ((uint32_t)(data[1]) << 8) +
83
+ ((uint32_t)(data[2]) << 16));
84
+ }
85
+
86
+ static inline uint32_t
87
+ rbm2_read_uint24_bigendian(const uint8_t *data)
88
+ {
89
+ return (((uint32_t)(data[0]) << 16) +
90
+ ((uint32_t)(data[1]) << 8) +
91
+ ((uint32_t)(data[2])));
92
+ }
93
+
94
+ static inline int32_t
95
+ rbm2_read_int32(const uint8_t *data)
96
+ {
97
+ return *((const int32_t *)data);
98
+ }
99
+
100
+ static inline uint32_t
101
+ rbm2_read_uint32(const uint8_t *data)
102
+ {
103
+ return *((const uint32_t *)data);
104
+ }
105
+
106
+ static inline uint32_t
107
+ rbm2_read_uint32_bigendian(const uint8_t *data)
108
+ {
109
+ return (((uint32_t)(data[0]) << 24) +
110
+ ((uint32_t)(data[1]) << 16) +
111
+ ((uint32_t)(data[2]) << 8) +
112
+ ((uint32_t)(data[3])));
113
+ }
114
+
115
+ static inline uint64_t
116
+ rbm2_read_uint40_bigendian(const uint8_t *data)
117
+ {
118
+ return (((uint64_t)(data[0]) << 32) +
119
+ ((uint64_t)(data[1]) << 24) +
120
+ ((uint64_t)(data[2]) << 16) +
121
+ ((uint64_t)(data[3]) << 8) +
122
+ ((uint64_t)(data[4])));
123
+ }
124
+
125
+ static inline int64_t
126
+ rbm2_read_int64(const uint8_t *data)
127
+ {
128
+ return *((const int64_t *)data);
129
+ }
130
+
131
+ static inline uint64_t
132
+ rbm2_read_uint64(const uint8_t *data)
133
+ {
134
+ return *((const uint64_t *)data);
135
+ }
136
+
137
+ static ID
138
+ rbm2_column_type_to_id(enum enum_field_types column_type)
139
+ {
140
+ switch (column_type) {
141
+ case MYSQL_TYPE_DECIMAL:
142
+ return rb_intern("decimal");
143
+ case MYSQL_TYPE_TINY:
144
+ return rb_intern("tiny");
145
+ case MYSQL_TYPE_SHORT:
146
+ return rb_intern("short");
147
+ case MYSQL_TYPE_LONG:
148
+ return rb_intern("long");
149
+ case MYSQL_TYPE_FLOAT:
150
+ return rb_intern("float");
151
+ case MYSQL_TYPE_DOUBLE:
152
+ return rb_intern("double");
153
+ case MYSQL_TYPE_NULL:
154
+ return rb_intern("null");
155
+ case MYSQL_TYPE_TIMESTAMP:
156
+ return rb_intern("timestamp");
157
+ case MYSQL_TYPE_LONGLONG:
158
+ return rb_intern("longlong");
159
+ case MYSQL_TYPE_INT24:
160
+ return rb_intern("int24");
161
+ case MYSQL_TYPE_DATE:
162
+ return rb_intern("date");
163
+ case MYSQL_TYPE_TIME:
164
+ return rb_intern("time");
165
+ case MYSQL_TYPE_DATETIME:
166
+ return rb_intern("datetime");
167
+ case MYSQL_TYPE_YEAR:
168
+ return rb_intern("year");
169
+ case MYSQL_TYPE_NEWDATE:
170
+ return rb_intern("newdate");
171
+ case MYSQL_TYPE_VARCHAR:
172
+ return rb_intern("varchar");
173
+ case MYSQL_TYPE_BIT:
174
+ return rb_intern("bit");
175
+ case MYSQL_TYPE_TIMESTAMP2:
176
+ return rb_intern("timestamp2");
177
+ case MYSQL_TYPE_DATETIME2:
178
+ return rb_intern("datetime2");
179
+ case MYSQL_TYPE_TIME2:
180
+ return rb_intern("time2");
181
+ case MYSQL_TYPE_JSON:
182
+ return rb_intern("json");
183
+ case MYSQL_TYPE_NEWDECIMAL:
184
+ return rb_intern("newdecimal");
185
+ case MYSQL_TYPE_ENUM:
186
+ return rb_intern("enum");
187
+ case MYSQL_TYPE_SET:
188
+ return rb_intern("set");
189
+ case MYSQL_TYPE_TINY_BLOB:
190
+ return rb_intern("tiny_blob");
191
+ case MYSQL_TYPE_MEDIUM_BLOB:
192
+ return rb_intern("medium_blob");
193
+ case MYSQL_TYPE_LONG_BLOB:
194
+ return rb_intern("long_blob");
195
+ case MYSQL_TYPE_BLOB:
196
+ return rb_intern("blob");
197
+ case MYSQL_TYPE_VAR_STRING:
198
+ return rb_intern("var_string");
199
+ case MYSQL_TYPE_STRING:
200
+ return rb_intern("string");
201
+ case MYSQL_TYPE_GEOMETRY:
202
+ return rb_intern("geometry");
203
+ default:
204
+ return rb_intern("unknown");
205
+ }
206
+ }
207
+
208
+ static VALUE
209
+ rbm2_column_type_to_symbol(enum enum_field_types column_type)
210
+ {
211
+ return rb_id2sym(rbm2_column_type_to_id(column_type));
212
+ }
213
+
214
+ static void
215
+ rbm2_metadata_parse(enum enum_field_types *column_type,
216
+ const uint8_t **metadata,
217
+ VALUE rb_column)
218
+ {
219
+ switch (*column_type) {
220
+ case MYSQL_TYPE_DECIMAL:
221
+ case MYSQL_TYPE_TINY:
222
+ case MYSQL_TYPE_SHORT:
223
+ case MYSQL_TYPE_LONG:
224
+ break;
225
+ case MYSQL_TYPE_FLOAT:
226
+ case MYSQL_TYPE_DOUBLE:
227
+ rb_hash_aset(rb_column,
228
+ rb_id2sym(rb_intern("size")),
229
+ UINT2NUM((*metadata)[0]));
230
+ (*metadata) += 1;
231
+ break;
232
+ case MYSQL_TYPE_NULL:
233
+ case MYSQL_TYPE_TIMESTAMP:
234
+ case MYSQL_TYPE_LONGLONG:
235
+ case MYSQL_TYPE_INT24:
236
+ case MYSQL_TYPE_DATE:
237
+ case MYSQL_TYPE_TIME:
238
+ case MYSQL_TYPE_DATETIME:
239
+ case MYSQL_TYPE_YEAR:
240
+ case MYSQL_TYPE_NEWDATE:
241
+ break;
242
+ case MYSQL_TYPE_VARCHAR:
243
+ rb_hash_aset(rb_column,
244
+ rb_id2sym(rb_intern("max_length")),
245
+ UINT2NUM(rbm2_read_uint16(*metadata)));
246
+ (*metadata) += 2;
247
+ break;
248
+ case MYSQL_TYPE_BIT:
249
+ {
250
+ uint8_t bits = (*metadata)[0];
251
+ uint8_t bytes = (*metadata)[1];
252
+ rb_hash_aset(rb_column,
253
+ rb_id2sym(rb_intern("bits")),
254
+ UINT2NUM((bytes * 8) + bits));
255
+ (*metadata) += 2;
256
+ }
257
+ break;
258
+ case MYSQL_TYPE_TIMESTAMP2:
259
+ case MYSQL_TYPE_DATETIME2:
260
+ case MYSQL_TYPE_TIME2:
261
+ rb_hash_aset(rb_column,
262
+ rb_id2sym(rb_intern("decimals")),
263
+ UINT2NUM((*metadata)[0]));
264
+ (*metadata) += 1;
265
+ break;
266
+ case MYSQL_TYPE_JSON:
267
+ rb_hash_aset(rb_column,
268
+ rb_id2sym(rb_intern("length_size")),
269
+ UINT2NUM((*metadata)[0]));
270
+ (*metadata) += 1;
271
+ break;
272
+ case MYSQL_TYPE_NEWDECIMAL:
273
+ rb_hash_aset(rb_column,
274
+ rb_id2sym(rb_intern("precision")),
275
+ UINT2NUM((*metadata)[0]));
276
+ rb_hash_aset(rb_column,
277
+ rb_id2sym(rb_intern("scale")),
278
+ UINT2NUM((*metadata)[1]));
279
+ (*metadata) += 2;
280
+ break;
281
+ case MYSQL_TYPE_ENUM:
282
+ case MYSQL_TYPE_SET:
283
+ rb_hash_aset(rb_column,
284
+ rb_id2sym(rb_intern("size")),
285
+ UINT2NUM((*metadata)[1]));
286
+ (*metadata) += 2;
287
+ break;
288
+ case MYSQL_TYPE_TINY_BLOB:
289
+ case MYSQL_TYPE_MEDIUM_BLOB:
290
+ case MYSQL_TYPE_LONG_BLOB:
291
+ break;
292
+ case MYSQL_TYPE_BLOB:
293
+ rb_hash_aset(rb_column,
294
+ rb_id2sym(rb_intern("length_size")),
295
+ UINT2NUM((*metadata)[0]));
296
+ (*metadata) += 1;
297
+ break;
298
+ case MYSQL_TYPE_VAR_STRING:
299
+ case MYSQL_TYPE_STRING:
300
+ {
301
+ /* See also Field_string::do_save_field_metadata() */
302
+ *column_type = (*metadata)[0];
303
+ switch (*column_type) {
304
+ case MYSQL_TYPE_ENUM:
305
+ case MYSQL_TYPE_SET:
306
+ rb_hash_aset(rb_column,
307
+ rb_id2sym(rb_intern("size")),
308
+ UINT2NUM((*metadata)[1]));
309
+ break;
310
+ default:
311
+ rb_hash_aset(rb_column,
312
+ rb_id2sym(rb_intern("max_length")),
313
+ UINT2NUM((((((*metadata)[0] >> 4) & 0x03) ^ 0x03) << 8) +
314
+ (*metadata)[1]));
315
+ break;
316
+ }
317
+ (*metadata) += 2;
318
+ }
319
+ break;
320
+ case MYSQL_TYPE_GEOMETRY:
321
+ rb_hash_aset(rb_column,
322
+ rb_id2sym(rb_intern("length_size")),
323
+ UINT2NUM((*metadata)[0]));
324
+ (*metadata) += 1;
325
+ break;
326
+ default:
327
+ break;
328
+ }
329
+ }
330
+
331
+ static inline VALUE
332
+ rbm2_column_parse_variable_length_string(VALUE rb_column,
333
+ const uint8_t **row_data)
334
+ {
335
+ /* https://mariadb.com/kb/en/rows_event_v1/#mysql_type_varchar-and-other-variable-length-string-types */
336
+ uint32_t max_length =
337
+ NUM2UINT(rb_hash_aref(rb_column,
338
+ rb_id2sym(rb_intern("max_length"))));
339
+ VALUE rb_value;
340
+ if (max_length > 255) {
341
+ uint16_t length = rbm2_read_uint16(*row_data);
342
+ (*row_data) += 2;
343
+ rb_value = rb_str_new((const char *)(*row_data),
344
+ length);
345
+ (*row_data) += length;
346
+ } else {
347
+ uint8_t length = rbm2_read_uint8(*row_data);
348
+ (*row_data) += 1;
349
+ rb_value = rb_str_new((const char *)(*row_data),
350
+ length);
351
+ (*row_data) += length;
352
+ }
353
+ return rb_value;
354
+ }
355
+
356
+ static inline VALUE
357
+ rbm2_column_parse_blob(VALUE rb_column,
358
+ const uint8_t **row_data)
359
+ {
360
+ /* https://mariadb.com/kb/en/rows_event_v1/#mysql_type_blob-and-other-blob-types */
361
+ uint32_t length_size =
362
+ NUM2UINT(rb_hash_aref(rb_column,
363
+ rb_id2sym(rb_intern("length_size"))));
364
+ VALUE rb_value;
365
+ switch (length_size) {
366
+ case 1:
367
+ {
368
+ uint8_t length = rbm2_read_uint8(*row_data);
369
+ (*row_data) += 1;
370
+ rb_value = rb_str_new((const char *)(*row_data),
371
+ length);
372
+ (*row_data) += length;
373
+ }
374
+ break;
375
+ case 2:
376
+ {
377
+ uint16_t length = rbm2_read_uint16(*row_data);
378
+ (*row_data) += 2;
379
+ rb_value = rb_str_new((const char *)(*row_data),
380
+ length);
381
+ (*row_data) += length;
382
+ }
383
+ break;
384
+ case 3:
385
+ {
386
+ uint32_t length = rbm2_read_uint24(*row_data);
387
+ (*row_data) += 3;
388
+ rb_value = rb_str_new((const char *)(*row_data),
389
+ length);
390
+ (*row_data) += length;
391
+ }
392
+ break;
393
+ case 4:
394
+ {
395
+ uint32_t length = rbm2_read_uint32(*row_data);
396
+ (*row_data) += 4;
397
+ rb_value = rb_str_new((const char *)(*row_data),
398
+ length);
399
+ (*row_data) += length;
400
+ }
401
+ break;
402
+ default:
403
+ rb_raise(rb_eNotImpError,
404
+ "unsupported length size for blob: %u",
405
+ length_size);
406
+ break;
407
+ }
408
+ return rb_value;
409
+ }
410
+
411
+ static VALUE
412
+ rbm2_column_parse(VALUE rb_column, const uint8_t **row_data)
413
+ {
414
+ VALUE rb_value = RUBY_Qnil;
415
+ VALUE rb_type = rb_hash_aref(rb_column, rb_id2sym(rb_intern("type")));
416
+ VALUE rb_type_id = rb_hash_aref(rb_column, rb_id2sym(rb_intern("type_id")));
417
+ enum enum_field_types type = NUM2UINT(rb_type_id);
418
+ switch (type) {
419
+ case MYSQL_TYPE_DECIMAL:
420
+ rb_raise(rb_eNotImpError,
421
+ "decimal type isn't implemented yet: %+" PRIsVALUE,
422
+ rb_type);
423
+ break;
424
+ case MYSQL_TYPE_TINY:
425
+ rb_value = RB_CHR2FIX(*(*row_data));
426
+ (*row_data) += 1;
427
+ break;
428
+ case MYSQL_TYPE_SHORT:
429
+ rb_value = RB_INT2NUM(rbm2_read_int16(*row_data));
430
+ (*row_data) += 2;
431
+ break;
432
+ case MYSQL_TYPE_LONG:
433
+ rb_value = RB_INT2NUM(rbm2_read_int32(*row_data));
434
+ (*row_data) += 4;
435
+ break;
436
+ case MYSQL_TYPE_FLOAT:
437
+ rb_value = rb_float_new(*((const float *)(*row_data)));
438
+ (*row_data) += 4;
439
+ break;
440
+ case MYSQL_TYPE_DOUBLE:
441
+ rb_value = rb_float_new(*((const double *)(*row_data)));
442
+ (*row_data) += 8;
443
+ break;
444
+ case MYSQL_TYPE_NULL:
445
+ break;
446
+ case MYSQL_TYPE_TIMESTAMP:
447
+ rb_value = rb_funcall(rb_cTime,
448
+ rb_intern("at"),
449
+ 1,
450
+ RB_UINT2NUM(rbm2_read_uint32(*row_data)));
451
+ (*row_data) += 4;
452
+ break;
453
+ case MYSQL_TYPE_LONGLONG:
454
+ rb_value = RB_LL2NUM(rbm2_read_int64(*row_data));
455
+ (*row_data) += 8;
456
+ break;
457
+ case MYSQL_TYPE_INT24:
458
+ rb_value = RB_INT2NUM(rbm2_read_int24(*row_data));
459
+ (*row_data) += 3;
460
+ break;
461
+ case MYSQL_TYPE_DATE:
462
+ {
463
+ /* https://mariadb.com/kb/en/rows_event_v1/#mysql_type_date */
464
+ uint32_t raw_date = rbm2_read_uint24(*row_data);
465
+ /*
466
+ YYYYYYYMMMMDDDDD
467
+ Y: 6bit
468
+ M: 4bit
469
+ D: 5bit
470
+ */
471
+ rb_value = rb_funcall(rb_cDate,
472
+ rb_intern("new"),
473
+ 3,
474
+ RB_UINT2NUM((raw_date >> 9)),
475
+ RB_UINT2NUM((raw_date >> 5) & ((1 << 4) - 1)),
476
+ RB_UINT2NUM((raw_date & ((1 << 5) - 1))));
477
+ (*row_data) += 3;
478
+ }
479
+ break;
480
+ case MYSQL_TYPE_TIME:
481
+ {
482
+ /* https://mariadb.com/kb/en/rows_event_v1/#mysql_type_time */
483
+ uint32_t raw_time = rbm2_read_uint24(*row_data);
484
+ /* HHMMSS */
485
+ rb_value = rb_sprintf("%02u:%02u:%02u",
486
+ (raw_time / (10 * 4)),
487
+ (raw_time % (10 * 4)) / (10 * 2),
488
+ (raw_time % (10 * 2)));
489
+ (*row_data) += 3;
490
+ }
491
+ break;
492
+ case MYSQL_TYPE_DATETIME:
493
+ {
494
+ /* https://mariadb.com/kb/en/rows_event_v1/#mysql_type_datetime */
495
+ uint64_t raw_time = rbm2_read_uint64(*row_data);
496
+ /* YYYYMMDDHHMMSS */
497
+ rb_value = rb_funcall(rb_cTime,
498
+ rb_intern("utc"),
499
+ 6,
500
+ RB_UINT2NUM((raw_time / (10 * 10))),
501
+ RB_UINT2NUM((raw_time % (10 * 10)) / (10 * 8)),
502
+ RB_UINT2NUM((raw_time % (10 * 8)) / (10 * 6)),
503
+ RB_UINT2NUM((raw_time % (10 * 6)) / (10 * 4)),
504
+ RB_UINT2NUM((raw_time % (10 * 4)) / (10 * 2)),
505
+ RB_UINT2NUM((raw_time % (10 * 2))));
506
+ (*row_data) += 8;
507
+ }
508
+ break;
509
+ case MYSQL_TYPE_YEAR:
510
+ rb_value = RB_UINT2NUM(rbm2_read_uint8(*row_data) + 1900);
511
+ (*row_data) += 1;
512
+ break;
513
+ case MYSQL_TYPE_NEWDATE:
514
+ rb_raise(rb_eNotImpError,
515
+ "newdate type isn't implemented yet: %+" PRIsVALUE,
516
+ rb_type);
517
+ break;
518
+ case MYSQL_TYPE_VARCHAR:
519
+ rb_value = rbm2_column_parse_variable_length_string(rb_column, row_data);
520
+ break;
521
+ case MYSQL_TYPE_BIT:
522
+ {
523
+ uint8_t bits =
524
+ RB_NUM2UINT(rb_hash_aref(rb_column,
525
+ rb_id2sym(rb_intern("bits"))));
526
+ switch ((bits + 7) / 8) {
527
+ case 1:
528
+ rb_value = RB_UINT2NUM(rbm2_read_uint8(*row_data));
529
+ (*row_data) += 1;
530
+ break;
531
+ case 2:
532
+ rb_value = RB_UINT2NUM(rbm2_read_uint16(*row_data));
533
+ (*row_data) += 2;
534
+ break;
535
+ case 3:
536
+ rb_value = RB_UINT2NUM(rbm2_read_uint24(*row_data));
537
+ (*row_data) += 3;
538
+ break;
539
+ case 4:
540
+ rb_value = RB_UINT2NUM(rbm2_read_uint32(*row_data));
541
+ (*row_data) += 4;
542
+ break;
543
+ default :
544
+ rb_raise(rb_eNotImpError,
545
+ "%d bit type isn't implemented yet: %+" PRIsVALUE,
546
+ bits,
547
+ rb_type);
548
+ break;
549
+ }
550
+ }
551
+ break;
552
+ case MYSQL_TYPE_TIMESTAMP2:
553
+ {
554
+ /* https://mariadb.com/kb/en/rows_event_v1/#mysql_type_timestamp2 */
555
+ uint32_t decimals =
556
+ RB_NUM2UINT(rb_hash_aref(rb_column,
557
+ rb_id2sym(rb_intern("decimals"))));
558
+ uint32_t seconds = rbm2_read_uint32_bigendian(*row_data);
559
+ (*row_data) += 4;
560
+ uint32_t fractional_seconds = 0;
561
+ switch ((decimals + 1) / 2) {
562
+ case 1:
563
+ fractional_seconds = rbm2_read_uint8(*row_data) * 10000;
564
+ (*row_data) += 1;
565
+ break;
566
+ case 2:
567
+ fractional_seconds = rbm2_read_uint16_bigendian(*row_data) * 100;
568
+ (*row_data) += 2;
569
+ break;
570
+ case 3:
571
+ fractional_seconds = rbm2_read_uint24_bigendian(*row_data);
572
+ (*row_data) += 3;
573
+ break;
574
+ default :
575
+ break;
576
+ }
577
+ rb_value = rb_funcall(rb_cTime,
578
+ rb_intern("at"),
579
+ 2,
580
+ UINT2NUM(seconds),
581
+ UINT2NUM(fractional_seconds));
582
+ }
583
+ break;
584
+ case MYSQL_TYPE_DATETIME2:
585
+ {
586
+ /*
587
+ See the documentation of TIME_to_longlong_datetime_packed().
588
+
589
+ https://github.com/mysql/mysql-server/blob/mysql-8.0.27/mysys/my_time.cc#L1672-L1691
590
+ */
591
+ uint64_t integer_part = rbm2_read_uint40_bigendian(*row_data);
592
+ (*row_data) += 5;
593
+ uint32_t fractional_seconds = 0;
594
+ uint32_t decimals =
595
+ NUM2UINT(rb_hash_aref(rb_column,
596
+ rb_id2sym(rb_intern("decimals"))));
597
+ switch ((decimals + 1) / 2) {
598
+ case 1:
599
+ fractional_seconds = rbm2_read_uint8(*row_data) * 10000;
600
+ (*row_data) += 1;
601
+ break;
602
+ case 2:
603
+ fractional_seconds = rbm2_read_uint16_bigendian(*row_data) * 100;
604
+ (*row_data) += 2;
605
+ break;
606
+ case 3:
607
+ fractional_seconds = rbm2_read_uint24_bigendian(*row_data);
608
+ (*row_data) += 3;
609
+ break;
610
+ default :
611
+ break;
612
+ }
613
+ uint32_t symd = integer_part >> 17;
614
+ uint32_t sym = symd >> 5;
615
+ uint32_t sign = sym >> 17;
616
+ uint32_t ym = sym % (1 << 17);
617
+ uint32_t year = ym / 13;
618
+ if (sign == 0) {
619
+ year = -year;
620
+ }
621
+ uint32_t month = ym % 13;
622
+ uint32_t day = symd % (1 << 5);
623
+ uint32_t hms = integer_part % (1 << 17);
624
+ uint32_t hour = hms >> 12;
625
+ uint32_t minute = (hms >> 6) % (1 << 6);
626
+ uint32_t second = hms % (1 << 6);
627
+ rb_value = rb_funcall(rb_cTime,
628
+ rb_intern("utc"),
629
+ 7,
630
+ UINT2NUM(year),
631
+ UINT2NUM(month),
632
+ UINT2NUM(day),
633
+ UINT2NUM(hour),
634
+ UINT2NUM(minute),
635
+ UINT2NUM(second),
636
+ UINT2NUM(fractional_seconds));
637
+ }
638
+ break;
639
+ case MYSQL_TYPE_TIME2:
640
+ rb_raise(rb_eNotImpError,
641
+ "time2 type isn't implemented yet: %+" PRIsVALUE,
642
+ rb_type);
643
+ break;
644
+ case MYSQL_TYPE_JSON:
645
+ rb_value = rbm2_column_parse_blob(rb_column, row_data);
646
+ break;
647
+ case MYSQL_TYPE_NEWDECIMAL:
648
+ {
649
+ /* TODO: See also bin2decimal(). */
650
+ const uint32_t digits_per_integer = 9;
651
+ const int32_t compressed_bytes[] = {0, 1, 1, 2, 3, 3, 4, 4, 4};
652
+ uint32_t precision =
653
+ NUM2UINT(rb_hash_aref(rb_column,
654
+ rb_id2sym(rb_intern("precision"))));
655
+ uint32_t scale =
656
+ NUM2UINT(rb_hash_aref(rb_column,
657
+ rb_id2sym(rb_intern("scale"))));
658
+ uint32_t integral = precision - scale;
659
+ uint32_t uncompressed_integral = integral / digits_per_integer;
660
+ uint32_t uncompressed_fractional = scale / digits_per_integer;
661
+ uint32_t compressed_integral =
662
+ integral - (uncompressed_integral * digits_per_integer);
663
+ uint32_t compressed_fractional =
664
+ scale - (uncompressed_fractional * digits_per_integer);
665
+
666
+ (*row_data) += compressed_bytes[compressed_integral];
667
+ if (uncompressed_integral > 1) {
668
+ (*row_data) += (4 * (uncompressed_integral - 1));
669
+ }
670
+ if (uncompressed_fractional > 1) {
671
+ (*row_data) += (4 * (uncompressed_fractional - 1));
672
+ }
673
+ (*row_data) += compressed_bytes[compressed_fractional];
674
+ }
675
+ break;
676
+ case MYSQL_TYPE_ENUM:
677
+ case MYSQL_TYPE_SET:
678
+ rb_raise(rb_eNotImpError,
679
+ "enum/set types aren't implemented yet: %+" PRIsVALUE,
680
+ rb_type);
681
+ break;
682
+ case MYSQL_TYPE_TINY_BLOB:
683
+ case MYSQL_TYPE_MEDIUM_BLOB:
684
+ case MYSQL_TYPE_LONG_BLOB:
685
+ rb_raise(rb_eNotImpError,
686
+ "blob types aren't implemented yet: %+" PRIsVALUE,
687
+ rb_type);
688
+ break;
689
+ case MYSQL_TYPE_BLOB:
690
+ rb_value = rbm2_column_parse_blob(rb_column, row_data);
691
+ break;
692
+ case MYSQL_TYPE_VAR_STRING:
693
+ rb_value = rbm2_column_parse_variable_length_string(rb_column, row_data);
694
+ break;
695
+ case MYSQL_TYPE_STRING:
696
+ rb_value = rbm2_column_parse_variable_length_string(rb_column, row_data);
697
+ break;
698
+ case MYSQL_TYPE_GEOMETRY:
699
+ rb_raise(rb_eNotImpError,
700
+ "geometry type isn't implemented yet: %+" PRIsVALUE,
701
+ rb_type);
702
+ break;
703
+ default:
704
+ rb_raise(rb_eNotImpError,
705
+ "unknown type isn't implemented yet: %+" PRIsVALUE,
706
+ rb_type);
707
+ break;
708
+ }
709
+ return rb_value;
710
+ }
711
+
712
+ static inline bool
713
+ rbm2_bitmap_is_set(const uint8_t *bitmap, uint32_t i)
714
+ {
715
+ return (bitmap[i >> 3] >> (i & 0x07)) & 1;
716
+ }
717
+
718
+ typedef struct
719
+ {
720
+ MARIADB_RPL *rpl;
721
+ MARIADB_RPL_EVENT *rpl_event;
722
+ VALUE rb_client;
723
+ VALUE rb_table_maps;
724
+ bool force_disable_use_checksum;
725
+ } rbm2_replication_client_wrapper;
726
+
727
+ static void
728
+ rbm2_replication_client_mark(void *data)
729
+ {
730
+ rbm2_replication_client_wrapper *wrapper = data;
731
+ rb_gc_mark(wrapper->rb_client);
732
+ rb_gc_mark(wrapper->rb_table_maps);
733
+ }
734
+
735
+ static void
736
+ rbm2_replication_client_free(void *data)
737
+ {
738
+ rbm2_replication_client_wrapper *wrapper = data;
739
+ if (wrapper->rpl_event) {
740
+ mariadb_free_rpl_event(wrapper->rpl_event);
741
+ }
742
+ if (wrapper->rpl) {
743
+ mariadb_rpl_close(wrapper->rpl);
744
+ }
745
+ ruby_xfree(wrapper);
746
+ }
747
+
748
+ static const rb_data_type_t rbm2_replication_client_type = {
749
+ "Mysql2Replication::Client",
750
+ {
751
+ rbm2_replication_client_mark,
752
+ rbm2_replication_client_free,
753
+ },
754
+ NULL,
755
+ NULL,
756
+ RUBY_TYPED_FREE_IMMEDIATELY,
757
+ };
758
+
759
+ static VALUE
760
+ rbm2_replication_client_alloc(VALUE klass)
761
+ {
762
+ rbm2_replication_client_wrapper *wrapper;
763
+ VALUE rb_wrapper = TypedData_Make_Struct(klass,
764
+ rbm2_replication_client_wrapper,
765
+ &rbm2_replication_client_type,
766
+ wrapper);
767
+ wrapper->rpl = NULL;
768
+ wrapper->rpl_event = NULL;
769
+ wrapper->rb_client = RUBY_Qnil;
770
+ wrapper->rb_table_maps = rb_hash_new();
771
+ return rb_wrapper;
772
+ }
773
+
774
+ static inline rbm2_replication_client_wrapper *
775
+ rbm2_replication_client_get_wrapper(VALUE self)
776
+ {
777
+ rbm2_replication_client_wrapper *wrapper;
778
+ TypedData_Get_Struct(self,
779
+ rbm2_replication_client_wrapper,
780
+ &rbm2_replication_client_type,
781
+ wrapper);
782
+ return wrapper;
783
+ }
784
+
785
+ static inline mysql_client_wrapper *
786
+ rbm2_replication_client_wrapper_get_client_wrapper(
787
+ rbm2_replication_client_wrapper *replication_client_wrapper)
788
+ {
789
+ GET_CLIENT(replication_client_wrapper->rb_client);
790
+ return wrapper;
791
+ }
792
+
793
+ static inline MYSQL *
794
+ rbm2_replication_client_wrapper_get_client(
795
+ rbm2_replication_client_wrapper *wrapper)
796
+ {
797
+ return rbm2_replication_client_wrapper_get_client_wrapper(wrapper)->client;
798
+ }
799
+
800
+ static void
801
+ rbm2_replication_client_raise(VALUE self)
802
+ {
803
+ rbm2_replication_client_wrapper *wrapper =
804
+ rbm2_replication_client_get_wrapper(self);
805
+ mysql_client_wrapper *client_wrapper =
806
+ rbm2_replication_client_wrapper_get_client_wrapper(wrapper);
807
+ VALUE rb_error_message =
808
+ rb_enc_str_new_cstr(mysql_error(client_wrapper->client),
809
+ rb_utf8_encoding());
810
+ VALUE rb_sql_state =
811
+ rb_enc_str_new_cstr(mysql_sqlstate(client_wrapper->client),
812
+ rb_usascii_encoding());
813
+ ID new_with_args;
814
+ CONST_ID(new_with_args, "new_with_args");
815
+ VALUE rb_error = rb_funcall(rb_cMysql2Error,
816
+ new_with_args,
817
+ 4,
818
+ rb_error_message,
819
+ LONG2NUM(client_wrapper->server_version),
820
+ UINT2NUM(mysql_errno(client_wrapper->client)),
821
+ rb_sql_state);
822
+ rb_exc_raise(rb_error);
823
+ }
824
+
825
+ static VALUE
826
+ rbm2_replication_client_initialize(int argc, VALUE *argv, VALUE self)
827
+ {
828
+ VALUE rb_client;
829
+ VALUE rb_options;
830
+ VALUE rb_checksum = RUBY_Qnil;
831
+
832
+ rb_scan_args(argc, argv, "10:", &rb_client, &rb_options);
833
+ if (!RB_NIL_P(rb_options)) {
834
+ static ID keyword_ids[1];
835
+ VALUE keyword_args[1];
836
+ if (keyword_ids[0] == 0) {
837
+ CONST_ID(keyword_ids[0], "checksum");
838
+ }
839
+ rb_get_kwargs(rb_options, keyword_ids, 0, 1, keyword_args);
840
+ if (keyword_args[0] != RUBY_Qundef) {
841
+ rb_checksum = keyword_args[0];
842
+ }
843
+ }
844
+
845
+ rbm2_replication_client_wrapper *wrapper =
846
+ rbm2_replication_client_get_wrapper(self);
847
+ wrapper->rb_client = rb_client;
848
+ wrapper->rpl =
849
+ mariadb_rpl_init(rbm2_replication_client_wrapper_get_client(wrapper));
850
+ if (!wrapper->rpl) {
851
+ rbm2_replication_client_raise(self);
852
+ }
853
+
854
+ {
855
+ VALUE rb_query;
856
+ if (RB_NIL_P(rb_checksum)) {
857
+ rb_query = rb_str_new_cstr("SET @master_binlog_checksum = "
858
+ "@@global.binlog_checksum");
859
+ } else {
860
+ rb_query = rb_sprintf("SET @master_binlog_checksum = '%"PRIsVALUE"'",
861
+ rb_checksum);
862
+ }
863
+ ID id_query;
864
+ CONST_ID(id_query, "query");
865
+ rb_funcall(rb_client, id_query, 1, rb_query);
866
+ }
867
+ if (rb_equal(rb_str_new_cstr("NONE"), rb_checksum)) {
868
+ wrapper->force_disable_use_checksum = true;
869
+ } else {
870
+ wrapper->force_disable_use_checksum = false;
871
+ }
872
+
873
+ return RUBY_Qnil;
874
+ }
875
+
876
+ static VALUE
877
+ rbm2_replication_client_get_file_name(VALUE self)
878
+ {
879
+ rbm2_replication_client_wrapper *wrapper =
880
+ rbm2_replication_client_get_wrapper(self);
881
+ const char *file_name;
882
+ size_t file_name_length;
883
+ int result = mariadb_rpl_get_optionsv(wrapper->rpl,
884
+ MARIADB_RPL_FILENAME,
885
+ &file_name,
886
+ &file_name_length);
887
+ if (result != 0) {
888
+ rbm2_replication_client_raise(self);
889
+ }
890
+ return rb_str_new(file_name, file_name_length);
891
+ }
892
+
893
+ static VALUE
894
+ rbm2_replication_client_set_file_name(VALUE self, VALUE file_name)
895
+ {
896
+ rbm2_replication_client_wrapper *wrapper =
897
+ rbm2_replication_client_get_wrapper(self);
898
+ int result;
899
+ if (RB_NIL_P(file_name)) {
900
+ result = mariadb_rpl_optionsv(wrapper->rpl,
901
+ MARIADB_RPL_FILENAME,
902
+ NULL,
903
+ 0);
904
+ } else {
905
+ result = mariadb_rpl_optionsv(wrapper->rpl,
906
+ MARIADB_RPL_FILENAME,
907
+ RSTRING_PTR(file_name),
908
+ RSTRING_LEN(file_name));
909
+ }
910
+ if (result != 0) {
911
+ rbm2_replication_client_raise(self);
912
+ }
913
+ return file_name;
914
+ }
915
+
916
+ static VALUE
917
+ rbm2_replication_client_get_start_position(VALUE self)
918
+ {
919
+ rbm2_replication_client_wrapper *wrapper =
920
+ rbm2_replication_client_get_wrapper(self);
921
+ unsigned long start_position;
922
+ int result = mariadb_rpl_get_optionsv(wrapper->rpl,
923
+ MARIADB_RPL_START,
924
+ &start_position);
925
+ if (result != 0) {
926
+ rbm2_replication_client_raise(self);
927
+ }
928
+ return ULONG2NUM(start_position);
929
+ }
930
+
931
+ static VALUE
932
+ rbm2_replication_client_set_start_position(VALUE self, VALUE start_position)
933
+ {
934
+ rbm2_replication_client_wrapper *wrapper =
935
+ rbm2_replication_client_get_wrapper(self);
936
+ int result = mariadb_rpl_optionsv(wrapper->rpl,
937
+ MARIADB_RPL_START,
938
+ NUM2ULONG(start_position));
939
+ if (result != 0) {
940
+ rbm2_replication_client_raise(self);
941
+ }
942
+ return start_position;
943
+ }
944
+
945
+ static VALUE
946
+ rbm2_replication_client_get_server_id(VALUE self)
947
+ {
948
+ rbm2_replication_client_wrapper *wrapper =
949
+ rbm2_replication_client_get_wrapper(self);
950
+ unsigned int server_id;
951
+ int result = mariadb_rpl_get_optionsv(wrapper->rpl,
952
+ MARIADB_RPL_SERVER_ID,
953
+ &server_id);
954
+ if (result != 0) {
955
+ rbm2_replication_client_raise(self);
956
+ }
957
+ return UINT2NUM(server_id);
958
+ }
959
+
960
+ static VALUE
961
+ rbm2_replication_client_set_server_id(VALUE self, VALUE server_id)
962
+ {
963
+ rbm2_replication_client_wrapper *wrapper =
964
+ rbm2_replication_client_get_wrapper(self);
965
+ int result = mariadb_rpl_optionsv(wrapper->rpl,
966
+ MARIADB_RPL_SERVER_ID,
967
+ NUM2UINT(server_id));
968
+ if (result != 0) {
969
+ rbm2_replication_client_raise(self);
970
+ }
971
+ return server_id;
972
+ }
973
+
974
+ static VALUE
975
+ rbm2_replication_client_get_flags(VALUE self)
976
+ {
977
+ rbm2_replication_client_wrapper *wrapper =
978
+ rbm2_replication_client_get_wrapper(self);
979
+ unsigned int flags;
980
+ int result = mariadb_rpl_get_optionsv(wrapper->rpl,
981
+ MARIADB_RPL_FLAGS,
982
+ &flags);
983
+ if (result != 0) {
984
+ rbm2_replication_client_raise(self);
985
+ }
986
+ return UINT2NUM(flags);
987
+ }
988
+
989
+ static VALUE
990
+ rbm2_replication_client_set_flags(VALUE self, VALUE flags)
991
+ {
992
+ rbm2_replication_client_wrapper *wrapper =
993
+ rbm2_replication_client_get_wrapper(self);
994
+ int result = mariadb_rpl_optionsv(wrapper->rpl,
995
+ MARIADB_RPL_FLAGS,
996
+ NUM2UINT(flags));
997
+ if (result != 0) {
998
+ rbm2_replication_client_raise(self);
999
+ }
1000
+ return flags;
1001
+ }
1002
+
1003
+ static void *
1004
+ rbm2_replication_client_close_without_gvl(void *data)
1005
+ {
1006
+ rbm2_replication_client_wrapper *wrapper = data;
1007
+ if (wrapper->rpl_event) {
1008
+ mariadb_free_rpl_event(wrapper->rpl_event);
1009
+ wrapper->rpl_event = NULL;
1010
+ }
1011
+ if (wrapper->rpl) {
1012
+ mariadb_rpl_close(wrapper->rpl);
1013
+ wrapper->rpl = NULL;
1014
+ }
1015
+ return NULL;
1016
+ }
1017
+
1018
+ static VALUE
1019
+ rbm2_replication_client_close(VALUE self)
1020
+ {
1021
+ rbm2_replication_client_wrapper *wrapper =
1022
+ rbm2_replication_client_get_wrapper(self);
1023
+ rb_thread_call_without_gvl(rbm2_replication_client_close_without_gvl,
1024
+ wrapper,
1025
+ RUBY_UBF_IO,
1026
+ 0);
1027
+ return Qnil;
1028
+ }
1029
+
1030
+ static void *
1031
+ rbm2_replication_client_open_without_gvl(void *data)
1032
+ {
1033
+ rbm2_replication_client_wrapper *wrapper = data;
1034
+ int result = mariadb_rpl_open(wrapper->rpl);
1035
+ return (void *)(intptr_t)result;
1036
+ }
1037
+
1038
+ static VALUE
1039
+ rbm2_replication_client_open(VALUE self)
1040
+ {
1041
+ rbm2_replication_client_wrapper *wrapper =
1042
+ rbm2_replication_client_get_wrapper(self);
1043
+ int result =
1044
+ (intptr_t)rb_thread_call_without_gvl(
1045
+ rbm2_replication_client_open_without_gvl,
1046
+ wrapper,
1047
+ RUBY_UBF_IO,
1048
+ 0);
1049
+ if (result != 0) {
1050
+ rbm2_replication_client_raise(self);
1051
+ }
1052
+ if (rb_block_given_p()) {
1053
+ return rb_ensure(rb_yield, self,
1054
+ rbm2_replication_client_close, self);
1055
+ } else {
1056
+ return Qnil;
1057
+ }
1058
+ }
1059
+
1060
+ static void *
1061
+ rbm2_replication_client_fetch_without_gvl(void *data)
1062
+ {
1063
+ rbm2_replication_client_wrapper *wrapper = data;
1064
+ wrapper->rpl_event = mariadb_rpl_fetch(wrapper->rpl, wrapper->rpl_event);
1065
+ return wrapper->rpl_event;
1066
+ }
1067
+
1068
+ static VALUE
1069
+ rbm2_row_parse(const uint8_t **row_data,
1070
+ uint32_t n_columns,
1071
+ const uint8_t *column_bitmap,
1072
+ VALUE rb_columns)
1073
+ {
1074
+ VALUE rb_row = rb_hash_new();
1075
+ uint32_t i;
1076
+ const uint8_t *row_null_bitmap = *row_data;
1077
+ (*row_data) += (n_columns + 7) / 8;
1078
+ for (i = 0; i < n_columns; i++) {
1079
+ if (!rbm2_bitmap_is_set(column_bitmap, i)) {
1080
+ continue;
1081
+ }
1082
+ if (rbm2_bitmap_is_set(row_null_bitmap, i)) {
1083
+ rb_hash_aset(rb_row, UINT2NUM(i), RUBY_Qnil);
1084
+ } else {
1085
+ VALUE rb_column = RARRAY_PTR(rb_columns)[i];
1086
+ VALUE rb_column_value = rbm2_column_parse(rb_column, row_data);
1087
+ rb_hash_aset(rb_row, UINT2NUM(i), rb_column_value);
1088
+ }
1089
+ }
1090
+ return rb_row;
1091
+ }
1092
+
1093
+ static VALUE
1094
+ rbm2_replication_event_new(rbm2_replication_client_wrapper *wrapper,
1095
+ MARIADB_RPL_EVENT *event)
1096
+ {
1097
+ VALUE klass;
1098
+ VALUE rb_event;
1099
+ switch (event->event_type) {
1100
+ case ROTATE_EVENT:
1101
+ klass = rb_cMysql2ReplicationRotateEvent;
1102
+ rb_event = rb_class_new_instance(0, NULL, klass);
1103
+ {
1104
+ struct st_mariadb_rpl_rotate_event *e = &(event->event.rotate);
1105
+ rb_iv_set(rb_event, "@position", ULL2NUM(e->position));
1106
+ size_t filename_size;
1107
+ if (event->timestamp == 0) {
1108
+ /* Fake ROTATE_EVENT: https://mariadb.com/kb/en/fake-rotate_event/ */
1109
+ filename_size = wrapper->rpl->buffer_size -
1110
+ EVENT_HEADER_OFS -
1111
+ sizeof(uint64_t) - /* position */
1112
+ sizeof(uint32_t); /* checksum */
1113
+ } else {
1114
+ filename_size = e->filename.length;
1115
+ }
1116
+ rb_iv_set(rb_event,
1117
+ "@file_name",
1118
+ rb_str_new(e->filename.str, filename_size));
1119
+ }
1120
+ break;
1121
+ case FORMAT_DESCRIPTION_EVENT:
1122
+ klass = rb_cMysql2ReplicationFormatDescriptionEvent;
1123
+ rb_event = rb_class_new_instance(0, NULL, klass);
1124
+ {
1125
+ struct st_mariadb_rpl_format_description_event *e =
1126
+ &(event->event.format_description);
1127
+ rb_iv_set(rb_event, "@format", USHORT2NUM(e->format));
1128
+ rb_iv_set(rb_event, "@server_version", rb_str_new_cstr(e->server_version));
1129
+ rb_iv_set(rb_event, "@timestamp", UINT2NUM(e->timestamp));
1130
+ rb_iv_set(rb_event, "@header_length", UINT2NUM(e->header_len));
1131
+ }
1132
+ if (wrapper->force_disable_use_checksum) {
1133
+ wrapper->rpl->use_checksum = false;
1134
+ }
1135
+ break;
1136
+ case TABLE_MAP_EVENT:
1137
+ klass = rb_cMysql2ReplicationTableMapEvent;
1138
+ rb_event = rb_class_new_instance(0, NULL, klass);
1139
+ {
1140
+ struct st_mariadb_rpl_table_map_event *e = &(event->event.table_map);
1141
+ VALUE rb_table_id = ULONG2NUM(e->table_id);
1142
+ rb_iv_set(rb_event, "@table_id", rb_table_id);
1143
+ rb_iv_set(rb_event, "@database", rb_str_new(e->database.str,
1144
+ e->database.length));
1145
+ rb_iv_set(rb_event, "@table", rb_str_new(e->table.str,
1146
+ e->table.length));
1147
+ {
1148
+ VALUE rb_columns = rb_ary_new_capa(e->column_count);
1149
+ const uint8_t *column_types = (const uint8_t *)(e->column_types.str);
1150
+ const uint8_t *metadata = (const uint8_t *)(e->metadata.str);
1151
+ uint32_t i;
1152
+ for (i = 0; i < e->column_count; i++) {
1153
+ uint8_t column_type = column_types[i];
1154
+ enum enum_field_types real_column_type = column_type;
1155
+ VALUE rb_column = rb_hash_new();
1156
+ rbm2_metadata_parse(&real_column_type,
1157
+ &metadata,
1158
+ rb_column);
1159
+ rb_hash_aset(rb_column,
1160
+ rb_id2sym(rb_intern("type")),
1161
+ rbm2_column_type_to_symbol(real_column_type));
1162
+ rb_hash_aset(rb_column,
1163
+ rb_id2sym(rb_intern("type_id")),
1164
+ UINT2NUM(real_column_type));
1165
+ rb_ary_push(rb_columns, rb_column);
1166
+ }
1167
+ rb_iv_set(rb_event, "@columns", rb_columns);
1168
+ }
1169
+ rb_hash_aset(wrapper->rb_table_maps, rb_table_id, rb_event);
1170
+ }
1171
+ break;
1172
+ case WRITE_ROWS_EVENT_V1:
1173
+ case WRITE_ROWS_EVENT:
1174
+ case UPDATE_ROWS_EVENT_V1:
1175
+ case UPDATE_ROWS_EVENT:
1176
+ case DELETE_ROWS_EVENT_V1:
1177
+ case DELETE_ROWS_EVENT:
1178
+ switch (event->event_type) {
1179
+ case WRITE_ROWS_EVENT_V1:
1180
+ case WRITE_ROWS_EVENT:
1181
+ klass = rb_cMysql2ReplicationWriteRowsEvent;
1182
+ break;
1183
+ case UPDATE_ROWS_EVENT_V1:
1184
+ case UPDATE_ROWS_EVENT:
1185
+ klass = rb_cMysql2ReplicationUpdateRowsEvent;
1186
+ break;
1187
+ default:
1188
+ klass = rb_cMysql2ReplicationDeleteRowsEvent;
1189
+ break;
1190
+ }
1191
+ rb_event = rb_class_new_instance(0, NULL, klass);
1192
+ {
1193
+ struct st_mariadb_rpl_rows_event *e = &(event->event.rows);
1194
+ VALUE rb_table_id = ULONG2NUM(e->table_id);
1195
+ VALUE rb_table_map = rb_hash_aref(wrapper->rb_table_maps, rb_table_id);
1196
+ const uint8_t *column_bitmap =
1197
+ (const uint8_t *)(e->column_bitmap);
1198
+ const uint8_t *column_update_bitmap =
1199
+ (const uint8_t *)(e->column_update_bitmap);
1200
+ rb_iv_set(rb_event, "@table_id", rb_table_id);
1201
+ rb_iv_set(rb_event, "@table_map", rb_table_map);
1202
+ rb_iv_set(rb_event, "@rows_flags", USHORT2NUM(e->flags));
1203
+ VALUE rb_rows = rb_ary_new();
1204
+ VALUE rb_updated_rows = RUBY_Qnil;
1205
+ if (klass == rb_cMysql2ReplicationUpdateRowsEvent) {
1206
+ rb_updated_rows = rb_ary_new();
1207
+ }
1208
+ const uint8_t *row_data = e->row_data;
1209
+ const uint8_t *row_data_end = row_data + e->row_data_size;
1210
+ if (!RB_NIL_P(rb_table_map)) {
1211
+ VALUE rb_columns = rb_iv_get(rb_table_map, "@columns");
1212
+ while (row_data < row_data_end) {
1213
+ VALUE rb_row = rbm2_row_parse(&row_data,
1214
+ e->column_count,
1215
+ column_bitmap,
1216
+ rb_columns);
1217
+ rb_ary_push(rb_rows, rb_row);
1218
+ if (klass == rb_cMysql2ReplicationUpdateRowsEvent) {
1219
+ VALUE rb_updated_row = rbm2_row_parse(&row_data,
1220
+ e->column_count,
1221
+ column_update_bitmap,
1222
+ rb_columns);
1223
+ rb_ary_push(rb_updated_rows, rb_updated_row);
1224
+ }
1225
+ }
1226
+ }
1227
+ rb_iv_set(rb_event, "@rows", rb_rows);
1228
+ if (klass == rb_cMysql2ReplicationUpdateRowsEvent) {
1229
+ rb_iv_set(rb_event, "@updated_rows", rb_updated_rows);
1230
+ }
1231
+ if (e->flags & FL_STMT_END) {
1232
+ rb_hash_clear(wrapper->rb_table_maps);
1233
+ }
1234
+ }
1235
+ break;
1236
+ default:
1237
+ klass = rb_cMysql2ReplicationEvent;
1238
+ rb_event = rb_class_new_instance(0, NULL, klass);
1239
+ break;
1240
+ }
1241
+ rb_iv_set(rb_event, "@type", UINT2NUM(event->event_type));
1242
+ rb_iv_set(rb_event, "@timestamp", UINT2NUM(event->timestamp));
1243
+ rb_iv_set(rb_event, "@server_id", UINT2NUM(event->server_id));
1244
+ rb_iv_set(rb_event, "@length", UINT2NUM(event->event_length));
1245
+ rb_iv_set(rb_event, "@next_position", UINT2NUM(event->next_event_pos));
1246
+ rb_iv_set(rb_event, "@flags", USHORT2NUM(event->flags));
1247
+ return rb_event;
1248
+ }
1249
+
1250
+ static VALUE
1251
+ rbm2_replication_client_fetch(VALUE self)
1252
+ {
1253
+ rbm2_replication_client_wrapper *wrapper =
1254
+ rbm2_replication_client_get_wrapper(self);
1255
+ MYSQL *client = rbm2_replication_client_wrapper_get_client(wrapper);
1256
+ do {
1257
+ MARIADB_RPL_EVENT *event =
1258
+ rb_thread_call_without_gvl(rbm2_replication_client_fetch_without_gvl,
1259
+ wrapper,
1260
+ RUBY_UBF_IO,
1261
+ 0);
1262
+ if (mysql_errno(client) != 0) {
1263
+ rbm2_replication_client_raise(self);
1264
+ }
1265
+ if (!event) {
1266
+ if (wrapper->rpl->buffer_size == 0) {
1267
+ return RUBY_Qnil;
1268
+ }
1269
+ continue;
1270
+ }
1271
+ return rbm2_replication_event_new(wrapper, event);
1272
+ } while (true);
1273
+ }
1274
+
1275
+ static VALUE
1276
+ rbm2_replication_client_each(VALUE self)
1277
+ {
1278
+ rbm2_replication_client_wrapper *wrapper =
1279
+ rbm2_replication_client_get_wrapper(self);
1280
+ MYSQL *client = rbm2_replication_client_wrapper_get_client(wrapper);
1281
+ do {
1282
+ MARIADB_RPL_EVENT *event =
1283
+ rb_thread_call_without_gvl(rbm2_replication_client_fetch_without_gvl,
1284
+ wrapper,
1285
+ RUBY_UBF_IO,
1286
+ 0);
1287
+ if (mysql_errno(client) != 0) {
1288
+ rbm2_replication_client_raise(self);
1289
+ }
1290
+ if (!event) {
1291
+ if (wrapper->rpl->buffer_size == 0) {
1292
+ return RUBY_Qnil;
1293
+ }
1294
+ continue;
1295
+ }
1296
+ rb_yield(rbm2_replication_event_new(wrapper, event));
1297
+ } while (true);
1298
+ return RUBY_Qnil;
1299
+ }
1300
+
1301
+ void
1302
+ Init_mysql2_replication(void)
1303
+ {
1304
+ rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1305
+
1306
+ VALUE rb_mMysql2 = rb_const_get(rb_cObject, rb_intern("Mysql2"));
1307
+ rb_cMysql2Error = rb_const_get(rb_mMysql2, rb_intern("Error"));
1308
+
1309
+ VALUE rb_mMysql2Replication = rb_define_module("Mysql2Replication");
1310
+
1311
+ rb_cMysql2ReplicationEvent =
1312
+ rb_define_class_under(rb_mMysql2Replication, "Event", rb_cObject);
1313
+ rb_define_attr(rb_cMysql2ReplicationEvent, "type", true, false);
1314
+ rb_define_attr(rb_cMysql2ReplicationEvent, "timestamp", true, false);
1315
+ rb_define_attr(rb_cMysql2ReplicationEvent, "server_id", true, false);
1316
+ rb_define_attr(rb_cMysql2ReplicationEvent, "length", true, false);
1317
+ rb_define_attr(rb_cMysql2ReplicationEvent, "next_position", true, false);
1318
+ rb_define_attr(rb_cMysql2ReplicationEvent, "flags", true, false);
1319
+
1320
+ rb_cMysql2ReplicationRotateEvent =
1321
+ rb_define_class_under(rb_mMysql2Replication,
1322
+ "RotateEvent",
1323
+ rb_cMysql2ReplicationEvent);
1324
+ rb_define_attr(rb_cMysql2ReplicationRotateEvent, "position", true, false);
1325
+ rb_define_attr(rb_cMysql2ReplicationRotateEvent, "file_name", true, false);
1326
+
1327
+ rb_cMysql2ReplicationFormatDescriptionEvent =
1328
+ rb_define_class_under(rb_mMysql2Replication,
1329
+ "FormatDescriptionEvent",
1330
+ rb_cMysql2ReplicationEvent);
1331
+ rb_define_attr(rb_cMysql2ReplicationFormatDescriptionEvent,
1332
+ "format", true, false);
1333
+ rb_define_attr(rb_cMysql2ReplicationFormatDescriptionEvent,
1334
+ "server_version", true, false);
1335
+ rb_define_attr(rb_cMysql2ReplicationFormatDescriptionEvent,
1336
+ "timestamp", true, false);
1337
+ rb_define_attr(rb_cMysql2ReplicationFormatDescriptionEvent,
1338
+ "header_length", true, false);
1339
+
1340
+ VALUE rb_cMysql2ReplicationRowsEvent =
1341
+ rb_define_class_under(rb_mMysql2Replication,
1342
+ "RowsEvent",
1343
+ rb_cMysql2ReplicationEvent);
1344
+ rb_define_attr(rb_cMysql2ReplicationRowsEvent, "table_id", true, false);
1345
+ rb_define_attr(rb_cMysql2ReplicationRowsEvent, "table_map", true, false);
1346
+ rb_define_attr(rb_cMysql2ReplicationRowsEvent, "rows_flags", true, false);
1347
+ rb_define_attr(rb_cMysql2ReplicationRowsEvent, "rows", true, false);
1348
+ rb_define_method(rb_cMysql2ReplicationRowsEvent,
1349
+ "statement_end?",
1350
+ rbm2_replication_rows_event_statement_end_p,
1351
+ 0);
1352
+
1353
+ rb_cMysql2ReplicationWriteRowsEvent =
1354
+ rb_define_class_under(rb_mMysql2Replication,
1355
+ "WriteRowsEvent",
1356
+ rb_cMysql2ReplicationRowsEvent);
1357
+ rb_cMysql2ReplicationUpdateRowsEvent =
1358
+ rb_define_class_under(rb_mMysql2Replication,
1359
+ "UpdateRowsEvent",
1360
+ rb_cMysql2ReplicationRowsEvent);
1361
+ rb_define_attr(rb_cMysql2ReplicationUpdateRowsEvent,
1362
+ "updated_rows", true, false);
1363
+ rb_cMysql2ReplicationDeleteRowsEvent =
1364
+ rb_define_class_under(rb_mMysql2Replication,
1365
+ "DeleteRowsEvent",
1366
+ rb_cMysql2ReplicationRowsEvent);
1367
+
1368
+ rb_cMysql2ReplicationTableMapEvent =
1369
+ rb_define_class_under(rb_mMysql2Replication,
1370
+ "TableMapEvent",
1371
+ rb_cMysql2ReplicationEvent);
1372
+ rb_define_attr(rb_cMysql2ReplicationTableMapEvent, "table_id", true, false);
1373
+ rb_define_attr(rb_cMysql2ReplicationTableMapEvent, "database", true, false);
1374
+ rb_define_attr(rb_cMysql2ReplicationTableMapEvent, "table", true, false);
1375
+ rb_define_attr(rb_cMysql2ReplicationTableMapEvent, "columns", true, false);
1376
+
1377
+ VALUE rb_cMysql2ReplicationClient =
1378
+ rb_define_class_under(rb_mMysql2Replication,
1379
+ "Client",
1380
+ rb_cObject);
1381
+ rb_define_alloc_func(rb_cMysql2ReplicationClient,
1382
+ rbm2_replication_client_alloc);
1383
+ rb_include_module(rb_cMysql2ReplicationClient, rb_mEnumerable);
1384
+
1385
+ rb_define_method(rb_cMysql2ReplicationClient,
1386
+ "initialize", rbm2_replication_client_initialize, -1);
1387
+ rb_define_method(rb_cMysql2ReplicationClient,
1388
+ "file_name", rbm2_replication_client_get_file_name, 0);
1389
+ rb_define_method(rb_cMysql2ReplicationClient,
1390
+ "file_name=", rbm2_replication_client_set_file_name, 1);
1391
+ rb_define_method(rb_cMysql2ReplicationClient,
1392
+ "start_position",
1393
+ rbm2_replication_client_get_start_position, 0);
1394
+ rb_define_method(rb_cMysql2ReplicationClient,
1395
+ "start_position=",
1396
+ rbm2_replication_client_set_start_position, 1);
1397
+ rb_define_method(rb_cMysql2ReplicationClient,
1398
+ "server_id", rbm2_replication_client_get_server_id, 0);
1399
+ rb_define_method(rb_cMysql2ReplicationClient,
1400
+ "server_id=", rbm2_replication_client_set_server_id, 1);
1401
+ rb_define_method(rb_cMysql2ReplicationClient,
1402
+ "flags", rbm2_replication_client_get_flags, 0);
1403
+ rb_define_method(rb_cMysql2ReplicationClient,
1404
+ "flags=", rbm2_replication_client_set_flags, 1);
1405
+
1406
+ rb_define_method(rb_cMysql2ReplicationClient,
1407
+ "open", rbm2_replication_client_open, 0);
1408
+ rb_define_method(rb_cMysql2ReplicationClient,
1409
+ "fetch", rbm2_replication_client_fetch, 0);
1410
+ rb_define_method(rb_cMysql2ReplicationClient,
1411
+ "close", rbm2_replication_client_close, 0);
1412
+
1413
+ rb_define_method(rb_cMysql2ReplicationClient,
1414
+ "each", rbm2_replication_client_each, 0);
1415
+
1416
+ VALUE rb_cMysql2ReplicationFlags =
1417
+ rb_define_module_under(rb_mMysql2Replication, "Flags");
1418
+ rb_define_const(rb_cMysql2ReplicationFlags,
1419
+ "BINLOG_DUMP_NON_BLOCK",
1420
+ UINT2NUM(MARIADB_RPL_BINLOG_DUMP_NON_BLOCK));
1421
+ rb_define_const(rb_cMysql2ReplicationFlags,
1422
+ "BINLOG_SEND_ANNOTATE_ROWS",
1423
+ UINT2NUM(MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS));
1424
+ rb_define_const(rb_cMysql2ReplicationFlags,
1425
+ "IGNORE_HEARTBEAT",
1426
+ UINT2NUM(MARIADB_RPL_IGNORE_HEARTBEAT));
1427
+ }