Skip to content

Commit eca73fe

Browse files
committed
Merge pull request #673 from jeremy/statement-close
Support prepared_statement.close
2 parents de7c375 + bcd8770 commit eca73fe

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

ext/mysql2/statement.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, inter
77

88
#define GET_STATEMENT(self) \
99
mysql_stmt_wrapper *stmt_wrapper; \
10-
Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper);
10+
Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper); \
11+
if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); }
1112

1213

1314
static void rb_mysql_stmt_mark(void * ptr) {
@@ -17,6 +18,15 @@ static void rb_mysql_stmt_mark(void * ptr) {
1718
rb_gc_mark(stmt_wrapper->client);
1819
}
1920

21+
static void *nogvl_stmt_close(void * ptr) {
22+
mysql_stmt_wrapper *stmt_wrapper = (mysql_stmt_wrapper *)ptr;
23+
if (stmt_wrapper->stmt) {
24+
mysql_stmt_close(stmt_wrapper->stmt);
25+
stmt_wrapper->stmt = NULL;
26+
}
27+
return NULL;
28+
}
29+
2030
static void rb_mysql_stmt_free(void * ptr) {
2131
mysql_stmt_wrapper* stmt_wrapper = (mysql_stmt_wrapper *)ptr;
2232
decr_mysql2_stmt(stmt_wrapper);
@@ -26,7 +36,7 @@ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
2636
stmt_wrapper->refcount--;
2737

2838
if (stmt_wrapper->refcount == 0) {
29-
mysql_stmt_close(stmt_wrapper->stmt);
39+
nogvl_stmt_close(stmt_wrapper);
3040
xfree(stmt_wrapper);
3141
}
3242
}
@@ -442,6 +452,19 @@ static VALUE rb_mysql_stmt_affected_rows(VALUE self) {
442452
return ULL2NUM(affected);
443453
}
444454

455+
/* call-seq:
456+
* stmt.close
457+
*
458+
* Explicitly closing this will free up server resources immediately rather
459+
* than waiting for the garbage collector. Useful if you're managing your
460+
* own prepared statement cache.
461+
*/
462+
static VALUE rb_mysql_stmt_close(VALUE self) {
463+
GET_STATEMENT(self);
464+
rb_thread_call_without_gvl(nogvl_stmt_close, stmt_wrapper, RUBY_UBF_IO, 0);
465+
return Qnil;
466+
}
467+
445468
void init_mysql2_statement() {
446469
cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
447470

@@ -451,6 +474,7 @@ void init_mysql2_statement() {
451474
rb_define_method(cMysql2Statement, "fields", fields, 0);
452475
rb_define_method(cMysql2Statement, "last_id", rb_mysql_stmt_last_id, 0);
453476
rb_define_method(cMysql2Statement, "affected_rows", rb_mysql_stmt_affected_rows, 0);
477+
rb_define_method(cMysql2Statement, "close", rb_mysql_stmt_close, 0);
454478

455479
sym_stream = ID2SYM(rb_intern("stream"));
456480

spec/mysql2/statement_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,4 +664,17 @@
664664
expect(stmt.affected_rows).to eq 1
665665
end
666666
end
667+
668+
context 'close' do
669+
it 'should free server resources' do
670+
stmt = @client.prepare 'SELECT 1'
671+
expect(stmt.close).to eq nil
672+
end
673+
674+
it 'should raise an error on subsequent execution' do
675+
stmt = @client.prepare 'SELECT 1'
676+
stmt.close
677+
expect { stmt.execute }.to raise_error(Mysql2::Error, /Invalid statement handle/)
678+
end
679+
end
667680
end

0 commit comments

Comments
 (0)