trilogy 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 693efafac411af8e0859eee682d4432a501f17d90c3735faabad4cac4ab07537
4
- data.tar.gz: 646bc59fce2e3b26058bf867f448496e5d5caf1344ca0832d706bd3e2db99a2f
3
+ metadata.gz: 2f62d4a5617aaf6b532cfa2888b297eba7bfb18def74e98b6a12847d2845b81f
4
+ data.tar.gz: 7c74e6d32f1ec845bccbace0b989a269d833d1582ba38ffd78e8e5f35305883d
5
5
  SHA512:
6
- metadata.gz: 311d5640cdd3da6e3ba331c8222698ff18a10b06886d277712fb8da453924d862f4c9269163a5f748b73dcbc19087993ae2b3cbdc6fccbbd97a91c0c88f83bed
7
- data.tar.gz: 77442107a8527f806c2f602d95c000d81c2e7b7a09f6efc8a77a5dcbb091eb4c6890948a1352a72a8fa2f7ef6fe1d74cb0eda84b0d385049514a8dd0236d597d
6
+ metadata.gz: 7ba24a84f4e946c1c7f797c10738fda98bcc9e1fbb0bd19f289fe9cc6cbb9bae252f9cf307c873c8a958c2d2519075d4a677cb681648e6429b18bf9c9a897bb7
7
+ data.tar.gz: 4ce6afb50256faf2ec873a71194295c9538a4d2188ecae65018b51f8869a97e4cb68eecef6829f9053ef31a5dca1defd107b46281478e02eb5b10606dae3da99
@@ -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,
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
 
@@ -554,10 +565,45 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
554
565
  return Qtrue;
555
566
  }
556
567
 
568
+ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
569
+ {
570
+ struct trilogy_ctx *ctx = get_open_ctx(self);
571
+
572
+ int rc = trilogy_set_option_send(&ctx->conn, NUM2INT(option));
573
+
574
+ if (rc == TRILOGY_AGAIN) {
575
+ rc = flush_writes(ctx);
576
+ }
577
+
578
+ if (rc != TRILOGY_OK) {
579
+ handle_trilogy_error(ctx, rc, "trilogy_set_option_send");
580
+ }
581
+
582
+ while (1) {
583
+ rc = trilogy_set_option_recv(&ctx->conn);
584
+
585
+ if (rc == TRILOGY_OK) {
586
+ break;
587
+ }
588
+
589
+ if (rc != TRILOGY_AGAIN) {
590
+ handle_trilogy_error(ctx, rc, "trilogy_set_option_recv");
591
+ }
592
+
593
+ if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
594
+ rb_raise(Trilogy_TimeoutError, "trilogy_set_option_recv");
595
+ }
596
+ }
597
+
598
+ return Qtrue;
599
+ }
600
+
601
+
557
602
  static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_options *cast_options)
558
603
  {
559
604
  cast_options->cast = (query_flags & TRILOGY_FLAGS_CAST) != 0;
560
605
  cast_options->cast_booleans = (query_flags & TRILOGY_FLAGS_CAST_BOOLEANS) != 0;
606
+ cast_options->cast_decimals_to_bigdecimals = (query_flags & TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS) != 0;
561
607
  cast_options->database_local_time = (query_flags & TRILOGY_FLAGS_LOCAL_TIMEZONE) != 0;
562
608
  cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
563
609
  }
@@ -566,10 +612,6 @@ struct read_query_response_state {
566
612
  struct rb_trilogy_cast_options *cast_options;
567
613
  struct trilogy_ctx *ctx;
568
614
 
569
- // to free by caller:
570
- struct column_info *column_info;
571
- trilogy_value_t *row_values;
572
-
573
615
  // Error state for tracking
574
616
  const char *msg;
575
617
  int rc;
@@ -648,10 +690,13 @@ static VALUE read_query_response(VALUE vargs)
648
690
  rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
649
691
 
650
692
  return result;
693
+ } else {
694
+ rb_ivar_set(result, id_ivar_last_insert_id, Qnil);
695
+ rb_ivar_set(result, id_ivar_affected_rows, Qnil);
651
696
  }
652
697
 
653
- struct column_info *column_info = ALLOC_N(struct column_info, column_count);
654
- args->column_info = column_info;
698
+ VALUE rb_column_info;
699
+ struct column_info *column_info = ALLOCV_N(struct column_info, rb_column_info, column_count);
655
700
 
656
701
  for (uint64_t i = 0; i < column_count; i++) {
657
702
  trilogy_column_t column;
@@ -688,8 +733,8 @@ static VALUE read_query_response(VALUE vargs)
688
733
  column_info[i].decimals = column.decimals;
689
734
  }
690
735
 
691
- trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
692
- args->row_values = row_values;
736
+ VALUE rb_row_values;
737
+ trilogy_value_t *row_values = ALLOCV_N(trilogy_value_t, rb_row_values, column_count);
693
738
 
694
739
  while (1) {
695
740
  int rc = trilogy_read_row(&ctx->conn, row_values);
@@ -732,9 +777,7 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
732
777
 
733
778
  struct read_query_response_state args = {
734
779
  .cast_options = &cast_options,
735
- .column_info = NULL,
736
780
  .ctx = ctx,
737
- .row_values = NULL,
738
781
  .rc = TRILOGY_OK,
739
782
  .msg = NULL,
740
783
  };
@@ -742,9 +785,6 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
742
785
  int state = 0;
743
786
  VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
744
787
 
745
- xfree(args.column_info);
746
- xfree(args.row_values);
747
-
748
788
  // If we have seen an unexpected exception, jump to it so it gets raised.
749
789
  if (state) {
750
790
  trilogy_sock_shutdown(ctx->conn.socket);
@@ -787,6 +827,7 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
787
827
  struct trilogy_ctx *ctx = get_open_ctx(self);
788
828
 
789
829
  StringValue(query);
830
+ query = rb_str_export_to_enc(query, rb_to_encoding(ctx->encoding));
790
831
 
791
832
  int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
792
833
 
@@ -902,6 +943,25 @@ static VALUE rb_trilogy_closed(VALUE self)
902
943
  }
903
944
  }
904
945
 
946
+ static VALUE rb_trilogy_discard(VALUE self)
947
+ {
948
+ struct trilogy_ctx *ctx = get_ctx(self);
949
+
950
+ if (ctx->conn.socket == NULL) {
951
+ return Qtrue;
952
+ }
953
+
954
+ int rc = trilogy_discard(&ctx->conn);
955
+ switch (rc) {
956
+ case TRILOGY_OK:
957
+ return Qtrue;
958
+ case TRILOGY_SYSERR:
959
+ trilogy_syserr_fail_str(errno, rb_str_new_cstr("Failed to discard connection"));
960
+ UNREACHABLE_RETURN(Qfalse);
961
+ }
962
+ return Qfalse;
963
+ }
964
+
905
965
  static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
906
966
 
907
967
  static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
@@ -961,18 +1021,19 @@ static VALUE rb_trilogy_server_status(VALUE self) { return LONG2FIX(get_open_ctx
961
1021
 
962
1022
  static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_open_ctx(self)->server_version); }
963
1023
 
964
- void Init_cext()
1024
+ RUBY_FUNC_EXPORTED void Init_cext()
965
1025
  {
966
1026
  VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
967
1027
  rb_define_alloc_func(Trilogy, allocate_trilogy);
968
1028
 
969
- rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
1029
+ rb_define_private_method(Trilogy, "_initialize", rb_trilogy_initialize, 3);
970
1030
  rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
971
1031
  rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
972
1032
  rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
973
1033
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
974
1034
  rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
975
1035
  rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
1036
+ rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
976
1037
  rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
977
1038
  rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
978
1039
  rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
@@ -987,6 +1048,7 @@ void Init_cext()
987
1048
  rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
988
1049
  rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
989
1050
  rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
1051
+ rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
990
1052
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
991
1053
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
992
1054
  rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
@@ -1001,6 +1063,7 @@ void Init_cext()
1001
1063
  rb_define_const(Trilogy, "QUERY_FLAGS_NONE", INT2NUM(0));
1002
1064
  rb_define_const(Trilogy, "QUERY_FLAGS_CAST", INT2NUM(TRILOGY_FLAGS_CAST));
1003
1065
  rb_define_const(Trilogy, "QUERY_FLAGS_CAST_BOOLEANS", INT2NUM(TRILOGY_FLAGS_CAST_BOOLEANS));
1066
+ rb_define_const(Trilogy, "QUERY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS", INT2NUM(TRILOGY_FLAGS_CAST_ALL_DECIMALS_TO_BIGDECIMALS));
1004
1067
  rb_define_const(Trilogy, "QUERY_FLAGS_LOCAL_TIMEZONE", INT2NUM(TRILOGY_FLAGS_LOCAL_TIMEZONE));
1005
1068
  rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
1006
1069
  rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
@@ -1017,6 +1080,12 @@ void Init_cext()
1017
1080
  Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1018
1081
  rb_global_variable(&Trilogy_TimeoutError);
1019
1082
 
1083
+ Trilogy_ConnectionRefusedError = rb_const_get(Trilogy, rb_intern("ConnectionRefusedError"));
1084
+ rb_global_variable(&Trilogy_ConnectionRefusedError);
1085
+
1086
+ Trilogy_ConnectionResetError = rb_const_get(Trilogy, rb_intern("ConnectionResetError"));
1087
+ rb_global_variable(&Trilogy_ConnectionResetError);
1088
+
1020
1089
  Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1021
1090
  rb_global_variable(&Trilogy_BaseConnectionError);
1022
1091
 
@@ -1026,12 +1095,12 @@ void Init_cext()
1026
1095
  Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result"));
1027
1096
  rb_global_variable(&Trilogy_Result);
1028
1097
 
1098
+ Trilogy_SyscallError = rb_const_get(Trilogy, rb_intern("SyscallError"));
1099
+ rb_global_variable(&Trilogy_SyscallError);
1100
+
1029
1101
  Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
1030
1102
  rb_global_variable(&Trilogy_CastError);
1031
1103
 
1032
- rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
1033
- rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
1034
-
1035
1104
  id_socket = rb_intern("socket");
1036
1105
  id_host = rb_intern("host");
1037
1106
  id_port = rb_intern("port");
@@ -1059,6 +1128,7 @@ void Init_cext()
1059
1128
  id_tls_max_version = rb_intern("tls_max_version");
1060
1129
  id_multi_statement = rb_intern("multi_statement");
1061
1130
  id_from_code = rb_intern("from_code");
1131
+ id_from_errno = rb_intern("from_errno");
1062
1132
  id_ivar_affected_rows = rb_intern("@affected_rows");
1063
1133
  id_ivar_fields = rb_intern("@fields");
1064
1134
  id_ivar_last_insert_id = rb_intern("@last_insert_id");
@@ -1072,4 +1142,14 @@ void Init_cext()
1072
1142
  #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1073
1143
  TRILOGY_SERVER_STATUS(XX)
1074
1144
  #undef XX
1145
+
1146
+ // set_server_option options
1147
+ #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1148
+ TRILOGY_SET_SERVER_OPTION(XX)
1149
+ #undef XX
1150
+
1151
+ // charsets
1152
+ #define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
1153
+ TRILOGY_CHARSETS(XX)
1154
+ #undef XX
1075
1155
  }
@@ -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 \
@@ -191,7 +187,8 @@ typedef enum {
191
187
  /* A convenience bitmask with common client capabilities set. */
192
188
  TRILOGY_CAPABILITIES_CLIENT = (TRILOGY_CAPABILITIES_PROTOCOL_41 | TRILOGY_CAPABILITIES_SECURE_CONNECTION |
193
189
  TRILOGY_CAPABILITIES_DEPRECATE_EOF | TRILOGY_CAPABILITIES_SESSION_TRACK |
194
- TRILOGY_CAPABILITIES_PLUGIN_AUTH | TRILOGY_CAPABILITIES_TRANSACTIONS)
190
+ TRILOGY_CAPABILITIES_PLUGIN_AUTH | TRILOGY_CAPABILITIES_TRANSACTIONS |
191
+ TRILOGY_CAPABILITIES_MULTI_RESULTS)
195
192
  } TRILOGY_CAPABILITIES_t;
196
193
 
197
194
  #define TRILOGY_SERVER_STATUS(XX) \
@@ -397,22 +394,34 @@ typedef enum {
397
394
  #undef XX
398
395
  } TRILOGY_SESSION_TRACK_TYPE_t;
399
396
 
397
+ #define TRILOGY_SET_SERVER_OPTION(XX) \
398
+ XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_ON, 0x00) \
399
+ XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_OFF, 0x01) \
400
+
401
+ typedef enum {
402
+ #define XX(name, code) name = code,
403
+ TRILOGY_SET_SERVER_OPTION(XX)
404
+ #undef XX
405
+ } TRILOGY_SET_SERVER_OPTION_TYPE_t;
406
+
400
407
  /* trilogy_build_auth_packet - Build a handshake response (or authentication)
401
408
  * packet.
402
409
  *
403
410
  * This should be sent in response to the initial handshake packet the server
404
411
  * sends upon connection.
405
412
  *
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.
413
+ * builder - A pointer to a pre-initialized trilogy_builder_t.
414
+ * user - The username to use for authentication. Must be a C-string.
415
+ * pass - The password to use for authentication. Optional, and can be NULL.
416
+ * pass_len - The length of password in bytes.
417
+ * database - The initial database to connect to. Optional, and can be NULL.
418
+ * client_encoding - The charset to use for the connection.
419
+ * auth_plugin - Plugin authentication mechanism that the server requested.
420
+ * scramble - The scramble value the server sent in the initial handshake.
421
+ * flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
422
+ * The TRILOGY_CAPABILITIES_PROTOCOL_41 and
423
+ * TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
424
+ * internally.
416
425
  *
417
426
  * Return values:
418
427
  * TRILOGY_OK - The packet was successfully built and written to the
@@ -420,8 +429,8 @@ typedef enum {
420
429
  * TRILOGY_SYSERR - A system error occurred, check errno.
421
430
  */
422
431
  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);
432
+ const char *database, TRILOGY_CHARSET_t client_encoding, const char *auth_plugin,
433
+ const char *scramble, TRILOGY_CAPABILITIES_t flags);
425
434
 
426
435
  /* trilogy_build_auth_switch_response_packet - Build a response for when
427
436
  * authentication switching it requested.
@@ -447,7 +456,7 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
447
456
  * command will change the default database for the connection.
448
457
  *
449
458
  * builder - A pointer to a pre-initialized trilogy_builder_t.
450
- * name - The name of the databaset to set as the default.
459
+ * name - The name of the database to set as the default.
451
460
  * name_len - The length of name in bytes.
452
461
  *
453
462
  * Return values:
@@ -457,6 +466,20 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
457
466
  */
458
467
  int trilogy_build_change_db_packet(trilogy_builder_t *builder, const char *name, size_t name_len);
459
468
 
469
+ /* trilogy_build_set_option_packet - Build a set option command packet. This
470
+ * command will enable/disable server capabilities for the connection. Options
471
+ * must be one of `enum_mysql_set_option`.
472
+ *
473
+ * builder - A pointer to a pre-initialized trilogy_builder_t.
474
+ * option - An integer corresponding to the operation to perform.
475
+ *
476
+ * Return values:
477
+ * TRILOGY_OK - The packet was successfully built and written to the
478
+ * builder's internal buffer.
479
+ * TRILOGY_SYSERR - A system error occurred, check errno.
480
+ */
481
+ int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option);
482
+
460
483
  /* trilogy_build_ping_packet - Build a ping command packet.
461
484
  *
462
485
  * builder - A pointer to a pre-initialized trilogy_builder_t.
@@ -497,19 +520,21 @@ int trilogy_build_quit_packet(trilogy_builder_t *builder);
497
520
  * sends upon connection, where an auth packet would normally be sent. A regular
498
521
  * auth packet is to be sent after the SSL handshake completes.
499
522
  *
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.
523
+ * builder - A pointer to a pre-initialized trilogy_builder_t.
524
+ * flags - Bitmask of TRILOGY_CAPABILITIES_t flags.
525
+ * The TRILOGY_CAPABILITIES_PROTOCOL_41 and
526
+ * TRILOGY_CAPABILITIES_SECURE_CONNECTION flags will always be set
527
+ * internally.
528
+ * The TRILOGY_CAPABILITIES_SSL flag will also be set.
529
+ * client_encoding - The charset to use for the connection.
506
530
  *
507
531
  * Return values:
508
532
  * TRILOGY_OK - The packet was successfully built and written to the
509
533
  * builder's internal buffer.
510
534
  * TRILOGY_SYSERR - A system error occurred, check errno.
511
535
  */
512
- int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags);
536
+ int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags,
537
+ TRILOGY_CHARSET_t client_encoding);
513
538
 
514
539
  #define TRILOGY_SERVER_VERSION_SIZE 32
515
540
 
@@ -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.1"
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)
@@ -43,14 +65,20 @@ class Trilogy
43
65
  class TimeoutError < Errno::ETIMEDOUT
44
66
  include ConnectionError
45
67
 
46
- attr_reader :error_code
47
-
48
68
  def initialize(error_message = nil, error_code = nil)
49
69
  super
50
70
  @error_code = error_code
51
71
  end
52
72
  end
53
73
 
74
+ class ConnectionRefusedError < Errno::ECONNREFUSED
75
+ include ConnectionError
76
+ end
77
+
78
+ class ConnectionResetError < Errno::ECONNRESET
79
+ include ConnectionError
80
+ end
81
+
54
82
  # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
55
83
  # ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
56
84
  # still works. We can remove this class in the next major release.
@@ -92,6 +120,60 @@ class Trilogy
92
120
  include ConnectionError
93
121
  end
94
122
 
123
+ MYSQL_TO_RUBY_ENCODINGS_MAP = {
124
+ "big5" => "Big5",
125
+ "dec8" => nil,
126
+ "cp850" => "CP850",
127
+ "hp8" => nil,
128
+ "koi8r" => "KOI8-R",
129
+ "latin1" => "ISO-8859-1",
130
+ "latin2" => "ISO-8859-2",
131
+ "swe7" => nil,
132
+ "ascii" => "US-ASCII",
133
+ "ujis" => "eucJP-ms",
134
+ "sjis" => "Shift_JIS",
135
+ "hebrew" => "ISO-8859-8",
136
+ "tis620" => "TIS-620",
137
+ "euckr" => "EUC-KR",
138
+ "koi8u" => "KOI8-R",
139
+ "gb2312" => "GB2312",
140
+ "greek" => "ISO-8859-7",
141
+ "cp1250" => "Windows-1250",
142
+ "gbk" => "GBK",
143
+ "latin5" => "ISO-8859-9",
144
+ "armscii8" => nil,
145
+ "utf8" => "UTF-8",
146
+ "ucs2" => "UTF-16BE",
147
+ "cp866" => "IBM866",
148
+ "keybcs2" => nil,
149
+ "macce" => "macCentEuro",
150
+ "macroman" => "macRoman",
151
+ "cp852" => "CP852",
152
+ "latin7" => "ISO-8859-13",
153
+ "utf8mb4" => "UTF-8",
154
+ "cp1251" => "Windows-1251",
155
+ "utf16" => "UTF-16",
156
+ "cp1256" => "Windows-1256",
157
+ "cp1257" => "Windows-1257",
158
+ "utf32" => "UTF-32",
159
+ "binary" => "ASCII-8BIT",
160
+ "geostd8" => nil,
161
+ "cp932" => "Windows-31J",
162
+ "eucjpms" => "eucJP-ms",
163
+ "utf16le" => "UTF-16LE",
164
+ "gb18030" => "GB18030",
165
+ }.freeze
166
+
167
+ def initialize(options = {})
168
+ mysql_encoding = options[:encoding] || "utf8mb4"
169
+ unless rb_encoding = MYSQL_TO_RUBY_ENCODINGS_MAP[mysql_encoding]
170
+ raise ArgumentError, "Unknown or unsupported encoding: #{mysql_encoding}"
171
+ end
172
+ encoding = Encoding.find(rb_encoding)
173
+ charset = charset_for_mysql_encoding(mysql_encoding)
174
+ _initialize(encoding, charset, **options)
175
+ end
176
+
95
177
  def connection_options
96
178
  @connection_options.dup.freeze
97
179
  end
@@ -124,7 +206,7 @@ class Trilogy
124
206
  end
125
207
 
126
208
  class Result
127
- attr_reader :fields, :rows, :query_time
209
+ attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
128
210
 
129
211
  def count
130
212
  rows.count
@@ -154,6 +236,49 @@ class Trilogy
154
236
 
155
237
  include Enumerable
156
238
  end
239
+
240
+ private
241
+
242
+ def charset_for_mysql_encoding(mysql_encoding)
243
+ @mysql_encodings_map ||= {
244
+ "big5" => CHARSET_BIG5_CHINESE_CI,
245
+ "cp850" => CHARSET_CP850_GENERAL_CI,
246
+ "koi8r" => CHARSET_KOI8R_GENERAL_CI,
247
+ "latin1" => CHARSET_LATIN1_GENERAL_CI,
248
+ "latin2" => CHARSET_LATIN2_GENERAL_CI,
249
+ "ascii" => CHARSET_ASCII_GENERAL_CI,
250
+ "ujis" => CHARSET_UJIS_JAPANESE_CI,
251
+ "sjis" => CHARSET_SJIS_JAPANESE_CI,
252
+ "hebrew" => CHARSET_HEBREW_GENERAL_CI,
253
+ "tis620" => CHARSET_TIS620_THAI_CI,
254
+ "euckr" => CHARSET_EUCKR_KOREAN_CI,
255
+ "koi8u" => CHARSET_KOI8U_GENERAL_CI,
256
+ "gb2312" => CHARSET_GB2312_CHINESE_CI,
257
+ "greek" => CHARSET_GREEK_GENERAL_CI,
258
+ "cp1250" => CHARSET_CP1250_GENERAL_CI,
259
+ "gbk" => CHARSET_GBK_CHINESE_CI,
260
+ "latin5" => CHARSET_LATIN5_TURKISH_CI,
261
+ "utf8" => CHARSET_UTF8_GENERAL_CI,
262
+ "ucs2" => CHARSET_UCS2_GENERAL_CI,
263
+ "cp866" => CHARSET_CP866_GENERAL_CI,
264
+ "cp932" => CHARSET_CP932_JAPANESE_CI,
265
+ "eucjpms" => CHARSET_EUCJPMS_JAPANESE_CI,
266
+ "utf16le" => CHARSET_UTF16_GENERAL_CI,
267
+ "gb18030" => CHARSET_GB18030_CHINESE_CI,
268
+ "macce" => CHARSET_MACCE_GENERAL_CI,
269
+ "macroman" => CHARSET_MACROMAN_GENERAL_CI,
270
+ "cp852" => CHARSET_CP852_GENERAL_CI,
271
+ "latin7" => CHARSET_LATIN7_GENERAL_CI,
272
+ "utf8mb4" => CHARSET_UTF8MB4_GENERAL_CI,
273
+ "cp1251" => CHARSET_CP1251_GENERAL_CI,
274
+ "utf16" => CHARSET_UTF16_GENERAL_CI,
275
+ "cp1256" => CHARSET_CP1256_GENERAL_CI,
276
+ "cp1257" => CHARSET_CP1257_GENERAL_CI,
277
+ "utf32" => CHARSET_UTF32_GENERAL_CI,
278
+ "binary" => CHARSET_BINARY,
279
+ }.freeze
280
+ @mysql_encodings_map[mysql_encoding]
281
+ end
157
282
  end
158
283
 
159
284
  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.1
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-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler