trilogy 2.9.0 → 2.12.4

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.
@@ -11,10 +11,149 @@
11
11
  #include <unistd.h>
12
12
  #include <fcntl.h>
13
13
 
14
- #include <trilogy.h>
15
-
16
14
  #include "trilogy-ruby.h"
17
15
 
16
+ typedef struct _buffer_pool_entry_struct {
17
+ size_t cap;
18
+ uint8_t *buff;
19
+ } buffer_pool_entry;
20
+
21
+ typedef struct _buffer_pool_struct {
22
+ size_t capa;
23
+ size_t len;
24
+ buffer_pool_entry *entries;
25
+ } buffer_pool;
26
+
27
+ #ifndef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
28
+ static VALUE _global_buffer_pool = Qnil;
29
+ #endif
30
+
31
+ #define BUFFER_POOL_MAX_SIZE 8
32
+
33
+ static void buffer_pool_free(void *data)
34
+ {
35
+ #ifndef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
36
+ _global_buffer_pool = Qnil;
37
+ #endif
38
+
39
+ buffer_pool *pool = (buffer_pool *)data;
40
+ if (pool->capa) {
41
+ for (size_t index = 0; index < pool->len; index++) {
42
+ xfree(pool->entries[index].buff);
43
+ }
44
+ xfree(pool->entries);
45
+ }
46
+ xfree(pool);
47
+ }
48
+
49
+ static size_t buffer_pool_memsize(const void *data)
50
+ {
51
+ const buffer_pool *pool = (const buffer_pool *)data;
52
+
53
+ size_t memsize = sizeof(buffer_pool) + sizeof(buffer_pool_entry) * pool->capa;
54
+
55
+ if (pool->capa) {
56
+ for (size_t index = 0; index < pool->len; index++) {
57
+ memsize += pool->entries[index].cap;
58
+ }
59
+ }
60
+
61
+ return memsize;
62
+ }
63
+
64
+ static const rb_data_type_t buffer_pool_type = {
65
+ .wrap_struct_name = "trilogy/buffer_pool",
66
+ .function = {
67
+ .dmark = NULL,
68
+ .dfree = buffer_pool_free,
69
+ .dsize = buffer_pool_memsize,
70
+ },
71
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
72
+ };
73
+
74
+ static VALUE create_rb_buffer_pool(void)
75
+ {
76
+ buffer_pool *pool;
77
+ return TypedData_Make_Struct(Qfalse, buffer_pool, &buffer_pool_type, pool);
78
+ }
79
+
80
+ #ifdef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
81
+ #include <ruby/ractor.h>
82
+ static rb_ractor_local_key_t buffer_pool_key;
83
+
84
+ static VALUE get_rb_buffer_pool(void)
85
+ {
86
+ VALUE pool;
87
+ if (!rb_ractor_local_storage_value_lookup(buffer_pool_key, &pool)) {
88
+ pool = create_rb_buffer_pool();
89
+ rb_ractor_local_storage_value_set(buffer_pool_key, pool);
90
+ }
91
+ return pool;
92
+ }
93
+ #else
94
+ static VALUE get_rb_buffer_pool(void)
95
+ {
96
+ if (NIL_P(_global_buffer_pool)) {
97
+ _global_buffer_pool = create_rb_buffer_pool();
98
+ }
99
+ return _global_buffer_pool;
100
+ }
101
+ #endif
102
+
103
+ static inline buffer_pool *get_buffer_pool(void)
104
+ {
105
+ buffer_pool *pool;
106
+ VALUE rb_pool = get_rb_buffer_pool();
107
+ if (NIL_P(rb_pool)) {
108
+ return NULL;
109
+ }
110
+
111
+ TypedData_Get_Struct(rb_pool, buffer_pool, &buffer_pool_type, pool);
112
+ return pool;
113
+ }
114
+
115
+ static void buffer_checkout(trilogy_buffer_t *buffer, size_t initial_capacity)
116
+ {
117
+ buffer_pool * pool = get_buffer_pool();
118
+ if (pool->len) {
119
+ pool->len--;
120
+ buffer->buff = pool->entries[pool->len].buff;
121
+ buffer->cap = pool->entries[pool->len].cap;
122
+ } else {
123
+ buffer->buff = xmalloc(initial_capacity);
124
+ buffer->cap = initial_capacity;
125
+ }
126
+ }
127
+
128
+ static bool buffer_checkin(trilogy_buffer_t *buffer)
129
+ {
130
+ buffer_pool * pool = get_buffer_pool();
131
+
132
+ if (pool->len >= BUFFER_POOL_MAX_SIZE) {
133
+ xfree(buffer->buff);
134
+ buffer->buff = NULL;
135
+ buffer->cap = 0;
136
+ return false;
137
+ }
138
+
139
+ if (!pool->capa) {
140
+ pool->entries = RB_ALLOC_N(buffer_pool_entry, 16);
141
+ pool->capa = 16;
142
+ } else if (pool->len >= pool->capa) {
143
+ pool->capa *= 2;
144
+ RB_REALLOC_N(pool->entries, buffer_pool_entry, pool->capa);
145
+ }
146
+
147
+ pool->entries[pool->len].buff = buffer->buff;
148
+ pool->entries[pool->len].cap = buffer->cap;
149
+ pool->len++;
150
+
151
+ buffer->buff = NULL;
152
+ buffer->cap = 0;
153
+
154
+ return true;
155
+ }
156
+
18
157
  VALUE Trilogy_CastError;
19
158
  static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
20
159
  Trilogy_ConnectionClosedError,
@@ -22,27 +161,36 @@ static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLErro
22
161
 
23
162
  static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
24
163
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
25
- id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
26
- id_database, id_enable_cleartext_plugin, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
164
+ id_password, id_database, id_enable_cleartext_plugin,
165
+ id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
27
166
  id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result,
28
- id_from_code, id_from_errno, id_connection_options, id_max_allowed_packet;
167
+ id_from_code, id_from_errno, id_max_allowed_packet;
29
168
 
30
169
  struct trilogy_ctx {
31
170
  trilogy_conn_t conn;
32
- char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
171
+ rb_encoding *encoding;
33
172
  unsigned int query_flags;
34
- VALUE encoding;
173
+ char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
35
174
  };
36
175
 
37
- static void mark_trilogy(void *ptr)
176
+ static void rb_trilogy_acquire_buffer(struct trilogy_ctx *ctx)
38
177
  {
39
- struct trilogy_ctx *ctx = ptr;
40
- rb_gc_mark(ctx->encoding);
178
+ if (!ctx->conn.packet_buffer.buff) {
179
+ buffer_checkout(&ctx->conn.packet_buffer, TRILOGY_DEFAULT_BUF_SIZE);
180
+ }
181
+ }
182
+
183
+ static void rb_trilogy_release_buffer(struct trilogy_ctx *ctx)
184
+ {
185
+ if (ctx->conn.packet_buffer.buff) {
186
+ buffer_checkin(&ctx->conn.packet_buffer);
187
+ }
41
188
  }
42
189
 
43
190
  static void free_trilogy(void *ptr)
44
191
  {
45
192
  struct trilogy_ctx *ctx = ptr;
193
+
46
194
  trilogy_free(&ctx->conn);
47
195
  xfree(ptr);
48
196
  }
@@ -60,7 +208,7 @@ static size_t trilogy_memsize(const void *ptr) {
60
208
  static const rb_data_type_t trilogy_data_type = {
61
209
  .wrap_struct_name = "trilogy",
62
210
  .function = {
63
- .dmark = mark_trilogy,
211
+ .dmark = NULL,
64
212
  .dfree = free_trilogy,
65
213
  .dsize = trilogy_memsize,
66
214
  },
@@ -116,6 +264,8 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
116
264
  VALUE rbmsg = rb_vsprintf(msg, args);
117
265
  va_end(args);
118
266
 
267
+ rb_trilogy_release_buffer(ctx);
268
+
119
269
  if (!trilogy_error_recoverable_p(rc)) {
120
270
  if (ctx->conn.socket != NULL) {
121
271
  // trilogy_sock_shutdown may affect errno
@@ -178,7 +328,7 @@ static VALUE allocate_trilogy(VALUE klass)
178
328
 
179
329
  ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
180
330
 
181
- if (trilogy_init(&ctx->conn) < 0) {
331
+ if (trilogy_init_no_buffer(&ctx->conn) < 0) {
182
332
  VALUE rbmsg = rb_str_new("trilogy_init", 13);
183
333
  trilogy_syserr_fail_str(errno, rbmsg);
184
334
  }
@@ -295,42 +445,29 @@ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
295
445
  return TRILOGY_OK;
296
446
  }
297
447
 
298
- struct nogvl_sock_args {
299
- int rc;
300
- trilogy_sock_t *sock;
301
- };
302
-
303
- static void *no_gvl_resolve(void *data)
448
+ static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts, int fd)
304
449
  {
305
- struct nogvl_sock_args *args = data;
306
- args->rc = trilogy_sock_resolve(args->sock);
307
- return NULL;
308
- }
450
+ if (fd < 0) {
451
+ return TRILOGY_ERR;
452
+ }
309
453
 
310
- static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts)
311
- {
312
454
  trilogy_sock_t *sock = trilogy_sock_new(opts);
313
455
  if (sock == NULL) {
314
456
  return TRILOGY_ERR;
315
457
  }
316
458
 
317
- struct nogvl_sock_args args = {.rc = 0, .sock = sock};
318
-
319
- // Do the DNS resolving with the GVL unlocked. At this point all
320
- // configuration data is copied and available to the trilogy socket.
321
- rb_thread_call_without_gvl(no_gvl_resolve, (void *)&args, RUBY_UBF_IO, NULL);
322
-
323
- int rc = args.rc;
324
-
325
- if (rc != TRILOGY_OK) {
326
- trilogy_sock_close(sock);
327
- return rc;
328
- }
459
+ int rc;
329
460
 
330
461
  /* replace the default wait callback with our GVL-aware callback so we can
331
462
  escape the GVL on each wait operation without going through call_without_gvl */
332
463
  sock->wait_cb = _cb_ruby_wait;
333
- rc = trilogy_connect_send_socket(&ctx->conn, sock);
464
+
465
+ int newfd = dup(fd);
466
+ if (newfd < 0) {
467
+ return TRILOGY_ERR;
468
+ }
469
+
470
+ rc = trilogy_connect_set_fd(&ctx->conn, sock, newfd);
334
471
  if (rc < 0) {
335
472
  trilogy_sock_close(sock);
336
473
  return rc;
@@ -374,7 +511,12 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
374
511
  }
375
512
 
376
513
  if (rc != TRILOGY_AGAIN) {
377
- handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
514
+ if (rc == TRILOGY_UNSUPPORTED) {
515
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
516
+ }
517
+ else {
518
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
519
+ }
378
520
  }
379
521
 
380
522
  rc = trilogy_sock_wait_read(ctx->conn.socket);
@@ -428,12 +570,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
428
570
  }
429
571
 
430
572
  if (rc != TRILOGY_AGAIN) {
431
- if (rc == TRILOGY_UNSUPPORTED) {
432
- handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
433
- }
434
- else {
435
- handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
436
- }
573
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
437
574
  }
438
575
 
439
576
  rc = trilogy_sock_wait_read(ctx->conn.socket);
@@ -447,14 +584,24 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
447
584
  }
448
585
  }
449
586
 
450
- static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE opts)
587
+ #ifndef HAVE_RB_IO_DESCRIPTOR /* Ruby < 3.1 */
588
+ static int rb_io_descriptor(VALUE io)
589
+ {
590
+ rb_io_t *fptr;
591
+ GetOpenFile(io, fptr);
592
+ rb_io_check_closed(fptr);
593
+ return fptr->fd;
594
+ }
595
+ #endif
596
+
597
+ static VALUE rb_trilogy_connect(VALUE self, VALUE raw_socket, VALUE encoding, VALUE charset, VALUE opts)
451
598
  {
452
599
  struct trilogy_ctx *ctx = get_ctx(self);
453
600
  trilogy_sockopt_t connopt = {0};
454
601
  trilogy_handshake_t handshake;
455
602
  VALUE val;
456
603
 
457
- RB_OBJ_WRITE(self, &ctx->encoding, encoding);
604
+ ctx->encoding = rb_to_encoding(encoding);
458
605
  connopt.encoding = NUM2INT(charset);
459
606
 
460
607
  Check_Type(opts, T_HASH);
@@ -602,7 +749,17 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
602
749
  connopt.tls_max_version = NUM2INT(val);
603
750
  }
604
751
 
605
- int rc = try_connect(ctx, &handshake, &connopt);
752
+ VALUE io = rb_io_get_io(raw_socket);
753
+
754
+ rb_io_t *fptr;
755
+ GetOpenFile(io, fptr);
756
+ rb_io_check_readable(fptr);
757
+ rb_io_check_writable(fptr);
758
+
759
+ int fd = rb_io_descriptor(io);
760
+
761
+ rb_trilogy_acquire_buffer(ctx);
762
+ int rc = try_connect(ctx, &handshake, &connopt, fd);
606
763
  if (rc != TRILOGY_OK) {
607
764
  if (connopt.path) {
608
765
  handle_trilogy_error(ctx, rc, "trilogy_connect - unable to connect to %s", connopt.path);
@@ -617,6 +774,8 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
617
774
 
618
775
  authenticate(ctx, &handshake, connopt.ssl_mode);
619
776
 
777
+ rb_trilogy_release_buffer(ctx);
778
+
620
779
  return Qnil;
621
780
  }
622
781
 
@@ -626,6 +785,8 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
626
785
 
627
786
  StringValue(database);
628
787
 
788
+ rb_trilogy_acquire_buffer(ctx);
789
+
629
790
  int rc = trilogy_change_db_send(&ctx->conn, RSTRING_PTR(database), RSTRING_LEN(database));
630
791
 
631
792
  if (rc == TRILOGY_AGAIN) {
@@ -653,6 +814,8 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
653
814
  }
654
815
  }
655
816
 
817
+ rb_trilogy_release_buffer(ctx);
818
+
656
819
  return Qtrue;
657
820
  }
658
821
 
@@ -660,6 +823,8 @@ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
660
823
  {
661
824
  struct trilogy_ctx *ctx = get_open_ctx(self);
662
825
 
826
+ rb_trilogy_acquire_buffer(ctx);
827
+
663
828
  int rc = trilogy_set_option_send(&ctx->conn, NUM2INT(option));
664
829
 
665
830
  if (rc == TRILOGY_AGAIN) {
@@ -687,6 +852,8 @@ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
687
852
  }
688
853
  }
689
854
 
855
+ rb_trilogy_release_buffer(ctx);
856
+
690
857
  return Qtrue;
691
858
  }
692
859
 
@@ -767,102 +934,107 @@ static VALUE read_query_response(VALUE vargs)
767
934
  double query_time = finish.tv_sec - start.tv_sec;
768
935
  query_time += (double)(finish.tv_nsec - start.tv_nsec) / 1000000000.0;
769
936
 
770
- VALUE result = rb_obj_alloc(Trilogy_Result);
771
-
772
- VALUE column_names = rb_ary_new2(column_count);
773
- rb_ivar_set(result, id_ivar_fields, column_names);
937
+ VALUE column_names = 0;
774
938
 
775
939
  VALUE rows = rb_ary_new();
776
- rb_ivar_set(result, id_ivar_rows, rows);
777
-
778
- rb_ivar_set(result, id_ivar_query_time, DBL2NUM(query_time));
779
940
 
941
+ VALUE last_insert_id = Qnil, affected_rows = Qnil;
780
942
  if (rc == TRILOGY_OK) {
781
- rb_ivar_set(result, id_ivar_last_insert_id, ULL2NUM(ctx->conn.last_insert_id));
782
-
783
- rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
784
-
785
- return result;
786
- } else {
787
- rb_ivar_set(result, id_ivar_last_insert_id, Qnil);
788
- rb_ivar_set(result, id_ivar_affected_rows, Qnil);
943
+ last_insert_id = ULL2NUM(ctx->conn.last_insert_id);
944
+ affected_rows = ULL2NUM(ctx->conn.affected_rows);
789
945
  }
946
+ else {
947
+ VALUE rb_column_info;
948
+ struct column_info *column_info = ALLOCV_N(struct column_info, rb_column_info, column_count);
949
+ VALUE rb_ruby_values;
950
+ VALUE *row_ruby_values = ALLOCV_N(VALUE, rb_ruby_values, column_count);
790
951
 
791
- VALUE rb_column_info;
792
- struct column_info *column_info = ALLOCV_N(struct column_info, rb_column_info, column_count);
952
+ for (uint64_t i = 0; i < column_count; i++) {
953
+ trilogy_column_t column;
793
954
 
794
- for (uint64_t i = 0; i < column_count; i++) {
795
- trilogy_column_t column;
955
+ while (1) {
956
+ rc = trilogy_read_column(&ctx->conn, &column);
796
957
 
797
- while (1) {
798
- rc = trilogy_read_column(&ctx->conn, &column);
958
+ if (rc == TRILOGY_OK) {
959
+ break;
960
+ }
799
961
 
800
- if (rc == TRILOGY_OK) {
801
- break;
802
- }
962
+ if (rc != TRILOGY_AGAIN) {
963
+ return read_query_error(args, rc, "trilogy_read_column");
964
+ }
803
965
 
804
- if (rc != TRILOGY_AGAIN) {
805
- return read_query_error(args, rc, "trilogy_read_column");
966
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
967
+ if (rc != TRILOGY_OK) {
968
+ return read_query_error(args, rc, "trilogy_read_column");
969
+ }
806
970
  }
807
971
 
808
- rc = trilogy_sock_wait_read(ctx->conn.socket);
809
- if (rc != TRILOGY_OK) {
810
- return read_query_error(args, rc, "trilogy_read_column");
811
- }
972
+ #ifdef HAVE_RB_ENC_INTERNED_STR
973
+ row_ruby_values[i] = rb_enc_interned_str(column.name, column.name_len, ctx->encoding);
974
+ #else
975
+ row_ruby_values[i] = rb_enc_str_new(column.name, column.name_len, ctx->encoding);
976
+ OBJ_FREEZE(row_ruby_values[i]);
977
+ #endif
978
+
979
+ column_info[i].type = column.type;
980
+ column_info[i].flags = column.flags;
981
+ column_info[i].len = column.len;
982
+ column_info[i].charset = column.charset;
983
+ column_info[i].decimals = column.decimals;
812
984
  }
813
985
 
814
- #ifdef HAVE_RB_INTERNED_STR
815
- VALUE column_name = rb_interned_str(column.name, column.name_len);
816
- #else
817
- VALUE column_name = rb_str_new(column.name, column.name_len);
818
- OBJ_FREEZE(column_name);
819
- #endif
986
+ column_names = rb_ary_new_from_values(column_count, row_ruby_values);
820
987
 
821
- rb_ary_push(column_names, column_name);
988
+ VALUE rb_trilogy_values;
989
+ trilogy_value_t *row_trilogy_values = ALLOCV_N(trilogy_value_t, rb_trilogy_values, column_count);
822
990
 
823
- column_info[i].type = column.type;
824
- column_info[i].flags = column.flags;
825
- column_info[i].len = column.len;
826
- column_info[i].charset = column.charset;
827
- column_info[i].decimals = column.decimals;
828
- }
991
+ while (1) {
992
+ int rc = trilogy_read_row(&ctx->conn, row_trilogy_values);
829
993
 
830
- VALUE rb_row_values;
831
- trilogy_value_t *row_values = ALLOCV_N(trilogy_value_t, rb_row_values, column_count);
994
+ if (rc == TRILOGY_AGAIN) {
995
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
996
+ if (rc != TRILOGY_OK) {
997
+ return read_query_error(args, rc, "trilogy_read_row");
998
+ }
999
+ continue;
1000
+ }
832
1001
 
833
- while (1) {
834
- int rc = trilogy_read_row(&ctx->conn, row_values);
1002
+ if (rc == TRILOGY_EOF) {
1003
+ break;
1004
+ }
835
1005
 
836
- if (rc == TRILOGY_AGAIN) {
837
- rc = trilogy_sock_wait_read(ctx->conn.socket);
838
1006
  if (rc != TRILOGY_OK) {
839
1007
  return read_query_error(args, rc, "trilogy_read_row");
840
1008
  }
841
- continue;
842
- }
843
-
844
- if (rc == TRILOGY_EOF) {
845
- break;
846
- }
847
1009
 
848
- if (rc != TRILOGY_OK) {
849
- return read_query_error(args, rc, "trilogy_read_row");
850
- }
851
-
852
- if (args->cast_options->flatten_rows) {
853
1010
  for (uint64_t i = 0; i < column_count; i++) {
854
- rb_ary_push(rows, rb_trilogy_cast_value(row_values + i, column_info + i, args->cast_options));
1011
+ row_ruby_values[i] = rb_trilogy_cast_value(row_trilogy_values + i, column_info + i, args->cast_options);
855
1012
  }
856
- } else {
857
- VALUE row = rb_ary_new2(column_count);
858
- for (uint64_t i = 0; i < column_count; i++) {
859
- rb_ary_push(row, rb_trilogy_cast_value(row_values + i, column_info + i, args->cast_options));
1013
+
1014
+ if (args->cast_options->flatten_rows) {
1015
+ rb_ary_cat(rows, row_ruby_values, column_count);
1016
+ } else {
1017
+ rb_ary_push(rows, rb_ary_new_from_values(column_count, row_ruby_values));
860
1018
  }
861
- rb_ary_push(rows, row);
862
1019
  }
863
- }
864
1020
 
865
- return result;
1021
+ ALLOCV_END(rb_column_info);
1022
+ ALLOCV_END(rb_trilogy_values);
1023
+ ALLOCV_END(rb_ruby_values);
1024
+ }
1025
+
1026
+ return rb_class_new_instance(
1027
+ 6,
1028
+ (VALUE []){
1029
+ column_names,
1030
+ rows,
1031
+ DBL2NUM(query_time),
1032
+ (ctx->conn.server_status & TRILOGY_SERVER_STATUS_IN_TRANS) ? Qtrue : Qfalse,
1033
+ affected_rows,
1034
+ last_insert_id,
1035
+ },
1036
+ Trilogy_Result
1037
+ );
866
1038
  }
867
1039
 
868
1040
  static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
@@ -892,6 +1064,10 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
892
1064
  handle_trilogy_error(ctx, args.rc, args.msg);
893
1065
  }
894
1066
 
1067
+ if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) {
1068
+ rb_trilogy_release_buffer(ctx);
1069
+ }
1070
+
895
1071
  return result;
896
1072
  }
897
1073
 
@@ -917,12 +1093,39 @@ static VALUE rb_trilogy_more_results_exist(VALUE self)
917
1093
  }
918
1094
  }
919
1095
 
1096
+ static VALUE rb_trilogy_abandon_results(VALUE self)
1097
+ {
1098
+ struct trilogy_ctx *ctx = get_open_ctx(self);
1099
+
1100
+ long count = 0;
1101
+ while (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
1102
+ count++;
1103
+ int rc = trilogy_drain_results(&ctx->conn);
1104
+ while (rc == TRILOGY_AGAIN) {
1105
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
1106
+ if (rc != TRILOGY_OK) {
1107
+ handle_trilogy_error(ctx, rc, "trilogy_sock_wait_read");
1108
+ }
1109
+
1110
+ rc = trilogy_drain_results(&ctx->conn);
1111
+ }
1112
+
1113
+ if (rc != TRILOGY_OK) {
1114
+ handle_trilogy_error(ctx, rc, "trilogy_drain_results");
1115
+ }
1116
+ }
1117
+
1118
+ return LONG2NUM(count);
1119
+ }
1120
+
920
1121
  static VALUE rb_trilogy_query(VALUE self, VALUE query)
921
1122
  {
922
1123
  struct trilogy_ctx *ctx = get_open_ctx(self);
923
1124
 
924
1125
  StringValue(query);
925
- query = rb_str_export_to_enc(query, rb_to_encoding(ctx->encoding));
1126
+ query = rb_str_export_to_enc(query, ctx->encoding);
1127
+
1128
+ rb_trilogy_acquire_buffer(ctx);
926
1129
 
927
1130
  int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
928
1131
 
@@ -941,6 +1144,8 @@ static VALUE rb_trilogy_ping(VALUE self)
941
1144
  {
942
1145
  struct trilogy_ctx *ctx = get_open_ctx(self);
943
1146
 
1147
+ rb_trilogy_acquire_buffer(ctx);
1148
+
944
1149
  int rc = trilogy_ping_send(&ctx->conn);
945
1150
 
946
1151
  if (rc == TRILOGY_AGAIN) {
@@ -968,6 +1173,7 @@ static VALUE rb_trilogy_ping(VALUE self)
968
1173
  }
969
1174
  }
970
1175
 
1176
+ rb_trilogy_release_buffer(ctx);
971
1177
  return Qtrue;
972
1178
  }
973
1179
 
@@ -985,13 +1191,19 @@ static VALUE rb_trilogy_escape(VALUE self, VALUE str)
985
1191
  const char *escaped_str;
986
1192
  size_t escaped_len;
987
1193
 
1194
+ rb_trilogy_acquire_buffer(ctx);
1195
+
988
1196
  int rc = trilogy_escape(&ctx->conn, RSTRING_PTR(str), RSTRING_LEN(str), &escaped_str, &escaped_len);
989
1197
 
990
1198
  if (rc < 0) {
991
1199
  handle_trilogy_error(ctx, rc, "trilogy_escape");
992
1200
  }
993
1201
 
994
- return rb_enc_str_new(escaped_str, escaped_len, str_enc);
1202
+ VALUE escaped_string = rb_enc_str_new(escaped_str, escaped_len, str_enc);
1203
+
1204
+ rb_trilogy_release_buffer(ctx);
1205
+
1206
+ return escaped_string;
995
1207
  }
996
1208
 
997
1209
  static VALUE rb_trilogy_close(VALUE self)
@@ -1002,6 +1214,8 @@ static VALUE rb_trilogy_close(VALUE self)
1002
1214
  return Qnil;
1003
1215
  }
1004
1216
 
1217
+ rb_trilogy_acquire_buffer(ctx);
1218
+
1005
1219
  int rc = trilogy_close_send(&ctx->conn);
1006
1220
 
1007
1221
  if (rc == TRILOGY_AGAIN) {
@@ -1027,6 +1241,8 @@ static VALUE rb_trilogy_close(VALUE self)
1027
1241
  // we must clear any SSL errors left in the queue from a read/write.
1028
1242
  ERR_clear_error();
1029
1243
 
1244
+ rb_trilogy_release_buffer(ctx);
1245
+
1030
1246
  trilogy_free(&ctx->conn);
1031
1247
 
1032
1248
  return Qnil;
@@ -1134,14 +1350,15 @@ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_
1134
1350
 
1135
1351
  RUBY_FUNC_EXPORTED void Init_cext(void)
1136
1352
  {
1137
- #ifdef HAVE_RB_EXT_RACTOR_SAFE
1353
+ #ifdef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
1138
1354
  rb_ext_ractor_safe(true);
1355
+ buffer_pool_key = rb_ractor_local_storage_value_newkey();
1139
1356
  #endif
1140
1357
 
1141
1358
  VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
1142
1359
  rb_define_alloc_func(Trilogy, allocate_trilogy);
1143
1360
 
1144
- rb_define_private_method(Trilogy, "_connect", rb_trilogy_connect, 3);
1361
+ rb_define_private_method(Trilogy, "_connect", rb_trilogy_connect, 4);
1145
1362
  rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
1146
1363
  rb_define_alias(Trilogy, "select_db", "change_db");
1147
1364
  rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
@@ -1165,6 +1382,7 @@ RUBY_FUNC_EXPORTED void Init_cext(void)
1165
1382
  rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
1166
1383
  rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
1167
1384
  rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
1385
+ rb_define_method(Trilogy, "abandon_results!", rb_trilogy_abandon_results, 0);
1168
1386
  rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
1169
1387
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
1170
1388
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
@@ -1249,12 +1467,6 @@ RUBY_FUNC_EXPORTED void Init_cext(void)
1249
1467
  id_multi_result = rb_intern("multi_result");
1250
1468
  id_from_code = rb_intern("from_code");
1251
1469
  id_from_errno = rb_intern("from_errno");
1252
- id_ivar_affected_rows = rb_intern("@affected_rows");
1253
- id_ivar_fields = rb_intern("@fields");
1254
- id_ivar_last_insert_id = rb_intern("@last_insert_id");
1255
- id_ivar_rows = rb_intern("@rows");
1256
- id_ivar_query_time = rb_intern("@query_time");
1257
- id_connection_options = rb_intern("@connection_options");
1258
1470
 
1259
1471
  rb_trilogy_cast_init();
1260
1472