trilogy_w_prepared_statements 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +80 -0
  4. data/Rakefile +22 -0
  5. data/ext/trilogy-ruby/cast.c +277 -0
  6. data/ext/trilogy-ruby/cext.c +1048 -0
  7. data/ext/trilogy-ruby/extconf.rb +17 -0
  8. data/ext/trilogy-ruby/inc/trilogy/blocking.h +281 -0
  9. data/ext/trilogy-ruby/inc/trilogy/buffer.h +64 -0
  10. data/ext/trilogy-ruby/inc/trilogy/builder.h +165 -0
  11. data/ext/trilogy-ruby/inc/trilogy/charset.h +277 -0
  12. data/ext/trilogy-ruby/inc/trilogy/client.h +760 -0
  13. data/ext/trilogy-ruby/inc/trilogy/error.h +44 -0
  14. data/ext/trilogy-ruby/inc/trilogy/packet_parser.h +34 -0
  15. data/ext/trilogy-ruby/inc/trilogy/protocol.h +1014 -0
  16. data/ext/trilogy-ruby/inc/trilogy/reader.h +216 -0
  17. data/ext/trilogy-ruby/inc/trilogy/socket.h +111 -0
  18. data/ext/trilogy-ruby/inc/trilogy/vendor/curl_hostcheck.h +29 -0
  19. data/ext/trilogy-ruby/inc/trilogy/vendor/openssl_hostname_validation.h +51 -0
  20. data/ext/trilogy-ruby/inc/trilogy.h +8 -0
  21. data/ext/trilogy-ruby/src/blocking.c +358 -0
  22. data/ext/trilogy-ruby/src/buffer.c +60 -0
  23. data/ext/trilogy-ruby/src/builder.c +236 -0
  24. data/ext/trilogy-ruby/src/charset.c +212 -0
  25. data/ext/trilogy-ruby/src/client.c +903 -0
  26. data/ext/trilogy-ruby/src/error.c +17 -0
  27. data/ext/trilogy-ruby/src/packet_parser.c +140 -0
  28. data/ext/trilogy-ruby/src/protocol.c +1175 -0
  29. data/ext/trilogy-ruby/src/reader.c +282 -0
  30. data/ext/trilogy-ruby/src/socket.c +623 -0
  31. data/ext/trilogy-ruby/src/vendor/curl_hostcheck.c +206 -0
  32. data/ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c +175 -0
  33. data/ext/trilogy-ruby/trilogy-ruby.h +37 -0
  34. data/lib/trilogy/version.rb +3 -0
  35. data/lib/trilogy.rb +61 -0
  36. data/trilogy.gemspec +27 -0
  37. metadata +107 -0
@@ -0,0 +1,1048 @@
1
+ #include <arpa/inet.h>
2
+ #include <errno.h>
3
+ #include <ruby.h>
4
+ #include <ruby/encoding.h>
5
+ #include <ruby/io.h>
6
+ #include <ruby/thread.h>
7
+ #include <sys/socket.h>
8
+ #include <sys/time.h>
9
+ #include <sys/un.h>
10
+
11
+ #include <trilogy.h>
12
+
13
+ #include "trilogy-ruby.h"
14
+
15
+ #define TRILOGY_RB_TIMEOUT 1
16
+
17
+ VALUE
18
+ rb_cTrilogyError;
19
+
20
+ static VALUE Trilogy_DatabaseError, Trilogy_Result;
21
+
22
+ static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
23
+ id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
24
+ id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
25
+ id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
26
+ id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement;
27
+
28
+ struct trilogy_ctx {
29
+ trilogy_conn_t conn;
30
+ char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
31
+ unsigned int query_flags;
32
+ };
33
+
34
+ static void free_trilogy(void *ptr)
35
+ {
36
+ struct trilogy_ctx *ctx = ptr;
37
+ if (ctx->conn.socket != NULL) {
38
+ trilogy_free(&ctx->conn);
39
+ }
40
+ xfree(ptr);
41
+ }
42
+
43
+ static size_t trilogy_memsize(const void *ptr) {
44
+ const struct trilogy_ctx *ctx = ptr;
45
+ size_t memsize = sizeof(struct trilogy_ctx);
46
+ if (ctx->conn.socket != NULL) {
47
+ memsize += sizeof(trilogy_sock_t);
48
+ }
49
+ memsize += ctx->conn.packet_buffer.cap;
50
+ return memsize;
51
+ }
52
+
53
+ const rb_data_type_t trilogy_data_type = {
54
+ .wrap_struct_name = "trilogy",
55
+ .function = {
56
+ .dmark = NULL,
57
+ .dfree = free_trilogy,
58
+ .dsize = trilogy_memsize,
59
+ },
60
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
61
+ };
62
+
63
+ static struct trilogy_ctx *get_ctx(VALUE obj)
64
+ {
65
+ struct trilogy_ctx *ctx;
66
+ TypedData_Get_Struct(obj, struct trilogy_ctx, &trilogy_data_type, ctx);
67
+ return ctx;
68
+ }
69
+
70
+ static struct trilogy_ctx *get_open_ctx(VALUE obj)
71
+ {
72
+ struct trilogy_ctx *ctx = get_ctx(obj);
73
+
74
+ if (ctx->conn.socket == NULL) {
75
+ rb_raise(rb_eIOError, "connection closed");
76
+ }
77
+
78
+ return ctx;
79
+ }
80
+
81
+ NORETURN(static void handle_trilogy_error(struct trilogy_ctx *, int, const char *, ...));
82
+ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *msg, ...)
83
+ {
84
+ va_list args;
85
+ va_start(args, msg);
86
+ VALUE rbmsg = rb_vsprintf(msg, args);
87
+ va_end(args);
88
+
89
+ switch (rc) {
90
+ case TRILOGY_SYSERR:
91
+ rb_syserr_fail_str(errno, rbmsg);
92
+
93
+ case TRILOGY_ERR: {
94
+ VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
95
+ VALUE exc = rb_exc_new3(Trilogy_DatabaseError,
96
+ rb_sprintf("%" PRIsVALUE ": %d %" PRIsVALUE, rbmsg, ctx->conn.error_code, message));
97
+
98
+ rb_ivar_set(exc, rb_intern("@error_code"), INT2FIX(ctx->conn.error_code));
99
+ rb_ivar_set(exc, rb_intern("@error_message"), message);
100
+
101
+ rb_exc_raise(exc);
102
+ }
103
+
104
+ case TRILOGY_OPENSSL_ERR: {
105
+ unsigned long ossl_error = ERR_get_error();
106
+ ERR_clear_error();
107
+ if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
108
+ rb_syserr_fail_str(ERR_GET_REASON(ossl_error), rbmsg);
109
+ }
110
+ // We can't recover from OpenSSL level errors if there's
111
+ // an active connection.
112
+ if (ctx->conn.socket != NULL) {
113
+ trilogy_sock_shutdown(ctx->conn.socket);
114
+ }
115
+ rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
116
+ }
117
+
118
+ default:
119
+ rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
120
+ }
121
+ }
122
+
123
+ static VALUE allocate_trilogy(VALUE klass)
124
+ {
125
+ struct trilogy_ctx *ctx;
126
+
127
+ VALUE obj = TypedData_Make_Struct(klass, struct trilogy_ctx, &trilogy_data_type, ctx);
128
+
129
+ ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
130
+
131
+ if (trilogy_init(&ctx->conn) < 0) {
132
+ rb_syserr_fail(errno, "trilogy_init");
133
+ }
134
+
135
+ return obj;
136
+ }
137
+
138
+ static int flush_writes(struct trilogy_ctx *ctx)
139
+ {
140
+ while (1) {
141
+ int rc = trilogy_flush_writes(&ctx->conn);
142
+
143
+ if (rc != TRILOGY_AGAIN) {
144
+ return rc;
145
+ }
146
+
147
+ if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
148
+ rb_syserr_fail(ETIMEDOUT, "trilogy_flush_writes");
149
+ }
150
+ }
151
+ }
152
+
153
+ static struct timeval double_to_timeval(double secs)
154
+ {
155
+ double whole_secs = floor(secs);
156
+
157
+ return (struct timeval){
158
+ .tv_sec = whole_secs,
159
+ .tv_usec = floor((secs - whole_secs) * 1000000),
160
+ };
161
+ }
162
+
163
+ static double timeval_to_double(struct timeval tv)
164
+ {
165
+ return (double)tv.tv_sec + ((double)tv.tv_usec) / 1000000.0;
166
+ }
167
+
168
+ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
169
+ {
170
+ struct timeval *timeout = NULL;
171
+ int wait_flag = 0;
172
+
173
+ switch (wait) {
174
+ case TRILOGY_WAIT_READ:
175
+ timeout = &sock->opts.read_timeout;
176
+ wait_flag = RB_WAITFD_IN;
177
+ break;
178
+
179
+ case TRILOGY_WAIT_WRITE:
180
+ timeout = &sock->opts.write_timeout;
181
+ wait_flag = RB_WAITFD_OUT;
182
+ break;
183
+
184
+ case TRILOGY_WAIT_HANDSHAKE:
185
+ timeout = &sock->opts.connect_timeout;
186
+ wait_flag = RB_WAITFD_IN;
187
+ break;
188
+
189
+ default:
190
+ return TRILOGY_ERR;
191
+ }
192
+
193
+ if (timeout->tv_sec == 0 && timeout->tv_usec == 0) {
194
+ timeout = NULL;
195
+ }
196
+
197
+ int fd = trilogy_sock_fd(sock);
198
+ if (rb_wait_for_single_fd(fd, wait_flag, timeout) <= 0)
199
+ return TRILOGY_SYSERR;
200
+
201
+ return 0;
202
+ }
203
+
204
+ struct nogvl_sock_args {
205
+ int rc;
206
+ trilogy_sock_t *sock;
207
+ };
208
+
209
+ static void *no_gvl_resolve(void *data)
210
+ {
211
+ struct nogvl_sock_args *args = data;
212
+ args->rc = trilogy_sock_resolve(args->sock);
213
+ return NULL;
214
+ }
215
+
216
+ static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts)
217
+ {
218
+ trilogy_sock_t *sock = trilogy_sock_new(opts);
219
+ if (sock == NULL) {
220
+ return TRILOGY_ERR;
221
+ }
222
+
223
+ struct nogvl_sock_args args = {.rc = 0, .sock = sock};
224
+
225
+ // Do the DNS resolving with the GVL unlocked. At this point all
226
+ // configuration data is copied and available to the trilogy socket.
227
+ rb_thread_call_without_gvl(no_gvl_resolve, (void *)&args, RUBY_UBF_IO, NULL);
228
+
229
+ int rc = args.rc;
230
+
231
+ if (rc != TRILOGY_OK) {
232
+ return rc;
233
+ }
234
+
235
+ /* replace the default wait callback with our GVL-aware callback so we can
236
+ escape the GVL on each wait operation without going through call_without_gvl */
237
+ sock->wait_cb = _cb_ruby_wait;
238
+ rc = trilogy_connect_send_socket(&ctx->conn, sock);
239
+ if (rc < 0)
240
+ return rc;
241
+
242
+ while (1) {
243
+ rc = trilogy_connect_recv(&ctx->conn, handshake);
244
+
245
+ if (rc == TRILOGY_OK) {
246
+ return rc;
247
+ }
248
+
249
+ if (rc != TRILOGY_AGAIN) {
250
+ return rc;
251
+ }
252
+
253
+ if (trilogy_sock_wait(ctx->conn.socket, TRILOGY_WAIT_HANDSHAKE) < 0)
254
+ return TRILOGY_RB_TIMEOUT;
255
+ }
256
+ }
257
+
258
+ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
259
+ {
260
+ int rc = trilogy_auth_switch_send(&ctx->conn, handshake);
261
+
262
+ if (rc == TRILOGY_AGAIN) {
263
+ rc = flush_writes(ctx);
264
+ }
265
+
266
+ if (rc != TRILOGY_OK) {
267
+ handle_trilogy_error(ctx, rc, "trilogy_auth_switch_send");
268
+ }
269
+
270
+ while (1) {
271
+ rc = trilogy_auth_recv(&ctx->conn, handshake);
272
+
273
+ if (rc == TRILOGY_OK) {
274
+ return;
275
+ }
276
+
277
+ if (rc != TRILOGY_AGAIN) {
278
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
279
+ }
280
+
281
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
282
+ rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
283
+ }
284
+ }
285
+ }
286
+
287
+ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, trilogy_ssl_mode_t ssl_mode)
288
+ {
289
+ int rc;
290
+
291
+ if (ssl_mode != TRILOGY_SSL_DISABLED) {
292
+ if (handshake->capabilities & TRILOGY_CAPABILITIES_SSL) {
293
+ rc = trilogy_ssl_request_send(&ctx->conn);
294
+ if (rc == TRILOGY_AGAIN) {
295
+ rc = flush_writes(ctx);
296
+ }
297
+
298
+ if (rc != TRILOGY_OK) {
299
+ handle_trilogy_error(ctx, rc, "trilogy_ssl_request_send");
300
+ }
301
+
302
+ rc = trilogy_sock_upgrade_ssl(ctx->conn.socket);
303
+ if (rc != TRILOGY_OK) {
304
+ handle_trilogy_error(ctx, rc, "trilogy_ssl_upgrade");
305
+ }
306
+ } else {
307
+ if (ssl_mode != TRILOGY_SSL_PREFERRED_NOVERIFY) {
308
+ rb_raise(rb_cTrilogyError, "SSL required, not supported by server");
309
+ }
310
+ }
311
+ }
312
+
313
+ rc = trilogy_auth_send(&ctx->conn, handshake);
314
+
315
+ if (rc == TRILOGY_AGAIN) {
316
+ rc = flush_writes(ctx);
317
+ }
318
+
319
+ if (rc != TRILOGY_OK) {
320
+ handle_trilogy_error(ctx, rc, "trilogy_auth_send");
321
+ }
322
+
323
+ while (1) {
324
+ rc = trilogy_auth_recv(&ctx->conn, handshake);
325
+
326
+ if (rc == TRILOGY_OK || rc == TRILOGY_AUTH_SWITCH) {
327
+ break;
328
+ }
329
+
330
+ if (rc != TRILOGY_AGAIN) {
331
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
332
+ }
333
+
334
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
335
+ rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
336
+ }
337
+ }
338
+
339
+ if (rc == TRILOGY_AUTH_SWITCH) {
340
+ auth_switch(ctx, handshake);
341
+ }
342
+ }
343
+
344
+ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
345
+ {
346
+ struct trilogy_ctx *ctx = get_ctx(self);
347
+ trilogy_sockopt_t connopt = {0};
348
+ trilogy_handshake_t handshake;
349
+ VALUE val;
350
+
351
+ Check_Type(opts, T_HASH);
352
+
353
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_ssl_mode))) != Qnil) {
354
+ Check_Type(val, T_FIXNUM);
355
+ connopt.ssl_mode = (trilogy_ssl_mode_t)NUM2INT(val);
356
+ }
357
+
358
+ if ((val = rb_hash_aref(opts, ID2SYM(id_connect_timeout))) != Qnil) {
359
+ connopt.connect_timeout = double_to_timeval(NUM2DBL(val));
360
+ }
361
+
362
+ if ((val = rb_hash_aref(opts, ID2SYM(id_read_timeout))) != Qnil) {
363
+ connopt.read_timeout = double_to_timeval(NUM2DBL(val));
364
+ }
365
+
366
+ if ((val = rb_hash_aref(opts, ID2SYM(id_write_timeout))) != Qnil) {
367
+ connopt.write_timeout = double_to_timeval(NUM2DBL(val));
368
+ }
369
+
370
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_keepalive_enabled)))) {
371
+ connopt.keepalive_enabled = true;
372
+ }
373
+
374
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_keepalive_idle))) != Qnil) {
375
+ Check_Type(val, T_FIXNUM);
376
+ connopt.keepalive_idle = NUM2USHORT(val);
377
+ }
378
+
379
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_keepalive_count))) != Qnil) {
380
+ Check_Type(val, T_FIXNUM);
381
+ connopt.keepalive_count = NUM2USHORT(val);
382
+ }
383
+
384
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_keepalive_interval))) != Qnil) {
385
+ Check_Type(val, T_FIXNUM);
386
+ connopt.keepalive_interval = NUM2USHORT(val);
387
+ }
388
+
389
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_host))) != Qnil) {
390
+ Check_Type(val, T_STRING);
391
+
392
+ connopt.hostname = StringValueCStr(val);
393
+ connopt.port = 3306;
394
+
395
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_port))) != Qnil) {
396
+ Check_Type(val, T_FIXNUM);
397
+ connopt.port = NUM2USHORT(val);
398
+ }
399
+ } else {
400
+ connopt.path = (char *)"/tmp/mysql.sock";
401
+
402
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_socket))) != Qnil) {
403
+ Check_Type(val, T_STRING);
404
+ connopt.path = StringValueCStr(val);
405
+ }
406
+ }
407
+
408
+ if ((val = rb_hash_aref(opts, ID2SYM(id_username))) != Qnil) {
409
+ Check_Type(val, T_STRING);
410
+ connopt.username = StringValueCStr(val);
411
+ }
412
+
413
+ if ((val = rb_hash_aref(opts, ID2SYM(id_password))) != Qnil) {
414
+ Check_Type(val, T_STRING);
415
+ connopt.password = RSTRING_PTR(val);
416
+ connopt.password_len = RSTRING_LEN(val);
417
+ }
418
+
419
+ if ((val = rb_hash_aref(opts, ID2SYM(id_database))) != Qnil) {
420
+ Check_Type(val, T_STRING);
421
+ connopt.database = StringValueCStr(val);
422
+ connopt.flags |= TRILOGY_CAPABILITIES_CONNECT_WITH_DB;
423
+ }
424
+
425
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_found_rows)))) {
426
+ connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
427
+ }
428
+
429
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
430
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
431
+ }
432
+
433
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
434
+ Check_Type(val, T_STRING);
435
+ connopt.ssl_ca = StringValueCStr(val);
436
+ }
437
+
438
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_capath))) != Qnil) {
439
+ Check_Type(val, T_STRING);
440
+ connopt.ssl_capath = StringValueCStr(val);
441
+ }
442
+
443
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_cert))) != Qnil) {
444
+ Check_Type(val, T_STRING);
445
+ connopt.ssl_cert = StringValueCStr(val);
446
+ }
447
+
448
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_cipher))) != Qnil) {
449
+ Check_Type(val, T_STRING);
450
+ connopt.ssl_cipher = StringValueCStr(val);
451
+ }
452
+
453
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_crl))) != Qnil) {
454
+ Check_Type(val, T_STRING);
455
+ connopt.ssl_crl = StringValueCStr(val);
456
+ }
457
+
458
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_crlpath))) != Qnil) {
459
+ Check_Type(val, T_STRING);
460
+ connopt.ssl_crlpath = StringValueCStr(val);
461
+ }
462
+
463
+ if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_key))) != Qnil) {
464
+ Check_Type(val, T_STRING);
465
+ connopt.ssl_key = StringValueCStr(val);
466
+ }
467
+
468
+ if ((val = rb_hash_aref(opts, ID2SYM(id_tls_ciphersuites))) != Qnil) {
469
+ Check_Type(val, T_STRING);
470
+ connopt.tls_ciphersuites = StringValueCStr(val);
471
+ }
472
+
473
+ if ((val = rb_hash_aref(opts, ID2SYM(id_tls_min_version))) != Qnil) {
474
+ Check_Type(val, T_FIXNUM);
475
+ connopt.tls_min_version = NUM2INT(val);
476
+ }
477
+
478
+ if ((val = rb_hash_aref(opts, ID2SYM(id_tls_max_version))) != Qnil) {
479
+ Check_Type(val, T_FIXNUM);
480
+ connopt.tls_max_version = NUM2INT(val);
481
+ }
482
+
483
+ int rc = try_connect(ctx, &handshake, &connopt);
484
+ if (rc == TRILOGY_RB_TIMEOUT) {
485
+ rb_syserr_fail(ETIMEDOUT, "trilogy_connect_recv");
486
+ }
487
+ if (rc != TRILOGY_OK) {
488
+ if (connopt.path) {
489
+ handle_trilogy_error(ctx, rc, "trilogy_connect - unable to connect to %s", connopt.path);
490
+ } else {
491
+ handle_trilogy_error(ctx, rc, "trilogy_connect - unable to connect to %s:%hu", connopt.hostname,
492
+ connopt.port);
493
+ }
494
+ }
495
+
496
+ memcpy(ctx->server_version, handshake.server_version, TRILOGY_SERVER_VERSION_SIZE);
497
+ ctx->server_version[TRILOGY_SERVER_VERSION_SIZE] = 0;
498
+
499
+ authenticate(ctx, &handshake, connopt.ssl_mode);
500
+
501
+ return Qnil;
502
+ }
503
+
504
+ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
505
+ {
506
+ struct trilogy_ctx *ctx = get_open_ctx(self);
507
+
508
+ StringValue(database);
509
+
510
+ int rc = trilogy_change_db_send(&ctx->conn, RSTRING_PTR(database), RSTRING_LEN(database));
511
+
512
+ if (rc == TRILOGY_AGAIN) {
513
+ rc = flush_writes(ctx);
514
+ }
515
+
516
+ if (rc != TRILOGY_OK) {
517
+ handle_trilogy_error(ctx, rc, "trilogy_change_db_send");
518
+ }
519
+
520
+ while (1) {
521
+ rc = trilogy_change_db_recv(&ctx->conn);
522
+
523
+ if (rc == TRILOGY_OK) {
524
+ break;
525
+ }
526
+
527
+ if (rc != TRILOGY_AGAIN) {
528
+ handle_trilogy_error(ctx, rc, "trilogy_change_db_recv");
529
+ }
530
+
531
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
532
+ rb_syserr_fail(ETIMEDOUT, "trilogy_change_db_recv");
533
+ }
534
+ }
535
+
536
+ return Qtrue;
537
+ }
538
+
539
+ static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_options *cast_options)
540
+ {
541
+ cast_options->cast = (query_flags & TRILOGY_FLAGS_CAST) != 0;
542
+ cast_options->cast_booleans = (query_flags & TRILOGY_FLAGS_CAST_BOOLEANS) != 0;
543
+ cast_options->database_local_time = (query_flags & TRILOGY_FLAGS_LOCAL_TIMEZONE) != 0;
544
+ cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
545
+ }
546
+
547
+ struct read_query_response_state {
548
+ struct rb_trilogy_cast_options *cast_options;
549
+ struct trilogy_ctx *ctx;
550
+
551
+ // to free by caller:
552
+ struct column_info *column_info;
553
+ trilogy_value_t *row_values;
554
+
555
+ // Error state for tracking
556
+ const char *msg;
557
+ int rc;
558
+ };
559
+
560
+ static void get_timespec_monotonic(struct timespec *ts)
561
+ {
562
+ #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
563
+ if (clock_gettime(CLOCK_MONOTONIC, ts) == -1) {
564
+ rb_sys_fail("clock_gettime");
565
+ }
566
+ #else
567
+ struct timeval tv;
568
+ if (gettimeofday(&tv, 0) < 0) {
569
+ rb_sys_fail("gettimeofday");
570
+ }
571
+ ts->tv_sec = tv.tv_sec;
572
+ ts->tv_nsec = tv.tv_usec * 1000;
573
+ #endif
574
+ }
575
+
576
+ static VALUE read_query_error(struct read_query_response_state *args, int rc, const char *msg)
577
+ {
578
+ args->rc = rc;
579
+ args->msg = msg;
580
+ return Qundef;
581
+ }
582
+
583
+ static VALUE read_query_response(VALUE vargs)
584
+ {
585
+ struct read_query_response_state *args = (void *)vargs;
586
+ struct trilogy_ctx *ctx = args->ctx;
587
+
588
+ struct timespec start;
589
+ get_timespec_monotonic(&start);
590
+
591
+ uint64_t column_count = 0;
592
+
593
+ int rc;
594
+
595
+ while (1) {
596
+ rc = trilogy_query_recv(&ctx->conn, &column_count);
597
+
598
+ if (rc == TRILOGY_OK || rc == TRILOGY_HAVE_RESULTS) {
599
+ break;
600
+ }
601
+
602
+ if (rc != TRILOGY_AGAIN) {
603
+ return read_query_error(args, rc, "trilogy_query_recv");
604
+ }
605
+
606
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
607
+ rb_syserr_fail(ETIMEDOUT, "trilogy_query_recv");
608
+ }
609
+ }
610
+
611
+ struct timespec finish;
612
+ get_timespec_monotonic(&finish);
613
+
614
+ double query_time = finish.tv_sec - start.tv_sec;
615
+ query_time += (double)(finish.tv_nsec - start.tv_nsec) / 1000000000.0;
616
+
617
+ VALUE result = rb_obj_alloc(Trilogy_Result);
618
+
619
+ VALUE column_names = rb_ary_new2(column_count);
620
+ rb_ivar_set(result, id_ivar_fields, column_names);
621
+
622
+ VALUE rows = rb_ary_new();
623
+ rb_ivar_set(result, id_ivar_rows, rows);
624
+
625
+ rb_ivar_set(result, id_ivar_query_time, DBL2NUM(query_time));
626
+
627
+ if (rc == TRILOGY_OK) {
628
+ rb_ivar_set(result, id_ivar_last_insert_id, ULL2NUM(ctx->conn.last_insert_id));
629
+
630
+ rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
631
+
632
+ return result;
633
+ }
634
+
635
+ struct column_info *column_info = ALLOC_N(struct column_info, column_count);
636
+ args->column_info = column_info;
637
+
638
+ for (uint64_t i = 0; i < column_count; i++) {
639
+ trilogy_column_t column;
640
+
641
+ while (1) {
642
+ rc = trilogy_read_column(&ctx->conn, &column);
643
+
644
+ if (rc == TRILOGY_OK) {
645
+ break;
646
+ }
647
+
648
+ if (rc != TRILOGY_AGAIN) {
649
+ return read_query_error(args, rc, "trilogy_read_column");
650
+ }
651
+
652
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
653
+ rb_syserr_fail(ETIMEDOUT, "trilogy_read_column");
654
+ }
655
+ }
656
+
657
+ #ifdef HAVE_RB_INTERNED_STR
658
+ VALUE column_name = rb_interned_str(column.name, column.name_len);
659
+ #else
660
+ VALUE column_name = rb_str_new(column.name, column.name_len);
661
+ OBJ_FREEZE(column_name);
662
+ #endif
663
+
664
+ rb_ary_push(column_names, column_name);
665
+
666
+ column_info[i].type = column.type;
667
+ column_info[i].flags = column.flags;
668
+ column_info[i].len = column.len;
669
+ column_info[i].charset = column.charset;
670
+ column_info[i].decimals = column.decimals;
671
+ }
672
+
673
+ trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
674
+ args->row_values = row_values;
675
+
676
+ while (1) {
677
+ int rc = trilogy_read_row(&ctx->conn, row_values);
678
+
679
+ if (rc == TRILOGY_AGAIN) {
680
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
681
+ rb_syserr_fail(ETIMEDOUT, "trilogy_read_row");
682
+ }
683
+ continue;
684
+ }
685
+
686
+ if (rc == TRILOGY_EOF) {
687
+ break;
688
+ }
689
+
690
+ if (rc != TRILOGY_OK) {
691
+ return read_query_error(args, rc, "trilogy_read_row");
692
+ }
693
+
694
+ if (args->cast_options->flatten_rows) {
695
+ for (uint64_t i = 0; i < column_count; i++) {
696
+ rb_ary_push(rows, rb_trilogy_cast_value(row_values + i, column_info + i, args->cast_options));
697
+ }
698
+ } else {
699
+ VALUE row = rb_ary_new2(column_count);
700
+ for (uint64_t i = 0; i < column_count; i++) {
701
+ rb_ary_push(row, rb_trilogy_cast_value(row_values + i, column_info + i, args->cast_options));
702
+ }
703
+ rb_ary_push(rows, row);
704
+ }
705
+ }
706
+
707
+ return result;
708
+ }
709
+
710
+ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
711
+ {
712
+ struct rb_trilogy_cast_options cast_options;
713
+ load_query_options(ctx->query_flags, &cast_options);
714
+
715
+ struct read_query_response_state args = {
716
+ .cast_options = &cast_options,
717
+ .column_info = NULL,
718
+ .ctx = ctx,
719
+ .row_values = NULL,
720
+ .rc = TRILOGY_OK,
721
+ .msg = NULL,
722
+ };
723
+
724
+ int state = 0;
725
+ VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
726
+
727
+ xfree(args.column_info);
728
+ xfree(args.row_values);
729
+
730
+ // If we have seen an unexpected exception, jump to it so it gets raised.
731
+ if (state) {
732
+ trilogy_sock_shutdown(ctx->conn.socket);
733
+ rb_jump_tag(state);
734
+ }
735
+
736
+ // Handle errors we can gracefully recover from here that were due to
737
+ // errors signaled at the protocol level, not unexpected exceptions.
738
+ if (result == Qundef) {
739
+ handle_trilogy_error(ctx, args.rc, args.msg);
740
+ }
741
+
742
+ return result;
743
+ }
744
+
745
+ static VALUE rb_trilogy_next_result(VALUE self)
746
+ {
747
+ struct trilogy_ctx *ctx = get_open_ctx(self);
748
+
749
+ if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) {
750
+ return Qnil;
751
+ }
752
+
753
+ return execute_read_query_response(ctx);
754
+ }
755
+
756
+ static VALUE rb_trilogy_more_results_exist(VALUE self)
757
+ {
758
+ struct trilogy_ctx *ctx = get_open_ctx(self);
759
+
760
+ if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
761
+ return Qtrue;
762
+ } else {
763
+ return Qfalse;
764
+ }
765
+ }
766
+
767
+ static VALUE rb_trilogy_query(VALUE self, VALUE query)
768
+ {
769
+ struct trilogy_ctx *ctx = get_open_ctx(self);
770
+
771
+ StringValue(query);
772
+
773
+ int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
774
+
775
+ if (rc == TRILOGY_AGAIN) {
776
+ rc = flush_writes(ctx);
777
+ }
778
+
779
+ if (rc < 0) {
780
+ handle_trilogy_error(ctx, rc, "trilogy_query_send");
781
+ }
782
+
783
+ return execute_read_query_response(ctx);
784
+ }
785
+
786
+ static VALUE rb_trilogy_ping(VALUE self)
787
+ {
788
+ struct trilogy_ctx *ctx = get_open_ctx(self);
789
+
790
+ int rc = trilogy_ping_send(&ctx->conn);
791
+
792
+ if (rc == TRILOGY_AGAIN) {
793
+ rc = flush_writes(ctx);
794
+ }
795
+
796
+ if (rc < 0) {
797
+ handle_trilogy_error(ctx, rc, "trilogy_ping_send");
798
+ }
799
+
800
+ while (1) {
801
+ rc = trilogy_ping_recv(&ctx->conn);
802
+
803
+ if (rc == TRILOGY_OK) {
804
+ break;
805
+ }
806
+
807
+ if (rc != TRILOGY_AGAIN) {
808
+ handle_trilogy_error(ctx, rc, "trilogy_ping_recv");
809
+ }
810
+
811
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
812
+ rb_syserr_fail(ETIMEDOUT, "trilogy_ping_recv");
813
+ }
814
+ }
815
+
816
+ return Qtrue;
817
+ }
818
+
819
+ static VALUE rb_trilogy_escape(VALUE self, VALUE str)
820
+ {
821
+ struct trilogy_ctx *ctx = get_open_ctx(self);
822
+ rb_encoding *str_enc = rb_enc_get(str);
823
+
824
+ StringValue(str);
825
+
826
+ if (!rb_enc_asciicompat(str_enc)) {
827
+ rb_raise(rb_eEncCompatError, "input string must be ASCII-compatible");
828
+ }
829
+
830
+ const char *escaped_str;
831
+ size_t escaped_len;
832
+
833
+ int rc = trilogy_escape(&ctx->conn, RSTRING_PTR(str), RSTRING_LEN(str), &escaped_str, &escaped_len);
834
+
835
+ if (rc < 0) {
836
+ handle_trilogy_error(ctx, rc, "trilogy_escape");
837
+ }
838
+
839
+ return rb_enc_str_new(escaped_str, escaped_len, str_enc);
840
+ }
841
+
842
+ static VALUE rb_trilogy_close(VALUE self)
843
+ {
844
+ struct trilogy_ctx *ctx = get_ctx(self);
845
+
846
+ if (ctx->conn.socket == NULL) {
847
+ return Qnil;
848
+ }
849
+
850
+ int rc = trilogy_close_send(&ctx->conn);
851
+
852
+ if (rc == TRILOGY_AGAIN) {
853
+ rc = flush_writes(ctx);
854
+ }
855
+
856
+ if (rc == TRILOGY_OK) {
857
+ while (1) {
858
+ rc = trilogy_close_recv(&ctx->conn);
859
+
860
+ if (rc != TRILOGY_AGAIN) {
861
+ break;
862
+ }
863
+
864
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
865
+ // timed out
866
+ break;
867
+ }
868
+ }
869
+ }
870
+
871
+ trilogy_free(&ctx->conn);
872
+
873
+ return Qnil;
874
+ }
875
+
876
+ static VALUE rb_trilogy_closed(VALUE self)
877
+ {
878
+ struct trilogy_ctx *ctx = get_ctx(self);
879
+
880
+ if (ctx->conn.socket == NULL) {
881
+ return Qtrue;
882
+ } else {
883
+ return Qfalse;
884
+ }
885
+ }
886
+
887
+ static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
888
+
889
+ static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
890
+
891
+ static VALUE rb_trilogy_warning_count(VALUE self) { return UINT2NUM(get_open_ctx(self)->conn.warning_count); }
892
+
893
+ static VALUE rb_trilogy_last_gtid(VALUE self)
894
+ {
895
+ struct trilogy_ctx *ctx = get_open_ctx(self);
896
+ if (ctx->conn.last_gtid_len > 0) {
897
+ return rb_str_new(ctx->conn.last_gtid, ctx->conn.last_gtid_len);
898
+ } else {
899
+ return Qnil;
900
+ }
901
+ }
902
+
903
+ static VALUE rb_trilogy_query_flags(VALUE self) { return UINT2NUM(get_ctx(self)->query_flags); }
904
+
905
+ static VALUE rb_trilogy_query_flags_set(VALUE self, VALUE query_flags)
906
+ {
907
+ return get_ctx(self)->query_flags = NUM2UINT(query_flags);
908
+ }
909
+
910
+ static VALUE rb_trilogy_read_timeout(VALUE self) {
911
+ struct trilogy_ctx *ctx = get_open_ctx(self);
912
+ return DBL2NUM(timeval_to_double(ctx->conn.socket->opts.read_timeout));
913
+ }
914
+
915
+ static VALUE rb_trilogy_read_timeout_set(VALUE self, VALUE read_timeout)
916
+ {
917
+ struct trilogy_ctx *ctx = get_open_ctx(self);
918
+ if (read_timeout == Qnil) {
919
+ ctx->conn.socket->opts.read_timeout = double_to_timeval(0.0);
920
+ } else {
921
+ ctx->conn.socket->opts.read_timeout = double_to_timeval(NUM2DBL(read_timeout));
922
+ }
923
+ return read_timeout;
924
+ }
925
+
926
+ static VALUE rb_trilogy_write_timeout(VALUE self) {
927
+ struct trilogy_ctx *ctx = get_open_ctx(self);
928
+ return DBL2NUM(timeval_to_double(ctx->conn.socket->opts.write_timeout));
929
+ }
930
+
931
+ static VALUE rb_trilogy_write_timeout_set(VALUE self, VALUE write_timeout)
932
+ {
933
+ struct trilogy_ctx *ctx = get_open_ctx(self);
934
+ if (write_timeout == Qnil) {
935
+ ctx->conn.socket->opts.write_timeout = double_to_timeval(0.0);
936
+ } else {
937
+ ctx->conn.socket->opts.write_timeout = double_to_timeval(NUM2DBL(write_timeout));
938
+ }
939
+ return write_timeout;
940
+ }
941
+
942
+ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx(self)->conn.server_status); }
943
+
944
+ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
945
+
946
+ void Init_cext()
947
+ {
948
+ VALUE Trilogy = rb_define_class("Trilogy", rb_cObject);
949
+
950
+ rb_define_alloc_func(Trilogy, allocate_trilogy);
951
+
952
+ rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
953
+ rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
954
+ rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
955
+ rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
956
+ rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
957
+ rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
958
+ rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
959
+ rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
960
+ rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
961
+ rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
962
+ rb_define_method(Trilogy, "last_gtid", rb_trilogy_last_gtid, 0);
963
+ rb_define_method(Trilogy, "query_flags", rb_trilogy_query_flags, 0);
964
+ rb_define_method(Trilogy, "query_flags=", rb_trilogy_query_flags_set, 1);
965
+ rb_define_method(Trilogy, "read_timeout", rb_trilogy_read_timeout, 0);
966
+ rb_define_method(Trilogy, "read_timeout=", rb_trilogy_read_timeout_set, 1);
967
+ rb_define_method(Trilogy, "write_timeout", rb_trilogy_write_timeout, 0);
968
+ rb_define_method(Trilogy, "write_timeout=", rb_trilogy_write_timeout_set, 1);
969
+ rb_define_method(Trilogy, "server_status", rb_trilogy_server_status, 0);
970
+ rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
971
+ rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
972
+ rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
973
+ rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
974
+ rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
975
+ rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
976
+ rb_define_const(Trilogy, "TLS_VERSION_13", INT2NUM(TRILOGY_TLS_VERSION_13));
977
+
978
+ rb_define_const(Trilogy, "SSL_DISABLED", INT2NUM(TRILOGY_SSL_DISABLED));
979
+ rb_define_const(Trilogy, "SSL_VERIFY_IDENTITY", INT2NUM(TRILOGY_SSL_VERIFY_IDENTITY));
980
+ rb_define_const(Trilogy, "SSL_VERIFY_CA", INT2NUM(TRILOGY_SSL_VERIFY_CA));
981
+ rb_define_const(Trilogy, "SSL_REQUIRED_NOVERIFY", INT2NUM(TRILOGY_SSL_REQUIRED_NOVERIFY));
982
+ rb_define_const(Trilogy, "SSL_PREFERRED_NOVERIFY", INT2NUM(TRILOGY_SSL_PREFERRED_NOVERIFY));
983
+
984
+ rb_define_const(Trilogy, "QUERY_FLAGS_NONE", INT2NUM(0));
985
+ rb_define_const(Trilogy, "QUERY_FLAGS_CAST", INT2NUM(TRILOGY_FLAGS_CAST));
986
+ rb_define_const(Trilogy, "QUERY_FLAGS_CAST_BOOLEANS", INT2NUM(TRILOGY_FLAGS_CAST_BOOLEANS));
987
+ rb_define_const(Trilogy, "QUERY_FLAGS_LOCAL_TIMEZONE", INT2NUM(TRILOGY_FLAGS_LOCAL_TIMEZONE));
988
+ rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
989
+ rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
990
+
991
+ rb_cTrilogyError = rb_define_class_under(Trilogy, "Error", rb_eStandardError);
992
+ rb_global_variable(&rb_cTrilogyError);
993
+
994
+ Trilogy_DatabaseError = rb_define_class_under(Trilogy, "DatabaseError", rb_cTrilogyError);
995
+ rb_global_variable(&Trilogy_DatabaseError);
996
+
997
+ rb_define_attr(Trilogy_DatabaseError, "error_code", 1, 0);
998
+ rb_define_attr(Trilogy_DatabaseError, "error_message", 1, 0);
999
+
1000
+ Trilogy_Result = rb_define_class_under(Trilogy, "Result", rb_cObject);
1001
+ rb_global_variable(&Trilogy_Result);
1002
+
1003
+ rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
1004
+ rb_define_attr(Trilogy_Result, "fields", 1, 0);
1005
+ rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
1006
+ rb_define_attr(Trilogy_Result, "rows", 1, 0);
1007
+ rb_define_attr(Trilogy_Result, "query_time", 1, 0);
1008
+
1009
+ id_socket = rb_intern("socket");
1010
+ id_host = rb_intern("host");
1011
+ id_port = rb_intern("port");
1012
+ id_username = rb_intern("username");
1013
+ id_password = rb_intern("password");
1014
+ id_found_rows = rb_intern("found_rows");
1015
+ id_connect_timeout = rb_intern("connect_timeout");
1016
+ id_read_timeout = rb_intern("read_timeout");
1017
+ id_write_timeout = rb_intern("write_timeout");
1018
+ id_keepalive_enabled = rb_intern("keepalive_enabled");
1019
+ id_keepalive_idle = rb_intern("keepalive_idle");
1020
+ id_keepalive_count = rb_intern("keepalive_count");
1021
+ id_keepalive_interval = rb_intern("keepalive_interval");
1022
+ id_database = rb_intern("database");
1023
+ id_ssl_ca = rb_intern("ssl_ca");
1024
+ id_ssl_capath = rb_intern("ssl_capath");
1025
+ id_ssl_cert = rb_intern("ssl_cert");
1026
+ id_ssl_cipher = rb_intern("ssl_cipher");
1027
+ id_ssl_crl = rb_intern("ssl_crl");
1028
+ id_ssl_crlpath = rb_intern("ssl_crlpath");
1029
+ id_ssl_key = rb_intern("ssl_key");
1030
+ id_ssl_mode = rb_intern("ssl_mode");
1031
+ id_tls_ciphersuites = rb_intern("tls_ciphersuites");
1032
+ id_tls_min_version = rb_intern("tls_min_version");
1033
+ id_tls_max_version = rb_intern("tls_max_version");
1034
+ id_multi_statement = rb_intern("multi_statement");
1035
+
1036
+ id_ivar_affected_rows = rb_intern("@affected_rows");
1037
+ id_ivar_fields = rb_intern("@fields");
1038
+ id_ivar_last_insert_id = rb_intern("@last_insert_id");
1039
+ id_ivar_rows = rb_intern("@rows");
1040
+ id_ivar_query_time = rb_intern("@query_time");
1041
+
1042
+ rb_trilogy_cast_init();
1043
+
1044
+ // server_status flags
1045
+ #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1046
+ TRILOGY_SERVER_STATUS(XX)
1047
+ #undef XX
1048
+ }