mysql2-replication 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }