Skip to content

Commit 435b550

Browse files
committed
Merge pull request #591 from justincase/prepared_mysql2_rebase
Prepared statements (rebase of #558, collected from #289)
2 parents f2642d6 + cf687ba commit 435b550

File tree

13 files changed

+1531
-122
lines changed

13 files changed

+1531
-122
lines changed

ext/mysql2/client.c

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ VALUE cMysql2Client;
1818
extern VALUE mMysql2, cMysql2Error;
1919
static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
2020
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
21+
static ID intern_brackets, intern_new;
2122

2223
#ifndef HAVE_RB_HASH_DUP
23-
static VALUE rb_hash_dup(VALUE other) {
24-
return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
24+
VALUE rb_hash_dup(VALUE other) {
25+
return rb_funcall(rb_cHash, intern_brackets, 1, other);
2526
}
2627
#endif
2728

@@ -30,25 +31,12 @@ static VALUE rb_hash_dup(VALUE other) {
3031
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
3132
}
3233

33-
#define REQUIRE_CONNECTED(wrapper) \
34-
REQUIRE_INITIALIZED(wrapper) \
35-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
36-
rb_raise(cMysql2Error, "closed MySQL connection"); \
37-
}
38-
3934
#define REQUIRE_NOT_CONNECTED(wrapper) \
4035
REQUIRE_INITIALIZED(wrapper) \
4136
if (wrapper->connected) { \
4237
rb_raise(cMysql2Error, "MySQL connection is already open"); \
4338
}
4439

45-
#define MARK_CONN_INACTIVE(conn) \
46-
wrapper->active_thread = Qnil;
47-
48-
#define GET_CLIENT(self) \
49-
mysql_client_wrapper *wrapper; \
50-
Data_Get_Struct(self, mysql_client_wrapper, wrapper)
51-
5240
/*
5341
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
5442
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
@@ -136,7 +124,7 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
136124
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
137125
#endif
138126

139-
e = rb_funcall(cMysql2Error, rb_intern("new"), 2, rb_error_msg, LONG2FIX(wrapper->server_version));
127+
e = rb_funcall(cMysql2Error, intern_new, 2, rb_error_msg, LONG2FIX(wrapper->server_version));
140128
rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
141129
rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
142130
rb_exc_raise(e);
@@ -247,6 +235,7 @@ static void rb_mysql_client_free(void *ptr) {
247235
void decr_mysql2_client(mysql_client_wrapper *wrapper)
248236
{
249237
wrapper->refcount--;
238+
250239
if (wrapper->refcount == 0) {
251240
nogvl_close(wrapper);
252241
xfree(wrapper->client);
@@ -267,6 +256,7 @@ static VALUE allocate(VALUE klass) {
267256
wrapper->initialized = 0; /* means that that the wrapper is initialized */
268257
wrapper->refcount = 1;
269258
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
259+
270260
return obj;
271261
}
272262

@@ -415,7 +405,7 @@ static VALUE do_send_query(void *args) {
415405
mysql_client_wrapper *wrapper = query_args->wrapper;
416406
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
417407
/* an error occurred, we're not active anymore */
418-
MARK_CONN_INACTIVE(self);
408+
wrapper->active_thread = Qnil;
419409
return rb_raise_mysql2_error(wrapper);
420410
}
421411
return Qnil;
@@ -501,7 +491,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
501491
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
502492
RB_GC_GUARD(current);
503493
Check_Type(current, T_HASH);
504-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
494+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, NULL);
505495

506496
return resultObj;
507497
}
@@ -598,6 +588,25 @@ static VALUE finish_and_mark_inactive(void *args) {
598588
}
599589
#endif
600590

591+
void rb_mysql_client_set_active_thread(VALUE self) {
592+
VALUE thread_current = rb_thread_current();
593+
GET_CLIENT(self);
594+
595+
// see if this connection is still waiting on a result from a previous query
596+
if (NIL_P(wrapper->active_thread)) {
597+
// mark this connection active
598+
wrapper->active_thread = thread_current;
599+
} else if (wrapper->active_thread == thread_current) {
600+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
601+
} else {
602+
VALUE inspect = rb_inspect(wrapper->active_thread);
603+
const char *thr = StringValueCStr(inspect);
604+
605+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
606+
RB_GC_GUARD(inspect);
607+
}
608+
}
609+
601610
/* call-seq:
602611
* client.abandon_results!
603612
*
@@ -641,7 +650,6 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
641650
struct nogvl_send_query_args args;
642651
int async = 0;
643652
VALUE opts, current;
644-
VALUE thread_current = rb_thread_current();
645653
#ifdef HAVE_RUBY_ENCODING_H
646654
rb_encoding *conn_enc;
647655
#endif
@@ -671,23 +679,10 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
671679
#endif
672680
args.sql_ptr = StringValuePtr(args.sql);
673681
args.sql_len = RSTRING_LEN(args.sql);
674-
675-
/* see if this connection is still waiting on a result from a previous query */
676-
if (NIL_P(wrapper->active_thread)) {
677-
/* mark this connection active */
678-
wrapper->active_thread = thread_current;
679-
} else if (wrapper->active_thread == thread_current) {
680-
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
681-
} else {
682-
VALUE inspect = rb_inspect(wrapper->active_thread);
683-
const char *thr = StringValueCStr(inspect);
684-
685-
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
686-
RB_GC_GUARD(inspect);
687-
}
688-
689682
args.wrapper = wrapper;
690683

684+
rb_mysql_client_set_active_thread(self);
685+
691686
#ifndef _WIN32
692687
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
693688

@@ -1080,7 +1075,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
10801075
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
10811076
RB_GC_GUARD(current);
10821077
Check_Type(current, T_HASH);
1083-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
1078+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, NULL);
10841079

10851080
return resultObj;
10861081
}
@@ -1220,6 +1215,17 @@ static VALUE initialize_ext(VALUE self) {
12201215
return self;
12211216
}
12221217

1218+
/* call-seq: client.prepare # => Mysql2::Statement
1219+
*
1220+
* Create a new prepared statement.
1221+
*/
1222+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
1223+
GET_CLIENT(self);
1224+
REQUIRE_CONNECTED(wrapper);
1225+
1226+
return rb_mysql_stmt_new(self, sql);
1227+
}
1228+
12231229
void init_mysql2_client() {
12241230
/* verify the libmysql we're about to use was the version we were built against
12251231
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
@@ -1265,6 +1271,7 @@ void init_mysql2_client() {
12651271
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
12661272
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
12671273
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1274+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
12681275
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
12691276
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
12701277
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
@@ -1299,6 +1306,8 @@ void init_mysql2_client() {
12991306
sym_array = ID2SYM(rb_intern("array"));
13001307
sym_stream = ID2SYM(rb_intern("stream"));
13011308

1309+
intern_brackets = rb_intern("[]");
1310+
intern_new = rb_intern("new");
13021311
intern_merge = rb_intern("merge");
13031312
intern_merge_bang = rb_intern("merge!");
13041313
intern_error_number_eql = rb_intern("error_number=");

ext/mysql2/client.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,28 @@ typedef struct {
5050
MYSQL *client;
5151
} mysql_client_wrapper;
5252

53+
#define REQUIRE_CONNECTED(wrapper) \
54+
REQUIRE_INITIALIZED(wrapper) \
55+
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
56+
rb_raise(cMysql2Error, "closed MySQL connection"); \
57+
}
58+
59+
void rb_mysql_client_set_active_thread(VALUE self);
60+
61+
#define MARK_CONN_INACTIVE(conn) do {\
62+
GET_CLIENT(conn); \
63+
wrapper->active_thread = Qnil; \
64+
} while(0)
65+
66+
#define GET_CLIENT(self) \
67+
mysql_client_wrapper *wrapper; \
68+
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
69+
5370
void init_mysql2_client();
5471
void decr_mysql2_client(mysql_client_wrapper *wrapper);
5572

5673
#endif
74+
75+
#ifndef HAVE_RB_HASH_DUP
76+
VALUE rb_hash_dup(VALUE other);
77+
#endif

ext/mysql2/mysql2_ext.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ void Init_mysql2() {
99

1010
init_mysql2_client();
1111
init_mysql2_result();
12+
init_mysql2_statement();
1213
}

ext/mysql2/mysql2_ext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef unsigned int uint;
4040

4141
#include <client.h>
4242
#include <result.h>
43+
#include <statement.h>
4344
#include <infile.h>
4445

4546
#endif

0 commit comments

Comments
 (0)