trilogy 2.11.1 → 2.12.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: 5072d4f5e6bf162eb99d8a465ffb746f0d4cc9673c18ae5bf8bf3972ea9bf061
4
- data.tar.gz: 44b42a75a259456bad0f5ef3120f355ef2685cfb18d4440ea4656b1f48a55ced
3
+ metadata.gz: 9159fca1baf9c5d9f2a23ef0850ca007ba4f0fc2d1f70ed678bde01dedb0e9d8
4
+ data.tar.gz: f8239356e08a8fe724f0ae3c2fc2d628eed88a9a15a6ab1ef5b7b696bec6a8b6
5
5
  SHA512:
6
- metadata.gz: 9dc778f0bfeefcbfc5233f61e4d43f549ceb47f95e407bdea29c1aaafe0b19945ad7e2a34d1a28eb73accae4fa447f9eb07a546cde0d1fe1015e9cdc6cf32b17
7
- data.tar.gz: a03e0efd31525f14669ca4a0fdf5b77117ed7da34d6dacccea748c75468e0ec0f927eca797c966cfd7da5ebdd5a10dc458aed8af965e9e9b0a9a1920c86281a0
6
+ metadata.gz: cec828b0440de7874f7e2df56738e8e99b024649a2d40c256ea955b652ef9375a345bc0021c4ce083c7f0fb4ce56b0cfd84524ce4d055c0d934ae702399b4f79
7
+ data.tar.gz: a92b013118f91b5c7ff34b9cf3d0cc4eca2bf64199ced3191fc5f17ea9f450e2c2a860d20ed82acc659a2b057e7bf366a31525f8d8fee1bede64b794c2f1a66e
@@ -1,13 +1,13 @@
1
1
  #include <ruby.h>
2
2
  #include <ruby/encoding.h>
3
-
4
- #include <trilogy.h>
3
+ #include <time.h>
4
+ #include <limits.h>
5
5
 
6
6
  #include "trilogy-ruby.h"
7
7
 
8
8
  #define CAST_STACK_SIZE 64
9
9
 
10
- static ID id_BigDecimal, id_Integer, id_new, id_local, id_localtime, id_utc;
10
+ static ID id_BigDecimal, id_Integer, id_new;
11
11
 
12
12
  static const char *ruby_encoding_name_map[] = {
13
13
  [TRILOGY_ENCODING_ARMSCII8] = NULL,
@@ -54,17 +54,20 @@ static const char *ruby_encoding_name_map[] = {
54
54
  [TRILOGY_ENCODING_MAX] = NULL,
55
55
  };
56
56
 
57
- static int encoding_for_charset(TRILOGY_CHARSET_t charset)
57
+ static rb_encoding * encoding_for_charset(TRILOGY_CHARSET_t charset)
58
58
  {
59
- static int map[TRILOGY_CHARSET_MAX];
59
+ static rb_encoding * map[TRILOGY_CHARSET_MAX];
60
60
 
61
- if (map[charset]) {
61
+ if (RB_LIKELY(map[charset])) {
62
62
  return map[charset];
63
63
  }
64
64
 
65
65
  const char *encoding_name = ruby_encoding_name_map[trilogy_encoding_from_charset(charset)];
66
-
67
- return map[charset] = (encoding_name ? rb_enc_find_index(encoding_name) : -1);
66
+ map[charset] = rb_enc_find(encoding_name);
67
+ if (!map[charset]) {
68
+ map[charset] = rb_ascii8bit_encoding();
69
+ }
70
+ return map[charset];
68
71
  }
69
72
 
70
73
  static void cstr_from_value(char *buf, const trilogy_value_t *value, const char *errmsg)
@@ -78,6 +81,92 @@ static void cstr_from_value(char *buf, const trilogy_value_t *value, const char
78
81
  buf[value->data_len] = 0;
79
82
  }
80
83
 
84
+ // For UTC: uses the Hinnant civil_to_days algorithm (C++20 std::chrono
85
+ // foundation, handles the full MySQL 1000-9999 year range without timegm).
86
+ // For local: uses mktime (standard C) which consults the system timezone.
87
+ // http://howardhinnant.github.io/date_algorithms.html
88
+ static time_t civil_to_epoch_utc(int year, int month, int day, int hour, int min, int sec)
89
+ {
90
+ year -= (month <= 2);
91
+ int era = (year >= 0 ? year : year - 399) / 400;
92
+ unsigned yoe = (unsigned)(year - era * 400);
93
+ unsigned doy = (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + (unsigned)day - 1;
94
+ unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
95
+ long days = (long)era * 146097 + (long)doe - 719468;
96
+ return (time_t)(days * 86400 + hour * 3600 + min * 60 + sec);
97
+ }
98
+
99
+ static VALUE trilogy_make_time(int year, int month, int day, int hour, int min, int sec,
100
+ int usec, int local)
101
+ {
102
+ struct timespec ts;
103
+ if (local) {
104
+ struct tm tm = {
105
+ .tm_year = year - 1900,
106
+ .tm_mon = month - 1,
107
+ .tm_mday = day,
108
+ .tm_hour = hour,
109
+ .tm_min = min,
110
+ .tm_sec = sec,
111
+ .tm_isdst = -1,
112
+ };
113
+ ts.tv_sec = mktime(&tm);
114
+ } else {
115
+ ts.tv_sec = civil_to_epoch_utc(year, month, day, hour, min, sec);
116
+ }
117
+ ts.tv_nsec = (long)usec * 1000;
118
+ return rb_time_timespec_new(&ts, local ? INT_MAX : INT_MAX - 1);
119
+ }
120
+
121
+ // Byte-arithmetic datetime parsing helpers (inspired by Go's go-sql-driver/mysql)
122
+ // These avoid sscanf overhead by parsing ASCII digits directly.
123
+
124
+ static inline int byte_to_digit(const char b)
125
+ {
126
+ if (b < '0' || b > '9')
127
+ return -1;
128
+ return b - '0';
129
+ }
130
+
131
+ static inline int parse_2digits(const char *p)
132
+ {
133
+ int d1 = byte_to_digit(p[0]);
134
+ int d2 = byte_to_digit(p[1]);
135
+ if (d1 < 0 || d2 < 0)
136
+ return -1;
137
+ return d1 * 10 + d2;
138
+ }
139
+
140
+ static inline int parse_4digits(const char *p)
141
+ {
142
+ int d0 = byte_to_digit(p[0]);
143
+ int d1 = byte_to_digit(p[1]);
144
+ int d2 = byte_to_digit(p[2]);
145
+ int d3 = byte_to_digit(p[3]);
146
+ if (d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0)
147
+ return -1;
148
+ return d0 * 1000 + d1 * 100 + d2 * 10 + d3;
149
+ }
150
+
151
+ // Parse 1-6 fractional digits into microseconds (6-digit value).
152
+ // "1" => 100000
153
+ // "12" => 120000
154
+ // "123" => 123000
155
+ // "123456" => 123456
156
+ static inline int parse_microseconds(const char *p, size_t len)
157
+ {
158
+ int usec = 0;
159
+ int multiplier = 100000;
160
+ for (size_t i = 0; i < len && i < 6; i++) {
161
+ int d = byte_to_digit(p[i]);
162
+ if (d < 0)
163
+ return -1;
164
+ usec += d * multiplier;
165
+ multiplier /= 10;
166
+ }
167
+ return usec;
168
+ }
169
+
81
170
  static unsigned long long ull_from_buf(const char *digits, size_t len)
82
171
  {
83
172
  if (!len)
@@ -166,16 +255,54 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
166
255
  }
167
256
  case TRILOGY_TYPE_TIMESTAMP:
168
257
  case TRILOGY_TYPE_DATETIME: {
169
- int year, month, day, hour, min, sec;
170
- char msec_char[7] = {0};
258
+ const char *p = (const char *)value->data;
259
+ size_t len = value->data_len;
260
+ int year, month, day, hour = 0, min = 0, sec = 0, usec = 0;
261
+
262
+ // Length-based dispatch like Go's parseDateTime:
263
+ // 10 = "YYYY-MM-DD"
264
+ // 19 = "YYYY-MM-DD HH:MM:SS"
265
+ // 21-26 = "YYYY-MM-DD HH:MM:SS.F" through "YYYY-MM-DD HH:MM:SS.FFFFFF"
266
+ if (len < 10)
267
+ return Qnil;
171
268
 
172
- char cstr[CAST_STACK_SIZE];
173
- cstr_from_value(cstr, value, "Invalid date: %.*s");
269
+ year = parse_4digits(p);
270
+ if (year < 0 || p[4] != '-')
271
+ return Qnil;
272
+
273
+ month = parse_2digits(p + 5);
274
+ if (month < 0 || p[7] != '-')
275
+ return Qnil;
276
+
277
+ day = parse_2digits(p + 8);
278
+ if (day < 0)
279
+ return Qnil;
280
+
281
+ if (len >= 19) {
282
+ if (p[10] != ' ')
283
+ return Qnil;
174
284
 
175
- int tokens = sscanf(cstr, "%4u-%2u-%2u %2u:%2u:%2u.%6s", &year, &month, &day, &hour, &min, &sec, msec_char);
285
+ hour = parse_2digits(p + 11);
286
+ if (hour < 0 || p[13] != ':')
287
+ return Qnil;
176
288
 
177
- // msec might not be present, so check for 6 tokens rather than 7
178
- if (tokens < 6) {
289
+ min = parse_2digits(p + 14);
290
+ if (min < 0 || p[16] != ':')
291
+ return Qnil;
292
+
293
+ sec = parse_2digits(p + 17);
294
+ if (sec < 0)
295
+ return Qnil;
296
+
297
+ if (len > 19) {
298
+ if (p[19] != '.' || len < 21 || len > 26)
299
+ return Qnil;
300
+
301
+ usec = parse_microseconds(p + 20, len - 20);
302
+ if (usec < 0)
303
+ return Qnil;
304
+ }
305
+ } else if (len != 10) {
179
306
  return Qnil;
180
307
  }
181
308
 
@@ -187,28 +314,29 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
187
314
  rb_raise(Trilogy_CastError, "Invalid date: %.*s", (int)value->data_len, (char *)value->data);
188
315
  }
189
316
 
190
- // pad out msec_char with zeroes at the end as it could be at any
191
- // level of precision
192
- for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
193
- msec_char[i] = '0';
194
- }
195
-
196
- return rb_funcall(rb_cTime, options->database_local_time ? id_local : id_utc, 7, INT2NUM(year),
197
- INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec),
198
- INT2NUM(atoi(msec_char)));
317
+ return trilogy_make_time(year, month, day, hour, min, sec, usec,
318
+ options->database_local_time);
199
319
  }
200
320
  case TRILOGY_TYPE_DATE: {
201
- int year, month, day;
321
+ const char *p = (const char *)value->data;
322
+ size_t len = value->data_len;
202
323
 
203
- char cstr[CAST_STACK_SIZE];
204
- cstr_from_value(cstr, value, "Invalid date: %.*s");
324
+ if (len != 10)
325
+ return Qnil;
205
326
 
206
- int tokens = sscanf(cstr, "%4u-%2u-%2u", &year, &month, &day);
207
- VALUE Date = rb_const_get(rb_cObject, rb_intern("Date"));
327
+ int year = parse_4digits(p);
328
+ if (year < 0 || p[4] != '-')
329
+ return Qnil;
208
330
 
209
- if (tokens < 3) {
331
+ int month = parse_2digits(p + 5);
332
+ if (month < 0 || p[7] != '-')
210
333
  return Qnil;
211
- }
334
+
335
+ int day = parse_2digits(p + 8);
336
+ if (day < 0)
337
+ return Qnil;
338
+
339
+ VALUE Date = rb_const_get(rb_cObject, rb_intern("Date"));
212
340
 
213
341
  if (year == 0 && month == 0 && day == 0) {
214
342
  return Qnil;
@@ -221,26 +349,37 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
221
349
  return rb_funcall(Date, id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
222
350
  }
223
351
  case TRILOGY_TYPE_TIME: {
224
- int hour, min, sec;
225
- char msec_char[7] = {0};
352
+ const char *p = (const char *)value->data;
353
+ size_t len = value->data_len;
226
354
 
227
- char cstr[CAST_STACK_SIZE];
228
- cstr_from_value(cstr, value, "Invalid time: %.*s");
355
+ // Expected: "HH:MM:SS" (8) or "HH:MM:SS.F" through "HH:MM:SS.FFFFFF" (10-15)
356
+ if (len < 8)
357
+ return Qnil;
229
358
 
230
- int tokens = sscanf(cstr, "%2u:%2u:%2u.%6s", &hour, &min, &sec, msec_char);
359
+ int hour = parse_2digits(p);
360
+ if (hour < 0 || p[2] != ':')
361
+ return Qnil;
231
362
 
232
- if (tokens < 3) {
363
+ int min = parse_2digits(p + 3);
364
+ if (min < 0 || p[5] != ':')
233
365
  return Qnil;
234
- }
235
366
 
236
- // pad out msec_char with zeroes at the end as it could be at any
237
- // level of precision
238
- for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
239
- msec_char[i] = '0';
367
+ int sec = parse_2digits(p + 6);
368
+ if (sec < 0)
369
+ return Qnil;
370
+
371
+ int usec = 0;
372
+ if (len > 8) {
373
+ if (p[8] != '.' || len < 10 || len > 15)
374
+ return Qnil;
375
+
376
+ usec = parse_microseconds(p + 9, len - 9);
377
+ if (usec < 0)
378
+ return Qnil;
240
379
  }
241
380
 
242
- return rb_funcall(rb_cTime, options->database_local_time ? id_local : id_utc, 7, INT2NUM(2000), INT2NUM(1),
243
- INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(atoi(msec_char)));
381
+ return trilogy_make_time(2000, 1, 1, hour, min, sec, usec,
382
+ options->database_local_time);
244
383
  }
245
384
  default:
246
385
  break;
@@ -248,15 +387,7 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
248
387
  }
249
388
 
250
389
  // for all other types, just return a string
251
-
252
- VALUE str = rb_str_new(value->data, value->data_len);
253
-
254
- int encoding_index = encoding_for_charset(column->charset);
255
- if (encoding_index != -1) {
256
- rb_enc_associate_index(str, encoding_index);
257
- }
258
-
259
- return str;
390
+ return rb_enc_str_new(value->data, value->data_len, encoding_for_charset(column->charset));
260
391
  }
261
392
 
262
393
  void rb_trilogy_cast_init(void)
@@ -267,7 +398,4 @@ void rb_trilogy_cast_init(void)
267
398
  id_BigDecimal = rb_intern("BigDecimal");
268
399
  id_Integer = rb_intern("Integer");
269
400
  id_new = rb_intern("new");
270
- id_local = rb_intern("local");
271
- id_localtime = rb_intern("localtime");
272
- id_utc = rb_intern("utc");
273
401
  }
@@ -11,8 +11,6 @@
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
 
18
16
  typedef struct _buffer_pool_entry_struct {
@@ -41,9 +39,7 @@ static void buffer_pool_free(void *data)
41
39
  buffer_pool *pool = (buffer_pool *)data;
42
40
  if (pool->capa) {
43
41
  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);
42
+ xfree(pool->entries[index].buff);
47
43
  }
48
44
  xfree(pool->entries);
49
45
  }
@@ -124,7 +120,7 @@ static void buffer_checkout(trilogy_buffer_t *buffer, size_t initial_capacity)
124
120
  buffer->buff = pool->entries[pool->len].buff;
125
121
  buffer->cap = pool->entries[pool->len].cap;
126
122
  } else {
127
- buffer->buff = malloc(initial_capacity);
123
+ buffer->buff = xmalloc(initial_capacity);
128
124
  buffer->cap = initial_capacity;
129
125
  }
130
126
  }
@@ -134,7 +130,7 @@ static bool buffer_checkin(trilogy_buffer_t *buffer)
134
130
  buffer_pool * pool = get_buffer_pool();
135
131
 
136
132
  if (pool->len >= BUFFER_POOL_MAX_SIZE) {
137
- free(buffer->buff);
133
+ xfree(buffer->buff);
138
134
  buffer->buff = NULL;
139
135
  buffer->cap = 0;
140
136
  return false;
@@ -165,10 +161,10 @@ static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLErro
165
161
 
166
162
  static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
167
163
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
168
- id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
169
- 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,
170
166
  id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result,
171
- id_from_code, id_from_errno, id_connection_options, id_max_allowed_packet;
167
+ id_from_code, id_from_errno, id_max_allowed_packet;
172
168
 
173
169
  struct trilogy_ctx {
174
170
  trilogy_conn_t conn;
@@ -938,102 +934,107 @@ static VALUE read_query_response(VALUE vargs)
938
934
  double query_time = finish.tv_sec - start.tv_sec;
939
935
  query_time += (double)(finish.tv_nsec - start.tv_nsec) / 1000000000.0;
940
936
 
941
- VALUE result = rb_obj_alloc(Trilogy_Result);
942
-
943
- VALUE column_names = rb_ary_new2(column_count);
944
- rb_ivar_set(result, id_ivar_fields, column_names);
937
+ VALUE column_names = 0;
945
938
 
946
939
  VALUE rows = rb_ary_new();
947
- rb_ivar_set(result, id_ivar_rows, rows);
948
-
949
- rb_ivar_set(result, id_ivar_query_time, DBL2NUM(query_time));
950
940
 
941
+ VALUE last_insert_id = Qnil, affected_rows = Qnil;
951
942
  if (rc == TRILOGY_OK) {
952
- rb_ivar_set(result, id_ivar_last_insert_id, ULL2NUM(ctx->conn.last_insert_id));
953
-
954
- rb_ivar_set(result, id_ivar_affected_rows, ULL2NUM(ctx->conn.affected_rows));
955
-
956
- return result;
957
- } else {
958
- rb_ivar_set(result, id_ivar_last_insert_id, Qnil);
959
- 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);
960
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);
961
951
 
962
- VALUE rb_column_info;
963
- 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;
964
954
 
965
- for (uint64_t i = 0; i < column_count; i++) {
966
- trilogy_column_t column;
955
+ while (1) {
956
+ rc = trilogy_read_column(&ctx->conn, &column);
967
957
 
968
- while (1) {
969
- rc = trilogy_read_column(&ctx->conn, &column);
958
+ if (rc == TRILOGY_OK) {
959
+ break;
960
+ }
970
961
 
971
- if (rc == TRILOGY_OK) {
972
- break;
973
- }
962
+ if (rc != TRILOGY_AGAIN) {
963
+ return read_query_error(args, rc, "trilogy_read_column");
964
+ }
974
965
 
975
- if (rc != TRILOGY_AGAIN) {
976
- 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
+ }
977
970
  }
978
971
 
979
- rc = trilogy_sock_wait_read(ctx->conn.socket);
980
- if (rc != TRILOGY_OK) {
981
- return read_query_error(args, rc, "trilogy_read_column");
982
- }
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;
983
984
  }
984
985
 
985
- #ifdef HAVE_RB_ENC_INTERNED_STR
986
- VALUE column_name = rb_enc_interned_str(column.name, column.name_len, ctx->encoding);
987
- #else
988
- VALUE column_name = rb_enc_str_new(column.name, column.name_len, ctx->encoding);
989
- OBJ_FREEZE(column_name);
990
- #endif
986
+ column_names = rb_ary_new_from_values(column_count, row_ruby_values);
991
987
 
992
- 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);
993
990
 
994
- column_info[i].type = column.type;
995
- column_info[i].flags = column.flags;
996
- column_info[i].len = column.len;
997
- column_info[i].charset = column.charset;
998
- column_info[i].decimals = column.decimals;
999
- }
991
+ while (1) {
992
+ int rc = trilogy_read_row(&ctx->conn, row_trilogy_values);
1000
993
 
1001
- VALUE rb_row_values;
1002
- 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
+ }
1003
1001
 
1004
- while (1) {
1005
- int rc = trilogy_read_row(&ctx->conn, row_values);
1002
+ if (rc == TRILOGY_EOF) {
1003
+ break;
1004
+ }
1006
1005
 
1007
- if (rc == TRILOGY_AGAIN) {
1008
- rc = trilogy_sock_wait_read(ctx->conn.socket);
1009
1006
  if (rc != TRILOGY_OK) {
1010
1007
  return read_query_error(args, rc, "trilogy_read_row");
1011
1008
  }
1012
- continue;
1013
- }
1014
-
1015
- if (rc == TRILOGY_EOF) {
1016
- break;
1017
- }
1018
1009
 
1019
- if (rc != TRILOGY_OK) {
1020
- return read_query_error(args, rc, "trilogy_read_row");
1021
- }
1022
-
1023
- if (args->cast_options->flatten_rows) {
1024
1010
  for (uint64_t i = 0; i < column_count; i++) {
1025
- 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);
1026
1012
  }
1027
- } else {
1028
- VALUE row = rb_ary_new2(column_count);
1029
- for (uint64_t i = 0; i < column_count; i++) {
1030
- 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));
1031
1018
  }
1032
- rb_ary_push(rows, row);
1033
1019
  }
1034
- }
1035
1020
 
1036
- 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
+ );
1037
1038
  }
1038
1039
 
1039
1040
  static VALUE execute_read_query_response(struct trilogy_ctx *ctx)
@@ -1466,12 +1467,6 @@ RUBY_FUNC_EXPORTED void Init_cext(void)
1466
1467
  id_multi_result = rb_intern("multi_result");
1467
1468
  id_from_code = rb_intern("from_code");
1468
1469
  id_from_errno = rb_intern("from_errno");
1469
- id_ivar_affected_rows = rb_intern("@affected_rows");
1470
- id_ivar_fields = rb_intern("@fields");
1471
- id_ivar_last_insert_id = rb_intern("@last_insert_id");
1472
- id_ivar_rows = rb_intern("@rows");
1473
- id_ivar_query_time = rb_intern("@query_time");
1474
- id_connection_options = rb_intern("@connection_options");
1475
1470
 
1476
1471
  rb_trilogy_cast_init();
1477
1472
 
@@ -10,7 +10,7 @@ File.binwrite("trilogy.c",
10
10
  }.join)
11
11
 
12
12
  $objs = %w[trilogy.o cast.o cext.o]
13
- append_cflags(["-I #{__dir__}/inc", "-std=gnu99", "-fvisibility=hidden"])
13
+ append_cflags(["-I #{__dir__}/inc", "-std=gnu99", "-fvisibility=hidden", "-DTRILOGY_XALLOCATOR", "-g"])
14
14
 
15
15
  dir_config("openssl").any? || pkg_config("openssl")
16
16
 
@@ -0,0 +1,61 @@
1
+ #ifndef TRILOGY_INTERNAL_ALLOCATOR_H
2
+ #define TRILOGY_INTERNAL_ALLOCATOR_H
3
+
4
+ /* If you build Trilogy with a custom allocator, configure it with
5
+ * "-D TRILOGY_XALLOCATOR" to use your own allocator that defines xmalloc,
6
+ * xrealloc, xcalloc, and xfree.
7
+ *
8
+ * For example, your `trilogy_xallocator.h` file could look like this:
9
+ *
10
+ * ```
11
+ * #ifndef TRILOGY_XALLOCATOR_H
12
+ * #define TRILOGY_XALLOCATOR_H
13
+ * #define xmalloc my_malloc
14
+ * #define xrealloc my_realloc
15
+ * #define xcalloc my_calloc
16
+ * #define xfree my_free
17
+ * #endif
18
+ * ```
19
+ */
20
+ #ifdef TRILOGY_XALLOCATOR
21
+ #include "trilogy_xallocator.h"
22
+ #else
23
+ #ifndef xmalloc
24
+ /* The malloc function that should be used. This can be overridden with
25
+ * the TRILOGY_XALLOCATOR define. */
26
+ #define xmalloc malloc
27
+ #endif
28
+
29
+ #ifndef xrealloc
30
+ /* The realloc function that should be used. This can be overridden with
31
+ * the TRILOGY_XALLOCATOR define. */
32
+ #define xrealloc realloc
33
+ #endif
34
+
35
+ #ifndef xcalloc
36
+ /* The calloc function that should be used. This can be overridden with
37
+ * the TRILOGY_XALLOCATOR define. */
38
+ #define xcalloc calloc
39
+ #endif
40
+
41
+ #ifndef xfree
42
+ /* The free function that should be used. This can be overridden with
43
+ * the TRILOGY_XALLOCATOR define. */
44
+ #define xfree free
45
+ #endif
46
+ #endif
47
+
48
+ #include <string.h>
49
+ static inline char *
50
+ xstrdup(const char *str)
51
+ {
52
+ char *tmp;
53
+ size_t len = strlen(str) + 1;
54
+
55
+ tmp = xmalloc(len);
56
+ memcpy(tmp, str, len);
57
+
58
+ return tmp;
59
+ }
60
+
61
+ #endif
@@ -1,6 +1,7 @@
1
1
  #ifndef TRILOGY_H
2
2
  #define TRILOGY_H
3
3
 
4
+ #include "trilogy/allocator.h"
4
5
  #include "trilogy/blocking.h"
5
6
  #include "trilogy/client.h"
6
7
  #include "trilogy/error.h"
@@ -2,6 +2,7 @@
2
2
  #include <stdlib.h>
3
3
  #include <string.h>
4
4
 
5
+ #include "trilogy/allocator.h"
5
6
  #include "trilogy/buffer.h"
6
7
  #include "trilogy/error.h"
7
8
 
@@ -9,7 +10,7 @@ int trilogy_buffer_init(trilogy_buffer_t *buffer, size_t initial_capacity)
9
10
  {
10
11
  buffer->len = 0;
11
12
  buffer->cap = initial_capacity;
12
- buffer->buff = malloc(initial_capacity);
13
+ buffer->buff = xmalloc(initial_capacity);
13
14
 
14
15
  if (buffer->buff == NULL) {
15
16
  return TRILOGY_SYSERR;
@@ -37,7 +38,7 @@ int trilogy_buffer_expand(trilogy_buffer_t *buffer, size_t needed)
37
38
  new_cap *= EXPAND_MULTIPLIER;
38
39
  }
39
40
 
40
- uint8_t *new_buff = realloc(buffer->buff, new_cap);
41
+ uint8_t *new_buff = xrealloc(buffer->buff, new_cap);
41
42
  if (new_buff == NULL)
42
43
  return TRILOGY_SYSERR;
43
44
 
@@ -77,7 +78,7 @@ int trilogy_buffer_write(trilogy_buffer_t *buffer, const uint8_t *ptr, size_t le
77
78
  void trilogy_buffer_free(trilogy_buffer_t *buffer)
78
79
  {
79
80
  if (buffer->buff) {
80
- free(buffer->buff);
81
+ xfree(buffer->buff);
81
82
  buffer->buff = NULL;
82
83
  buffer->len = buffer->cap = 0;
83
84
  }
@@ -7,6 +7,7 @@
7
7
  #include <stdlib.h>
8
8
  #include <string.h>
9
9
 
10
+ #include "trilogy/allocator.h"
10
11
  #include "trilogy/client.h"
11
12
  #include "trilogy/error.h"
12
13
 
@@ -279,6 +280,21 @@ static int read_deprecated_eof_packet(trilogy_conn_t *conn)
279
280
  return TRILOGY_EOF;
280
281
  }
281
282
 
283
+ bool is_eof_packet(trilogy_conn_t *conn)
284
+ {
285
+ // An EOF packet first byte can mark an EOF/OK packet, a deprecated EOF packet, or a huge data packet.
286
+ if (current_packet_type(conn) == TRILOGY_PACKET_EOF) {
287
+ if (conn->capabilities & TRILOGY_CAPABILITIES_DEPRECATE_EOF) {
288
+ // The EOF/OK packet can contain an info message and/or session state info up to max packet length.
289
+ return conn->packet_buffer.len <= TRILOGY_MAX_PACKET_LEN;
290
+ } else {
291
+ // The deprecated EOF packet must be smaller than 9 bytes (one 8-byte length-encoded integer).
292
+ return conn->packet_buffer.len < 9;
293
+ }
294
+ }
295
+ return false;
296
+ }
297
+
282
298
  static int read_eof_packet(trilogy_conn_t *conn)
283
299
  {
284
300
  int rc;
@@ -566,7 +582,7 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
566
582
  return TRILOGY_MEM_ERROR;
567
583
  }
568
584
  size_t plaintext_len = password_len + 1;
569
- uint8_t *plaintext = malloc(plaintext_len);
585
+ uint8_t *plaintext = xmalloc(plaintext_len);
570
586
 
571
587
  if (plaintext == NULL) {
572
588
  return TRILOGY_MEM_ERROR;
@@ -585,7 +601,7 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
585
601
 
586
602
  BIO *bio = BIO_new_mem_buf((void *)key_data, (int)key_data_len);
587
603
  if (bio == NULL) {
588
- free(plaintext);
604
+ xfree(plaintext);
589
605
  return TRILOGY_OPENSSL_ERR;
590
606
  }
591
607
 
@@ -600,7 +616,7 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
600
616
  if (public_key == NULL) {
601
617
  ERR_clear_error();
602
618
  memset(plaintext, 0, plaintext_len);
603
- free(plaintext);
619
+ xfree(plaintext);
604
620
  return TRILOGY_AUTH_PLUGIN_ERROR;
605
621
  }
606
622
 
@@ -609,7 +625,7 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
609
625
  if (key_size <= 0) {
610
626
  EVP_PKEY_free(public_key);
611
627
  memset(plaintext, 0, plaintext_len);
612
- free(plaintext);
628
+ xfree(plaintext);
613
629
  return TRILOGY_AUTH_PLUGIN_ERROR;
614
630
  }
615
631
  ciphertext_len = (size_t)key_size;
@@ -628,11 +644,11 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
628
644
  RSA_free(public_key);
629
645
  #endif
630
646
  memset(plaintext, 0, plaintext_len);
631
- free(plaintext);
647
+ xfree(plaintext);
632
648
  return TRILOGY_AUTH_PLUGIN_ERROR;
633
649
  }
634
650
 
635
- ciphertext = malloc(ciphertext_len);
651
+ ciphertext = xmalloc(ciphertext_len);
636
652
 
637
653
  if (ciphertext == NULL) {
638
654
  #if OPENSSL_VERSION_NUMBER >= 0x30000000L
@@ -641,7 +657,7 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
641
657
  RSA_free(public_key);
642
658
  #endif
643
659
  memset(plaintext, 0, plaintext_len);
644
- free(plaintext);
660
+ xfree(plaintext);
645
661
  return TRILOGY_MEM_ERROR;
646
662
  }
647
663
 
@@ -676,13 +692,13 @@ static int encrypt_password_with_public_key(const uint8_t *scramble, size_t scra
676
692
  #endif
677
693
 
678
694
  memset(plaintext, 0, plaintext_len);
679
- free(plaintext);
695
+ xfree(plaintext);
680
696
 
681
697
  if (rc == TRILOGY_OK) {
682
698
  *encrypted_out = ciphertext;
683
699
  } else {
684
700
  memset(ciphertext, 0, ciphertext_len);
685
- free(ciphertext);
701
+ xfree(ciphertext);
686
702
  }
687
703
 
688
704
  return rc;
@@ -755,7 +771,7 @@ static int handle_fast_auth_fail(trilogy_conn_t *conn, trilogy_handshake_t *hand
755
771
 
756
772
  rc = send_auth_buffer(conn, encrypted, encrypted_len);
757
773
  memset(encrypted, 0, encrypted_len);
758
- free(encrypted);
774
+ xfree(encrypted);
759
775
 
760
776
  if (rc < 0) {
761
777
  return rc;
@@ -982,7 +998,7 @@ int trilogy_read_row(trilogy_conn_t *conn, trilogy_value_t *values_out)
982
998
  return rc;
983
999
  }
984
1000
 
985
- if (current_packet_type(conn) == TRILOGY_PACKET_EOF && conn->packet_buffer.len < 9) {
1001
+ if (is_eof_packet(conn)) {
986
1002
  if ((rc = read_eof_packet(conn)) != TRILOGY_OK) {
987
1003
  return rc;
988
1004
  }
@@ -1017,7 +1033,7 @@ int trilogy_drain_results(trilogy_conn_t *conn)
1017
1033
  return rc;
1018
1034
  }
1019
1035
 
1020
- if (current_packet_type(conn) == TRILOGY_PACKET_EOF && conn->packet_buffer.len < 9) {
1036
+ if (is_eof_packet(conn)) {
1021
1037
  read_eof_packet(conn);
1022
1038
  return TRILOGY_OK;
1023
1039
  }
@@ -1260,7 +1276,7 @@ int trilogy_stmt_read_row(trilogy_conn_t *conn, trilogy_stmt_t *stmt, trilogy_co
1260
1276
  return rc;
1261
1277
  }
1262
1278
 
1263
- if (current_packet_type(conn) == TRILOGY_PACKET_EOF && conn->packet_buffer.len < 9) {
1279
+ if (is_eof_packet(conn)) {
1264
1280
  if ((rc = read_eof_packet(conn)) != TRILOGY_OK) {
1265
1281
  return rc;
1266
1282
  }
@@ -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,8 +22,9 @@
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
 
28
30
  void trilogy_sock_set_fd(trilogy_sock_t *_sock, int fd)
@@ -109,30 +111,30 @@ static int _cb_raw_close(trilogy_sock_t *_sock)
109
111
  }
110
112
 
111
113
  if (sock->addr) {
112
- if (sock->base.opts.hostname == NULL && sock->base.opts.path != NULL) {
113
- /* We created these with calloc so must free them instead of calling freeaddrinfo */
114
- free(sock->addr->ai_addr);
115
- free(sock->addr);
116
- } else {
114
+ if (sock->freeaddrinfo) {
117
115
  freeaddrinfo(sock->addr);
118
- }
119
- }
120
-
121
- free(sock->base.opts.hostname);
122
- free(sock->base.opts.path);
123
- free(sock->base.opts.database);
124
- free(sock->base.opts.username);
125
- free(sock->base.opts.password);
126
- free(sock->base.opts.ssl_ca);
127
- free(sock->base.opts.ssl_capath);
128
- free(sock->base.opts.ssl_cert);
129
- free(sock->base.opts.ssl_cipher);
130
- free(sock->base.opts.ssl_crl);
131
- free(sock->base.opts.ssl_crlpath);
132
- free(sock->base.opts.ssl_key);
133
- free(sock->base.opts.tls_ciphersuites);
134
-
135
- 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);
136
138
  return rc;
137
139
  }
138
140
 
@@ -306,12 +308,12 @@ static char *strdupnullok(const char *str)
306
308
  if (str == NULL) {
307
309
  return NULL;
308
310
  }
309
- return strdup(str);
311
+ return xstrdup(str);
310
312
  }
311
313
 
312
314
  trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts)
313
315
  {
314
- struct trilogy_sock *sock = malloc(sizeof(struct trilogy_sock));
316
+ struct trilogy_sock *sock = xmalloc(sizeof(struct trilogy_sock));
315
317
 
316
318
  sock->base.connect_cb = _cb_raw_connect;
317
319
  sock->base.read_cb = _cb_raw_read;
@@ -328,7 +330,7 @@ trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts)
328
330
  sock->base.opts.username = strdupnullok(opts->username);
329
331
 
330
332
  if (sock->base.opts.password) {
331
- sock->base.opts.password = malloc(opts->password_len);
333
+ sock->base.opts.password = xmalloc(opts->password_len);
332
334
  memcpy(sock->base.opts.password, opts->password, opts->password_len);
333
335
  }
334
336
 
@@ -358,6 +360,7 @@ int trilogy_sock_resolve(trilogy_sock_t *_sock)
358
360
  char port[6];
359
361
  snprintf(port, sizeof(port), "%hu", sock->base.opts.port);
360
362
 
363
+ sock->freeaddrinfo = true;
361
364
  if (getaddrinfo(sock->base.opts.hostname, port, &hint, &sock->addr) != 0) {
362
365
  return TRILOGY_DNS_ERR;
363
366
  }
@@ -368,15 +371,16 @@ int trilogy_sock_resolve(trilogy_sock_t *_sock)
368
371
  goto fail;
369
372
  }
370
373
 
371
- sa = calloc(1, sizeof(struct sockaddr_un));
374
+ sa = xcalloc(1, sizeof(struct sockaddr_un));
372
375
  sa->sun_family = AF_UNIX;
373
376
  strcpy(sa->sun_path, sock->base.opts.path);
374
377
 
375
- sock->addr = calloc(1, sizeof(struct addrinfo));
378
+ sock->addr = xcalloc(1, sizeof(struct addrinfo));
376
379
  sock->addr->ai_family = PF_UNIX;
377
380
  sock->addr->ai_socktype = SOCK_STREAM;
378
381
  sock->addr->ai_addr = (struct sockaddr *)sa;
379
382
  sock->addr->ai_addrlen = sizeof(struct sockaddr_un);
383
+ sock->freeaddrinfo = false;
380
384
  } else {
381
385
  goto fail;
382
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>
@@ -2,6 +2,22 @@ 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
@@ -1,3 +1,3 @@
1
1
  class Trilogy
2
- VERSION = "2.11.1"
2
+ VERSION = "2.12.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trilogy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.1
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Engineering
@@ -36,6 +36,7 @@ files:
36
36
  - ext/trilogy-ruby/cext.c
37
37
  - ext/trilogy-ruby/extconf.rb
38
38
  - ext/trilogy-ruby/inc/trilogy.h
39
+ - ext/trilogy-ruby/inc/trilogy/allocator.h
39
40
  - ext/trilogy-ruby/inc/trilogy/blocking.h
40
41
  - ext/trilogy-ruby/inc/trilogy/buffer.h
41
42
  - ext/trilogy-ruby/inc/trilogy/builder.h
@@ -61,6 +62,7 @@ files:
61
62
  - ext/trilogy-ruby/src/vendor/curl_hostcheck.c
62
63
  - ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c
63
64
  - ext/trilogy-ruby/trilogy-ruby.h
65
+ - ext/trilogy-ruby/trilogy_xallocator.h
64
66
  - lib/trilogy.rb
65
67
  - lib/trilogy/encoding.rb
66
68
  - lib/trilogy/error.rb
@@ -85,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
87
  - !ruby/object:Gem::Version
86
88
  version: '0'
87
89
  requirements: []
88
- rubygems_version: 4.0.3
90
+ rubygems_version: 3.6.9
89
91
  specification_version: 4
90
92
  summary: A friendly MySQL-compatible library for Ruby, binding to libtrilogy
91
93
  test_files: []