Class: Mysql2::Statement
Relationships & Source Files | |
Inherits: | Object |
Defined in: | lib/mysql2/statement.rb, ext/mysql2/statement.c |
Instance Method Summary
-
#execute
Executes the current prepared statement, returns
result
. -
#affected_rows
Returns the number of rows changed, deleted, or inserted.
-
#close
Explicitly closing this will free up server resources immediately rather than waiting for the garbage collector.
- #execute(*args, **kwargs)
-
#field_count(#) ⇒ Numeric
Returns the number of fields the prepared statement returns.
-
#fields(#) ⇒ Array
Returns a list of fields that will be returned by this statement.
-
#last_id
Returns the AUTO_INCREMENT value from the executed INSERT or UPDATE.
-
#param_count(#) ⇒ Numeric
Returns the number of parameters the prepared statement expects.
Instance Method Details
#execute
Executes the current prepared statement, returns result
.
# File 'ext/mysql2/statement.c', line 286
static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { MYSQL_BIND *bind_buffers = NULL; unsigned long *length_buffers = NULL; unsigned long bind_count; unsigned long i; MYSQL_STMT *stmt; MYSQL_RES *metadata; VALUE opts; VALUE current; VALUE resultObj; VALUE *params_enc = NULL; int is_streaming; rb_encoding *conn_enc; GET_STATEMENT(self); GET_CLIENT(stmt_wrapper->client); conn_enc = rb_to_encoding(wrapper->encoding); stmt = stmt_wrapper->stmt; bind_count = mysql_stmt_param_count(stmt); // Get count of ordinary arguments, and extract hash opts/keyword arguments // Use a local scope to avoid leaking the temporary count variable { int c = rb_scan_args(argc, argv, "*:", NULL, &opts); if (c != (long)bind_count) { rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c); } } // setup any bind variables in the query if (bind_count > 0) { // Scratch space for string encoding exports, allocate on the stack params_enc = alloca(sizeof(VALUE) * bind_count); bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND)); length_buffers = xcalloc(bind_count, sizeof(unsigned long)); for (i = 0; i < bind_count; i++) { bind_buffers[i].buffer = NULL; params_enc[i] = Qnil; switch (TYPE(argv[i])) { case T_NIL: bind_buffers[i].buffer_type = MYSQL_TYPE_NULL; break; case T_FIXNUM: #if SIZEOF_INT < SIZEOF_LONG bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG; bind_buffers[i].buffer = xmalloc(sizeof(long long int)); *(long*)(bind_buffers[i].buffer) = FIX2LONG(argv[i]); #else bind_buffers[i].buffer_type = MYSQL_TYPE_LONG; bind_buffers[i].buffer = xmalloc(sizeof(int)); *(long*)(bind_buffers[i].buffer) = FIX2INT(argv[i]); #endif break; case T_BIGNUM: { LONG_LONG num; if (my_big2ll(argv[i], &num) == 0) { bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG; bind_buffers[i].buffer = xmalloc(sizeof(long long int)); *(LONG_LONG*)(bind_buffers[i].buffer) = num; } else { /* The bignum was larger than we can fit in LONG_LONG, send it as a string */ bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL; params_enc[i] = rb_str_export_to_enc(rb_big2str(argv[i], 10), conn_enc); set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]); } } break; case T_FLOAT: bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE; bind_buffers[i].buffer = xmalloc(sizeof(double)); *(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]); break; case T_STRING: bind_buffers[i].buffer_type = MYSQL_TYPE_STRING; params_enc[i] = argv[i]; params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc); set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]); break; case T_TRUE: bind_buffers[i].buffer_type = MYSQL_TYPE_TINY; bind_buffers[i].buffer = xmalloc(sizeof(signed char)); *(signed char*)(bind_buffers[i].buffer) = 1; break; case T_FALSE: bind_buffers[i].buffer_type = MYSQL_TYPE_TINY; bind_buffers[i].buffer = xmalloc(sizeof(signed char)); *(signed char*)(bind_buffers[i].buffer) = 0; break; default: // TODO: what Ruby type should support MYSQL_TYPE_TIME if (CLASS_OF(argv[i]) == rb_cTime || CLASS_OF(argv[i]) == cDateTime) { MYSQL_TIME t; VALUE rb_time = argv[i]; bind_buffers[i].buffer_type = MYSQL_TYPE_DATETIME; bind_buffers[i].buffer = xmalloc(sizeof(MYSQL_TIME)); memset(&t, 0, sizeof(MYSQL_TIME)); t.neg = 0; if (CLASS_OF(argv[i]) == rb_cTime) { t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0)); } else if (CLASS_OF(argv[i]) == cDateTime) { t.second_part = NUM2DBL(rb_funcall(rb_time, intern_sec_fraction, 0)) * 1000000; } t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0)); t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0)); t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0)); t.day = FIX2INT(rb_funcall(rb_time, intern_day, 0)); t.month = FIX2INT(rb_funcall(rb_time, intern_month, 0)); t.year = FIX2INT(rb_funcall(rb_time, intern_year, 0)); *(MYSQL_TIME*)(bind_buffers[i].buffer) = t; } else if (CLASS_OF(argv[i]) == cDate) { MYSQL_TIME t; VALUE rb_time = argv[i]; bind_buffers[i].buffer_type = MYSQL_TYPE_DATE; bind_buffers[i].buffer = xmalloc(sizeof(MYSQL_TIME)); memset(&t, 0, sizeof(MYSQL_TIME)); t.second_part = 0; t.neg = 0; t.day = FIX2INT(rb_funcall(rb_time, intern_day, 0)); t.month = FIX2INT(rb_funcall(rb_time, intern_month, 0)); t.year = FIX2INT(rb_funcall(rb_time, intern_year, 0)); *(MYSQL_TIME*)(bind_buffers[i].buffer) = t; } else if (CLASS_OF(argv[i]) == cBigDecimal) { bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL; // DECIMAL are represented with the "string representation of the // original server-side value", see // https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-conversions.html // This should be independent of the locale used both on the server // and the client side. VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0); params_enc[i] = rb_val_as_string; params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc); set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]); } break; } } // copies bind_buffers into internal storage if (mysql_stmt_bind_param(stmt, bind_buffers)) { FREE_BINDS; rb_raise_mysql2_stmt_error(stmt_wrapper); } } // Duplicate the options hash, merge! extra opts, put the copy into the Result object current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options)); (void)RB_GC_GUARD(current); Check_Type(current, T_HASH); // Merge in hash opts/keyword arguments if (!NIL_P(opts)) { rb_funcall(current, intern_merge_bang, 1, opts); } is_streaming = (Qtrue == rb_hash_aref(current, sym_stream)); // From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no // Ruby API calls are allowed so that GC is not invoked. If the connection is // in results-streaming-mode for Statement A, and in the middle Statement B // gets garbage collected, a message will be sent to the server notifying it // to release Statement B, resulting in the following error: // Commands out of sync; you can't run this command now // // In streaming mode, statement execute must return a cursor because we // cannot prevent other Statement objects from being garbage collected // between fetches of each row of the result set. The following error // occurs if cursor mode is not set: // Row retrieval was canceled by mysql_stmt_close if (is_streaming) { unsigned long type = CURSOR_TYPE_READ_ONLY; if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) { FREE_BINDS; rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY"); } } if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) { FREE_BINDS; rb_raise_mysql2_stmt_error(stmt_wrapper); } FREE_BINDS; metadata = mysql_stmt_result_metadata(stmt); if (metadata == NULL) { if (mysql_stmt_errno(stmt) != 0) { // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal. wrapper->active_fiber = Qnil; rb_raise_mysql2_stmt_error(stmt_wrapper); } // no data and no error, so query was not a SELECT return Qnil; } if (!is_streaming) { // receive the whole result set from the server if (mysql_stmt_store_result(stmt)) { mysql_free_result(metadata); rb_raise_mysql2_stmt_error(stmt_wrapper); } wrapper->active_fiber = Qnil; } resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self); rb_mysql_set_server_query_flags(wrapper->client, resultObj); if (!is_streaming) { // cache all result rb_funcall(resultObj, intern_each, 0); } return resultObj; }
#affected_rows
Returns the number of rows changed, deleted, or inserted.
# File 'ext/mysql2/statement.c', line 586
static VALUE rb_mysql_stmt_affected_rows(VALUE self) { my_ulonglong affected; GET_STATEMENT(self); affected = mysql_stmt_affected_rows(stmt_wrapper->stmt); if (affected == (my_ulonglong)-1) { rb_raise_mysql2_stmt_error(stmt_wrapper); } return ULL2NUM(affected); }
#close
Explicitly closing this will free up server resources immediately rather than waiting for the garbage collector. Useful if you’re managing your own prepared statement cache.
# File 'ext/mysql2/statement.c', line 605
static VALUE rb_mysql_stmt_close(VALUE self) { GET_STATEMENT(self); stmt_wrapper->closed = 1; rb_thread_call_without_gvl(nogvl_stmt_close, stmt_wrapper, RUBY_UBF_IO, 0); return Qnil; }
#execute(*args, **kwargs)
[ GitHub ]# File 'lib/mysql2/statement.rb', line 3
def execute(*args, **kwargs) Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do _execute(*args, **kwargs) end end
#field_count(#) ⇒ Numeric
Returns the number of fields the prepared statement returns.
# File 'ext/mysql2/statement.c', line 195
static VALUE rb_mysql_stmt_field_count(VALUE self) { GET_STATEMENT(self); return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt)); }
#fields(#) ⇒ Array
Returns a list of fields that will be returned by this statement.
# File 'ext/mysql2/statement.c', line 522
static VALUE rb_mysql_stmt_fields(VALUE self) { MYSQL_FIELD *fields; MYSQL_RES *metadata; unsigned int field_count; unsigned int i; VALUE field_list; MYSQL_STMT* stmt; rb_encoding *default_internal_enc, *conn_enc; GET_STATEMENT(self); GET_CLIENT(stmt_wrapper->client); stmt = stmt_wrapper->stmt; default_internal_enc = rb_default_internal_encoding(); { GET_CLIENT(stmt_wrapper->client); conn_enc = rb_to_encoding(wrapper->encoding); } metadata = mysql_stmt_result_metadata(stmt); if (metadata == NULL) { if (mysql_stmt_errno(stmt) != 0) { // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal. wrapper->active_fiber = Qnil; rb_raise_mysql2_stmt_error(stmt_wrapper); } // no data and no error, so query was not a SELECT return Qnil; } fields = mysql_fetch_fields(metadata); field_count = mysql_stmt_field_count(stmt); field_list = rb_ary_new2((long)field_count); for (i = 0; i < field_count; i++) { VALUE rb_field; rb_field = rb_str_new(fields[i].name, fields[i].name_length); rb_enc_associate(rb_field, conn_enc); if (default_internal_enc) { rb_field = rb_str_export_to_enc(rb_field, default_internal_enc); } rb_ary_store(field_list, (long)i, rb_field); } mysql_free_result(metadata); return field_list; }
#last_id
Returns the AUTO_INCREMENT value from the executed INSERT or UPDATE.
# File 'ext/mysql2/statement.c', line 576
static VALUE rb_mysql_stmt_last_id(VALUE self) { GET_STATEMENT(self); return ULL2NUM(mysql_stmt_insert_id(stmt_wrapper->stmt)); }
#param_count(#) ⇒ Numeric
Returns the number of parameters the prepared statement expects.
# File 'ext/mysql2/statement.c', line 185
static VALUE rb_mysql_stmt_param_count(VALUE self) { GET_STATEMENT(self); return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt)); }