mysql2-replication 1.0.0 → 1.0.4

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