@@ -18,10 +18,11 @@ VALUE cMysql2Client;
1818extern VALUE mMysql2 , cMysql2Error ;
1919static VALUE sym_id , sym_version , sym_async , sym_symbolize_keys , sym_as , sym_array , sym_stream ;
2020static 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) {
247235void 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+
12231229void 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=" );
0 commit comments