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 +4 -4
- data/README.md +1 -1
- data/Rakefile +16 -1
- data/ext/trilogy-ruby/cext.c +266 -49
- data/ext/trilogy-ruby/extconf.rb +5 -3
- data/ext/trilogy-ruby/inc/trilogy/buffer.h +13 -0
- data/ext/trilogy-ruby/inc/trilogy/client.h +11 -0
- data/ext/trilogy-ruby/inc/trilogy/error.h +2 -1
- data/ext/trilogy-ruby/inc/trilogy/socket.h +2 -0
- data/ext/trilogy-ruby/src/buffer.c +22 -3
- data/ext/trilogy-ruby/src/client.c +407 -97
- data/ext/trilogy-ruby/src/packet_parser.c +1 -1
- data/ext/trilogy-ruby/src/socket.c +6 -0
- data/lib/trilogy/encoding.rb +2 -0
- data/lib/trilogy/error.rb +7 -0
- data/lib/trilogy/result.rb +2 -0
- data/lib/trilogy/version.rb +1 -1
- data/lib/trilogy.rb +76 -1
- data/trilogy.gemspec +3 -2
- metadata +10 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0625a7ff415d68c8f5e421188e66fe7c41ebadd2afc39a7d6b89c6318ea9ef9d
|
|
4
|
+
data.tar.gz: c6b10922d195af6ec3b5140963749b59d4ef4a967728aeeb826377bc66992bc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5da895e095a69094c7f1844d341ea2bf0c4461f7744f892a5a6f3aff1552761ebb8f099a5df151450c6c32e894e88cfebf72052c8190aef749be0d8d56ad0611
|
|
7
|
+
data.tar.gz: 60830ae194994fa09a975d3cea8146632300bfef62e98726c7cd6edfdb18a91bd3bce55a9fbf0d5b5d2fa0932d2a264333d87ccb57ae222e59a46bd3c5740fd7
|
data/README.md
CHANGED
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
|
-
|
|
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
|
data/ext/trilogy-ruby/cext.c
CHANGED
|
@@ -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
|
-
|
|
175
|
+
rb_encoding *encoding;
|
|
33
176
|
unsigned int query_flags;
|
|
34
|
-
|
|
177
|
+
char server_version[TRILOGY_SERVER_VERSION_SIZE + 1];
|
|
35
178
|
};
|
|
36
179
|
|
|
37
|
-
static void
|
|
180
|
+
static void rb_trilogy_acquire_buffer(struct trilogy_ctx *ctx)
|
|
38
181
|
{
|
|
39
|
-
|
|
40
|
-
|
|
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 =
|
|
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 (
|
|
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
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
815
|
-
VALUE column_name =
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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
|
|
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,
|
|
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));
|
data/ext/trilogy-ruby/extconf.rb
CHANGED
|
@@ -10,12 +10,14 @@ File.binwrite("trilogy.c",
|
|
|
10
10
|
}.join)
|
|
11
11
|
|
|
12
12
|
$objs = %w[trilogy.o cast.o cext.o]
|
|
13
|
-
|
|
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("
|
|
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);
|