trilogy 2.3.0 → 2.4.0

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