trilogy 2.2.0 → 2.3.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: ff98f0678711dd36bae4c5f8a7e0f399d3391f393816bc8df4c4281dbfd5d53f
4
- data.tar.gz: 13914f66cde555a81814e4f2b1761895bb1e33b026a2e7d8e3b74888862e9b69
3
+ metadata.gz: 693efafac411af8e0859eee682d4432a501f17d90c3735faabad4cac4ab07537
4
+ data.tar.gz: 646bc59fce2e3b26058bf867f448496e5d5caf1344ca0832d706bd3e2db99a2f
5
5
  SHA512:
6
- metadata.gz: 8f629502bc41dc22b8ebb65a00fb207d0bd41951d085b7bdd1d9de9956caf2b2c5f9504a533194b6fc5de30883330305482128091f4d417913b576233bf199e6
7
- data.tar.gz: 2342f373109f087b89e6802a2d7e6c05f4c6c1ed938a820af404b87c961b8ef455dad3c3ec1d8b32270340583a3cbb31bf38251d9ff0a7fa892c13c3f55c8307
6
+ metadata.gz: 311d5640cdd3da6e3ba331c8222698ff18a10b06886d277712fb8da453924d862f4c9269163a5f748b73dcbc19087993ae2b3cbdc6fccbbd97a91c0c88f83bed
7
+ data.tar.gz: 77442107a8527f806c2f602d95c000d81c2e7b7a09f6efc8a77a5dcbb091eb4c6890948a1352a72a8fa2f7ef6fe1d74cb0eda84b0d385049514a8dd0236d597d
data/README.md CHANGED
@@ -34,6 +34,12 @@ if client.ping
34
34
  result.each_hash do |user|
35
35
  p user
36
36
  end
37
+
38
+ # Multi-statement
39
+
40
+ results = []
41
+ results << client.query("SELECT name FROM users WHERE id = 1; SELECT name FROM users WHERE id = 2")
42
+ results << client.next_result while client.more_results_exist?
37
43
  end
38
44
  ```
39
45
 
@@ -59,7 +65,7 @@ The official Ruby bindings are inside of the canonical trilogy repository itself
59
65
  The trilogy API was heavily inspired by the mysql2 gem but has a few notable
60
66
  differences:
61
67
 
62
- * The `query_options` hash doesn't inherit from the connection options hash.
68
+ * The `query_flags` don't inherit from the connection options hash.
63
69
  This means that options like turning on/of casting will need to be set before
64
70
  a query and not passed in at connect time.
65
71
  * For performance reasons there is no `application_timezone` query option. If
@@ -7,7 +7,7 @@
7
7
 
8
8
  #define CAST_STACK_SIZE 64
9
9
 
10
- static ID id_BigDecimal, id_new, id_local, id_localtime, id_utc;
10
+ static ID id_BigDecimal, id_Integer, id_new, id_local, id_localtime, id_utc;
11
11
 
12
12
  static const char *ruby_encoding_name_map[] = {
13
13
  [TRILOGY_ENCODING_ARMSCII8] = NULL,
@@ -71,7 +71,7 @@ static void cstr_from_value(char *buf, const trilogy_value_t *value, const char
71
71
  {
72
72
 
73
73
  if (value->data_len > CAST_STACK_SIZE - 1) {
74
- rb_raise(rb_cTrilogyError, errmsg, (int)value->data_len, (char *)value->data);
74
+ rb_raise(Trilogy_CastError, errmsg, (int)value->data_len, (char *)value->data);
75
75
  }
76
76
 
77
77
  memcpy(buf, value->data, value->data_len);
@@ -145,7 +145,11 @@ 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
- return rb_funcall(rb_mKernel, id_BigDecimal, 1, str);
148
+ if (column->decimals == 0) {
149
+ return rb_funcall(rb_mKernel, id_Integer, 1, str);
150
+ } else {
151
+ return rb_funcall(rb_mKernel, id_BigDecimal, 1, str);
152
+ }
149
153
  }
150
154
  case TRILOGY_TYPE_FLOAT:
151
155
  case TRILOGY_TYPE_DOUBLE: {
@@ -156,7 +160,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
156
160
  double dbl = strtod(cstr, &err);
157
161
 
158
162
  if (*err != 0) {
159
- rb_raise(rb_cTrilogyError, "Invalid double value: %.*s", (int)value->data_len, (char *)value->data);
163
+ rb_raise(Trilogy_CastError, "Invalid double value: %.*s", (int)value->data_len, (char *)value->data);
160
164
  }
161
165
  return rb_float_new(dbl);
162
166
  }
@@ -180,7 +184,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
180
184
  }
181
185
 
182
186
  if (month < 1 || day < 1) {
183
- rb_raise(rb_cTrilogyError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
187
+ rb_raise(Trilogy_CastError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
184
188
  }
185
189
 
186
190
  // pad out msec_char with zeroes at the end as it could be at any
@@ -211,7 +215,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
211
215
  }
212
216
 
213
217
  if (month < 1 || day < 1) {
214
- rb_raise(rb_cTrilogyError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
218
+ rb_raise(Trilogy_CastError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
215
219
  }
216
220
 
217
221
  return rb_funcall(Date, id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
@@ -265,6 +269,7 @@ void rb_trilogy_cast_init(void)
265
269
  rb_require("date");
266
270
 
267
271
  id_BigDecimal = rb_intern("BigDecimal");
272
+ id_Integer = rb_intern("Integer");
268
273
  id_new = rb_intern("new");
269
274
  id_local = rb_intern("local");
270
275
  id_localtime = rb_intern("localtime");
@@ -14,16 +14,16 @@
14
14
 
15
15
  #define TRILOGY_RB_TIMEOUT 1
16
16
 
17
- VALUE
18
- rb_cTrilogyError;
19
-
20
- static VALUE Trilogy_DatabaseError, Trilogy_Result;
17
+ VALUE Trilogy_CastError;
18
+ static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
19
+ Trilogy_ConnectionClosedError, Trilogy_TimeoutError, Trilogy_Result;
21
20
 
22
21
  static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
23
22
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
24
23
  id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
25
24
  id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
26
- id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version;
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;
27
27
 
28
28
  struct trilogy_ctx {
29
29
  trilogy_conn_t conn;
@@ -72,7 +72,7 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
72
72
  struct trilogy_ctx *ctx = get_ctx(obj);
73
73
 
74
74
  if (ctx->conn.socket == NULL) {
75
- rb_raise(rb_eIOError, "connection closed");
75
+ rb_raise(Trilogy_ConnectionClosedError, "Attempted to use closed connection");
76
76
  }
77
77
 
78
78
  return ctx;
@@ -88,16 +88,16 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
88
88
 
89
89
  switch (rc) {
90
90
  case TRILOGY_SYSERR:
91
- rb_syserr_fail_str(errno, rbmsg);
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
+ }
92
97
 
93
98
  case TRILOGY_ERR: {
94
99
  VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
95
- VALUE exc = rb_exc_new3(Trilogy_DatabaseError,
96
- rb_sprintf("%" PRIsVALUE ": %d %" PRIsVALUE, rbmsg, ctx->conn.error_code, message));
97
-
98
- rb_ivar_set(exc, rb_intern("@error_code"), INT2FIX(ctx->conn.error_code));
99
- rb_ivar_set(exc, rb_intern("@error_message"), message);
100
-
100
+ VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
101
101
  rb_exc_raise(exc);
102
102
  }
103
103
 
@@ -105,18 +105,29 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
105
105
  unsigned long ossl_error = ERR_get_error();
106
106
  ERR_clear_error();
107
107
  if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
108
- rb_syserr_fail_str(ERR_GET_REASON(ossl_error), rbmsg);
108
+ 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
+ }
109
116
  }
110
117
  // We can't recover from OpenSSL level errors if there's
111
118
  // an active connection.
112
119
  if (ctx->conn.socket != NULL) {
113
120
  trilogy_sock_shutdown(ctx->conn.socket);
114
121
  }
115
- rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
122
+ rb_raise(Trilogy_SSLError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
123
+ }
124
+
125
+ case TRILOGY_DNS_ERR: {
126
+ rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_DNS_ERROR", rbmsg);
116
127
  }
117
128
 
118
129
  default:
119
- rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
130
+ rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
120
131
  }
121
132
  }
122
133
 
@@ -129,7 +140,13 @@ static VALUE allocate_trilogy(VALUE klass)
129
140
  ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
130
141
 
131
142
  if (trilogy_init(&ctx->conn) < 0) {
132
- rb_syserr_fail(errno, "trilogy_init");
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
+ }
133
150
  }
134
151
 
135
152
  return obj;
@@ -145,7 +162,7 @@ static int flush_writes(struct trilogy_ctx *ctx)
145
162
  }
146
163
 
147
164
  if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
148
- rb_syserr_fail(ETIMEDOUT, "trilogy_flush_writes");
165
+ rb_raise(Trilogy_TimeoutError, "trilogy_flush_writes");
149
166
  }
150
167
  }
151
168
  }
@@ -279,7 +296,7 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
279
296
  }
280
297
 
281
298
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
282
- rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
299
+ rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
283
300
  }
284
301
  }
285
302
  }
@@ -305,7 +322,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
305
322
  }
306
323
  } else {
307
324
  if (ssl_mode != TRILOGY_SSL_PREFERRED_NOVERIFY) {
308
- rb_raise(rb_cTrilogyError, "SSL required, not supported by server");
325
+ rb_raise(Trilogy_SSLError, "SSL required, not supported by server");
309
326
  }
310
327
  }
311
328
  }
@@ -332,7 +349,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
332
349
  }
333
350
 
334
351
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
335
- rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
352
+ rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
336
353
  }
337
354
  }
338
355
 
@@ -349,6 +366,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
349
366
  VALUE val;
350
367
 
351
368
  Check_Type(opts, T_HASH);
369
+ rb_ivar_set(self, id_connection_options, opts);
352
370
 
353
371
  if ((val = rb_hash_lookup(opts, ID2SYM(id_ssl_mode))) != Qnil) {
354
372
  Check_Type(val, T_FIXNUM);
@@ -426,6 +444,10 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
426
444
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
427
445
  }
428
446
 
447
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
448
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
449
+ }
450
+
429
451
  if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
430
452
  Check_Type(val, T_STRING);
431
453
  connopt.ssl_ca = StringValueCStr(val);
@@ -478,7 +500,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
478
500
 
479
501
  int rc = try_connect(ctx, &handshake, &connopt);
480
502
  if (rc == TRILOGY_RB_TIMEOUT) {
481
- rb_syserr_fail(ETIMEDOUT, "trilogy_connect_recv");
503
+ rb_raise(Trilogy_TimeoutError, "trilogy_connect_recv");
482
504
  }
483
505
  if (rc != TRILOGY_OK) {
484
506
  if (connopt.path) {
@@ -525,7 +547,7 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
525
547
  }
526
548
 
527
549
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
528
- rb_syserr_fail(ETIMEDOUT, "trilogy_change_db_recv");
550
+ rb_raise(Trilogy_TimeoutError, "trilogy_change_db_recv");
529
551
  }
530
552
  }
531
553
 
@@ -540,10 +562,9 @@ static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_
540
562
  cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
541
563
  }
542
564
 
543
- struct read_query_state {
565
+ struct read_query_response_state {
544
566
  struct rb_trilogy_cast_options *cast_options;
545
567
  struct trilogy_ctx *ctx;
546
- VALUE query;
547
568
 
548
569
  // to free by caller:
549
570
  struct column_info *column_info;
@@ -570,34 +591,25 @@ static void get_timespec_monotonic(struct timespec *ts)
570
591
  #endif
571
592
  }
572
593
 
573
- static VALUE read_query_error(struct read_query_state *args, int rc, const char *msg)
594
+ static VALUE read_query_error(struct read_query_response_state *args, int rc, const char *msg)
574
595
  {
575
596
  args->rc = rc;
576
597
  args->msg = msg;
577
598
  return Qundef;
578
599
  }
579
600
 
580
- static VALUE execute_read_query(VALUE vargs)
601
+ static VALUE read_query_response(VALUE vargs)
581
602
  {
582
- struct read_query_state *args = (void *)vargs;
603
+ struct read_query_response_state *args = (void *)vargs;
583
604
  struct trilogy_ctx *ctx = args->ctx;
584
- VALUE query = args->query;
585
605
 
586
606
  struct timespec start;
587
607
  get_timespec_monotonic(&start);
588
608
 
589
- int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
590
-
591
- if (rc == TRILOGY_AGAIN) {
592
- rc = flush_writes(ctx);
593
- }
594
-
595
- if (rc < 0) {
596
- return read_query_error(args, rc, "trilogy_query_send");
597
- }
598
-
599
609
  uint64_t column_count = 0;
600
610
 
611
+ int rc;
612
+
601
613
  while (1) {
602
614
  rc = trilogy_query_recv(&ctx->conn, &column_count);
603
615
 
@@ -610,7 +622,7 @@ static VALUE execute_read_query(VALUE vargs)
610
622
  }
611
623
 
612
624
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
613
- rb_syserr_fail(ETIMEDOUT, "trilogy_query_recv");
625
+ rb_raise(Trilogy_TimeoutError, "trilogy_query_recv");
614
626
  }
615
627
  }
616
628
 
@@ -656,7 +668,7 @@ static VALUE execute_read_query(VALUE vargs)
656
668
  }
657
669
 
658
670
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
659
- rb_syserr_fail(ETIMEDOUT, "trilogy_read_column");
671
+ rb_raise(Trilogy_TimeoutError, "trilogy_read_column");
660
672
  }
661
673
  }
662
674
 
@@ -673,6 +685,7 @@ static VALUE execute_read_query(VALUE vargs)
673
685
  column_info[i].flags = column.flags;
674
686
  column_info[i].len = column.len;
675
687
  column_info[i].charset = column.charset;
688
+ column_info[i].decimals = column.decimals;
676
689
  }
677
690
 
678
691
  trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
@@ -683,7 +696,7 @@ static VALUE execute_read_query(VALUE vargs)
683
696
 
684
697
  if (rc == TRILOGY_AGAIN) {
685
698
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
686
- rb_syserr_fail(ETIMEDOUT, "trilogy_read_row");
699
+ rb_raise(Trilogy_TimeoutError, "trilogy_read_row");
687
700
  }
688
701
  continue;
689
702
  }
@@ -709,34 +722,25 @@ static VALUE execute_read_query(VALUE vargs)
709
722
  }
710
723
  }
711
724
 
712
- if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
713
- rb_raise(rb_cTrilogyError, "MORE_RESULTS_EXIST");
714
- }
715
-
716
725
  return result;
717
726
  }
718
727
 
719
- static VALUE rb_trilogy_query(VALUE self, VALUE query)
728
+ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
720
729
  {
721
- struct trilogy_ctx *ctx = get_open_ctx(self);
722
-
723
- StringValue(query);
724
-
725
730
  struct rb_trilogy_cast_options cast_options;
726
731
  load_query_options(ctx->query_flags, &cast_options);
727
732
 
728
- struct read_query_state args = {
733
+ struct read_query_response_state args = {
729
734
  .cast_options = &cast_options,
730
735
  .column_info = NULL,
731
736
  .ctx = ctx,
732
- .query = query,
733
737
  .row_values = NULL,
734
738
  .rc = TRILOGY_OK,
735
739
  .msg = NULL,
736
740
  };
737
741
 
738
742
  int state = 0;
739
- VALUE result = rb_protect(execute_read_query, (VALUE)&args, &state);
743
+ VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
740
744
 
741
745
  xfree(args.column_info);
742
746
  xfree(args.row_values);
@@ -756,6 +760,47 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
756
760
  return result;
757
761
  }
758
762
 
763
+ static VALUE rb_trilogy_next_result(VALUE self)
764
+ {
765
+ struct trilogy_ctx *ctx = get_open_ctx(self);
766
+
767
+ if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) {
768
+ return Qnil;
769
+ }
770
+
771
+ return execute_read_query_response(ctx);
772
+ }
773
+
774
+ static VALUE rb_trilogy_more_results_exist(VALUE self)
775
+ {
776
+ struct trilogy_ctx *ctx = get_open_ctx(self);
777
+
778
+ if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
779
+ return Qtrue;
780
+ } else {
781
+ return Qfalse;
782
+ }
783
+ }
784
+
785
+ static VALUE rb_trilogy_query(VALUE self, VALUE query)
786
+ {
787
+ struct trilogy_ctx *ctx = get_open_ctx(self);
788
+
789
+ StringValue(query);
790
+
791
+ int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
792
+
793
+ if (rc == TRILOGY_AGAIN) {
794
+ rc = flush_writes(ctx);
795
+ }
796
+
797
+ if (rc < 0) {
798
+ handle_trilogy_error(ctx, rc, "trilogy_query_send");
799
+ }
800
+
801
+ return execute_read_query_response(ctx);
802
+ }
803
+
759
804
  static VALUE rb_trilogy_ping(VALUE self)
760
805
  {
761
806
  struct trilogy_ctx *ctx = get_open_ctx(self);
@@ -782,7 +827,7 @@ static VALUE rb_trilogy_ping(VALUE self)
782
827
  }
783
828
 
784
829
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
785
- rb_syserr_fail(ETIMEDOUT, "trilogy_ping_recv");
830
+ rb_raise(Trilogy_TimeoutError, "trilogy_ping_recv");
786
831
  }
787
832
  }
788
833
 
@@ -918,8 +963,7 @@ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_
918
963
 
919
964
  void Init_cext()
920
965
  {
921
- VALUE Trilogy = rb_define_class("Trilogy", rb_cObject);
922
-
966
+ VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
923
967
  rb_define_alloc_func(Trilogy, allocate_trilogy);
924
968
 
925
969
  rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
@@ -941,6 +985,8 @@ void Init_cext()
941
985
  rb_define_method(Trilogy, "write_timeout=", rb_trilogy_write_timeout_set, 1);
942
986
  rb_define_method(Trilogy, "server_status", rb_trilogy_server_status, 0);
943
987
  rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
988
+ rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
989
+ rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
944
990
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
945
991
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
946
992
  rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
@@ -959,23 +1005,32 @@ void Init_cext()
959
1005
  rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
960
1006
  rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
961
1007
 
962
- rb_cTrilogyError = rb_define_class_under(Trilogy, "Error", rb_eStandardError);
963
- rb_global_variable(&rb_cTrilogyError);
1008
+ Trilogy_ProtocolError = rb_const_get(Trilogy, rb_intern("ProtocolError"));
1009
+ rb_global_variable(&Trilogy_ProtocolError);
1010
+
1011
+ Trilogy_SSLError = rb_const_get(Trilogy, rb_intern("SSLError"));
1012
+ rb_global_variable(&Trilogy_SSLError);
964
1013
 
965
- Trilogy_DatabaseError = rb_define_class_under(Trilogy, "DatabaseError", rb_cTrilogyError);
966
- rb_global_variable(&Trilogy_DatabaseError);
1014
+ Trilogy_QueryError = rb_const_get(Trilogy, rb_intern("QueryError"));
1015
+ rb_global_variable(&Trilogy_QueryError);
967
1016
 
968
- rb_define_attr(Trilogy_DatabaseError, "error_code", 1, 0);
969
- rb_define_attr(Trilogy_DatabaseError, "error_message", 1, 0);
1017
+ Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1018
+ rb_global_variable(&Trilogy_TimeoutError);
970
1019
 
971
- Trilogy_Result = rb_define_class_under(Trilogy, "Result", rb_cObject);
1020
+ Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1021
+ rb_global_variable(&Trilogy_BaseConnectionError);
1022
+
1023
+ Trilogy_ConnectionClosedError = rb_const_get(Trilogy, rb_intern("ConnectionClosed"));
1024
+ rb_global_variable(&Trilogy_ConnectionClosedError);
1025
+
1026
+ Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result"));
972
1027
  rb_global_variable(&Trilogy_Result);
973
1028
 
1029
+ Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
1030
+ rb_global_variable(&Trilogy_CastError);
1031
+
974
1032
  rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
975
- rb_define_attr(Trilogy_Result, "fields", 1, 0);
976
1033
  rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
977
- rb_define_attr(Trilogy_Result, "rows", 1, 0);
978
- rb_define_attr(Trilogy_Result, "query_time", 1, 0);
979
1034
 
980
1035
  id_socket = rb_intern("socket");
981
1036
  id_host = rb_intern("host");
@@ -1002,12 +1057,14 @@ void Init_cext()
1002
1057
  id_tls_ciphersuites = rb_intern("tls_ciphersuites");
1003
1058
  id_tls_min_version = rb_intern("tls_min_version");
1004
1059
  id_tls_max_version = rb_intern("tls_max_version");
1005
-
1060
+ id_multi_statement = rb_intern("multi_statement");
1061
+ id_from_code = rb_intern("from_code");
1006
1062
  id_ivar_affected_rows = rb_intern("@affected_rows");
1007
1063
  id_ivar_fields = rb_intern("@fields");
1008
1064
  id_ivar_last_insert_id = rb_intern("@last_insert_id");
1009
1065
  id_ivar_rows = rb_intern("@rows");
1010
1066
  id_ivar_query_time = rb_intern("@query_time");
1067
+ id_connection_options = rb_intern("@connection_options");
1011
1068
 
1012
1069
  rb_trilogy_cast_init();
1013
1070
 
@@ -121,9 +121,7 @@
121
121
  * query/ prepared statement. \
122
122
  */ \
123
123
  XX(TRILOGY_CAPABILITIES_MULTI_STATEMENTS, 0x00010000) \
124
- /* Not implemented. \
125
- * \
126
- * From server: the server is capable of sending multiple result sets from \
124
+ /* From server: the server is capable of sending multiple result sets from \
127
125
  * a query. \
128
126
  * \
129
127
  * From client: tells the server it's capable of handling multiple result \
@@ -23,9 +23,10 @@ struct column_info {
23
23
  TRILOGY_CHARSET_t charset;
24
24
  uint32_t len;
25
25
  uint16_t flags;
26
+ uint8_t decimals;
26
27
  };
27
28
 
28
- extern VALUE rb_cTrilogyError;
29
+ extern VALUE Trilogy_CastError;
29
30
 
30
31
  VALUE
31
32
  rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *column,
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.2.0"
2
+ VERSION = "2.3.0"
3
3
  end
data/lib/trilogy.rb CHANGED
@@ -1,7 +1,101 @@
1
- require "trilogy/cext"
2
1
  require "trilogy/version"
3
2
 
4
3
  class Trilogy
4
+ # Trilogy::Error is the base error type. All errors raised by Trilogy
5
+ # should be descendants of Trilogy::Error
6
+ module Error
7
+ end
8
+
9
+ # Trilogy::ConnectionError is the base error type for all potentially transient
10
+ # network errors.
11
+ module ConnectionError
12
+ include Error
13
+ end
14
+
15
+ class BaseError < StandardError
16
+ include Error
17
+
18
+ attr_reader :error_code
19
+
20
+ def initialize(error_message = nil, error_code = nil)
21
+ message = error_code ? "#{error_code}: #{error_message}" : error_message
22
+ super(message)
23
+ @error_code = error_code
24
+ end
25
+ end
26
+
27
+ class BaseConnectionError < BaseError
28
+ include ConnectionError
29
+ end
30
+
31
+ # Trilogy::ClientError is the base error type for invalid queries or parameters
32
+ # that shouldn't be retried.
33
+ class ClientError < BaseError
34
+ include Error
35
+ end
36
+
37
+ class QueryError < ClientError
38
+ end
39
+
40
+ class CastError < ClientError
41
+ end
42
+
43
+ class TimeoutError < Errno::ETIMEDOUT
44
+ include ConnectionError
45
+
46
+ attr_reader :error_code
47
+
48
+ def initialize(error_message = nil, error_code = nil)
49
+ super
50
+ @error_code = error_code
51
+ end
52
+ end
53
+
54
+ # DatabaseError was replaced by ProtocolError, but we'll keep it around as an
55
+ # ancestor of ProtocolError for compatibility reasons (e.g. so `rescue DatabaseError`
56
+ # still works. We can remove this class in the next major release.
57
+ module DatabaseError
58
+ end
59
+
60
+ class ProtocolError < BaseError
61
+ include DatabaseError
62
+
63
+ ERROR_CODES = {
64
+ 1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
65
+ 1044 => BaseConnectionError, # ER_DBACCESS_DENIED_ERROR
66
+ 1045 => BaseConnectionError, # ER_ACCESS_DENIED_ERROR
67
+ 1064 => QueryError, # ER_PARSE_ERROR
68
+ 1152 => BaseConnectionError, # ER_ABORTING_CONNECTION
69
+ 1153 => BaseConnectionError, # ER_NET_PACKET_TOO_LARGE
70
+ 1154 => BaseConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
71
+ 1155 => BaseConnectionError, # ER_NET_FCNTL_ERROR
72
+ 1156 => BaseConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
73
+ 1157 => BaseConnectionError, # ER_NET_UNCOMPRESS_ERROR
74
+ 1158 => BaseConnectionError, # ER_NET_READ_ERROR
75
+ 1159 => BaseConnectionError, # ER_NET_READ_INTERRUPTED
76
+ 1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
77
+ 1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
78
+ 1927 => BaseConnectionError, # ER_CONNECTION_KILLED
79
+ }
80
+ class << self
81
+ def from_code(message, code)
82
+ ERROR_CODES.fetch(code, self).new(message, code)
83
+ end
84
+ end
85
+ end
86
+
87
+ class SSLError < BaseError
88
+ include ConnectionError
89
+ end
90
+
91
+ class ConnectionClosed < IOError
92
+ include ConnectionError
93
+ end
94
+
95
+ def connection_options
96
+ @connection_options.dup.freeze
97
+ end
98
+
5
99
  def in_transaction?
6
100
  (server_status & SERVER_STATUS_IN_TRANS) != 0
7
101
  end
@@ -28,34 +122,38 @@ class Trilogy
28
122
  ensure
29
123
  self.query_flags = old_flags
30
124
  end
31
- end
32
125
 
33
- Trilogy::Result.class_eval do
34
- def count
35
- rows.count
36
- end
126
+ class Result
127
+ attr_reader :fields, :rows, :query_time
128
+
129
+ def count
130
+ rows.count
131
+ end
132
+
133
+ def each_hash
134
+ return enum_for(:each_hash) unless block_given?
37
135
 
38
- def each_hash
39
- return enum_for(:each_hash) unless block_given?
136
+ rows.each do |row|
137
+ this_row = {}
40
138
 
41
- rows.each do |row|
42
- this_row = {}
139
+ idx = 0
140
+ row.each do |col|
141
+ this_row[fields[idx]] = col
142
+ idx += 1
143
+ end
43
144
 
44
- idx = 0
45
- row.each do |col|
46
- this_row[fields[idx]] = col
47
- idx += 1
145
+ yield this_row
48
146
  end
49
147
 
50
- yield this_row
148
+ self
51
149
  end
52
150
 
53
- self
54
- end
151
+ def each(&bk)
152
+ rows.each(&bk)
153
+ end
55
154
 
56
- def each(&bk)
57
- rows.each(&bk)
155
+ include Enumerable
58
156
  end
59
-
60
- include Enumerable
61
157
  end
158
+
159
+ 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.2.0
4
+ version: 2.3.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: 2022-10-17 00:00:00.000000000 Z
11
+ date: 2023-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
99
  - !ruby/object:Gem::Version
100
100
  version: '0'
101
101
  requirements: []
102
- rubygems_version: 3.3.3
102
+ rubygems_version: 3.4.7
103
103
  signing_key:
104
104
  specification_version: 4
105
105
  summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy