trilogy 2.1.2 → 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: 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