trilogy 2.2.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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