trilogy 2.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 693efafac411af8e0859eee682d4432a501f17d90c3735faabad4cac4ab07537
4
- data.tar.gz: 646bc59fce2e3b26058bf867f448496e5d5caf1344ca0832d706bd3e2db99a2f
3
+ metadata.gz: e9c5df97a7642030f5c79167a0ff54ab25f500e38555b0303b9755f4102b0b17
4
+ data.tar.gz: a67ea6052ca5d2346a5b7cb72f976c6c6a445361365e7bc3f343a54cd43151dd
5
5
  SHA512:
6
- metadata.gz: 311d5640cdd3da6e3ba331c8222698ff18a10b06886d277712fb8da453924d862f4c9269163a5f748b73dcbc19087993ae2b3cbdc6fccbbd97a91c0c88f83bed
7
- data.tar.gz: 77442107a8527f806c2f602d95c000d81c2e7b7a09f6efc8a77a5dcbb091eb4c6890948a1352a72a8fa2f7ef6fe1d74cb0eda84b0d385049514a8dd0236d597d
6
+ metadata.gz: 8d356765239b247f1902582abfb7a724eee0b2b18a3e8e0eabe71db196555bf5749192f259dba8c65f8a0945106df4413f328abeef8241cb3517495725dedabc
7
+ data.tar.gz: c77e07b1a5e423d1ed7065a613feee4cd038be3606f519f7dc2fbbe87b389cf9d1fd4cc41993023060b504c40f12f85f8e75c688e1b4c418535c8891c192f26f
@@ -145,7 +145,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
145
145
  // TODO - optimize so we don't have to allocate a ruby string for
146
146
  // decimal columns
147
147
  VALUE str = rb_str_new(value->data, value->data_len);
148
- if (column->decimals == 0) {
148
+ if (column->decimals == 0 && !options->cast_decimals_to_bigdecimals) {
149
149
  return rb_funcall(rb_mKernel, id_Integer, 1, str);
150
150
  } else {
151
151
  return rb_funcall(rb_mKernel, id_BigDecimal, 1, str);
@@ -240,7 +240,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
240
240
  // pad out msec_char with zeroes at the end as it could be at any
241
241
  // level of precision
242
242
  for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
243
- msec_char[i] = 0;
243
+ msec_char[i] = '0';
244
244
  }
245
245
 
246
246
  return rb_funcall(rb_cTime, options->database_local_time ? id_local : id_utc, 7, INT2NUM(2000), INT2NUM(1),
@@ -8,6 +8,9 @@
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"
@@ -16,21 +19,29 @@
16
19
 
17
20
  VALUE Trilogy_CastError;
18
21
  static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
19
- Trilogy_ConnectionClosedError, Trilogy_TimeoutError, Trilogy_Result;
22
+ Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError,
23
+ Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result;
20
24
 
21
25
  static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
22
26
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
23
27
  id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
24
28
  id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
25
- id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_from_code,
26
- id_connection_options;
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
  },
@@ -78,6 +89,19 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
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,12 +112,7 @@ 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
- if (errno == ECONNREFUSED || errno == ECONNRESET) {
92
- rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE, rbmsg);
93
- } else {
94
- // TODO: All syserr should be wrapped.
95
- rb_syserr_fail_str(errno, rbmsg);
96
- }
115
+ trilogy_syserr_fail_str(errno, rbmsg);
97
116
 
98
117
  case TRILOGY_ERR: {
99
118
  VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
@@ -106,13 +125,7 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
106
125
  ERR_clear_error();
107
126
  if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
108
127
  int err_reason = ERR_GET_REASON(ossl_error);
109
-
110
- if (err_reason == ECONNREFUSED || err_reason == ECONNRESET) {
111
- rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE, rbmsg);
112
- } else {
113
- // TODO: All syserr should be wrapped.
114
- rb_syserr_fail_str(err_reason, rbmsg);
115
- }
128
+ trilogy_syserr_fail_str(err_reason, rbmsg);
116
129
  }
117
130
  // We can't recover from OpenSSL level errors if there's
118
131
  // an active connection.
@@ -140,13 +153,8 @@ static VALUE allocate_trilogy(VALUE klass)
140
153
  ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
141
154
 
142
155
  if (trilogy_init(&ctx->conn) < 0) {
143
- if (errno == ECONNREFUSED || errno == ECONNRESET) {
144
- rb_raise(Trilogy_BaseConnectionError, "trilogy_init");
145
- } else {
146
- // TODO: All syserr should be wrapped.
147
- VALUE rbmsg = rb_str_new("trilogy_init", 13);
148
- rb_syserr_fail_str(errno, rbmsg);
149
- }
156
+ VALUE rbmsg = rb_str_new("trilogy_init", 13);
157
+ trilogy_syserr_fail_str(errno, rbmsg);
150
158
  }
151
159
 
152
160
  return obj;
@@ -358,13 +366,16 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
358
366
  }
359
367
  }
360
368
 
361
- static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
369
+ static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VALUE opts)
362
370
  {
363
371
  struct trilogy_ctx *ctx = get_ctx(self);
364
372
  trilogy_sockopt_t connopt = {0};
365
373
  trilogy_handshake_t handshake;
366
374
  VALUE val;
367
375
 
376
+ RB_OBJ_WRITE(self, &ctx->encoding, encoding);
377
+ connopt.encoding = NUM2INT(charset);
378
+
368
379
  Check_Type(opts, T_HASH);
369
380
  rb_ivar_set(self, id_connection_options, opts);
370
381
 
@@ -444,8 +455,12 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
444
455
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
445
456
  }
446
457
 
458
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_result)))) {
459
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_RESULTS;
460
+ }
461
+
447
462
  if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
448
- connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
463
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS | TRILOGY_CAPABILITIES_MULTI_RESULTS;
449
464
  }
450
465
 
451
466
  if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
@@ -554,10 +569,45 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
554
569
  return Qtrue;
555
570
  }
556
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");
599
+ }
600
+ }
601
+
602
+ return Qtrue;
603
+ }
604
+
605
+
557
606
  static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_options *cast_options)
558
607
  {
559
608
  cast_options->cast = (query_flags & TRILOGY_FLAGS_CAST) != 0;
560
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;
561
611
  cast_options->database_local_time = (query_flags & TRILOGY_FLAGS_LOCAL_TIMEZONE) != 0;
562
612
  cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
563
613
  }
@@ -566,10 +616,6 @@ struct read_query_response_state {
566
616
  struct rb_trilogy_cast_options *cast_options;
567
617
  struct trilogy_ctx *ctx;
568
618
 
569
- // to free by caller:
570
- struct column_info *column_info;
571
- trilogy_value_t *row_values;
572
-
573
619
  // Error state for tracking
574
620
  const char *msg;
575
621
  int rc;
@@ -648,10 +694,13 @@ static VALUE read_query_response(VALUE vargs)
648
694
  rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
649
695
 
650
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);
651
700
  }
652
701
 
653
- struct column_info *column_info = ALLOC_N(struct column_info, column_count);
654
- 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);
655
704
 
656
705
  for (uint64_t i = 0; i < column_count; i++) {
657
706
  trilogy_column_t column;
@@ -688,8 +737,8 @@ static VALUE read_query_response(VALUE vargs)
688
737
  column_info[i].decimals = column.decimals;
689
738
  }
690
739
 
691
- trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
692
- 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);
693
742
 
694
743
  while (1) {
695
744
  int rc = trilogy_read_row(&ctx->conn, row_values);
@@ -732,9 +781,7 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
732
781
 
733
782
  struct read_query_response_state args = {
734
783
  .cast_options = &cast_options,
735
- .column_info = NULL,
736
784
  .ctx = ctx,
737
- .row_values = NULL,
738
785
  .rc = TRILOGY_OK,
739
786
  .msg = NULL,
740
787
  };
@@ -742,9 +789,6 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
742
789
  int state = 0;
743
790
  VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
744
791
 
745
- xfree(args.column_info);
746
- xfree(args.row_values);
747
-
748
792
  // If we have seen an unexpected exception, jump to it so it gets raised.
749
793
  if (state) {
750
794
  trilogy_sock_shutdown(ctx->conn.socket);
@@ -787,6 +831,7 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
787
831
  struct trilogy_ctx *ctx = get_open_ctx(self);
788
832
 
789
833
  StringValue(query);
834
+ query = rb_str_export_to_enc(query, rb_to_encoding(ctx->encoding));
790
835
 
791
836
  int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
792
837
 
@@ -902,6 +947,25 @@ static VALUE rb_trilogy_closed(VALUE self)
902
947
  }
903
948
  }
904
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
+
905
969
  static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
906
970
 
907
971
  static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
@@ -961,18 +1025,19 @@ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx
961
1025
 
962
1026
  static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
963
1027
 
964
- void Init_cext()
1028
+ RUBY_FUNC_EXPORTED void Init_cext()
965
1029
  {
966
1030
  VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
967
1031
  rb_define_alloc_func(Trilogy, allocate_trilogy);
968
1032
 
969
- rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
1033
+ rb_define_private_method(Trilogy, "_initialize", rb_trilogy_initialize, 3);
970
1034
  rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
971
1035
  rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
972
1036
  rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
973
1037
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
974
1038
  rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
975
1039
  rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
1040
+ rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
976
1041
  rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
977
1042
  rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
978
1043
  rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
@@ -987,6 +1052,7 @@ void Init_cext()
987
1052
  rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
988
1053
  rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
989
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);
990
1056
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
991
1057
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
992
1058
  rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
@@ -1001,6 +1067,7 @@ void Init_cext()
1001
1067
  rb_define_const(Trilogy, "QUERY_FLAGS_NONE", INT2NUM(0));
1002
1068
  rb_define_const(Trilogy, "QUERY_FLAGS_CAST", INT2NUM(TRILOGY_FLAGS_CAST));
1003
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));
1004
1071
  rb_define_const(Trilogy, "QUERY_FLAGS_LOCAL_TIMEZONE", INT2NUM(TRILOGY_FLAGS_LOCAL_TIMEZONE));
1005
1072
  rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
1006
1073
  rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
@@ -1017,6 +1084,12 @@ void Init_cext()
1017
1084
  Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1018
1085
  rb_global_variable(&Trilogy_TimeoutError);
1019
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);
1092
+
1020
1093
  Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1021
1094
  rb_global_variable(&Trilogy_BaseConnectionError);
1022
1095
 
@@ -1026,12 +1099,12 @@ void Init_cext()
1026
1099
  Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result"));
1027
1100
  rb_global_variable(&Trilogy_Result);
1028
1101
 
1102
+ Trilogy_SyscallError = rb_const_get(Trilogy, rb_intern("SyscallError"));
1103
+ rb_global_variable(&Trilogy_SyscallError);
1104
+
1029
1105
  Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
1030
1106
  rb_global_variable(&Trilogy_CastError);
1031
1107
 
1032
- rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
1033
- rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
1034
-
1035
1108
  id_socket = rb_intern("socket");
1036
1109
  id_host = rb_intern("host");
1037
1110
  id_port = rb_intern("port");
@@ -1058,7 +1131,9 @@ void Init_cext()
1058
1131
  id_tls_min_version = rb_intern("tls_min_version");
1059
1132
  id_tls_max_version = rb_intern("tls_max_version");
1060
1133
  id_multi_statement = rb_intern("multi_statement");
1134
+ id_multi_result = rb_intern("multi_result");
1061
1135
  id_from_code = rb_intern("from_code");
1136
+ id_from_errno = rb_intern("from_errno");
1062
1137
  id_ivar_affected_rows = rb_intern("@affected_rows");
1063
1138
  id_ivar_fields = rb_intern("@fields");
1064
1139
  id_ivar_last_insert_id = rb_intern("@last_insert_id");
@@ -1072,4 +1147,14 @@ void Init_cext()
1072
1147
  #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1073
1148
  TRILOGY_SERVER_STATUS(XX)
1074
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
1075
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
@@ -299,6 +299,50 @@ int trilogy_change_db_send(trilogy_conn_t *conn, const char *name, size_t name_l
299
299
  */
300
300
  int trilogy_change_db_recv(trilogy_conn_t *conn);
301
301
 
302
+ /* trilogy_set_option_send - Send a set option command to the server. This
303
+ * will change server capabilities based on the option selected.
304
+ *
305
+ * This should only be called while the connection is ready for commands.
306
+ *
307
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected
308
+ * trilogy_conn_t is undefined.
309
+ * option - The server option to send.
310
+ *
311
+ * Return values:
312
+ * TRILOGY_OK - The change database command was successfully sent to the
313
+ * server.
314
+ * TRILOGY_AGAIN - The socket wasn't ready for writing. The caller should wait
315
+ * for writeability using `conn->sock`. Then call
316
+ * trilogy_flush_writes.
317
+ * TRILOGY_SYSERR - A system error occurred, check errno.
318
+ */
319
+ int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option);
320
+
321
+ /* trilogy_set_option_recv - Read the set option command response from the
322
+ * server.
323
+ *
324
+ * This should be called after all data written by trilogy_set_option_send is
325
+ * flushed to the network. Calling this at any other time during the connection
326
+ * lifecycle is undefined.
327
+ *
328
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
329
+ * undefined.
330
+ *
331
+ * Return values:
332
+ * TRILOGY_OK - The set option command was successfully
333
+ * sent to the server.
334
+ * TRILOGY_AGAIN - The socket wasn't ready for reading. The
335
+ * caller should wait for readability using
336
+ * `conn->sock`. Then call this function until
337
+ * it returns a different value.
338
+ * TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
339
+ * TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
340
+ * packet.
341
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
342
+ * TRILOGY_SYSERR - A system error occurred, check errno.
343
+ */
344
+ int trilogy_set_option_recv(trilogy_conn_t *conn);
345
+
302
346
  /* trilogy_query_send - Send a query command to the server.
303
347
  *
304
348
  * This should only be called while the connection is ready for commands.
@@ -543,4 +587,18 @@ int trilogy_close_recv(trilogy_conn_t *conn);
543
587
  */
544
588
  void trilogy_free(trilogy_conn_t *conn);
545
589
 
590
+ /* trilogy_free - Discard the connection and free any internal buffers.
591
+ *
592
+ * The server won't be notified that connection was closed. This is useful to
593
+ * silently close connections that were inherited after forking without disrupting
594
+ * the parent's process connections.
595
+ *
596
+ * conn - A pre-initialized trilogy_conn_t pointer.
597
+ *
598
+ * Return values:
599
+ * TRILOGY_OK - The connection was successfuly discarded and freed.
600
+ * TRILOGY_SYSERR - A system error occurred, check errno. The connection wasn't freed.
601
+ */
602
+ int trilogy_discard(trilogy_conn_t *conn);
603
+
546
604
  #endif
@@ -90,16 +90,14 @@
90
90
  * From client: the client is interactive. \
91
91
  */ \
92
92
  XX(TRILOGY_CAPABILITIES_INTERACTIVE, 0x00000400) \
93
- /* Not implemented. \
94
- * \
95
- * From server: the server supports ssl. \
93
+ /* From server: the server supports ssl. \
96
94
  * \
97
95
  * From client: tells the server it should switch to an ssl connection. \
98
96
  */ \
99
97
  XX(TRILOGY_CAPABILITIES_SSL, 0x00000800) \
100
- /* Not used. \
98
+ /* From server: the server supports transactions and is capable of reporting transaction status. \
101
99
  * \
102
- * This is assumed for the 4.1+ protocol. \
100
+ * From client: the client is aware of servers that support transactions. \
103
101
  */ \
104
102
  XX(TRILOGY_CAPABILITIES_TRANSACTIONS, 0x00002000) \
105
103
  /* Not used. \
@@ -112,9 +110,7 @@
112
110
  * scheme. This will always be set. \
113
111
  */ \
114
112
  XX(TRILOGY_CAPABILITIES_SECURE_CONNECTION, 0x00008000) \
115
- /* Not implemented. \
116
- * \
117
- * From server: the server can handle multiple statements per \
113
+ /* From server: the server can handle multiple statements per \
118
114
  * query/prepared statement. \
119
115
  * \
120
116
  * From client: tells the server it may send multiple statements per \
@@ -128,9 +124,7 @@
128
124
  * sets from a query. \
129
125
  */ \
130
126
  XX(TRILOGY_CAPABILITIES_MULTI_RESULTS, 0x00020000) \
131
- /* Not implemented. \
132
- * \
133
- * From server: the server is capable of sending multiple result sets from \
127
+ /* From server: the server is capable of sending multiple result sets from \
134
128
  * a prepared statement. \
135
129
  * \
136
130
  * From client: tells the server it's capable of handling multiple result \
@@ -191,7 +185,8 @@ typedef enum {
191
185
  /* A convenience bitmask with common client capabilities set. */
192
186
  TRILOGY_CAPABILITIES_CLIENT = (TRILOGY_CAPABILITIES_PROTOCOL_41 | TRILOGY_CAPABILITIES_SECURE_CONNECTION |
193
187
  TRILOGY_CAPABILITIES_DEPRECATE_EOF | TRILOGY_CAPABILITIES_SESSION_TRACK |
194
- TRILOGY_CAPABILITIES_PLUGIN_AUTH | TRILOGY_CAPABILITIES_TRANSACTIONS)
188
+ TRILOGY_CAPABILITIES_PLUGIN_AUTH | TRILOGY_CAPABILITIES_TRANSACTIONS |
189
+ TRILOGY_CAPABILITIES_MULTI_RESULTS)
195
190
  } TRILOGY_CAPABILITIES_t;
196
191
 
197
192
  #define TRILOGY_SERVER_STATUS(XX) \
@@ -397,22 +392,34 @@ typedef enum {
397
392
  #undef XX
398
393
  } TRILOGY_SESSION_TRACK_TYPE_t;
399
394
 
395
+ #define TRILOGY_SET_SERVER_OPTION(XX) \
396
+ XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_ON, 0x00) \
397
+ XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_OFF, 0x01) \
398
+
399
+ typedef enum {
400
+ #define XX(name, code) name = code,
401
+ TRILOGY_SET_SERVER_OPTION(XX)
402
+ #undef XX
403
+ } TRILOGY_SET_SERVER_OPTION_TYPE_t;
404
+
400
405
  /* trilogy_build_auth_packet - Build a handshake response (or authentication)
401
406
  * packet.
402
407
  *
403
408
  * This should be sent in response to the initial handshake packet the server
404
409
  * sends upon connection.
405
410
  *
406
- * builder - A pointer to a pre-initialized trilogy_builder_t.
407
- * user - The username to use for authentication. Must be a C-string.
408
- * pass - The password to use for authentication. Optional, and can be NULL.
409
- * pass_len - The length of password in bytes.
410
- * auth_plugin - Plugin authentication mechanism that the server requested.
411
- * scramble - The scramble value the server sent in the initial handshake.
412
- * flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
413
- * The TRILOGY_CAPABILITIES_PROTOCOL_41 and
414
- * TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
415
- * internally.
411
+ * builder - A pointer to a pre-initialized trilogy_builder_t.
412
+ * user - The username to use for authentication. Must be a C-string.
413
+ * pass - The password to use for authentication. Optional, and can be NULL.
414
+ * pass_len - The length of password in bytes.
415
+ * database - The initial database to connect to. Optional, and can be NULL.
416
+ * client_encoding - The charset to use for the connection.
417
+ * auth_plugin - Plugin authentication mechanism that the server requested.
418
+ * scramble - The scramble value the server sent in the initial handshake.
419
+ * flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
420
+ * The TRILOGY_CAPABILITIES_PROTOCOL_41 and
421
+ * TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
422
+ * internally.
416
423
  *
417
424
  * Return values:
418
425
  * TRILOGY_OK - The packet was successfully built and written to the
@@ -420,8 +427,8 @@ typedef enum {
420
427
  * TRILOGY_SYSERR - A system error occurred, check errno.
421
428
  */
422
429
  int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, const char *pass, size_t pass_len,
423
- const char *database, const char *auth_plugin, const char *scramble,
424
- TRILOGY_CAPABILITIES_t flags);
430
+ const char *database, TRILOGY_CHARSET_t client_encoding, const char *auth_plugin,
431
+ const char *scramble, TRILOGY_CAPABILITIES_t flags);
425
432
 
426
433
  /* trilogy_build_auth_switch_response_packet - Build a response for when
427
434
  * authentication switching it requested.
@@ -447,7 +454,7 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
447
454
  * command will change the default database for the connection.
448
455
  *
449
456
  * builder - A pointer to a pre-initialized trilogy_builder_t.
450
- * name - The name of the databaset to set as the default.
457
+ * name - The name of the database to set as the default.
451
458
  * name_len - The length of name in bytes.
452
459
  *
453
460
  * Return values:
@@ -457,6 +464,20 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
457
464
  */
458
465
  int trilogy_build_change_db_packet(trilogy_builder_t *builder, const char *name, size_t name_len);
459
466
 
467
+ /* trilogy_build_set_option_packet - Build a set option command packet. This
468
+ * command will enable/disable server capabilities for the connection. Options
469
+ * must be one of `enum_mysql_set_option`.
470
+ *
471
+ * builder - A pointer to a pre-initialized trilogy_builder_t.
472
+ * option - An integer corresponding to the operation to perform.
473
+ *
474
+ * Return values:
475
+ * TRILOGY_OK - The packet was successfully built and written to the
476
+ * builder's internal buffer.
477
+ * TRILOGY_SYSERR - A system error occurred, check errno.
478
+ */
479
+ int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option);
480
+
460
481
  /* trilogy_build_ping_packet - Build a ping command packet.
461
482
  *
462
483
  * builder - A pointer to a pre-initialized trilogy_builder_t.
@@ -497,19 +518,21 @@ int trilogy_build_quit_packet(trilogy_builder_t *builder);
497
518
  * sends upon connection, where an auth packet would normally be sent. A regular
498
519
  * auth packet is to be sent after the SSL handshake completes.
499
520
  *
500
- * builder - A pointer to a pre-initialized trilogy_builder_t.
501
- * flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
502
- * The TRILOGY_CAPABILITIES_PROTOCOL_41 and
503
- * TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
504
- * internally.
505
- * The TRILOGY_CAPABILITIES_SSL flag will also be set.
521
+ * builder - A pointer to a pre-initialized trilogy_builder_t.
522
+ * flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
523
+ * The TRILOGY_CAPABILITIES_PROTOCOL_41 and
524
+ * TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
525
+ * internally.
526
+ * The TRILOGY_CAPABILITIES_SSL flag will also be set.
527
+ * client_encoding - The charset to use for the connection.
506
528
  *
507
529
  * Return values:
508
530
  * TRILOGY_OK - The packet was successfully built and written to the
509
531
  * builder's internal buffer.
510
532
  * TRILOGY_SYSERR - A system error occurred, check errno.
511
533
  */
512
- int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags);
534
+ int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags,
535
+ TRILOGY_CHARSET_t client_encoding);
513
536
 
514
537
  #define TRILOGY_SERVER_VERSION_SIZE 32
515
538
 
@@ -41,6 +41,7 @@ typedef struct {
41
41
  char *username;
42
42
  char *password;
43
43
  size_t password_len;
44
+ uint8_t encoding;
44
45
 
45
46
  trilogy_ssl_mode_t ssl_mode;
46
47
  trilogy_tls_version_t tls_min_version;
@@ -107,5 +108,6 @@ static inline int trilogy_sock_fd(trilogy_sock_t *sock) { return sock->fd_cb(soc
107
108
  trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts);
108
109
  int trilogy_sock_resolve(trilogy_sock_t *raw);
109
110
  int trilogy_sock_upgrade_ssl(trilogy_sock_t *raw);
111
+ int trilogy_sock_discard(trilogy_sock_t *sock);
110
112
 
111
113
  #endif
@@ -139,6 +139,29 @@ int trilogy_change_db(trilogy_conn_t *conn, const char *name, size_t name_len)
139
139
  }
140
140
  }
141
141
 
142
+ int trilogy_set_option(trilogy_conn_t *conn, const uint16_t option)
143
+ {
144
+ int rc = trilogy_set_option_send(conn, option);
145
+
146
+ if (rc == TRILOGY_AGAIN) {
147
+ rc = flush_full(conn);
148
+ }
149
+
150
+ if (rc < 0) {
151
+ return rc;
152
+ }
153
+
154
+ while (1) {
155
+ rc = trilogy_set_option_recv(conn);
156
+
157
+ if (rc != TRILOGY_AGAIN) {
158
+ return rc;
159
+ }
160
+
161
+ CHECKED(trilogy_sock_wait_read(conn->socket));
162
+ }
163
+ }
164
+
142
165
  int trilogy_ping(trilogy_conn_t *conn)
143
166
  {
144
167
  int rc = trilogy_ping_send(conn);
@@ -357,8 +357,9 @@ int trilogy_auth_send(trilogy_conn_t *conn, const trilogy_handshake_t *handshake
357
357
  }
358
358
 
359
359
  rc = trilogy_build_auth_packet(&builder, conn->socket->opts.username, conn->socket->opts.password,
360
- conn->socket->opts.password_len, conn->socket->opts.database, handshake->auth_plugin,
361
- handshake->scramble, conn->socket->opts.flags);
360
+ conn->socket->opts.password_len, conn->socket->opts.database,
361
+ conn->socket->opts.encoding, handshake->auth_plugin, handshake->scramble,
362
+ conn->socket->opts.flags);
362
363
 
363
364
  if (rc < 0) {
364
365
  return rc;
@@ -378,7 +379,7 @@ int trilogy_ssl_request_send(trilogy_conn_t *conn)
378
379
  }
379
380
 
380
381
  conn->socket->opts.flags |= TRILOGY_CAPABILITIES_SSL;
381
- rc = trilogy_build_ssl_request_packet(&builder, conn->socket->opts.flags);
382
+ rc = trilogy_build_ssl_request_packet(&builder, conn->socket->opts.flags, conn->socket->opts.encoding);
382
383
 
383
384
  if (rc < 0) {
384
385
  return rc;
@@ -466,6 +467,44 @@ int trilogy_change_db_send(trilogy_conn_t *conn, const char *name, size_t name_l
466
467
 
467
468
  int trilogy_change_db_recv(trilogy_conn_t *conn) { return read_generic_response(conn); }
468
469
 
470
+ int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option)
471
+ {
472
+ trilogy_builder_t builder;
473
+ int err = begin_command_phase(&builder, conn, 0);
474
+ if (err < 0) {
475
+ return err;
476
+ }
477
+
478
+ err = trilogy_build_set_option_packet(&builder, option);
479
+
480
+ if (err < 0) {
481
+ return err;
482
+ }
483
+
484
+ return begin_write(conn);
485
+ }
486
+
487
+ int trilogy_set_option_recv(trilogy_conn_t *conn) {
488
+ int rc = read_packet(conn);
489
+
490
+ if (rc < 0) {
491
+ return rc;
492
+ }
493
+
494
+ switch (current_packet_type(conn)) {
495
+ case TRILOGY_PACKET_OK:
496
+ case TRILOGY_PACKET_EOF: // COM_SET_OPTION returns an EOF packet, but it should be treated as an OK packet.
497
+ return read_ok_packet(conn);
498
+
499
+ case TRILOGY_PACKET_ERR:
500
+ return read_err_packet(conn);
501
+
502
+ default:
503
+ return TRILOGY_UNEXPECTED_PACKET;
504
+ }
505
+ }
506
+
507
+
469
508
  int trilogy_ping_send(trilogy_conn_t *conn)
470
509
  {
471
510
  trilogy_builder_t builder;
@@ -725,4 +764,13 @@ void trilogy_free(trilogy_conn_t *conn)
725
764
  trilogy_buffer_free(&conn->packet_buffer);
726
765
  }
727
766
 
767
+ int trilogy_discard(trilogy_conn_t *conn)
768
+ {
769
+ int rc = trilogy_sock_discard(conn->socket);
770
+ if (rc == TRILOGY_OK) {
771
+ trilogy_free(conn);
772
+ }
773
+ return rc;
774
+ }
775
+
728
776
  #undef CHECKED
@@ -10,6 +10,7 @@
10
10
  #define TRILOGY_CMD_CHANGE_DB 0x02
11
11
  #define TRILOGY_CMD_QUERY 0x03
12
12
  #define TRILOGY_CMD_PING 0x0e
13
+ #define TRILOGY_CMD_SET_OPTION 0x1b
13
14
 
14
15
  #define SCRAMBLE_LEN 20
15
16
 
@@ -493,8 +494,8 @@ static void trilogy_pack_scramble_sha2_hash(const char *scramble, const char *pa
493
494
  }
494
495
 
495
496
  int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, const char *pass, size_t pass_len,
496
- const char *database, const char *auth_plugin, const char *scramble,
497
- TRILOGY_CAPABILITIES_t flags)
497
+ const char *database, TRILOGY_CHARSET_t client_encoding, const char *auth_plugin,
498
+ const char *scramble, TRILOGY_CAPABILITIES_t flags)
498
499
  {
499
500
  int rc = TRILOGY_OK;
500
501
 
@@ -506,8 +507,6 @@ int trilogy_build_auth_packet(trilogy_builder_t *builder, const char *user, cons
506
507
 
507
508
  uint32_t max_packet_len = TRILOGY_MAX_PACKET_LEN;
508
509
 
509
- uint8_t client_encoding = TRILOGY_CHARSET_UTF8_GENERAL_CI;
510
-
511
510
  unsigned int auth_response_len = 0;
512
511
  uint8_t auth_response[EVP_MAX_MD_SIZE];
513
512
 
@@ -646,12 +645,28 @@ fail:
646
645
  return rc;
647
646
  }
648
647
 
649
- int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags)
648
+ int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option)
649
+ {
650
+ int rc = TRILOGY_OK;
651
+
652
+ CHECKED(trilogy_builder_write_uint8(builder, TRILOGY_CMD_SET_OPTION));
653
+ CHECKED(trilogy_builder_write_uint16(builder, option));
654
+
655
+ trilogy_builder_finalize(builder);
656
+
657
+ return TRILOGY_OK;
658
+
659
+ fail:
660
+ return rc;
661
+ }
662
+
663
+
664
+ int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags,
665
+ TRILOGY_CHARSET_t client_encoding)
650
666
  {
651
667
  static const char zeroes[23] = {0};
652
668
 
653
669
  const uint32_t max_packet_len = TRILOGY_MAX_PACKET_LEN;
654
- const uint8_t client_encoding = TRILOGY_CHARSET_UTF8_GENERAL_CI;
655
670
  const uint32_t capabilities = flags | TRILOGY_CAPABILITIES_CLIENT | TRILOGY_CAPABILITIES_SSL;
656
671
 
657
672
  int rc = TRILOGY_OK;
@@ -621,3 +621,28 @@ fail:
621
621
  sock->ssl = NULL;
622
622
  return TRILOGY_OPENSSL_ERR;
623
623
  }
624
+
625
+ int trilogy_sock_discard(trilogy_sock_t *_sock)
626
+ {
627
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
628
+
629
+ if (sock->fd < 0) {
630
+ return TRILOGY_OK;
631
+ }
632
+
633
+ int null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
634
+ if (null_fd < 0) {
635
+ return TRILOGY_SYSERR;
636
+ }
637
+
638
+ if (dup2(null_fd, sock->fd) < 0) {
639
+ close(null_fd);
640
+ return TRILOGY_SYSERR;
641
+ }
642
+
643
+ if (close(null_fd) < 0) {
644
+ return TRILOGY_SYSERR;
645
+ }
646
+
647
+ return TRILOGY_OK;
648
+ }
@@ -9,6 +9,7 @@
9
9
  #define TRILOGY_FLAGS_CAST_BOOLEANS 2
10
10
  #define TRILOGY_FLAGS_LOCAL_TIMEZONE 4
11
11
  #define TRILOGY_FLAGS_FLATTEN_ROWS 8
12
+ #define TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS 16
12
13
  #define TRILOGY_FLAGS_DEFAULT (TRILOGY_FLAGS_CAST)
13
14
 
14
15
  struct rb_trilogy_cast_options {
@@ -16,6 +17,7 @@ struct rb_trilogy_cast_options {
16
17
  bool cast_booleans;
17
18
  bool database_local_time;
18
19
  bool flatten_rows;
20
+ bool cast_decimals_to_bigdecimals;
19
21
  };
20
22
 
21
23
  struct column_info {
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.3.0"
2
+ VERSION = "2.4.0"
3
3
  end
data/lib/trilogy.rb CHANGED
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "trilogy/version"
2
4
 
3
5
  class Trilogy
4
6
  # Trilogy::Error is the base error type. All errors raised by Trilogy
5
7
  # should be descendants of Trilogy::Error
6
8
  module Error
9
+ attr_reader :error_code
7
10
  end
8
11
 
9
12
  # Trilogy::ConnectionError is the base error type for all potentially transient
@@ -12,11 +15,30 @@ class Trilogy
12
15
  include Error
13
16
  end
14
17
 
18
+ # Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
19
+ class SyscallError
20
+ ERRORS = {}
21
+
22
+ Errno.constants
23
+ .map { |c| Errno.const_get(c) }.uniq
24
+ .select { |c| c.is_a?(Class) && c < SystemCallError }
25
+ .each do |c|
26
+ errno_name = c.to_s.split('::').last
27
+ ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::Error })
28
+ end
29
+
30
+ ERRORS.freeze
31
+
32
+ class << self
33
+ def from_errno(errno, message)
34
+ ERRORS[errno].new(message)
35
+ end
36
+ end
37
+ end
38
+
15
39
  class BaseError < StandardError
16
40
  include Error
17
41
 
18
- attr_reader :error_code
19
-
20
42
  def initialize(error_message = nil, error_code = nil)
21
43
  message = error_code ? "#{error_code}: #{error_message}" : error_message
22
44
  super(message)
@@ -42,13 +64,14 @@ class Trilogy
42
64
 
43
65
  class TimeoutError < Errno::ETIMEDOUT
44
66
  include ConnectionError
67
+ end
45
68
 
46
- attr_reader :error_code
69
+ class ConnectionRefusedError < Errno::ECONNREFUSED
70
+ include ConnectionError
71
+ end
47
72
 
48
- def initialize(error_message = nil, error_code = nil)
49
- super
50
- @error_code = error_code
51
- end
73
+ class ConnectionResetError < Errno::ECONNRESET
74
+ include ConnectionError
52
75
  end
53
76
 
54
77
  # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
@@ -92,6 +115,60 @@ class Trilogy
92
115
  include ConnectionError
93
116
  end
94
117
 
118
+ MYSQL_TO_RUBY_ENCODINGS_MAP = {
119
+ "big5" => "Big5",
120
+ "dec8" => nil,
121
+ "cp850" => "CP850",
122
+ "hp8" => nil,
123
+ "koi8r" => "KOI8-R",
124
+ "latin1" => "ISO-8859-1",
125
+ "latin2" => "ISO-8859-2",
126
+ "swe7" => nil,
127
+ "ascii" => "US-ASCII",
128
+ "ujis" => "eucJP-ms",
129
+ "sjis" => "Shift_JIS",
130
+ "hebrew" => "ISO-8859-8",
131
+ "tis620" => "TIS-620",
132
+ "euckr" => "EUC-KR",
133
+ "koi8u" => "KOI8-R",
134
+ "gb2312" => "GB2312",
135
+ "greek" => "ISO-8859-7",
136
+ "cp1250" => "Windows-1250",
137
+ "gbk" => "GBK",
138
+ "latin5" => "ISO-8859-9",
139
+ "armscii8" => nil,
140
+ "utf8" => "UTF-8",
141
+ "ucs2" => "UTF-16BE",
142
+ "cp866" => "IBM866",
143
+ "keybcs2" => nil,
144
+ "macce" => "macCentEuro",
145
+ "macroman" => "macRoman",
146
+ "cp852" => "CP852",
147
+ "latin7" => "ISO-8859-13",
148
+ "utf8mb4" => "UTF-8",
149
+ "cp1251" => "Windows-1251",
150
+ "utf16" => "UTF-16",
151
+ "cp1256" => "Windows-1256",
152
+ "cp1257" => "Windows-1257",
153
+ "utf32" => "UTF-32",
154
+ "binary" => "ASCII-8BIT",
155
+ "geostd8" => nil,
156
+ "cp932" => "Windows-31J",
157
+ "eucjpms" => "eucJP-ms",
158
+ "utf16le" => "UTF-16LE",
159
+ "gb18030" => "GB18030",
160
+ }.freeze
161
+
162
+ def initialize(options = {})
163
+ mysql_encoding = options[:encoding] || "utf8mb4"
164
+ unless rb_encoding = MYSQL_TO_RUBY_ENCODINGS_MAP[mysql_encoding]
165
+ raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
166
+ end
167
+ encoding = Encoding.find(rb_encoding)
168
+ charset = charset_for_mysql_encoding(mysql_encoding)
169
+ _initialize(encoding, charset, **options)
170
+ end
171
+
95
172
  def connection_options
96
173
  @connection_options.dup.freeze
97
174
  end
@@ -124,7 +201,7 @@ class Trilogy
124
201
  end
125
202
 
126
203
  class Result
127
- attr_reader :fields, :rows, :query_time
204
+ attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
128
205
 
129
206
  def count
130
207
  rows.count
@@ -154,6 +231,49 @@ class Trilogy
154
231
 
155
232
  include Enumerable
156
233
  end
234
+
235
+ private
236
+
237
+ def charset_for_mysql_encoding(mysql_encoding)
238
+ @mysql_encodings_map ||= {
239
+ "big5" => CHARSET_BIG5_CHINESE_CI,
240
+ "cp850" => CHARSET_CP850_GENERAL_CI,
241
+ "koi8r" => CHARSET_KOI8R_GENERAL_CI,
242
+ "latin1" => CHARSET_LATIN1_GENERAL_CI,
243
+ "latin2" => CHARSET_LATIN2_GENERAL_CI,
244
+ "ascii" => CHARSET_ASCII_GENERAL_CI,
245
+ "ujis" => CHARSET_UJIS_JAPANESE_CI,
246
+ "sjis" => CHARSET_SJIS_JAPANESE_CI,
247
+ "hebrew" => CHARSET_HEBREW_GENERAL_CI,
248
+ "tis620" => CHARSET_TIS620_THAI_CI,
249
+ "euckr" => CHARSET_EUCKR_KOREAN_CI,
250
+ "koi8u" => CHARSET_KOI8U_GENERAL_CI,
251
+ "gb2312" => CHARSET_GB2312_CHINESE_CI,
252
+ "greek" => CHARSET_GREEK_GENERAL_CI,
253
+ "cp1250" => CHARSET_CP1250_GENERAL_CI,
254
+ "gbk" => CHARSET_GBK_CHINESE_CI,
255
+ "latin5" => CHARSET_LATIN5_TURKISH_CI,
256
+ "utf8" => CHARSET_UTF8_GENERAL_CI,
257
+ "ucs2" => CHARSET_UCS2_GENERAL_CI,
258
+ "cp866" => CHARSET_CP866_GENERAL_CI,
259
+ "cp932" => CHARSET_CP932_JAPANESE_CI,
260
+ "eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
261
+ "utf16le" => CHARSET_UTF16_GENERAL_CI,
262
+ "gb18030" => CHARSET_GB18030_CHINESE_CI,
263
+ "macce" => CHARSET_MACCE_GENERAL_CI,
264
+ "macroman" => CHARSET_MACROMAN_GENERAL_CI,
265
+ "cp852" => CHARSET_CP852_GENERAL_CI,
266
+ "latin7" => CHARSET_LATIN7_GENERAL_CI,
267
+ "utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
268
+ "cp1251" => CHARSET_CP1251_GENERAL_CI,
269
+ "utf16" => CHARSET_UTF16_GENERAL_CI,
270
+ "cp1256" => CHARSET_CP1256_GENERAL_CI,
271
+ "cp1257" => CHARSET_CP1257_GENERAL_CI,
272
+ "utf32" => CHARSET_UTF32_GENERAL_CI,
273
+ "binary" => CHARSET_BINARY,
274
+ }.freeze
275
+ @mysql_encodings_map[mysql_encoding]
276
+ end
157
277
  end
158
278
 
159
279
  require "trilogy/cext"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trilogy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-02 00:00:00.000000000 Z
11
+ date: 2023-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler