trilogy 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f62d4a5617aaf6b532cfa2888b297eba7bfb18def74e98b6a12847d2845b81f
4
- data.tar.gz: 7c74e6d32f1ec845bccbace0b989a269d833d1582ba38ffd78e8e5f35305883d
3
+ metadata.gz: 0730bbbdae7e8eab195c4ad745e223bcd55fe6299f98cb324c88beea15ebdc5e
4
+ data.tar.gz: 7779f268cbfae85bf5575f680ebff9fee6d22b78bb5d3d315ba664969ab9618a
5
5
  SHA512:
6
- metadata.gz: 7ba24a84f4e946c1c7f797c10738fda98bcc9e1fbb0bd19f289fe9cc6cbb9bae252f9cf307c873c8a958c2d2519075d4a677cb681648e6429b18bf9c9a897bb7
7
- data.tar.gz: 4ce6afb50256faf2ec873a71194295c9538a4d2188ecae65018b51f8869a97e4cb68eecef6829f9053ef31a5dca1defd107b46281478e02eb5b10606dae3da99
6
+ metadata.gz: 42c81e496210cfc0538ef1dd9f4f7c34e2ce94de45e5bcfe4c5815839c463a6b3ae78af529b88c63a670b3cd2035a6b931ef76fdfe1ec508f6b7851858b664d2
7
+ data.tar.gz: ad05962a8a47cea68ab66188badf405fe96342587bd86d91faf43dd028dc641949ea58d9a6d25547e820c4700c6cd9548d70a2494f0a215b08bb8a61c9c920c8
data/README.md CHANGED
@@ -34,13 +34,22 @@ if client.ping
34
34
  result.each_hash do |user|
35
35
  p user
36
36
  end
37
+ end
38
+ ```
37
39
 
38
- # Multi-statement
40
+ ### Processing multiple result sets
39
41
 
40
- results = []
41
- results << client.query("SELECT name FROM users WHERE id = 1; SELECT name FROM users WHERE id = 2")
42
- results << client.next_result while client.more_results_exist?
43
- end
42
+ In order to send and receive multiple result sets, pass the `multi_statement` option when connecting.
43
+ `Trilogy#more_results_exist?` will return true if more results exist, false if no more results exist, or raise
44
+ an error if the respective query errored. `Trilogy#next_result` will retrieve the next result set, or return nil
45
+ if no more results exist.
46
+
47
+ ``` ruby
48
+ client = Trilogy.new(host: "127.0.0.1", port: 3306, username: "root", read_timeout: 2, multi_statement: true)
49
+
50
+ results = []
51
+ results << client.query("SELECT name FROM users WHERE id = 1; SELECT name FROM users WHERE id = 2")
52
+ results << client.next_result while client.more_results_exist?
44
53
  ```
45
54
 
46
55
  ## Building
data/Rakefile CHANGED
@@ -8,6 +8,12 @@ Rake::ExtensionTask.new do |ext|
8
8
  ext.lib_dir = "lib/trilogy"
9
9
  end
10
10
 
11
+ # When any of the parent library's files change, we need to re-run extconf
12
+ vendored_c_lib = FileList["ext/trilogy-ruby/src/**/*.c", "ext/trilogy-ruby/inc/**/*.h"]
13
+ if extconf_task = Rake.application.tasks.find { |t| t.name =~ /Makefile/ }
14
+ task extconf_task => vendored_c_lib
15
+ end
16
+
11
17
  Rake::TestTask.new do |t|
12
18
  t.libs << "test"
13
19
  t.test_files = FileList['test/*_test.rb']
@@ -233,10 +233,6 @@ rb_trilogy_cast_value(const trilogy_value_t *value, const struct column_info *co
233
233
  return Qnil;
234
234
  }
235
235
 
236
- if (hour == 0 && min == 0 && sec == 0) {
237
- return Qnil;
238
- }
239
-
240
236
  // pad out msec_char with zeroes at the end as it could be at any
241
237
  // level of precision
242
238
  for (size_t i = strlen(msec_char); i < sizeof(msec_char) - 1; i++) {
@@ -15,8 +15,6 @@
15
15
 
16
16
  #include "trilogy-ruby.h"
17
17
 
18
- #define TRILOGY_RB_TIMEOUT 1
19
-
20
18
  VALUE Trilogy_CastError;
21
19
  static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
22
20
  Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError,
@@ -26,8 +24,8 @@ static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows,
26
24
  id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
27
25
  id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
28
26
  id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
29
- id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement,
30
- id_from_code, id_from_errno, id_connection_options;
27
+ id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result,
28
+ id_from_code, id_from_errno, id_connection_options, id_max_allowed_packet;
31
29
 
32
30
  struct trilogy_ctx {
33
31
  trilogy_conn_t conn;
@@ -96,12 +94,26 @@ static void trilogy_syserr_fail_str(int e, VALUE msg)
96
94
  rb_raise(Trilogy_ConnectionRefusedError, "%" PRIsVALUE, msg);
97
95
  } else if (e == ECONNRESET) {
98
96
  rb_raise(Trilogy_ConnectionResetError, "%" PRIsVALUE, msg);
97
+ } else if (e == EPIPE) {
98
+ // Backwards compatibility: This error class makes no sense, but matches legacy behavior
99
+ rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": TRILOGY_CLOSED_CONNECTION", msg);
99
100
  } else {
100
101
  VALUE exc = rb_funcall(Trilogy_SyscallError, id_from_errno, 2, INT2NUM(e), msg);
101
102
  rb_exc_raise(exc);
102
103
  }
103
104
  }
104
105
 
106
+ static int trilogy_error_recoverable_p(int rc)
107
+ {
108
+ // TRILOGY_OPENSSL_ERR and TRILOGY_SYSERR (which can result from an SSL error) must shut down the socket, as further
109
+ // SSL calls would be invalid.
110
+ // TRILOGY_ERR, which represents an error message sent to us from the server, is recoverable.
111
+ // TRILOGY_MAX_PACKET_EXCEEDED is also recoverable as we do not send data when it occurs.
112
+ // For other exceptions we will also close the socket to prevent further use, as the connection is probably in an
113
+ // invalid state.
114
+ return rc == TRILOGY_ERR || rc == TRILOGY_MAX_PACKET_EXCEEDED;
115
+ }
116
+
105
117
  NORETURN(static void handle_trilogy_error(struct trilogy_ctx *, int, const char *, ...));
106
118
  static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *msg, ...)
107
119
  {
@@ -110,10 +122,22 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
110
122
  VALUE rbmsg = rb_vsprintf(msg, args);
111
123
  va_end(args);
112
124
 
125
+ if (!trilogy_error_recoverable_p(rc)) {
126
+ if (ctx->conn.socket != NULL) {
127
+ // trilogy_sock_shutdown may affect errno
128
+ int errno_was = errno;
129
+ trilogy_sock_shutdown(ctx->conn.socket);
130
+ errno = errno_was;
131
+ }
132
+ }
133
+
113
134
  switch (rc) {
114
135
  case TRILOGY_SYSERR:
115
136
  trilogy_syserr_fail_str(errno, rbmsg);
116
137
 
138
+ case TRILOGY_TIMEOUT:
139
+ rb_raise(Trilogy_TimeoutError, "%" PRIsVALUE, rbmsg);
140
+
117
141
  case TRILOGY_ERR: {
118
142
  VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
119
143
  VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
@@ -127,11 +151,6 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
127
151
  int err_reason = ERR_GET_REASON(ossl_error);
128
152
  trilogy_syserr_fail_str(err_reason, rbmsg);
129
153
  }
130
- // We can't recover from OpenSSL level errors if there's
131
- // an active connection.
132
- if (ctx->conn.socket != NULL) {
133
- trilogy_sock_shutdown(ctx->conn.socket);
134
- }
135
154
  rb_raise(Trilogy_SSLError, "%" PRIsVALUE ": SSL Error: %s", rbmsg, ERR_reason_error_string(ossl_error));
136
155
  }
137
156
 
@@ -169,8 +188,9 @@ static int flush_writes(struct trilogy_ctx *ctx)
169
188
  return rc;
170
189
  }
171
190
 
172
- if (trilogy_sock_wait_write(ctx->conn.socket) < 0) {
173
- rb_raise(Trilogy_TimeoutError, "trilogy_flush_writes");
191
+ rc = trilogy_sock_wait_write(ctx->conn.socket);
192
+ if (rc != TRILOGY_OK) {
193
+ return rc;
174
194
  }
175
195
  }
176
196
  }
@@ -190,6 +210,21 @@ static double timeval_to_double(struct timeval tv)
190
210
  return (double)tv.tv_sec + ((double)tv.tv_usec) / 1000000.0;
191
211
  }
192
212
 
213
+ struct rb_trilogy_wait_args {
214
+ struct timeval *timeout;
215
+ int wait_flag;
216
+ int fd;
217
+ int rc;
218
+ };
219
+
220
+ static VALUE rb_trilogy_wait_protected(VALUE vargs) {
221
+ struct rb_trilogy_wait_args *args = (void *)vargs;
222
+
223
+ args->rc = rb_wait_for_single_fd(args->fd, args->wait_flag, args->timeout);
224
+
225
+ return Qnil;
226
+ }
227
+
193
228
  static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
194
229
  {
195
230
  struct timeval *timeout = NULL;
@@ -219,11 +254,27 @@ static int _cb_ruby_wait(trilogy_sock_t *sock, trilogy_wait_t wait)
219
254
  timeout = NULL;
220
255
  }
221
256
 
222
- int fd = trilogy_sock_fd(sock);
223
- if (rb_wait_for_single_fd(fd, wait_flag, timeout) <= 0)
257
+ struct rb_trilogy_wait_args args;
258
+ args.fd = trilogy_sock_fd(sock);
259
+ args.wait_flag = wait_flag;
260
+ args.timeout = timeout;
261
+ args.rc = 0;
262
+
263
+ int state = 0;
264
+ rb_protect(rb_trilogy_wait_protected, (VALUE)&args, &state);
265
+ if (state) {
266
+ trilogy_sock_shutdown(sock);
267
+ rb_jump_tag(state);
268
+ }
269
+
270
+ // rc here comes from rb_wait_for_single_fd which (similar to poll(3)) returns 0 to indicate that the call timed out
271
+ // or -1 to indicate a system error with errno set.
272
+ if (args.rc < 0)
224
273
  return TRILOGY_SYSERR;
274
+ if (args.rc == 0)
275
+ return TRILOGY_TIMEOUT;
225
276
 
226
- return 0;
277
+ return TRILOGY_OK;
227
278
  }
228
279
 
229
280
  struct nogvl_sock_args {
@@ -275,8 +326,10 @@ escape the GVL on each wait operation without going through call_without_gvl */
275
326
  return rc;
276
327
  }
277
328
 
278
- if (trilogy_sock_wait(ctx->conn.socket, TRILOGY_WAIT_HANDSHAKE) < 0)
279
- return TRILOGY_RB_TIMEOUT;
329
+ rc = trilogy_sock_wait(ctx->conn.socket, TRILOGY_WAIT_HANDSHAKE);
330
+ if (rc != TRILOGY_OK) {
331
+ return rc;
332
+ }
280
333
  }
281
334
  }
282
335
 
@@ -303,8 +356,9 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
303
356
  handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
304
357
  }
305
358
 
306
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
307
- rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
359
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
360
+ if (rc != TRILOGY_OK) {
361
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
308
362
  }
309
363
  }
310
364
  }
@@ -356,8 +410,9 @@ static void authenticate(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake
356
410
  handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
357
411
  }
358
412
 
359
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
360
- rb_raise(Trilogy_TimeoutError, "trilogy_auth_recv");
413
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
414
+ if (rc != TRILOGY_OK) {
415
+ handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
361
416
  }
362
417
  }
363
418
 
@@ -415,6 +470,11 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VA
415
470
  connopt.keepalive_interval = NUM2USHORT(val);
416
471
  }
417
472
 
473
+ if ((val = rb_hash_lookup(opts, ID2SYM(id_max_allowed_packet))) != Qnil) {
474
+ Check_Type(val, T_FIXNUM);
475
+ connopt.max_allowed_packet = NUM2SIZET(val);
476
+ }
477
+
418
478
  if ((val = rb_hash_lookup(opts, ID2SYM(id_host))) != Qnil) {
419
479
  Check_Type(val, T_STRING);
420
480
 
@@ -455,6 +515,10 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VA
455
515
  connopt.flags |= TRILOGY_CAPABILITIES_FOUND_ROWS;
456
516
  }
457
517
 
518
+ if (rb_hash_aref(opts, ID2SYM(id_multi_result)) != Qfalse) {
519
+ connopt.flags |= TRILOGY_CAPABILITIES_MULTI_RESULTS;
520
+ }
521
+
458
522
  if (RTEST(rb_hash_aref(opts, ID2SYM(id_multi_statement)))) {
459
523
  connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS;
460
524
  }
@@ -510,7 +574,7 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VA
510
574
  }
511
575
 
512
576
  int rc = try_connect(ctx, &handshake, &connopt);
513
- if (rc == TRILOGY_RB_TIMEOUT) {
577
+ if (rc == TRILOGY_TIMEOUT) {
514
578
  rb_raise(Trilogy_TimeoutError, "trilogy_connect_recv");
515
579
  }
516
580
  if (rc != TRILOGY_OK) {
@@ -557,8 +621,9 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
557
621
  handle_trilogy_error(ctx, rc, "trilogy_change_db_recv");
558
622
  }
559
623
 
560
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
561
- rb_raise(Trilogy_TimeoutError, "trilogy_change_db_recv");
624
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
625
+ if (rc != TRILOGY_OK) {
626
+ handle_trilogy_error(ctx, rc, "trilogy_change_db_recv");
562
627
  }
563
628
  }
564
629
 
@@ -590,8 +655,9 @@ static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
590
655
  handle_trilogy_error(ctx, rc, "trilogy_set_option_recv");
591
656
  }
592
657
 
593
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
594
- rb_raise(Trilogy_TimeoutError, "trilogy_set_option_recv");
658
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
659
+ if (rc != TRILOGY_OK) {
660
+ handle_trilogy_error(ctx, rc, "trilogy_set_option_recv");
595
661
  }
596
662
  }
597
663
 
@@ -663,8 +729,9 @@ static VALUE read_query_response(VALUE vargs)
663
729
  return read_query_error(args, rc, "trilogy_query_recv");
664
730
  }
665
731
 
666
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
667
- rb_raise(Trilogy_TimeoutError, "trilogy_query_recv");
732
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
733
+ if (rc != TRILOGY_OK) {
734
+ handle_trilogy_error(ctx, rc, "trilogy_query_recv");
668
735
  }
669
736
  }
670
737
 
@@ -712,8 +779,9 @@ static VALUE read_query_response(VALUE vargs)
712
779
  return read_query_error(args, rc, "trilogy_read_column");
713
780
  }
714
781
 
715
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
716
- rb_raise(Trilogy_TimeoutError, "trilogy_read_column");
782
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
783
+ if (rc != TRILOGY_OK) {
784
+ return read_query_error(args, rc, "trilogy_read_column");
717
785
  }
718
786
  }
719
787
 
@@ -740,8 +808,9 @@ static VALUE read_query_response(VALUE vargs)
740
808
  int rc = trilogy_read_row(&ctx->conn, row_values);
741
809
 
742
810
  if (rc == TRILOGY_AGAIN) {
743
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
744
- rb_raise(Trilogy_TimeoutError, "trilogy_read_row");
811
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
812
+ if (rc != TRILOGY_OK) {
813
+ return read_query_error(args, rc, "trilogy_read_row");
745
814
  }
746
815
  continue;
747
816
  }
@@ -867,8 +936,9 @@ static VALUE rb_trilogy_ping(VALUE self)
867
936
  handle_trilogy_error(ctx, rc, "trilogy_ping_recv");
868
937
  }
869
938
 
870
- if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
871
- rb_raise(Trilogy_TimeoutError, "trilogy_ping_recv");
939
+ rc = trilogy_sock_wait_read(ctx->conn.socket);
940
+ if (rc != TRILOGY_OK) {
941
+ handle_trilogy_error(ctx, rc, "trilogy_ping_recv");
872
942
  }
873
943
  }
874
944
 
@@ -927,6 +997,10 @@ static VALUE rb_trilogy_close(VALUE self)
927
997
  }
928
998
  }
929
999
 
1000
+ // We aren't checking or raising errors here (we need close to always close the socket and free the connection), so
1001
+ // we must clear any SSL errors left in the queue from a read/write.
1002
+ ERR_clear_error();
1003
+
930
1004
  trilogy_free(&ctx->conn);
931
1005
 
932
1006
  return Qnil;
@@ -1028,6 +1102,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1028
1102
 
1029
1103
  rb_define_private_method(Trilogy, "_initialize", rb_trilogy_initialize, 3);
1030
1104
  rb_define_method(Trilogy, "change_db", rb_trilogy_change_db, 1);
1105
+ rb_define_alias(Trilogy, "select_db", "change_db");
1031
1106
  rb_define_method(Trilogy, "query", rb_trilogy_query, 1);
1032
1107
  rb_define_method(Trilogy, "ping", rb_trilogy_ping, 0);
1033
1108
  rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
@@ -1110,6 +1185,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1110
1185
  id_connect_timeout = rb_intern("connect_timeout");
1111
1186
  id_read_timeout = rb_intern("read_timeout");
1112
1187
  id_write_timeout = rb_intern("write_timeout");
1188
+ id_max_allowed_packet = rb_intern("max_allowed_packet");
1113
1189
  id_keepalive_enabled = rb_intern("keepalive_enabled");
1114
1190
  id_keepalive_idle = rb_intern("keepalive_idle");
1115
1191
  id_keepalive_count = rb_intern("keepalive_count");
@@ -1127,6 +1203,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
1127
1203
  id_tls_min_version = rb_intern("tls_min_version");
1128
1204
  id_tls_max_version = rb_intern("tls_max_version");
1129
1205
  id_multi_statement = rb_intern("multi_statement");
1206
+ id_multi_result = rb_intern("multi_result");
1130
1207
  id_from_code = rb_intern("from_code");
1131
1208
  id_from_errno = rb_intern("from_errno");
1132
1209
  id_ivar_affected_rows = rb_intern("@affected_rows");
@@ -177,4 +177,122 @@ int trilogy_ping(trilogy_conn_t *conn);
177
177
  */
178
178
  int trilogy_close(trilogy_conn_t *conn);
179
179
 
180
+ /* trilogy_stmt_prepare - Send a prepared statement prepare command to the server.
181
+ *
182
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
183
+ * undefined.
184
+ * stmt - A pointer to the buffer containing the statement to prepare.
185
+ * stmt_len - The length of the data buffer.
186
+ * stmt_out - A pointer to a pre-allocated trilogy_stmt_t.
187
+ *
188
+ * Return values:
189
+ * TRILOGY_OK - The prepare command was successfully sent to the server.
190
+ * TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
191
+ * TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
192
+ * packet.
193
+ * TRILOGY_SYSERR - A system error occurred, check errno.
194
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
195
+ */
196
+ int trilogy_stmt_prepare(trilogy_conn_t *conn, const char *stmt, size_t stmt_len, trilogy_stmt_t *stmt_out);
197
+
198
+ /* trilogy_stmt_execute - Send a prepared statement execute command to the server.
199
+ *
200
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
201
+ * undefined.
202
+ * stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
203
+ * requesting to execute.
204
+ * flags - The flags (TRILOGY_STMT_FLAGS_t) to be used with this execute command packet.
205
+ * binds - Pointer to an array of trilogy_binary_value_t's. The array size should
206
+ * match that of `trilogy_stmt_t.column_count`.
207
+ * column_count_out - Out parameter; A pointer to a pre-allocated uint64_t. Represents the
208
+ * number of columns in the response.
209
+ *
210
+ * Return values:
211
+ * TRILOGY_OK - The execute command was successfully sent to the server.
212
+ * TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
213
+ * TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
214
+ * packet.
215
+ * TRILOGY_SYSERR - A system error occurred, check errno.
216
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
217
+ */
218
+ int trilogy_stmt_execute(trilogy_conn_t *conn, trilogy_stmt_t *stmt, uint8_t flags, trilogy_binary_value_t *binds,
219
+ uint64_t *column_count_out);
220
+
221
+ /* trilogy_stmt_bind_data - Send a prepared statement bind long data command to the server.
222
+ *
223
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
224
+ * undefined.
225
+ * stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement for which
226
+ * to bind the supplied parameter data to.
227
+ * param_num - The parameter index for which the supplied data should be bound to.
228
+ * data - A pointer to the buffer containing the data to be bound.
229
+ * data_len - The length of the data buffer.
230
+ *
231
+ * Return values:
232
+ * TRILOGY_OK - The bind data command was successfully sent to the server.
233
+ * TRILOGY_SYSERR - A system error occurred, check errno.
234
+ */
235
+ int trilogy_stmt_bind_data(trilogy_conn_t *conn, trilogy_stmt_t *stmt, uint16_t param_num, uint8_t *data,
236
+ size_t data_len);
237
+
238
+ /* trilogy_stmt_read_full_row - Read a row from the prepared statement execute response.
239
+ *
240
+ * This should only be called after a sucessful call to trilogy_stmt_execute.
241
+ * You should continue calling this until TRILOGY_EOF is returned. Denoting the end
242
+ * of the result set.
243
+ *
244
+ * conn - A pre-initialized trilogy_conn_t pointer. It can also be connected but
245
+ * a disconnected trilogy_conn_t will also return TRILOGY_OK.
246
+ * stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
247
+ * requesting to execute.
248
+ * columns - The list of columns from the prepared statement.
249
+ * column_count - The number of columns in prepared statement.
250
+ * values_out - Out parameter; A pointer to a pre-allocated array of
251
+ * trilogy_binary_value_t's. There must be enough space to fit all of the
252
+ * values. This can be computed with:
253
+ * `(sizeof(trilogy_binary_value_t) * column_count)`.
254
+ *
255
+ * Return values:
256
+ * TRILOGY_OK - The prepare command response successfully read from
257
+ * the server.
258
+ * TRILOGY_EOF - There are no more rows to read from the result set.
259
+ * TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
260
+ * TRILOGY_PROTOCOL_VIOLATION - Invalid length parsed for a TIME/DATETIME/TIMESTAMP value.
261
+ * TRILOGY_UNKNOWN_TYPE - An unsupported or unknown MySQL type was seen.
262
+ * TRILOGY_SYSERR - A system error occurred, check errno.
263
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
264
+ */
265
+ int trilogy_stmt_read_full_row(trilogy_conn_t *conn, trilogy_stmt_t *stmt, trilogy_column_packet_t *columns,
266
+ trilogy_binary_value_t *values_out);
267
+
268
+ /* trilogy_stmt_reset - Send a prepared statement reset command to the server.
269
+ *
270
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
271
+ * undefined.
272
+ * stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
273
+ * requesting to reset.
274
+ *
275
+ * Return values:
276
+ * TRILOGY_OK - The reset command was successfully sent to the server.
277
+ * TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
278
+ * TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
279
+ * packet.
280
+ * TRILOGY_SYSERR - A system error occurred, check errno.
281
+ * TRILOGY_CLOSED_CONNECTION - The connection is closed.
282
+ */
283
+ int trilogy_stmt_reset(trilogy_conn_t *conn, trilogy_stmt_t *stmt);
284
+
285
+ /* trilogy_stmt_close_send - Send a prepared statement close command to the server.
286
+ *
287
+ * conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
288
+ * undefined.
289
+ * stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
290
+ * requesting to close.
291
+ *
292
+ * Return values:
293
+ * TRILOGY_OK - The close command was successfully sent to the server.
294
+ * TRILOGY_SYSERR - A system error occurred, check errno.
295
+ */
296
+ int trilogy_stmt_close(trilogy_conn_t *conn, trilogy_stmt_t *stmt);
297
+
180
298
  #endif
@@ -16,6 +16,8 @@
16
16
  typedef struct {
17
17
  trilogy_buffer_t *buffer;
18
18
  size_t header_offset;
19
+ size_t packet_length;
20
+ size_t packet_max_length;
19
21
  uint32_t fragment_length;
20
22
  uint8_t seq;
21
23
  } trilogy_builder_t;
@@ -54,6 +56,8 @@ void trilogy_builder_finalize(trilogy_builder_t *builder);
54
56
  * Return values:
55
57
  * TRILOGY_OK - The value was appended to the packet buffer.
56
58
  * TRILOGY_SYSERR - A system error occurred, check errno.
59
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
60
+ * packet size.
57
61
  */
58
62
  int trilogy_builder_write_uint8(trilogy_builder_t *builder, uint8_t val);
59
63
 
@@ -66,6 +70,8 @@ int trilogy_builder_write_uint8(trilogy_builder_t *builder, uint8_t val);
66
70
  * Return values:
67
71
  * TRILOGY_OK - The value was appended to the packet buffer.
68
72
  * TRILOGY_SYSERR - A system error occurred, check errno.
73
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
74
+ * packet size.
69
75
  */
70
76
  int trilogy_builder_write_uint16(trilogy_builder_t *builder, uint16_t val);
71
77
 
@@ -78,6 +84,8 @@ int trilogy_builder_write_uint16(trilogy_builder_t *builder, uint16_t val);
78
84
  * Return values:
79
85
  * TRILOGY_OK - The value was appended to the packet buffer.
80
86
  * TRILOGY_SYSERR - A system error occurred, check errno.
87
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
88
+ * packet size.
81
89
  */
82
90
  int trilogy_builder_write_uint24(trilogy_builder_t *builder, uint32_t val);
83
91
 
@@ -90,6 +98,8 @@ int trilogy_builder_write_uint24(trilogy_builder_t *builder, uint32_t val);
90
98
  * Return values:
91
99
  * TRILOGY_OK - The value was appended to the packet buffer.
92
100
  * TRILOGY_SYSERR - A system error occurred, check errno.
101
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
102
+ * packet size.
93
103
  */
94
104
  int trilogy_builder_write_uint32(trilogy_builder_t *builder, uint32_t val);
95
105
 
@@ -102,9 +112,37 @@ int trilogy_builder_write_uint32(trilogy_builder_t *builder, uint32_t val);
102
112
  * Return values:
103
113
  * TRILOGY_OK - The value was appended to the packet buffer.
104
114
  * TRILOGY_SYSERR - A system error occurred, check errno.
115
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
116
+ * packet size.
105
117
  */
106
118
  int trilogy_builder_write_uint64(trilogy_builder_t *builder, uint64_t val);
107
119
 
120
+ /* trilogy_builder_write_float - Append a float to the packet buffer.
121
+ *
122
+ * builder - A pre-initialized trilogy_builder_t pointer
123
+ * val - The value to append to the buffer
124
+ *
125
+ * Return values:
126
+ * TRILOGY_OK - The value was appended to the packet buffer.
127
+ * TRILOGY_SYSERR - A system error occurred, check errno.
128
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
129
+ * packet size.
130
+ */
131
+ int trilogy_builder_write_float(trilogy_builder_t *builder, float val);
132
+
133
+ /* trilogy_builder_write_double - Append a double to the packet buffer.
134
+ *
135
+ * builder - A pre-initialized trilogy_builder_t pointer
136
+ * val - The value to append to the buffer
137
+ *
138
+ * Return values:
139
+ * TRILOGY_OK - The value was appended to the packet buffer.
140
+ * TRILOGY_SYSERR - A system error occurred, check errno.
141
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
142
+ * packet size.
143
+ */
144
+ int trilogy_builder_write_double(trilogy_builder_t *builder, double val);
145
+
108
146
  /* trilogy_builder_write_lenenc - Append a length-encoded integer to the packet
109
147
  * buffer.
110
148
  *
@@ -117,6 +155,8 @@ int trilogy_builder_write_uint64(trilogy_builder_t *builder, uint64_t val);
117
155
  * Return values:
118
156
  * TRILOGY_OK - The value was appended to the packet buffer.
119
157
  * TRILOGY_SYSERR - A system error occurred, check errno.
158
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
159
+ * packet size.
120
160
  */
121
161
  int trilogy_builder_write_lenenc(trilogy_builder_t *builder, uint64_t val);
122
162
 
@@ -130,6 +170,8 @@ int trilogy_builder_write_lenenc(trilogy_builder_t *builder, uint64_t val);
130
170
  * Return values:
131
171
  * TRILOGY_OK - The value was appended to the packet buffer.
132
172
  * TRILOGY_SYSERR - A system error occurred, check errno.
173
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
174
+ * packet size.
133
175
  */
134
176
  int trilogy_builder_write_buffer(trilogy_builder_t *builder, const void *data, size_t len);
135
177
 
@@ -144,6 +186,8 @@ int trilogy_builder_write_buffer(trilogy_builder_t *builder, const void *data, s
144
186
  * Return values:
145
187
  * TRILOGY_OK - The value was appended to the packet buffer.
146
188
  * TRILOGY_SYSERR - A system error occurred, check errno.
189
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
190
+ * packet size.
147
191
  */
148
192
  int trilogy_builder_write_lenenc_buffer(trilogy_builder_t *builder, const void *data, size_t len);
149
193
 
@@ -155,7 +199,23 @@ int trilogy_builder_write_lenenc_buffer(trilogy_builder_t *builder, const void *
155
199
  * Return values:
156
200
  * TRILOGY_OK - The value was appended to the packet buffer.
157
201
  * TRILOGY_SYSERR - A system error occurred, check errno.
202
+ * TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
203
+ * packet size.
158
204
  */
159
205
  int trilogy_builder_write_string(trilogy_builder_t *builder, const char *data);
160
206
 
207
+ /* trilogy_builder_set_max_packet_length - Set the maximum packet length for
208
+ * the builder. Writing data to the builder that would cause the packet length
209
+ * to exceed this value will cause the builder to error.
210
+ *
211
+ * builder - A pre-initialized trilogy_builder_t pointer
212
+ * max_length - The new maximum packet length to set
213
+ *
214
+ * Return values:
215
+ * TRILOGY_OK - The maximum packet length was set.
216
+ * TRILOGY_MAX_PACKET_EXCEEDED - The current packet length is already
217
+ * larger than the requested maximum.
218
+ */
219
+ int trilogy_builder_set_max_packet_length(trilogy_builder_t *builder, size_t max_length);
220
+
161
221
  #endif