static void native_finalize(JNIEnv* env, jobject object) { int err; sqlite3_stmt * statement = GET_STATEMENT(env, object); if (statement != NULL) { sqlite3_finalize(statement); env->SetLongField(object, gStatementField, 0); } }
static void native_clear_bindings(JNIEnv* env, jobject object) { int err; sqlite3_stmt * statement = GET_STATEMENT(env, object); err = sqlite3_clear_bindings(statement); if (err != SQLITE_OK) { throw_sqlite3_exception(env, GET_HANDLE(env, object)); return; } }
/* call-seq: * stmt.affected_rows * * Returns the number of rows changed, deleted, or inserted. */ 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); }
static void native_bind_double(JNIEnv* env, jobject object, jint index, jdouble value) { int err; sqlite3_stmt * statement = GET_STATEMENT(env, object); err = sqlite3_bind_double(statement, index, value); if (err != SQLITE_OK) { char buf[32]; sprintf(buf, "handle %p", statement); throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); return; } }
static void native_bind_string(JNIEnv* env, jobject object, jint index, jstring sqlString) { int err; jchar const * sql; jsize sqlLen; sqlite3_stmt * statement= GET_STATEMENT(env, object); sql = env->GetStringChars(sqlString, NULL); sqlLen = env->GetStringLength(sqlString); err = sqlite3_bind_text16(statement, index, sql, sqlLen * 2, SQLITE_TRANSIENT); env->ReleaseStringChars(sqlString, sql); if (err != SQLITE_OK) { char buf[32]; sprintf(buf, "handle %p", statement); throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); return; } }
/* call-seq: stmt.fields # => array * * Returns a list of fields that will be returned by this statement. */ static VALUE fields(VALUE self) { MYSQL_FIELD *fields; MYSQL_RES *metadata; unsigned int field_count; unsigned int i; VALUE field_list; MYSQL_STMT* stmt; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc, *conn_enc; #endif GET_STATEMENT(self); stmt = stmt_wrapper->stmt; #ifdef HAVE_RUBY_ENCODING_H default_internal_enc = rb_default_internal_encoding(); { GET_CLIENT(stmt_wrapper->client); conn_enc = rb_to_encoding(wrapper->encoding); } #endif metadata = mysql_stmt_result_metadata(stmt); 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); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(rb_field, conn_enc); if (default_internal_enc) { rb_field = rb_str_export_to_enc(rb_field, default_internal_enc); } #endif rb_ary_store(field_list, (long)i, rb_field); } mysql_free_result(metadata); return field_list; }
sqlite3_stmt * compile(JNIEnv* env, jobject object, sqlite3 * handle, jstring sqlString) { int err; jchar const * sql; jsize sqlLen; sqlite3_stmt * statement = GET_STATEMENT(env, object); // Make sure not to leak the statement if it already exists if (statement != NULL) { sqlite3_finalize(statement); env->SetIntField(object, gStatementField, 0); } // Compile the SQL sql = env->GetStringChars(sqlString, NULL); sqlLen = env->GetStringLength(sqlString); err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL); env->ReleaseStringChars(sqlString, sql); if (err == SQLITE_OK) { // Store the statement in the Java object for future calls LOGV("Prepared statement %p on %p", statement, handle); env->SetIntField(object, gStatementField, (int)statement); return statement; } else { // Error messages like 'near ")": syntax error' are not // always helpful enough, so construct an error string that // includes the query itself. const char *query = env->GetStringUTFChars(sqlString, NULL); char *message = (char*) malloc(strlen(query) + 50); if (message) { strcpy(message, ", while compiling: "); // less than 50 chars strcat(message, query); } env->ReleaseStringUTFChars(sqlString, query); throw_sqlite3_exception(env, handle, message); free(message); return NULL; } }
static void native_bind_blob(JNIEnv* env, jobject object, jint index, jbyteArray value) { int err; jchar const * sql; jsize sqlLen; sqlite3_stmt * statement= GET_STATEMENT(env, object); jint len = env->GetArrayLength(value); jbyte * bytes = env->GetByteArrayElements(value, NULL); err = sqlite3_bind_blob(statement, index, bytes, len, SQLITE_TRANSIENT); env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); if (err != SQLITE_OK) { char buf[32]; sprintf(buf, "statement %p", statement); throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); return; } }
/* call-seq: * stmt.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. */ 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; }
/* call-seq: * stmt.last_id * * Returns the AUTO_INCREMENT value from the executed INSERT or UPDATE. */ static VALUE rb_mysql_stmt_last_id(VALUE self) { GET_STATEMENT(self); return ULL2NUM(mysql_stmt_insert_id(stmt_wrapper->stmt)); }
/* call-seq: stmt.execute * * Executes the current prepared statement, returns +result+. */ static VALUE execute(int argc, VALUE *argv, VALUE self) { MYSQL_BIND *bind_buffers = NULL; unsigned long *length_buffers = NULL; unsigned long bind_count; long i; MYSQL_STMT *stmt; MYSQL_RES *metadata; VALUE current; VALUE resultObj; VALUE *params_enc; int is_streaming; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *conn_enc; #endif GET_STATEMENT(self); GET_CLIENT(stmt_wrapper->client); #ifdef HAVE_RUBY_ENCODING_H conn_enc = rb_to_encoding(wrapper->encoding); #endif /* Scratch space for string encoding exports, allocate on the stack. */ params_enc = alloca(sizeof(VALUE) * argc); stmt = stmt_wrapper->stmt; bind_count = mysql_stmt_param_count(stmt); if (argc != (long)bind_count) { rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, argc); } // setup any bind variables in the query if (bind_count > 0) { bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND)); length_buffers = xcalloc(bind_count, sizeof(unsigned long)); for (i = 0; i < argc; 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: bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG; bind_buffers[i].buffer = xmalloc(sizeof(long long int)); *(LONG_LONG*)(bind_buffers[i].buffer) = rb_big2ll(argv[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: { params_enc[i] = argv[i]; #ifdef HAVE_RUBY_ENCODING_H params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc); #endif set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]); } 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; t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0)); 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; #ifdef HAVE_RUBY_ENCODING_H params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc); #endif 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); } } if ((VALUE)rb_thread_call_without_gvl(nogvl_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_thread = Qnil; rb_raise_mysql2_stmt_error(stmt_wrapper); } // no data and no error, so query was not a SELECT return Qnil; } current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options")); (void)RB_GC_GUARD(current); Check_Type(current, T_HASH); is_streaming = (Qtrue == rb_hash_aref(current, sym_stream)); if (!is_streaming) { // recieve 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_thread = Qnil; } resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self); if (!is_streaming) { // cache all result rb_funcall(resultObj, intern_each, 0); } return resultObj; }
/* call-seq: stmt.field_count # => Numeric * * Returns the number of fields the prepared statement returns. */ static VALUE field_count(VALUE self) { GET_STATEMENT(self); return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt)); }
/* call-seq: stmt.param_count # => Numeric * * Returns the number of parameters the prepared statement expects. */ static VALUE param_count(VALUE self) { GET_STATEMENT(self); return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt)); }