trilogy 2.1.2 → 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: af92055f653afc0f6a507574ae9d1e0dd41ef2bc17519d01521d64075501187d
4
- data.tar.gz: b056dbc89c70f108d0038019a0da1cd80782f4b6405ffd1eb58b8ed86acaa5b4
3
+ metadata.gz: 693efafac411af8e0859eee682d4432a501f17d90c3735faabad4cac4ab07537
4
+ data.tar.gz: 646bc59fce2e3b26058bf867f448496e5d5caf1344ca0832d706bd3e2db99a2f
5
5
  SHA512:
6
- metadata.gz: 5482a88724d329914717b423d9fd1d5ae71818a17b4104122c7c5a2f4cabd77df37e12808644a23d9dc58b9b8a728e00c486072de6e36f2d508531361a794185
7
- data.tar.gz: 66e9a9c02a948844d8e806761f1c3993d4e932ebbaa59b31acb686c60044406390f3e46df364860474326bca26f68406cf8abb57bd9fb75f8d95e27c3f54bdc2
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;
@@ -31,10 +31,39 @@ struct trilogy_ctx {
31
31
  unsigned int query_flags;
32
32
  };
33
33
 
34
+ static void free_trilogy(void *ptr)
35
+ {
36
+ struct trilogy_ctx *ctx = ptr;
37
+ if (ctx->conn.socket != NULL) {
38
+ trilogy_free(&ctx->conn);
39
+ }
40
+ xfree(ptr);
41
+ }
42
+
43
+ static size_t trilogy_memsize(const void *ptr) {
44
+ const struct trilogy_ctx *ctx = ptr;
45
+ size_t memsize = sizeof(struct trilogy_ctx);
46
+ if (ctx->conn.socket != NULL) {
47
+ memsize += sizeof(trilogy_sock_t);
48
+ }
49
+ memsize += ctx->conn.packet_buffer.cap;
50
+ return memsize;
51
+ }
52
+
53
+ const rb_data_type_t trilogy_data_type = {
54
+ .wrap_struct_name = "trilogy",
55
+ .function = {
56
+ .dmark = NULL,
57
+ .dfree = free_trilogy,
58
+ .dsize = trilogy_memsize,
59
+ },
60
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
61
+ };
62
+
34
63
  static struct trilogy_ctx *get_ctx(VALUE obj)
35
64
  {
36
65
  struct trilogy_ctx *ctx;
37
- Data_Get_Struct(obj, struct trilogy_ctx, ctx);
66
+ TypedData_Get_Struct(obj, struct trilogy_ctx, &trilogy_data_type, ctx);
38
67
  return ctx;
39
68
  }
40
69
 
@@ -43,7 +72,7 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
43
72
  struct trilogy_ctx *ctx = get_ctx(obj);
44
73
 
45
74
  if (ctx->conn.socket == NULL) {
46
- rb_raise(rb_eIOError, "connection closed");
75
+ rb_raise(Trilogy_ConnectionClosedError, "Attempted to use closed connection");
47
76
  }
48
77
 
49
78
  return ctx;
@@ -59,16 +88,16 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
59
88
 
60
89
  switch (rc) {
61
90
  case TRILOGY_SYSERR:
62
- 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
+ }
63
97
 
64
98
  case TRILOGY_ERR: {
65
99
  VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
66
- VALUE exc = rb_exc_new3(Trilogy_DatabaseError,
67
- rb_sprintf("%" PRIsVALUE ": %d %" PRIsVALUE, rbmsg, ctx->conn.error_code, message));
68
-
69
- rb_ivar_set(exc, rb_intern("@error_code"), INT2FIX(ctx->conn.error_code));
70
- rb_ivar_set(exc, rb_intern("@error_message"), message);
71
-
100
+ VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
72
101
  rb_exc_raise(exc);
73
102
  }
74
103
 
@@ -76,25 +105,29 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
76
105
  unsigned long ossl_error = ERR_get_error();
77
106
  ERR_clear_error();
78
107
  if (ERR_GET_LIB(ossl_error) == ERR_LIB_SYS) {
79
- 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
+ }
80
116
  }
81
117
  // We can't recover from OpenSSL level errors if there's
82
118
  // an active connection.
83
119
  if (ctx->conn.socket != NULL) {
84
120
  trilogy_sock_shutdown(ctx->conn.socket);
85
121
  }
86
- 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));
87
123
  }
88
124
 
89
- default:
90
- rb_raise(rb_cTrilogyError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
125
+ case TRILOGY_DNS_ERR: {
126
+ rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_DNS_ERROR", rbmsg);
91
127
  }
92
- }
93
128
 
94
- static void free_trilogy(struct trilogy_ctx *ctx)
95
- {
96
- if (ctx->conn.socket != NULL) {
97
- trilogy_free(&ctx->conn);
129
+ default:
130
+ rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc));
98
131
  }
99
132
  }
100
133
 
@@ -102,14 +135,18 @@ static VALUE allocate_trilogy(VALUE klass)
102
135
  {
103
136
  struct trilogy_ctx *ctx;
104
137
 
105
- VALUE obj = Data_Make_Struct(klass, struct trilogy_ctx, NULL, free_trilogy, ctx);
106
-
107
- memset(ctx->server_version, 0, sizeof(ctx->server_version));
138
+ VALUE obj = TypedData_Make_Struct(klass, struct trilogy_ctx, &trilogy_data_type, ctx);
108
139
 
109
140
  ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
110
141
 
111
142
  if (trilogy_init(&ctx->conn) < 0) {
112
- 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
+ }
113
150
  }
114
151
 
115
152
  return obj;
@@ -125,7 +162,7 @@ static int flush_writes(struct trilogy_ctx *ctx)
125
162
  }
126
163
 
127
164
  if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
128
- rb_syserr_fail(ETIMEDOUT, "trilogy_flush_writes");
165
+ rb_raise(Trilogy_TimeoutError, "trilogy_flush_writes");
129
166
  }
130
167
  }
131
168
  }
@@ -259,7 +296,7 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
259
296
  }
260
297
 
261
298
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
262
- rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
299
+ rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
263
300
  }
264
301
  }
265
302
  }
@@ -285,7 +322,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
285
322
  }
286
323
  } else {
287
324
  if (ssl_mode != TRILOGY_SSL_PREFERRED_NOVERIFY) {
288
- rb_raise(rb_cTrilogyError, "SSL required, not supported by server");
325
+ rb_raise(Trilogy_SSLError, "SSL required, not supported by server");
289
326
  }
290
327
  }
291
328
  }
@@ -312,7 +349,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
312
349
  }
313
350
 
314
351
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
315
- rb_syserr_fail(ETIMEDOUT, "trilogy_auth_recv");
352
+ rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
316
353
  }
317
354
  }
318
355
 
@@ -329,6 +366,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
329
366
  VALUE val;
330
367
 
331
368
  Check_Type(opts, T_HASH);
369
+ rb_ivar_set(self, id_connection_options, opts);
332
370
 
333
371
  if ((val = rb_hash_lookup(opts, ID2SYM(id_ssl_mode))) != Qnil) {
334
372
  Check_Type(val, T_FIXNUM);
@@ -406,6 +444,10 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
406
444
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
407
445
  }
408
446
 
447
+ if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
448
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
449
+ }
450
+
409
451
  if ((val = rb_hash_aref(opts, ID2SYM(id_ssl_ca))) != Qnil) {
410
452
  Check_Type(val, T_STRING);
411
453
  connopt.ssl_ca = StringValueCStr(val);
@@ -458,7 +500,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE opts)
458
500
 
459
501
  int rc = try_connect(ctx, &handshake, &connopt);
460
502
  if (rc == TRILOGY_RB_TIMEOUT) {
461
- rb_syserr_fail(ETIMEDOUT, "trilogy_connect_recv");
503
+ rb_raise(Trilogy_TimeoutError, "trilogy_connect_recv");
462
504
  }
463
505
  if (rc != TRILOGY_OK) {
464
506
  if (connopt.path) {
@@ -505,7 +547,7 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
505
547
  }
506
548
 
507
549
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
508
- rb_syserr_fail(ETIMEDOUT, "trilogy_change_db_recv");
550
+ rb_raise(Trilogy_TimeoutError, "trilogy_change_db_recv");
509
551
  }
510
552
  }
511
553
 
@@ -520,10 +562,9 @@ static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_
520
562
  cast_options->flatten_rows = (query_flags & TRILOGY_FLAGS_FLATTEN_ROWS) != 0;
521
563
  }
522
564
 
523
- struct read_query_state {
565
+ struct read_query_response_state {
524
566
  struct rb_trilogy_cast_options *cast_options;
525
567
  struct trilogy_ctx *ctx;
526
- VALUE query;
527
568
 
528
569
  // to free by caller:
529
570
  struct column_info *column_info;
@@ -550,34 +591,25 @@ static void get_timespec_monotonic(struct timespec *ts)
550
591
  #endif
551
592
  }
552
593
 
553
- 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)
554
595
  {
555
596
  args->rc = rc;
556
597
  args->msg = msg;
557
598
  return Qundef;
558
599
  }
559
600
 
560
- static VALUE execute_read_query(VALUE vargs)
601
+ static VALUE read_query_response(VALUE vargs)
561
602
  {
562
- struct read_query_state *args = (void *)vargs;
603
+ struct read_query_response_state *args = (void *)vargs;
563
604
  struct trilogy_ctx *ctx = args->ctx;
564
- VALUE query = args->query;
565
605
 
566
606
  struct timespec start;
567
607
  get_timespec_monotonic(&start);
568
608
 
569
- int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
570
-
571
- if (rc == TRILOGY_AGAIN) {
572
- rc = flush_writes(ctx);
573
- }
574
-
575
- if (rc < 0) {
576
- return read_query_error(args, rc, "trilogy_query_send");
577
- }
578
-
579
609
  uint64_t column_count = 0;
580
610
 
611
+ int rc;
612
+
581
613
  while (1) {
582
614
  rc = trilogy_query_recv(&ctx->conn, &column_count);
583
615
 
@@ -590,7 +622,7 @@ static VALUE execute_read_query(VALUE vargs)
590
622
  }
591
623
 
592
624
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
593
- rb_syserr_fail(ETIMEDOUT, "trilogy_query_recv");
625
+ rb_raise(Trilogy_TimeoutError, "trilogy_query_recv");
594
626
  }
595
627
  }
596
628
 
@@ -636,7 +668,7 @@ static VALUE execute_read_query(VALUE vargs)
636
668
  }
637
669
 
638
670
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
639
- rb_syserr_fail(ETIMEDOUT, "trilogy_read_column");
671
+ rb_raise(Trilogy_TimeoutError, "trilogy_read_column");
640
672
  }
641
673
  }
642
674
 
@@ -653,6 +685,7 @@ static VALUE execute_read_query(VALUE vargs)
653
685
  column_info[i].flags = column.flags;
654
686
  column_info[i].len = column.len;
655
687
  column_info[i].charset = column.charset;
688
+ column_info[i].decimals = column.decimals;
656
689
  }
657
690
 
658
691
  trilogy_value_t *row_values = ALLOC_N(trilogy_value_t, column_count);
@@ -663,7 +696,7 @@ static VALUE execute_read_query(VALUE vargs)
663
696
 
664
697
  if (rc == TRILOGY_AGAIN) {
665
698
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
666
- rb_syserr_fail(ETIMEDOUT, "trilogy_read_row");
699
+ rb_raise(Trilogy_TimeoutError, "trilogy_read_row");
667
700
  }
668
701
  continue;
669
702
  }
@@ -689,34 +722,25 @@ static VALUE execute_read_query(VALUE vargs)
689
722
  }
690
723
  }
691
724
 
692
- if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
693
- rb_raise(rb_cTrilogyError, "MORE_RESULTS_EXIST");
694
- }
695
-
696
725
  return result;
697
726
  }
698
727
 
699
- static VALUE rb_trilogy_query(VALUE self, VALUE query)
728
+ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
700
729
  {
701
- struct trilogy_ctx *ctx = get_open_ctx(self);
702
-
703
- StringValue(query);
704
-
705
730
  struct rb_trilogy_cast_options cast_options;
706
731
  load_query_options(ctx->query_flags, &cast_options);
707
732
 
708
- struct read_query_state args = {
733
+ struct read_query_response_state args = {
709
734
  .cast_options = &cast_options,
710
735
  .column_info = NULL,
711
736
  .ctx = ctx,
712
- .query = query,
713
737
  .row_values = NULL,
714
738
  .rc = TRILOGY_OK,
715
739
  .msg = NULL,
716
740
  };
717
741
 
718
742
  int state = 0;
719
- VALUE result = rb_protect(execute_read_query, (VALUE)&args, &state);
743
+ VALUE result = rb_protect(read_query_response, (VALUE)&args, &state);
720
744
 
721
745
  xfree(args.column_info);
722
746
  xfree(args.row_values);
@@ -736,6 +760,47 @@ static VALUE rb_trilogy_query(VALUE self, VALUE query)
736
760
  return result;
737
761
  }
738
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
+
739
804
  static VALUE rb_trilogy_ping(VALUE self)
740
805
  {
741
806
  struct trilogy_ctx *ctx = get_open_ctx(self);
@@ -762,7 +827,7 @@ static VALUE rb_trilogy_ping(VALUE self)
762
827
  }
763
828
 
764
829
  if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
765
- rb_syserr_fail(ETIMEDOUT, "trilogy_ping_recv");
830
+ rb_raise(Trilogy_TimeoutError, "trilogy_ping_recv");
766
831
  }
767
832
  }
768
833
 
@@ -826,6 +891,17 @@ static VALUE rb_trilogy_close(VALUE self)
826
891
  return Qnil;
827
892
  }
828
893
 
894
+ static VALUE rb_trilogy_closed(VALUE self)
895
+ {
896
+ struct trilogy_ctx *ctx = get_ctx(self);
897
+
898
+ if (ctx->conn.socket == NULL) {
899
+ return Qtrue;
900
+ } else {
901
+ return Qfalse;
902
+ }
903
+ }
904
+
829
905
  static VALUE rb_trilogy_last_insert_id(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.last_insert_id); }
830
906
 
831
907
  static VALUE rb_trilogy_affected_rows(VALUE self) { return ULL2NUM(get_open_ctx(self)->conn.affected_rows); }
@@ -887,8 +963,7 @@ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_
887
963
 
888
964
  void Init_cext()
889
965
  {
890
- VALUE Trilogy = rb_define_class("Trilogy", rb_cObject);
891
-
966
+ VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
892
967
  rb_define_alloc_func(Trilogy, allocate_trilogy);
893
968
 
894
969
  rb_define_method(Trilogy, "initialize", rb_trilogy_initialize, 1);
@@ -897,6 +972,7 @@ void Init_cext()
897
972
  rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
898
973
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
899
974
  rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
975
+ rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
900
976
  rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
901
977
  rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);
902
978
  rb_define_method(Trilogy, "warning_count", rb_trilogy_warning_count, 0);
@@ -909,6 +985,8 @@ void Init_cext()
909
985
  rb_define_method(Trilogy, "write_timeout=", rb_trilogy_write_timeout_set, 1);
910
986
  rb_define_method(Trilogy, "server_status", rb_trilogy_server_status, 0);
911
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);
912
990
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
913
991
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
914
992
  rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
@@ -927,23 +1005,32 @@ void Init_cext()
927
1005
  rb_define_const(Trilogy, "QUERY_FLAGS_FLATTEN_ROWS", INT2NUM(TRILOGY_FLAGS_FLATTEN_ROWS));
928
1006
  rb_define_const(Trilogy, "QUERY_FLAGS_DEFAULT", INT2NUM(TRILOGY_FLAGS_DEFAULT));
929
1007
 
930
- rb_cTrilogyError = rb_define_class_under(Trilogy, "Error", rb_eStandardError);
931
- 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);
1013
+
1014
+ Trilogy_QueryError = rb_const_get(Trilogy, rb_intern("QueryError"));
1015
+ rb_global_variable(&Trilogy_QueryError);
932
1016
 
933
- Trilogy_DatabaseError = rb_define_class_under(Trilogy, "DatabaseError", rb_cTrilogyError);
934
- rb_global_variable(&Trilogy_DatabaseError);
1017
+ Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
1018
+ rb_global_variable(&Trilogy_TimeoutError);
935
1019
 
936
- rb_define_attr(Trilogy_DatabaseError, "error_code", 1, 0);
937
- rb_define_attr(Trilogy_DatabaseError, "error_message", 1, 0);
1020
+ Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
1021
+ rb_global_variable(&Trilogy_BaseConnectionError);
938
1022
 
939
- Trilogy_Result = rb_define_class_under(Trilogy, "Result", rb_cObject);
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"));
940
1027
  rb_global_variable(&Trilogy_Result);
941
1028
 
1029
+ Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError"));
1030
+ rb_global_variable(&Trilogy_CastError);
1031
+
942
1032
  rb_define_attr(Trilogy_Result, "affected_rows", 1, 0);
943
- rb_define_attr(Trilogy_Result, "fields", 1, 0);
944
1033
  rb_define_attr(Trilogy_Result, "last_insert_id", 1, 0);
945
- rb_define_attr(Trilogy_Result, "rows", 1, 0);
946
- rb_define_attr(Trilogy_Result, "query_time", 1, 0);
947
1034
 
948
1035
  id_socket = rb_intern("socket");
949
1036
  id_host = rb_intern("host");
@@ -970,12 +1057,14 @@ void Init_cext()
970
1057
  id_tls_ciphersuites = rb_intern("tls_ciphersuites");
971
1058
  id_tls_min_version = rb_intern("tls_min_version");
972
1059
  id_tls_max_version = rb_intern("tls_max_version");
973
-
1060
+ id_multi_statement = rb_intern("multi_statement");
1061
+ id_from_code = rb_intern("from_code");
974
1062
  id_ivar_affected_rows = rb_intern("@affected_rows");
975
1063
  id_ivar_fields = rb_intern("@fields");
976
1064
  id_ivar_last_insert_id = rb_intern("@last_insert_id");
977
1065
  id_ivar_rows = rb_intern("@rows");
978
1066
  id_ivar_query_time = rb_intern("@query_time");
1067
+ id_connection_options = rb_intern("@connection_options");
979
1068
 
980
1069
  rb_trilogy_cast_init();
981
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.1.2"
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.1.2
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-04 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.7
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