trilogy 2.9.0 → 2.11.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: 998b06e77dcb78b2f30307834490a7c9c7d61422dea586f646cead7b74467058
4
- data.tar.gz: e5bf1ecf9e8b2b675db0d3c3ee7531db4ab022aa725c4ed0c16db088884c6075
3
+ metadata.gz: 0625a7ff415d68c8f5e421188e66fe7c41ebadd2afc39a7d6b89c6318ea9ef9d
4
+ data.tar.gz: c6b10922d195af6ec3b5140963749b59d4ef4a967728aeeb826377bc66992bc0
5
5
  SHA512:
6
- metadata.gz: bd2acff1c38bbed9bea64f3fbb9ad474e4d42198dccbcdca0aa1b8ba3b52e35711f1c9e2e1a6859c15a71c38ed08ba06b5adcb5fbb5054b1b4264fd51ae0933f
7
- data.tar.gz: e032a04cfb901d48a4e1e711cd576c380562f2b7f871724d06568dff8bd3b599fd3ea86360b3bcbe2f8f9471f986d6b1cc8f8d8e69309877778d1ce442dd65f3
6
+ metadata.gz: 5da895e095a69094c7f1844d341ea2bf0c4461f7744f892a5a6f3aff1552761ebb8f099a5df151450c6c32e894e88cfebf72052c8190aef749be0d8d56ad0611
7
+ data.tar.gz: 60830ae194994fa09a975d3cea8146632300bfef62e98726c7cd6edfdb18a91bd3bce55a9fbf0d5b5d2fa0932d2a264333d87ccb57ae222e59a46bd3c5740fd7
data/README.md CHANGED
@@ -13,7 +13,7 @@ gem 'trilogy'
13
13
  And then execute:
14
14
 
15
15
  ```
16
- $ bundle
16
+ $ bundle install
17
17
  ```
18
18
 
19
19
  Or install it yourself as:
data/Rakefile CHANGED
@@ -19,10 +19,25 @@ Rake::TestTask.new do |t|
19
19
  t.test_files = FileList['test/*_test.rb']
20
20
  t.verbose = true
21
21
  end
22
- task :test => :compile
22
+
23
+ task :test => [:compile, "db:clean"]
23
24
 
24
25
  task :default => :test
25
26
 
26
27
  task :console => :compile do
27
28
  sh "ruby -I lib -r trilogy -S irb"
28
29
  end
30
+
31
+ namespace :db do
32
+ task :create do
33
+ mysql_command = "mysql -uroot -e"
34
+ %x( #{mysql_command} "create DATABASE IF NOT EXISTS test DEFAULT CHARACTER SET utf8mb4" )
35
+ end
36
+
37
+ task :drop do
38
+ mysql_command = "mysql -uroot -e"
39
+ %x( #{mysql_command} "drop DATABASE IF EXISTS test" )
40
+ end
41
+
42
+ task :clean => ["db:drop", "db:create"]
43
+ end
@@ -15,6 +15,149 @@
15
15
 
16
16
  #include "trilogy-ruby.h"
17
17
 
18
+ typedef struct _buffer_pool_entry_struct {
19
+ size_t cap;
20
+ uint8_t *buff;
21
+ } buffer_pool_entry;
22
+
23
+ typedef struct _buffer_pool_struct {
24
+ size_t capa;
25
+ size_t len;
26
+ buffer_pool_entry *entries;
27
+ } buffer_pool;
28
+
29
+ #ifndef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
30
+ static VALUE _global_buffer_pool = Qnil;
31
+ #endif
32
+
33
+ #define BUFFER_POOL_MAX_SIZE 8
34
+
35
+ static void buffer_pool_free(void *data)
36
+ {
37
+ #ifndef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
38
+ _global_buffer_pool = Qnil;
39
+ #endif
40
+
41
+ buffer_pool *pool = (buffer_pool *)data;
42
+ if (pool->capa) {
43
+ for (size_t index = 0; index < pool->len; index++) {
44
+ // NB: buff was allocated by trilogy/buffer.h using raw `malloc`
45
+ // hence we must use raw `free`.
46
+ free(pool->entries[index].buff);
47
+ }
48
+ xfree(pool->entries);
49
+ }
50
+ xfree(pool);
51
+ }
52
+
53
+ static size_t buffer_pool_memsize(const void *data)
54
+ {
55
+ const buffer_pool *pool = (const buffer_pool *)data;
56
+
57
+ size_t memsize = sizeof(buffer_pool) + sizeof(buffer_pool_entry) * pool->capa;
58
+
59
+ if (pool->capa) {
60
+ for (size_t index = 0; index < pool->len; index++) {
61
+ memsize += pool->entries[index].cap;
62
+ }
63
+ }
64
+
65
+ return memsize;
66
+ }
67
+
68
+ static const rb_data_type_t buffer_pool_type = {
69
+ .wrap_struct_name = "trilogy/buffer_pool",
70
+ .function = {
71
+ .dmark = NULL,
72
+ .dfree = buffer_pool_free,
73
+ .dsize = buffer_pool_memsize,
74
+ },
75
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
76
+ };
77
+
78
+ static VALUE create_rb_buffer_pool(void)
79
+ {
80
+ buffer_pool *pool;
81
+ return TypedData_Make_Struct(Qfalse, buffer_pool, &buffer_pool_type, pool);
82
+ }
83
+
84
+ #ifdef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
85
+ #include <ruby/ractor.h>
86
+ static rb_ractor_local_key_t buffer_pool_key;
87
+
88
+ static VALUE get_rb_buffer_pool(void)
89
+ {
90
+ VALUE pool;
91
+ if (!rb_ractor_local_storage_value_lookup(buffer_pool_key, &pool)) {
92
+ pool = create_rb_buffer_pool();
93
+ rb_ractor_local_storage_value_set(buffer_pool_key, pool);
94
+ }
95
+ return pool;
96
+ }
97
+ #else
98
+ static VALUE get_rb_buffer_pool(void)
99
+ {
100
+ if (NIL_P(_global_buffer_pool)) {
101
+ _global_buffer_pool = create_rb_buffer_pool();
102
+ }
103
+ return _global_buffer_pool;
104
+ }
105
+ #endif
106
+
107
+ static inline buffer_pool *get_buffer_pool(void)
108
+ {
109
+ buffer_pool *pool;
110
+ VALUE rb_pool = get_rb_buffer_pool();
111
+ if (NIL_P(rb_pool)) {
112
+ return NULL;
113
+ }
114
+
115
+ TypedData_Get_Struct(rb_pool, buffer_pool, &buffer_pool_type, pool);
116
+ return pool;
117
+ }
118
+
119
+ static void buffer_checkout(trilogy_buffer_t *buffer, size_t initial_capacity)
120
+ {
121
+ buffer_pool * pool = get_buffer_pool();
122
+ if (pool->len) {
123
+ pool->len--;
124
+ buffer->buff = pool->entries[pool->len].buff;
125
+ buffer->cap = pool->entries[pool->len].cap;
126
+ } else {
127
+ buffer->buff = malloc(initial_capacity);
128
+ buffer->cap = initial_capacity;
129
+ }
130
+ }
131
+
132
+ static bool buffer_checkin(trilogy_buffer_t *buffer)
133
+ {
134
+ buffer_pool * pool = get_buffer_pool();
135
+
136
+ if (pool->len >= BUFFER_POOL_MAX_SIZE) {
137
+ free(buffer->buff);
138
+ buffer->buff = NULL;
139
+ buffer->cap = 0;
140
+ return false;
141
+ }
142
+
143
+ if (!pool->capa) {
144
+ pool->entries = RB_ALLOC_N(buffer_pool_entry, 16);
145
+ pool->capa = 16;
146
+ } else if (pool->len >= pool->capa) {
147
+ pool->capa *= 2;
148
+ RB_REALLOC_N(pool->entries, buffer_pool_entry, pool->capa);
149
+ }
150
+
151
+ pool->entries[pool->len].buff = buffer->buff;
152
+ pool->entries[pool->len].cap = buffer->cap;
153
+ pool->len++;
154
+
155
+ buffer->buff = NULL;
156
+ buffer->cap = 0;
157
+
158
+ return true;
159
+ }
160
+
18
161
  VALUE Trilogy_CastError;
19
162
  static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
20
163
  Trilogy_ConnectionClosedError,
@@ -29,20 +172,29 @@ static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows,
29
172
 
30
173
  struct trilogy_ctx {
31
174
  trilogy_conn_t conn;
32
- char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
175
+ rb_encoding *encoding;
33
176
  unsigned int query_flags;
34
- VALUE encoding;
177
+ char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
35
178
  };
36
179
 
37
- static void mark_trilogy(void *ptr)
180
+ static void rb_trilogy_acquire_buffer(struct trilogy_ctx *ctx)
38
181
  {
39
- struct trilogy_ctx *ctx = ptr;
40
- rb_gc_mark(ctx->encoding);
182
+ if (!ctx->conn.packet_buffer.buff) {
183
+ buffer_checkout(&ctx->conn.packet_buffer, TRILOGY_DEFAULT_BUF_SIZE);
184
+ }
185
+ }
186
+
187
+ static void rb_trilogy_release_buffer(struct trilogy_ctx *ctx)
188
+ {
189
+ if (ctx->conn.packet_buffer.buff) {
190
+ buffer_checkin(&ctx->conn.packet_buffer);
191
+ }
41
192
  }
42
193
 
43
194
  static void free_trilogy(void *ptr)
44
195
  {
45
196
  struct trilogy_ctx *ctx = ptr;
197
+
46
198
  trilogy_free(&ctx->conn);
47
199
  xfree(ptr);
48
200
  }
@@ -60,7 +212,7 @@ static size_t trilogy_memsize(const void *ptr) {
60
212
  static const rb_data_type_t trilogy_data_type = {
61
213
  .wrap_struct_name = "trilogy",
62
214
  .function = {
63
- .dmark = mark_trilogy,
215
+ .dmark = NULL,
64
216
  .dfree = free_trilogy,
65
217
  .dsize = trilogy_memsize,
66
218
  },
@@ -116,6 +268,8 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
116
268
  VALUE rbmsg = rb_vsprintf(msg, args);
117
269
  va_end(args);
118
270
 
271
+ rb_trilogy_release_buffer(ctx);
272
+
119
273
  if (!trilogy_error_recoverable_p(rc)) {
120
274
  if (ctx->conn.socket != NULL) {
121
275
  // trilogy_sock_shutdown may affect errno
@@ -178,7 +332,7 @@ static VALUE allocate_trilogy(VALUE klass)
178
332
 
179
333
  ctx->query_flags = TRILOGY_FLAGS_DEFAULT;
180
334
 
181
- if (trilogy_init(&ctx->conn) < 0) {
335
+ if (trilogy_init_no_buffer(&ctx->conn) < 0) {
182
336
  VALUE rbmsg = rb_str_new("trilogy_init", 13);
183
337
  trilogy_syserr_fail_str(errno, rbmsg);
184
338
  }
@@ -295,42 +449,29 @@ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
295
449
  return TRILOGY_OK;
296
450
  }
297
451
 
298
- struct nogvl_sock_args {
299
- int rc;
300
- trilogy_sock_t *sock;
301
- };
302
-
303
- static void *no_gvl_resolve(void *data)
452
+ static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts, int fd)
304
453
  {
305
- struct nogvl_sock_args *args = data;
306
- args->rc = trilogy_sock_resolve(args->sock);
307
- return NULL;
308
- }
454
+ if (fd < 0) {
455
+ return TRILOGY_ERR;
456
+ }
309
457
 
310
- static int try_connect(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake, const trilogy_sockopt_t *opts)
311
- {
312
458
  trilogy_sock_t *sock = trilogy_sock_new(opts);
313
459
  if (sock == NULL) {
314
460
  return TRILOGY_ERR;
315
461
  }
316
462
 
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
- }
463
+ int rc;
329
464
 
330
465
  /* replace the default wait callback with our GVL-aware callback so we can
331
466
  escape the GVL on each wait operation without going through call_without_gvl */
332
467
  sock->wait_cb = _cb_ruby_wait;
333
- rc = trilogy_connect_send_socket(&ctx->conn, sock);
468
+
469
+ int newfd = dup(fd);
470
+ if (newfd < 0) {
471
+ return TRILOGY_ERR;
472
+ }
473
+
474
+ rc = trilogy_connect_set_fd(&ctx->conn, sock, newfd);
334
475
  if (rc < 0) {
335
476
  trilogy_sock_close(sock);
336
477
  return rc;
@@ -374,7 +515,12 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
374
515
  }
375
516
 
376
517
  if (rc != TRILOGY_AGAIN) {
377
- handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
518
+ if (rc == TRILOGY_UNSUPPORTED) {
519
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
520
+ }
521
+ else {
522
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
523
+ }
378
524
  }
379
525
 
380
526
  rc = trilogy_sock_wait_read(ctx->conn.socket);
@@ -428,12 +574,7 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
428
574
  }
429
575
 
430
576
  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
- }
577
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
437
578
  }
438
579
 
439
580
  rc = trilogy_sock_wait_read(ctx->conn.socket);
@@ -447,14 +588,24 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
447
588
  }
448
589
  }
449
590
 
450
- static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE opts)
591
+ #ifndef HAVE_RB_IO_DESCRIPTOR /* Ruby < 3.1 */
592
+ static int rb_io_descriptor(VALUE io)
593
+ {
594
+ rb_io_t *fptr;
595
+ GetOpenFile(io, fptr);
596
+ rb_io_check_closed(fptr);
597
+ return fptr->fd;
598
+ }
599
+ #endif
600
+
601
+ static VALUE rb_trilogy_connect(VALUE self, VALUE raw_socket, VALUE encoding, VALUE charset, VALUE opts)
451
602
  {
452
603
  struct trilogy_ctx *ctx = get_ctx(self);
453
604
  trilogy_sockopt_t connopt = {0};
454
605
  trilogy_handshake_t handshake;
455
606
  VALUE val;
456
607
 
457
- RB_OBJ_WRITE(self, &ctx->encoding, encoding);
608
+ ctx->encoding = rb_to_encoding(encoding);
458
609
  connopt.encoding = NUM2INT(charset);
459
610
 
460
611
  Check_Type(opts, T_HASH);
@@ -602,7 +753,17 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
602
753
  connopt.tls_max_version = NUM2INT(val);
603
754
  }
604
755
 
605
- int rc = try_connect(ctx, &handshake, &connopt);
756
+ VALUE io = rb_io_get_io(raw_socket);
757
+
758
+ rb_io_t *fptr;
759
+ GetOpenFile(io, fptr);
760
+ rb_io_check_readable(fptr);
761
+ rb_io_check_writable(fptr);
762
+
763
+ int fd = rb_io_descriptor(io);
764
+
765
+ rb_trilogy_acquire_buffer(ctx);
766
+ int rc = try_connect(ctx, &handshake, &connopt, fd);
606
767
  if (rc != TRILOGY_OK) {
607
768
  if (connopt.path) {
608
769
  handle_trilogy_error(ctx, rc, "trilogy_connect - unable to connect to %s", connopt.path);
@@ -617,6 +778,8 @@ static VALUE rb_trilogy_connect(VALUE self, VALUE encoding, VALUE charset, VALUE
617
778
 
618
779
  authenticate(ctx, &handshake, connopt.ssl_mode);
619
780
 
781
+ rb_trilogy_release_buffer(ctx);
782
+
620
783
  return Qnil;
621
784
  }
622
785
 
@@ -626,6 +789,8 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
626
789
 
627
790
  StringValue(database);
628
791
 
792
+ rb_trilogy_acquire_buffer(ctx);
793
+
629
794
  int rc = trilogy_change_db_send(&ctx->conn, RSTRING_PTR(database), RSTRING_LEN(database));
630
795
 
631
796
  if (rc == TRILOGY_AGAIN) {
@@ -653,6 +818,8 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
653
818
  }
654
819
  }
655
820
 
821
+ rb_trilogy_release_buffer(ctx);
822
+
656
823
  return Qtrue;
657
824
  }
658
825
 
@@ -660,6 +827,8 @@ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
660
827
  {
661
828
  struct trilogy_ctx *ctx = get_open_ctx(self);
662
829
 
830
+ rb_trilogy_acquire_buffer(ctx);
831
+
663
832
  int rc = trilogy_set_option_send(&ctx->conn, NUM2INT(option));
664
833
 
665
834
  if (rc == TRILOGY_AGAIN) {
@@ -687,6 +856,8 @@ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
687
856
  }
688
857
  }
689
858
 
859
+ rb_trilogy_release_buffer(ctx);
860
+
690
861
  return Qtrue;
691
862
  }
692
863
 
@@ -811,10 +982,10 @@ static VALUE read_query_response(VALUE vargs)
811
982
  }
812
983
  }
813
984
 
814
- #ifdef HAVE_RB_INTERNED_STR
815
- VALUE column_name = rb_interned_str(column.name, column.name_len);
985
+ #ifdef HAVE_RB_ENC_INTERNED_STR
986
+ VALUE column_name = rb_enc_interned_str(column.name, column.name_len, ctx->encoding);
816
987
  #else
817
- VALUE column_name = rb_str_new(column.name, column.name_len);
988
+ VALUE column_name = rb_enc_str_new(column.name, column.name_len, ctx->encoding);
818
989
  OBJ_FREEZE(column_name);
819
990
  #endif
820
991
 
@@ -892,6 +1063,10 @@ static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
892
1063
  handle_trilogy_error(ctx, args.rc, args.msg);
893
1064
  }
894
1065
 
1066
+ if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) {
1067
+ rb_trilogy_release_buffer(ctx);
1068
+ }
1069
+
895
1070
  return result;
896
1071
  }
897
1072
 
@@ -917,12 +1092,39 @@ static VALUE rb_trilogy_more_results_exist(VALUE self)
917
1092
  }
918
1093
  }
919
1094
 
1095
+ static VALUE rb_trilogy_abandon_results(VALUE self)
1096
+ {
1097
+ struct trilogy_ctx *ctx = get_open_ctx(self);
1098
+
1099
+ long count = 0;
1100
+ while (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) {
1101
+ count++;
1102
+ int rc = trilogy_drain_results(&ctx->conn);
1103
+ while (rc == TRILOGY_AGAIN) {
1104
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
1105
+ if (rc != TRILOGY_OK) {
1106
+ handle_trilogy_error(ctx, rc, "trilogy_sock_wait_read");
1107
+ }
1108
+
1109
+ rc = trilogy_drain_results(&ctx->conn);
1110
+ }
1111
+
1112
+ if (rc != TRILOGY_OK) {
1113
+ handle_trilogy_error(ctx, rc, "trilogy_drain_results");
1114
+ }
1115
+ }
1116
+
1117
+ return LONG2NUM(count);
1118
+ }
1119
+
920
1120
  static VALUE rb_trilogy_query(VALUE self, VALUE query)
921
1121
  {
922
1122
  struct trilogy_ctx *ctx = get_open_ctx(self);
923
1123
 
924
1124
  StringValue(query);
925
- query = rb_str_export_to_enc(query, rb_to_encoding(ctx->encoding));
1125
+ query = rb_str_export_to_enc(query, ctx->encoding);
1126
+
1127
+ rb_trilogy_acquire_buffer(ctx);
926
1128
 
927
1129
  int rc = trilogy_query_send(&ctx->conn, RSTRING_PTR(query), RSTRING_LEN(query));
928
1130
 
@@ -941,6 +1143,8 @@ static VALUE rb_trilogy_ping(VALUE self)
941
1143
  {
942
1144
  struct trilogy_ctx *ctx = get_open_ctx(self);
943
1145
 
1146
+ rb_trilogy_acquire_buffer(ctx);
1147
+
944
1148
  int rc = trilogy_ping_send(&ctx->conn);
945
1149
 
946
1150
  if (rc == TRILOGY_AGAIN) {
@@ -968,6 +1172,7 @@ static VALUE rb_trilogy_ping(VALUE self)
968
1172
  }
969
1173
  }
970
1174
 
1175
+ rb_trilogy_release_buffer(ctx);
971
1176
  return Qtrue;
972
1177
  }
973
1178
 
@@ -985,13 +1190,19 @@ static VALUE rb_trilogy_escape(VALUE self, VALUE str)
985
1190
  const char *escaped_str;
986
1191
  size_t escaped_len;
987
1192
 
1193
+ rb_trilogy_acquire_buffer(ctx);
1194
+
988
1195
  int rc = trilogy_escape(&ctx->conn, RSTRING_PTR(str), RSTRING_LEN(str), &escaped_str, &escaped_len);
989
1196
 
990
1197
  if (rc < 0) {
991
1198
  handle_trilogy_error(ctx, rc, "trilogy_escape");
992
1199
  }
993
1200
 
994
- return rb_enc_str_new(escaped_str, escaped_len, str_enc);
1201
+ VALUE escaped_string = rb_enc_str_new(escaped_str, escaped_len, str_enc);
1202
+
1203
+ rb_trilogy_release_buffer(ctx);
1204
+
1205
+ return escaped_string;
995
1206
  }
996
1207
 
997
1208
  static VALUE rb_trilogy_close(VALUE self)
@@ -1002,6 +1213,8 @@ static VALUE rb_trilogy_close(VALUE self)
1002
1213
  return Qnil;
1003
1214
  }
1004
1215
 
1216
+ rb_trilogy_acquire_buffer(ctx);
1217
+
1005
1218
  int rc = trilogy_close_send(&ctx->conn);
1006
1219
 
1007
1220
  if (rc == TRILOGY_AGAIN) {
@@ -1027,6 +1240,8 @@ static VALUE rb_trilogy_close(VALUE self)
1027
1240
  // we must clear any SSL errors left in the queue from a read/write.
1028
1241
  ERR_clear_error();
1029
1242
 
1243
+ rb_trilogy_release_buffer(ctx);
1244
+
1030
1245
  trilogy_free(&ctx->conn);
1031
1246
 
1032
1247
  return Qnil;
@@ -1134,14 +1349,15 @@ static VALUE rb_trilogy_server_version(VALUE self) { return rb_str_new_cstr(get_
1134
1349
 
1135
1350
  RUBY_FUNC_EXPORTED void Init_cext(void)
1136
1351
  {
1137
- #ifdef HAVE_RB_EXT_RACTOR_SAFE
1352
+ #ifdef HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY
1138
1353
  rb_ext_ractor_safe(true);
1354
+ buffer_pool_key = rb_ractor_local_storage_value_newkey();
1139
1355
  #endif
1140
1356
 
1141
1357
  VALUE Trilogy = rb_const_get(rb_cObject, rb_intern("Trilogy"));
1142
1358
  rb_define_alloc_func(Trilogy, allocate_trilogy);
1143
1359
 
1144
- rb_define_private_method(Trilogy, "_connect", rb_trilogy_connect, 3);
1360
+ rb_define_private_method(Trilogy, "_connect", rb_trilogy_connect, 4);
1145
1361
  rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
1146
1362
  rb_define_alias(Trilogy, "select_db", "change_db");
1147
1363
  rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
@@ -1165,6 +1381,7 @@ RUBY_FUNC_EXPORTED void Init_cext(void)
1165
1381
  rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
1166
1382
  rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
1167
1383
  rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
1384
+ rb_define_method(Trilogy, "abandon_results!", rb_trilogy_abandon_results, 0);
1168
1385
  rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
1169
1386
  rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
1170
1387
  rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
@@ -10,12 +10,14 @@ File.binwrite("trilogy.c",
10
10
  }.join)
11
11
 
12
12
  $objs = %w[trilogy.o cast.o cext.o]
13
- $CFLAGS << " -I #{__dir__}/inc -std=gnu99 -fvisibility=hidden"
13
+ append_cflags(["-I #{__dir__}/inc", "-std=gnu99", "-fvisibility=hidden"])
14
14
 
15
- dir_config("openssl")
15
+ dir_config("openssl").any? || pkg_config("openssl")
16
16
 
17
17
  have_library("crypto", "CRYPTO_malloc")
18
18
  have_library("ssl", "SSL_new")
19
- have_func("rb_interned_str", "ruby.h")
19
+ have_func("rb_ractor_local_storage_value_newkey", "ruby.h")
20
+ have_func("rb_enc_interned_str", "ruby.h")
21
+ have_func("rb_io_descriptor", "ruby.h") # Ruby 3.1+
20
22
 
21
23
  create_makefile "trilogy/cext"
@@ -53,6 +53,19 @@ int trilogy_buffer_expand(trilogy_buffer_t *buffer, size_t needed);
53
53
  */
54
54
  int trilogy_buffer_putc(trilogy_buffer_t *buffer, uint8_t c);
55
55
 
56
+ /* trilogy_buffer_write - Appends multiple bytes to the buffer, resizing the underlying
57
+ * allocation if necessary.
58
+ *
59
+ * buffer - A pointer to a pre-initialized trilogy_buffer_t.
60
+ * ptr - The pointer to the byte array.
61
+ * len - How many bytes to append.
62
+ *
63
+ * Return values:
64
+ * TRILOGY_OK - The character was appended to the buffer
65
+ * TRILOGY_SYSERR - A system error occurred, check errno.
66
+ */
67
+ int trilogy_buffer_write(trilogy_buffer_t *buffer, const uint8_t *ptr, size_t len);
68
+
56
69
  /* trilogy_buffer_free - Free an trilogy_buffer_t's underlying storage. The buffer
57
70
  * must be re-initialized with trilogy_buffer_init if it is to be reused. Any
58
71
  * operations performed on an unintialized or freed buffer are undefined.
@@ -106,6 +106,15 @@ typedef struct {
106
106
  */
107
107
  int trilogy_init(trilogy_conn_t *conn);
108
108
 
109
+ /* trilogy_init_no_buffer - Same as trilogy_init but doesn't allocate the packet buffer
110
+ *
111
+ * conn - A pre-allocated trilogy_conn_t pointer.
112
+ *
113
+ * Return values:
114
+ * TRILOGY_OK - The trilogy_conn_t pointer was properly initialized
115
+ */
116
+ int trilogy_init_no_buffer(trilogy_conn_t *conn);
117
+
109
118
  /* trilogy_flush_writes - Attempt to flush the internal packet buffer to the
110
119
  * network. This must be used if a `_send` function returns TRILOGY_AGAIN, and
111
120
  * should continue to be called until it returns a value other than
@@ -150,6 +159,8 @@ int trilogy_connect_send(trilogy_conn_t *conn, const trilogy_sockopt_t *opts);
150
159
  */
151
160
  int trilogy_connect_send_socket(trilogy_conn_t *conn, trilogy_sock_t *sock);
152
161
 
162
+ int trilogy_connect_set_fd(trilogy_conn_t *conn, trilogy_sock_t *sock, int fd);
163
+
153
164
  /* trilogy_connect_recv - Read the initial handshake from the server.
154
165
  *
155
166
  * This should be called after trilogy_connect_send returns TRILOGY_OK. Calling
@@ -25,7 +25,8 @@
25
25
  XX(TRILOGY_MAX_PACKET_EXCEEDED, -20) \
26
26
  XX(TRILOGY_UNKNOWN_TYPE, -21) \
27
27
  XX(TRILOGY_TIMEOUT, -22) \
28
- XX(TRILOGY_AUTH_PLUGIN_ERROR, -23)
28
+ XX(TRILOGY_AUTH_PLUGIN_ERROR, -23) \
29
+ XX(TRILOGY_MEM_ERROR, -24)
29
30
 
30
31
  enum {
31
32
  #define XX(name, code) name = code,
@@ -88,6 +88,8 @@ typedef struct trilogy_sock_t {
88
88
 
89
89
  static inline int trilogy_sock_connect(trilogy_sock_t *sock) { return sock->connect_cb(sock); }
90
90
 
91
+ void trilogy_sock_set_fd(trilogy_sock_t *sock, int fd);
92
+
91
93
  static inline ssize_t trilogy_sock_read(trilogy_sock_t *sock, void *buf, size_t n)
92
94
  {
93
95
  return sock->read_cb(sock, buf, n);