trilogy 2.2.0 → 2.3.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: 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