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,6 +11,7 @@
11
11
  #include <stdlib.h>
12
12
  #include <unistd.h>
13
13
 
14
+ #include "trilogy/allocator.h"
14
15
  #include "trilogy/error.h"
15
16
  #include "trilogy/socket.h"
16
17
 
@@ -21,10 +22,17 @@
21
22
  struct trilogy_sock {
22
23
  trilogy_sock_t base;
23
24
  struct addrinfo *addr;
24
- int fd;
25
25
  SSL *ssl;
26
+ int fd;
27
+ bool freeaddrinfo;
26
28
  };
27
29
 
30
+ void trilogy_sock_set_fd(trilogy_sock_t *_sock, int fd)
31
+ {
32
+ struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
33
+ sock->fd = fd;
34
+ }
35
+
28
36
  static int _cb_raw_fd(trilogy_sock_t *_sock)
29
37
  {
30
38
  struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
@@ -103,30 +111,30 @@ static int _cb_raw_close(trilogy_sock_t *_sock)
103
111
  }
104
112
 
105
113
  if (sock->addr) {
106
- if (sock->base.opts.hostname == NULL && sock->base.opts.path != NULL) {
107
- /* We created these with calloc so must free them instead of calling freeaddrinfo */
108
- free(sock->addr->ai_addr);
109
- free(sock->addr);
110
- } else {
114
+ if (sock->freeaddrinfo) {
111
115
  freeaddrinfo(sock->addr);
112
- }
113
- }
114
-
115
- free(sock->base.opts.hostname);
116
- free(sock->base.opts.path);
117
- free(sock->base.opts.database);
118
- free(sock->base.opts.username);
119
- free(sock->base.opts.password);
120
- free(sock->base.opts.ssl_ca);
121
- free(sock->base.opts.ssl_capath);
122
- free(sock->base.opts.ssl_cert);
123
- free(sock->base.opts.ssl_cipher);
124
- free(sock->base.opts.ssl_crl);
125
- free(sock->base.opts.ssl_crlpath);
126
- free(sock->base.opts.ssl_key);
127
- free(sock->base.opts.tls_ciphersuites);
128
-
129
- free(sock);
116
+ } else {
117
+ /* We created these with xcalloc so must free them instead of calling freeaddrinfo */
118
+ xfree(sock->addr->ai_addr);
119
+ xfree(sock->addr);
120
+ }
121
+ }
122
+
123
+ xfree(sock->base.opts.hostname);
124
+ xfree(sock->base.opts.path);
125
+ xfree(sock->base.opts.database);
126
+ xfree(sock->base.opts.username);
127
+ xfree(sock->base.opts.password);
128
+ xfree(sock->base.opts.ssl_ca);
129
+ xfree(sock->base.opts.ssl_capath);
130
+ xfree(sock->base.opts.ssl_cert);
131
+ xfree(sock->base.opts.ssl_cipher);
132
+ xfree(sock->base.opts.ssl_crl);
133
+ xfree(sock->base.opts.ssl_crlpath);
134
+ xfree(sock->base.opts.ssl_key);
135
+ xfree(sock->base.opts.tls_ciphersuites);
136
+
137
+ xfree(sock);
130
138
  return rc;
131
139
  }
132
140
 
@@ -300,12 +308,12 @@ static char *strdupnullok(const char *str)
300
308
  if (str == NULL) {
301
309
  return NULL;
302
310
  }
303
- return strdup(str);
311
+ return xstrdup(str);
304
312
  }
305
313
 
306
314
  trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts)
307
315
  {
308
- struct trilogy_sock *sock = malloc(sizeof(struct trilogy_sock));
316
+ struct trilogy_sock *sock = xmalloc(sizeof(struct trilogy_sock));
309
317
 
310
318
  sock->base.connect_cb = _cb_raw_connect;
311
319
  sock->base.read_cb = _cb_raw_read;
@@ -322,7 +330,7 @@ trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts)
322
330
  sock->base.opts.username = strdupnullok(opts->username);
323
331
 
324
332
  if (sock->base.opts.password) {
325
- sock->base.opts.password = malloc(opts->password_len);
333
+ sock->base.opts.password = xmalloc(opts->password_len);
326
334
  memcpy(sock->base.opts.password, opts->password, opts->password_len);
327
335
  }
328
336
 
@@ -352,6 +360,7 @@ int trilogy_sock_resolve(trilogy_sock_t *_sock)
352
360
  char port[6];
353
361
  snprintf(port, sizeof(port), "%hu", sock->base.opts.port);
354
362
 
363
+ sock->freeaddrinfo = true;
355
364
  if (getaddrinfo(sock->base.opts.hostname, port, &hint, &sock->addr) != 0) {
356
365
  return TRILOGY_DNS_ERR;
357
366
  }
@@ -362,15 +371,16 @@ int trilogy_sock_resolve(trilogy_sock_t *_sock)
362
371
  goto fail;
363
372
  }
364
373
 
365
- sa = calloc(1, sizeof(struct sockaddr_un));
374
+ sa = xcalloc(1, sizeof(struct sockaddr_un));
366
375
  sa->sun_family = AF_UNIX;
367
376
  strcpy(sa->sun_path, sock->base.opts.path);
368
377
 
369
- sock->addr = calloc(1, sizeof(struct addrinfo));
378
+ sock->addr = xcalloc(1, sizeof(struct addrinfo));
370
379
  sock->addr->ai_family = PF_UNIX;
371
380
  sock->addr->ai_socktype = SOCK_STREAM;
372
381
  sock->addr->ai_addr = (struct sockaddr *)sa;
373
382
  sock->addr->ai_addrlen = sizeof(struct sockaddr_un);
383
+ sock->freeaddrinfo = false;
374
384
  } else {
375
385
  goto fail;
376
386
  }
@@ -1,10 +1,12 @@
1
1
  #ifndef TRILOGY_RUBY_H
2
2
  #define TRILOGY_RUBY_H
3
3
 
4
- #include <stdbool.h>
5
-
4
+ #include <ruby.h>
5
+ #include <trilogy_xallocator.h>
6
6
  #include <trilogy.h>
7
7
 
8
+ #include <stdbool.h>
9
+
8
10
  #define TRILOGY_FLAGS_CAST 1
9
11
  #define TRILOGY_FLAGS_CAST_BOOLEANS 2
10
12
  #define TRILOGY_FLAGS_LOCAL_TIMEZONE 4
@@ -0,0 +1 @@
1
+ #include <ruby.h>
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Trilogy
2
4
  module Encoding
3
5
  RUBY_ENCODINGS = {
data/lib/trilogy/error.rb CHANGED
@@ -54,6 +54,12 @@ class Trilogy
54
54
  include ConnectionError
55
55
  end
56
56
 
57
+ class SynchronizationError < BaseError
58
+ def initialize(message = "This connection is already in use by another thread or fiber")
59
+ super
60
+ end
61
+ end
62
+
57
63
  # Trilogy::ClientError is the base error type for invalid queries or parameters
58
64
  # that shouldn't be retried.
59
65
  class ClientError < BaseError
@@ -94,6 +100,7 @@ class Trilogy
94
100
  1160 => BaseConnectionError, # ER_NET_ERROR_ON_WRITE
95
101
  1161 => BaseConnectionError, # ER_NET_WRITE_INTERRUPTED
96
102
  1927 => BaseConnectionError, # ER_CONNECTION_KILLED
103
+ 4031 => BaseConnectionError, # Disconnected by server
97
104
  }
98
105
  class << self
99
106
  def from_code(message, code)
@@ -2,10 +2,28 @@ class Trilogy
2
2
  class Result
3
3
  attr_reader :fields, :rows, :query_time, :affected_rows, :last_insert_id
4
4
 
5
+ EMPTY_ARRAY = [].freeze
6
+ private_constant :EMPTY_ARRAY
7
+
8
+ def initialize(fields, rows, query_time, in_transaction, affected_rows, last_insert_id)
9
+ @fields = fields || EMPTY_ARRAY
10
+ @rows = rows || EMPTY_ARRAY
11
+ @query_time = query_time
12
+ @in_transaction = in_transaction
13
+ @affected_rows = affected_rows
14
+ @last_insert_id = last_insert_id
15
+ end
16
+
17
+ def in_transaction?
18
+ @in_transaction
19
+ end
20
+
5
21
  def count
6
22
  rows.count
7
23
  end
8
24
 
25
+ alias_method :size, :count
26
+
9
27
  def each_hash
10
28
  return enum_for(:each_hash) unless block_given?
11
29
 
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.9.0"
2
+ VERSION = "2.12.4"
3
3
  end
data/lib/trilogy.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "socket"
3
4
  require "trilogy/version"
4
5
  require "trilogy/error"
5
6
  require "trilogy/result"
@@ -7,6 +8,38 @@ require "trilogy/cext"
7
8
  require "trilogy/encoding"
8
9
 
9
10
  class Trilogy
11
+ IO_TIMEOUT_ERROR =
12
+ if defined?(IO::TimeoutError)
13
+ IO::TimeoutError
14
+ else
15
+ Class.new(StandardError)
16
+ end
17
+ private_constant :IO_TIMEOUT_ERROR
18
+
19
+ module Synchronization
20
+ def initialize(...)
21
+ @mutex = Mutex.new
22
+ super
23
+ end
24
+
25
+ synchronized_methods = Trilogy.public_instance_methods(false) - %i(closed? server_version)
26
+ source = synchronized_methods.flat_map do |method|
27
+ [
28
+ "def #{method}(...)",
29
+ "raise SynchronizationError unless @mutex.try_lock",
30
+ "begin",
31
+ "super",
32
+ "ensure",
33
+ "@mutex.unlock",
34
+ "end",
35
+ "end",
36
+ ]
37
+ end
38
+ class_eval(source.join(";"), __FILE__, __LINE__)
39
+ end
40
+
41
+ prepend(Synchronization)
42
+
10
43
  def initialize(options = {})
11
44
  options[:port] = options[:port].to_i if options[:port]
12
45
  mysql_encoding = options[:encoding] || "utf8mb4"
@@ -15,7 +48,54 @@ class Trilogy
15
48
  @connection_options = options
16
49
  @connected_host = nil
17
50
 
18
- _connect(encoding, charset, options)
51
+ socket = nil
52
+ begin
53
+ if host = options[:host]
54
+ port = options[:port] || 3306
55
+ connect_timeout = options[:connect_timeout] || options[:write_timeout]
56
+
57
+ socket = TCPSocket.new(host, port, connect_timeout: connect_timeout)
58
+
59
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
60
+
61
+ if keepalive_enabled = options[:keepalive_enabled]
62
+ keepalive_idle = options[:keepalive_idle]
63
+ keepalive_interval = options[:keepalive_interval]
64
+ keepalive_count = options[:keepalive_count]
65
+
66
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
67
+
68
+ if keepalive_idle > 0 && defined?(Socket::TCP_KEEPIDLE)
69
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, keepalive_idle)
70
+ end
71
+ if keepalive_interval > 0 && defined?(Socket::TCP_KEEPINTVL)
72
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, keepalive_interval)
73
+ end
74
+ if keepalive_count > 0 && defined?(Socket::TCP_KEEPCNT)
75
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, keepalive_count)
76
+ end
77
+ end
78
+ else
79
+ path = options[:socket] ||= "/tmp/mysql.sock"
80
+ socket = UNIXSocket.new(path)
81
+ end
82
+ rescue Errno::ETIMEDOUT, IO_TIMEOUT_ERROR => e
83
+ raise Trilogy::TimeoutError, e.message
84
+ rescue SocketError => e
85
+ connection_str = host ? "#{host}:#{port}" : path
86
+ raise Trilogy::BaseConnectionError, "unable to connect to \"#{connection_str}\": #{e.message}"
87
+ rescue => e
88
+ if e.respond_to?(:errno)
89
+ raise Trilogy::SyscallError.from_errno(e.errno, e.message)
90
+ else
91
+ raise
92
+ end
93
+ end
94
+
95
+ _connect(socket, encoding, charset, options)
96
+ ensure
97
+ # Socket's fd will be dup'd in C
98
+ socket&.close
19
99
  end
20
100
 
21
101
  def connection_options
data/trilogy.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.require_paths = ["lib"]
24
24
 
25
- s.add_development_dependency "rake-compiler", "~> 1.0"
26
- s.add_development_dependency "minitest", "~> 5.5"
25
+ s.required_ruby_version = ">= 3.0"
26
+
27
+ s.add_dependency "bigdecimal"
27
28
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trilogy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 2.12.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-11 00:00:00.000000000 Z
11
+ date: 2026-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rake-compiler
14
+ name: bigdecimal
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
20
- type: :development
19
+ version: '0'
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
27
- - !ruby/object:Gem::Dependency
28
- name: minitest
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '5.5'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '5.5'
26
+ version: '0'
41
27
  description:
42
28
  email: opensource+trilogy@github.com
43
29
  executables: []
@@ -52,6 +38,7 @@ files:
52
38
  - ext/trilogy-ruby/cext.c
53
39
  - ext/trilogy-ruby/extconf.rb
54
40
  - ext/trilogy-ruby/inc/trilogy.h
41
+ - ext/trilogy-ruby/inc/trilogy/allocator.h
55
42
  - ext/trilogy-ruby/inc/trilogy/blocking.h
56
43
  - ext/trilogy-ruby/inc/trilogy/buffer.h
57
44
  - ext/trilogy-ruby/inc/trilogy/builder.h
@@ -77,6 +64,7 @@ files:
77
64
  - ext/trilogy-ruby/src/vendor/curl_hostcheck.c
78
65
  - ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c
79
66
  - ext/trilogy-ruby/trilogy-ruby.h
67
+ - ext/trilogy-ruby/trilogy_xallocator.h
80
68
  - lib/trilogy.rb
81
69
  - lib/trilogy/encoding.rb
82
70
  - lib/trilogy/error.rb
@@ -95,14 +83,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
83
  requirements:
96
84
  - - ">="
97
85
  - !ruby/object:Gem::Version
98
- version: '0'
86
+ version: '3.0'
99
87
  required_rubygems_version: !ruby/object:Gem::Requirement
100
88
  requirements:
101
89
  - - ">="
102
90
  - !ruby/object:Gem::Version
103
91
  version: '0'
104
92
  requirements: []
105
- rubygems_version: 3.5.11
93
+ rubygems_version: 3.5.22
106
94
  signing_key:
107
95
  specification_version: 4
108
96
  summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy