trilogy 2.2.0 → 2.4.0

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.
@@ -8,29 +8,40 @@
8
8
  #include <sys/time.h>
9
9
  #include <sys/un.h>
10
10
 
11
+ #include <unistd.h>
12
+ #include <fcntl.h>
13
+
11
14
  #include <trilogy.h>
12
15
 
13
16
  #include "trilogy-ruby.h"
14
17
 
15
18
  #define TRILOGY_RB_TIMEOUT 1
16
19
 
17
- VALUE
18
- rb_cTrilogyError;
19
-
20
- static VALUE Trilogy_DatabaseError, Trilogy_Result;
20
+ VALUE Trilogy_CastError;
21
+ static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
22
+ Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError,
23
+ Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result;
21
24
 
22
25
  static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
23
26
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
24
27
  id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
25
28
  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;
29
+ id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result,
30
+ id_from_code, id_from_errno, id_connection_options;
27
31
 
28
32
  struct trilogy_ctx {
29
33
  trilogy_conn_t conn;
30
34
  char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
31
35
  unsigned int query_flags;
36
+ VALUE encoding;
32
37
  };
33
38
 
39
+ static void mark_trilogy(void *ptr)
40
+ {
41
+ struct trilogy_ctx *ctx = ptr;
42
+ rb_gc_mark(ctx->encoding);
43
+ }
44
+
34
45
  static void free_trilogy(void *ptr)
35
46
  {
36
47
  struct trilogy_ctx *ctx = ptr;
@@ -50,10 +61,10 @@ static size_t trilogy_memsize(const void *ptr) {
50
61
  return memsize;
51
62
  }
52
63
 
53
- const rb_data_type_t trilogy_data_type = {
64
+ static const rb_data_type_t trilogy_data_type = {
54
65
  .wrap_struct_name = "trilogy",
55
66
  .function = {
56
- .dmark = NULL,
67
+ .dmark = mark_trilogy,
57
68
  .dfree = free_trilogy,
58
69
  .dsize = trilogy_memsize,
59
70
  },
@@ -72,12 +83,25 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
72
83
  struct trilogy_ctx *ctx = get_ctx(obj);
73
84
 
74
85
  if (ctx->conn.socket == NULL) {
75
- rb_raise(rb_eIOError, "connection closed");
86
+ rb_raise(Trilogy_ConnectionClosedError, "Attempted to use closed connection");
76
87
  }
77
88
 
78
89
  return ctx;
79
90
  }
80
91
 
92
+ NORETURN(static void trilogy_syserr_fail_str(int, VALUE));
93
+ static void trilogy_syserr_fail_str(int e, VALUE msg)
94
+ {
95
+ if (e == ECONNREFUSED) {
96
+ rb_raise(Trilogy_ConnectionRefusedError, "%" PRIsVALUE, msg);
97
+ } else if (e == ECONNRESET) {
98
+ rb_raise(Trilogy_ConnectionResetError, "%" PRIsVALUE, msg);
99
+ } else {
100
+ VALUE exc = rb_funcall(Trilogy_SyscallError, id_from_errno, 2, INT2NUM(e), msg);
101
+ rb_exc_raise(exc);
102
+ }
103
+ }
104
+
81
105
  NORETURN(static void handle_trilogy_error(struct trilogy_ctx *, int, const char *, ...));
82
106
  static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *msg, ...)
83
107
  {
@@ -88,16 +112,11 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
88
112
 
89
113
  switch (rc) {
90
114
  case TRILOGY_SYSERR:
91
- rb_syserr_fail_str(errno, rbmsg);
115
+ trilogy_syserr_fail_str(errno, rbmsg);
92
116
 
93
117
  case TRILOGY_ERR: {
94
118
  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
-
119
+ VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
101
120
  rb_exc_raise(exc);
102
121
  }
103
122
 
@@ -105,18 +124,23 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
105
124
  unsigned long ossl_error = ERR_get_error();
106
125
  ERR_clear_error();
107
126
  if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
108
- rb_syserr_fail_str(ERR_GET_REASON(ossl_error), rbmsg);
127
+ int err_reason = ERR_GET_REASON(ossl_error);
128
+ trilogy_syserr_fail_str(err_reason, rbmsg);
109
129
  }
110
130
  // We can't recover from OpenSSL level errors if there's
111
131
  // an active connection.
112
132
  if (ctx->conn.socket != NULL) {
113
133
  trilogy_sock_shutdown(ctx->conn.socket);
114
134
  }
115
- rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
135
+ rb_raise(Trilogy_SSLError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
136
+ }
137
+
138
+ case TRILOGY_DNS_ERR: {
139
+ rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_DNS_ERROR", rbmsg);
116
140
  }
117
141
 
118
142
  default:
119
- rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
143
+ rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
120
144
  }
121
145
  }
122
146
 
@@ -129,7 +153,8 @@ static VALUE allocate_trilogy(VALUE klass)
129
153
  ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
130
154
 
131
155
  if (trilogy_init(&ctx->conn) < 0) {
132
- rb_syserr_fail(errno, "trilogy_init");
156
+ VALUE rbmsg = rb_str_new("trilogy_init", 13);
157
+ trilogy_syserr_fail_str(errno, rbmsg);
133
158
  }
134
159
 
135
160
  return obj;
@@ -145,7 +170,7 @@ static int flush_writes(struct trilogy_ctx *ctx)
145
170
  }
146
171
 
147
172
  if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
148
- rb_syserr_fail(ETIMEDOUT, "trilogy_flush_writes");
173
+ rb_raise(Trilogy_TimeoutError, "trilogy_flush_writes");
149
174
  }
150
175
  }
151
176
  }
@@ -279,7 +304,7 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
279
304
  }
280
305
 
281
306
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
282
- rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
307
+ rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
283
308
  }
284
309
  }
285
310
  }
@@ -305,7 +330,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
305
330
  }
306
331
  } else {
307
332
  if (ssl_mode != TRILOGY_SSL_PREFERRED_NOVERIFY) {
308
- rb_raise(rb_cTrilogyError, "SSL required, not supported by server");
333
+ rb_raise(Trilogy_SSLError, "SSL required, not supported by server");
309
334
  }
310
335
  }
311
336
  }
@@ -332,7 +357,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
332
357
  }
333
358
 
334
359
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
335
- rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
360
+ rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
336
361
  }
337
362
  }
338
363
 
@@ -341,14 +366,18 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
341
366
  }
342
367
  }
343
368
 
344
- static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
369
+ static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VALUE opts)
345
370
  {
346
371
  struct trilogy_ctx *ctx = get_ctx(self);
347
372
  trilogy_sockopt_t connopt = {0};
348
373
  trilogy_handshake_t handshake;
349
374
  VALUE val;
350
375
 
376
+ RB_OBJ_WRITE(self, &ctx->encoding, encoding);
377
+ connopt.encoding = NUM2INT(charset);
378
+
351
379
  Check_Type(opts, T_HASH);
380
+ rb_ivar_set(self, id_connection_options, opts);
352
381
 
353
382
  if ((val = rb_hash_lookup(opts, ID2SYM(id_ssl_mode))) != Qnil) {
354
383
  Check_Type(val, T_FIXNUM);
@@ -426,6 +455,14 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
426
455
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
427
456
  }
428
457
 
458
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_result)))) {
459
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_RESULTS;
460
+ }
461
+
462
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
463
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS | TRILOGY_CAPABILITIES_MULTI_RESULTS;
464
+ }
465
+
429
466
  if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
430
467
  Check_Type(val, T_STRING);
431
468
  connopt.ssl_ca = StringValueCStr(val);
@@ -478,7 +515,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
478
515
 
479
516
  int rc = try_connect(ctx, &handshake, &connopt);
480
517
  if (rc == TRILOGY_RB_TIMEOUT) {
481
- rb_syserr_fail(ETIMEDOUT, "trilogy_connect_recv");
518
+ rb_raise(Trilogy_TimeoutError, "trilogy_connect_recv");
482
519
  }
483
520
  if (rc != TRILOGY_OK) {
484
521
  if (connopt.path) {
@@ -525,29 +562,59 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
525
562
  }
526
563
 
527
564
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
528
- rb_syserr_fail(ETIMEDOUT, "trilogy_change_db_recv");
565
+ rb_raise(Trilogy_TimeoutError, "trilogy_change_db_recv");
566
+ }
567
+ }
568
+
569
+ return Qtrue;
570
+ }
571
+
572
+ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
573
+ {
574
+ struct trilogy_ctx *ctx = get_open_ctx(self);
575
+
576
+ int rc = trilogy_set_option_send(&ctx->conn, NUM2INT(option));
577
+
578
+ if (rc == TRILOGY_AGAIN) {
579
+ rc = flush_writes(ctx);
580
+ }
581
+
582
+ if (rc != TRILOGY_OK) {
583
+ handle_trilogy_error(ctx, rc, "trilogy_set_option_send");
584
+ }
585
+
586
+ while (1) {
587
+ rc = trilogy_set_option_recv(&ctx->conn);
588
+
589
+ if (rc == TRILOGY_OK) {
590
+ break;
591
+ }
592
+
593
+ if (rc != TRILOGY_AGAIN) {
594
+ handle_trilogy_error(ctx, rc, "trilogy_set_option_recv");
595
+ }
596
+
597
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
598
+ rb_raise(Trilogy_TimeoutError, "trilogy_set_option_recv");
529
599
  }
530
600
  }
531
601
 
532
602
  return Qtrue;
533
603
  }
534
604
 
605
+
535
606
  static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_options *cast_options)
536
607
  {
537
608
  cast_options->cast = (query_flags & TRILOGY_FLAGS_CAST) != 0;
538
609
  cast_options->cast_booleans = (query_flags & TRILOGY_FLAGS_CAST_BOOLEANS) != 0;
610
+ cast_options->cast_decimals_to_bigdecimals = (query_flags & TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS) != 0;
539
611
  cast_options->database_local_time = (query_flags & TRILOGY_FLAGS_LOCAL_TIMEZONE) != 0;
540
612
  cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
541
613
  }
542
614
 
543
- struct read_query_state {
615
+ struct read_query_response_state {
544
616
  struct rb_trilogy_cast_options *cast_options;
545
617
  struct trilogy_ctx *ctx;
546
- VALUE query;
547
-
548
- // to free by caller:
549
- struct column_info *column_info;
550
- trilogy_value_t *row_values;
551
618
 
552
619
  // Error state for tracking
553
620
  const char *msg;
@@ -570,34 +637,25 @@ static void get_timespec_monotonic(struct timespec *ts)
570
637
  #endif
571
638
  }
572
639
 
573
- static VALUE read_query_error(struct read_query_state *args, int rc, const char *msg)
640
+ static VALUE read_query_error(struct read_query_response_state *args, int rc, const char *msg)
574
641
  {
575
642
  args->rc = rc;
576
643
  args->msg = msg;
577
644
  return Qundef;
578
645
  }
579
646
 
580
- static VALUE execute_read_query(VALUE vargs)
647
+ static VALUE read_query_response(VALUE vargs)
581
648
  {
582
- struct read_query_state *args = (void *)vargs;
649
+ struct read_query_response_state *args = (void *)vargs;
583
650
  struct trilogy_ctx *ctx = args->ctx;
584
- VALUE query = args->query;
585
651
 
586
652
  struct timespec start;
587
653
  get_timespec_monotonic(&start);
588
654
 
589
- int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
590
-
591
- if (rc == TRILOGY_AGAIN) {
592
- rc = flush_writes(ctx);
593
- }
594
-
595
- if (rc < 0) {
596
- return read_query_error(args, rc, "trilogy_query_send");
597
- }
598
-
599
655
  uint64_t column_count = 0;
600
656
 
657
+ int rc;
658
+
601
659
  while (1) {
602
660
  rc = trilogy_query_recv(&ctx->conn, &column_count);
603
661
 
@@ -610,7 +668,7 @@ static VALUE execute_read_query(VALUE vargs)
610
668
  }
611
669
 
612
670
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
613
- rb_syserr_fail(ETIMEDOUT, "trilogy_query_recv");
671
+ rb_raise(Trilogy_TimeoutError, "trilogy_query_recv");
614
672
  }
615
673
  }
616
674
 
@@ -636,10 +694,13 @@ static VALUE execute_read_query(VALUE vargs)
636
694
  rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
637
695
 
638
696
  return result;
697
+ } else {
698
+ rb_ivar_set(result, id_ivar_last_insert_id, Qnil);
699
+ rb_ivar_set(result, id_ivar_affected_rows, Qnil);
639
700
  }
640
701
 
641
- struct column_info *column_info = ALLOC_N(struct column_info, column_count);
642
- args->column_info = column_info;
702
+ VALUE rb_column_info;
703
+ struct column_info *column_info = ALLOCV_N(struct column_info, rb_column_info, column_count);
643
704
 
644
705
  for (uint64_t i = 0; i < column_count; i++) {
645
706
  trilogy_column_t column;
@@ -656,7 +717,7 @@ static VALUE execute_read_query(VALUE vargs)
656
717
  }
657
718
 
658
719
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
659
- rb_syserr_fail(ETIMEDOUT, "trilogy_read_column");
720
+ rb_raise(Trilogy_TimeoutError, "trilogy_read_column");
660
721
  }
661
722
  }
662
723
 
@@ -673,17 +734,18 @@ static VALUE execute_read_query(VALUE vargs)
673
734
  column_info[i].flags = column.flags;
674
735
  column_info[i].len = column.len;
675
736
  column_info[i].charset = column.charset;
737
+ column_info[i].decimals = column.decimals;
676
738
  }
677
739
 
678
- trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
679
- args->row_values = row_values;
740
+ VALUE rb_row_values;
741
+ trilogy_value_t *row_values = ALLOCV_N(trilogy_value_t, rb_row_values, column_count);
680
742
 
681
743
  while (1) {
682
744
  int rc = trilogy_read_row(&ctx->conn, row_values);
683
745
 
684
746
  if (rc == TRILOGY_AGAIN) {
685
747
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
686
- rb_syserr_fail(ETIMEDOUT, "trilogy_read_row");
748
+ rb_raise(Trilogy_TimeoutError, "trilogy_read_row");
687
749
  }
688
750
  continue;
689
751
  }
@@ -709,37 +771,23 @@ static VALUE execute_read_query(VALUE vargs)
709
771
  }
710
772
  }
711
773
 
712
- if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
713
- rb_raise(rb_cTrilogyError, "MORE_RESULTS_EXIST");
714
- }
715
-
716
774
  return result;
717
775
  }
718
776
 
719
- static VALUE rb_trilogy_query(VALUE self, VALUE query)
777
+ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
720
778
  {
721
- struct trilogy_ctx *ctx = get_open_ctx(self);
722
-
723
- StringValue(query);
724
-
725
779
  struct rb_trilogy_cast_options cast_options;
726
780
  load_query_options(ctx->query_flags, &cast_options);
727
781
 
728
- struct read_query_state args = {
782
+ struct read_query_response_state args = {
729
783
  .cast_options = &cast_options,
730
- .column_info = NULL,
731
784
  .ctx = ctx,
732
- .query = query,
733
- .row_values = NULL,
734
785
  .rc = TRILOGY_OK,
735
786
  .msg = NULL,
736
787
  };
737
788
 
738
789
  int state = 0;
739
- VALUE result = rb_protect(execute_read_query, (VALUE)&args, &state);
740
-
741
- xfree(args.column_info);
742
- xfree(args.row_values);
790
+ VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
743
791
 
744
792
  // If we have seen an unexpected exception, jump to it so it gets raised.
745
793
  if (state) {
@@ -756,6 +804,48 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
756
804
  return result;
757
805
  }
758
806
 
807
+ static VALUE rb_trilogy_next_result(VALUE self)
808
+ {
809
+ struct trilogy_ctx *ctx = get_open_ctx(self);
810
+
811
+ if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) {
812
+ return Qnil;
813
+ }
814
+
815
+ return execute_read_query_response(ctx);
816
+ }
817
+
818
+ static VALUE rb_trilogy_more_results_exist(VALUE self)
819
+ {
820
+ struct trilogy_ctx *ctx = get_open_ctx(self);
821
+
822
+ if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
823
+ return Qtrue;
824
+ } else {
825
+ return Qfalse;
826
+ }
827
+ }
828
+
829
+ static VALUE rb_trilogy_query(VALUE self, VALUE query)
830
+ {
831
+ struct trilogy_ctx *ctx = get_open_ctx(self);
832
+
833
+ StringValue(query);
834
+ query = rb_str_export_to_enc(query, rb_to_encoding(ctx->encoding));
835
+
836
+ int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
837
+
838
+ if (rc == TRILOGY_AGAIN) {
839
+ rc = flush_writes(ctx);
840
+ }
841
+
842
+ if (rc < 0) {
843
+ handle_trilogy_error(ctx, rc, "trilogy_query_send");
844
+ }
845
+
846
+ return execute_read_query_response(ctx);
847
+ }
848
+
759
849
  static VALUE rb_trilogy_ping(VALUE self)
760
850
  {
761
851
  struct trilogy_ctx *ctx = get_open_ctx(self);
@@ -782,7 +872,7 @@ static VALUE rb_trilogy_ping(VALUE self)
782
872
  }
783
873
 
784
874
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
785
- rb_syserr_fail(ETIMEDOUT, "trilogy_ping_recv");
875
+ rb_raise(Trilogy_TimeoutError, "trilogy_ping_recv");
786
876
  }
787
877
  }
788
878
 
@@ -857,6 +947,25 @@ static VALUE rb_trilogy_closed(VALUE self)
857
947
  }
858
948
  }
859
949
 
950
+ static VALUE rb_trilogy_discard(VALUE self)
951
+ {
952
+ struct trilogy_ctx *ctx = get_ctx(self);
953
+
954
+ if (ctx->conn.socket == NULL) {
955
+ return Qtrue;
956
+ }
957
+
958
+ int rc = trilogy_discard(&ctx->conn);
959
+ switch (rc) {
960
+ case TRILOGY_OK:
961
+ return Qtrue;
962
+ case TRILOGY_SYSERR:
963
+ trilogy_syserr_fail_str(errno, rb_str_new_cstr("Failed to discard connection"));
964
+ UNREACHABLE_RETURN(Qfalse);
965
+ }
966
+ return Qfalse;
967
+ }
968
+
860
969
  static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
861
970
 
862
971
  static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
@@ -916,19 +1025,19 @@ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx
916
1025
 
917
1026
  static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
918
1027
 
919
- void Init_cext()
1028
+ RUBY_FUNC_EXPORTED void Init_cext()
920
1029
  {
921
- VALUE Trilogy = rb_define_class("Trilogy", rb_cObject);
922
-
1030
+ VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
923
1031
  rb_define_alloc_func(Trilogy, allocate_trilogy);
924
1032
 
925
- rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
1033
+ rb_define_private_method(Trilogy, "_initialize", rb_trilogy_initialize, 3);
926
1034
  rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
927
1035
  rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
928
1036
  rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
929
1037
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
930
1038
  rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
931
1039
  rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
1040
+ rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
932
1041
  rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
933
1042
  rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
934
1043
  rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
@@ -941,6 +1050,9 @@ void Init_cext()
941
1050
  rb_define_method(Trilogy, "write_timeout=", rb_trilogy_write_timeout_set, 1);
942
1051
  rb_define_method(Trilogy, "server_status", rb_trilogy_server_status, 0);
943
1052
  rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
1053
+ rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
1054
+ rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
1055
+ rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
944
1056
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
945
1057
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
946
1058
  rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
@@ -955,27 +1067,43 @@ void Init_cext()
955
1067
  rb_define_const(Trilogy, "QUERY_FLAGS_NONE", INT2NUM(0));
956
1068
  rb_define_const(Trilogy, "QUERY_FLAGS_CAST", INT2NUM(TRILOGY_FLAGS_CAST));
957
1069
  rb_define_const(Trilogy, "QUERY_FLAGS_CAST_BOOLEANS", INT2NUM(TRILOGY_FLAGS_CAST_BOOLEANS));
1070
+ rb_define_const(Trilogy, "QUERY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS", INT2NUM(TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS));
958
1071
  rb_define_const(Trilogy, "QUERY_FLAGS_LOCAL_TIMEZONE", INT2NUM(TRILOGY_FLAGS_LOCAL_TIMEZONE));
959
1072
  rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
960
1073
  rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
961
1074
 
962
- rb_cTrilogyError = rb_define_class_under(Trilogy, "Error", rb_eStandardError);
963
- rb_global_variable(&rb_cTrilogyError);
1075
+ Trilogy_ProtocolError = rb_const_get(Trilogy, rb_intern("ProtocolError"));
1076
+ rb_global_variable(&Trilogy_ProtocolError);
1077
+
1078
+ Trilogy_SSLError = rb_const_get(Trilogy, rb_intern("SSLError"));
1079
+ rb_global_variable(&Trilogy_SSLError);
1080
+
1081
+ Trilogy_QueryError = rb_const_get(Trilogy, rb_intern("QueryError"));
1082
+ rb_global_variable(&Trilogy_QueryError);
1083
+
1084
+ Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1085
+ rb_global_variable(&Trilogy_TimeoutError);
1086
+
1087
+ Trilogy_ConnectionRefusedError = rb_const_get(Trilogy, rb_intern("ConnectionRefusedError"));
1088
+ rb_global_variable(&Trilogy_ConnectionRefusedError);
1089
+
1090
+ Trilogy_ConnectionResetError = rb_const_get(Trilogy, rb_intern("ConnectionResetError"));
1091
+ rb_global_variable(&Trilogy_ConnectionResetError);
964
1092
 
965
- Trilogy_DatabaseError = rb_define_class_under(Trilogy, "DatabaseError", rb_cTrilogyError);
966
- rb_global_variable(&Trilogy_DatabaseError);
1093
+ Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1094
+ rb_global_variable(&Trilogy_BaseConnectionError);
967
1095
 
968
- rb_define_attr(Trilogy_DatabaseError, "error_code", 1, 0);
969
- rb_define_attr(Trilogy_DatabaseError, "error_message", 1, 0);
1096
+ Trilogy_ConnectionClosedError = rb_const_get(Trilogy, rb_intern("ConnectionClosed"));
1097
+ rb_global_variable(&Trilogy_ConnectionClosedError);
970
1098
 
971
- Trilogy_Result = rb_define_class_under(Trilogy, "Result", rb_cObject);
1099
+ Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result"));
972
1100
  rb_global_variable(&Trilogy_Result);
973
1101
 
974
- rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
975
- rb_define_attr(Trilogy_Result, "fields", 1, 0);
976
- rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
977
- rb_define_attr(Trilogy_Result, "rows", 1, 0);
978
- rb_define_attr(Trilogy_Result, "query_time", 1, 0);
1102
+ Trilogy_SyscallError = rb_const_get(Trilogy, rb_intern("SyscallError"));
1103
+ rb_global_variable(&Trilogy_SyscallError);
1104
+
1105
+ Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
1106
+ rb_global_variable(&Trilogy_CastError);
979
1107
 
980
1108
  id_socket = rb_intern("socket");
981
1109
  id_host = rb_intern("host");
@@ -1002,12 +1130,16 @@ void Init_cext()
1002
1130
  id_tls_ciphersuites = rb_intern("tls_ciphersuites");
1003
1131
  id_tls_min_version = rb_intern("tls_min_version");
1004
1132
  id_tls_max_version = rb_intern("tls_max_version");
1005
-
1133
+ id_multi_statement = rb_intern("multi_statement");
1134
+ id_multi_result = rb_intern("multi_result");
1135
+ id_from_code = rb_intern("from_code");
1136
+ id_from_errno = rb_intern("from_errno");
1006
1137
  id_ivar_affected_rows = rb_intern("@affected_rows");
1007
1138
  id_ivar_fields = rb_intern("@fields");
1008
1139
  id_ivar_last_insert_id = rb_intern("@last_insert_id");
1009
1140
  id_ivar_rows = rb_intern("@rows");
1010
1141
  id_ivar_query_time = rb_intern("@query_time");
1142
+ id_connection_options = rb_intern("@connection_options");
1011
1143
 
1012
1144
  rb_trilogy_cast_init();
1013
1145
 
@@ -1015,4 +1147,14 @@ void Init_cext()
1015
1147
  #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1016
1148
  TRILOGY_SERVER_STATUS(XX)
1017
1149
  #undef XX
1150
+
1151
+ // set_server_option options
1152
+ #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1153
+ TRILOGY_SET_SERVER_OPTION(XX)
1154
+ #undef XX
1155
+
1156
+ // charsets
1157
+ #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1158
+ TRILOGY_CHARSETS(XX)
1159
+ #undef XX
1018
1160
  }
@@ -6,7 +6,7 @@ File.binwrite("trilogy.c",
6
6
  Dir["#{__dir__}/src/**/*.c"].map { |src| File.binread(src) }.join)
7
7
 
8
8
  $objs = %w[trilogy.o cast.o cext.o]
9
- $CFLAGS << " -I #{__dir__}/inc -std=gnu99"
9
+ $CFLAGS << " -I #{__dir__}/inc -std=gnu99 -fvisibility=hidden"
10
10
 
11
11
  dir_config("openssl")
12
12
 
@@ -50,7 +50,8 @@ int trilogy_connect_sock(trilogy_conn_t *conn, trilogy_sock_t *sock);
50
50
  /* trilogy_change_db - Change the default database for a connection.
51
51
  *
52
52
  * conn - A connected trilogy_conn_t pointer. Using a disconnected
53
- * trilogy_conn_t is undefined. name - Name of the database to set as default.
53
+ * trilogy_conn_t is undefined.
54
+ * name - Name of the database to set as default.
54
55
  * name_len - Length of the database name string in bytes.
55
56
  *
56
57
  * Return values
@@ -63,6 +64,22 @@ int trilogy_connect_sock(trilogy_conn_t *conn, trilogy_sock_t *sock);
63
64
  */
64
65
  int trilogy_change_db(trilogy_conn_t *conn, const char *name, size_t name_len);
65
66
 
67
+ /* trilogy_set_option - Set server options for the connection.
68
+ *
69
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected
70
+ * trilogy_conn_t is undefined.
71
+ * option - The server option to set. See: TRILOGY_SET_SERVER_OPTION_TYPE_t;
72
+ *
73
+ * Return values
74
+ * TRILOGY_OK - The change db command completed successfully.
75
+ * TRILOGY_ERR - The server returned an error.
76
+ * TRILOGY_SYSERR - A system error occurred, check errno.
77
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
78
+ * TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
79
+ * packet.
80
+ */
81
+ int trilogy_set_option(trilogy_conn_t *conn, const uint16_t option);
82
+
66
83
  /* trilogy_query - Send and execute a query.
67
84
  *
68
85
  * conn - A connected trilogy_conn_t pointer. Using a disconnected