static VALUE rb_mysql_client_escape(VALUE self, VALUE str) { MYSQL * client; VALUE newStr; unsigned long newLen, oldLen; Check_Type(str, T_STRING); #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding")); // ensure the string is in the encoding the connection is expecting str = rb_str_export_to_enc(str, conn_enc); #endif oldLen = RSTRING_LEN(str); char escaped[(oldLen*2)+1]; Data_Get_Struct(self, MYSQL, client); REQUIRE_OPEN_DB(client); newLen = mysql_real_escape_string(client, escaped, StringValuePtr(str), RSTRING_LEN(str)); if (newLen == oldLen) { // no need to return a new ruby string if nothing changed return str; } else { newStr = rb_str_new(escaped, newLen); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(newStr, conn_enc); if (default_internal_enc) { newStr = rb_str_export_to_enc(newStr, default_internal_enc); } #endif return newStr; } }
/* call-seq: define_aggregator(name, aggregator) * * Define an aggregate function named +name+ using the object +aggregator+. * +aggregator+ must respond to +step+ and +finalize+. +step+ will be called * with row information and +finalize+ must return the return value for the * aggregator function. */ static VALUE define_aggregator(VALUE self, VALUE name, VALUE aggregator) { sqlite3RubyPtr ctx; int arity, status; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); arity = sqlite3_obj_method_arity(aggregator, rb_intern("step")); status = sqlite3_create_function( ctx->db, StringValuePtr(name), arity, SQLITE_UTF8, (void *)aggregator, NULL, rb_sqlite3_step, rb_sqlite3_final ); rb_iv_set(self, "@agregator", aggregator); CHECK(ctx->db, status); return self; }
static VALUE rb_mysql_client_server_info(VALUE self) { VALUE version, server_info; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc; rb_encoding *conn_enc; #endif GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); #ifdef HAVE_RUBY_ENCODING_H default_internal_enc = rb_default_internal_encoding(); conn_enc = rb_to_encoding(wrapper->encoding); #endif version = rb_hash_new(); rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client))); server_info = rb_str_new2(mysql_get_server_info(wrapper->client)); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(server_info, conn_enc); if (default_internal_enc) { server_info = rb_str_export_to_enc(server_info, default_internal_enc); } #endif rb_hash_aset(version, sym_version, server_info); return version; }
static VALUE rb_mysql_client_escape(VALUE self, VALUE str) { VALUE newStr; unsigned long newLen, oldLen; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); Check_Type(str, T_STRING); #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding); // ensure the string is in the encoding the connection is expecting str = rb_str_export_to_enc(str, conn_enc); #endif oldLen = RSTRING_LEN(str); newStr = rb_str_new(0, oldLen*2+1); newLen = mysql_real_escape_string(wrapper->client, RSTRING_PTR(newStr), StringValuePtr(str), oldLen); if (newLen == oldLen) { // no need to return a new ruby string if nothing changed return str; } else { rb_str_resize(newStr, newLen); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(newStr, conn_enc); if (default_internal_enc) { newStr = rb_str_export_to_enc(newStr, default_internal_enc); } #endif return newStr; } }
static VALUE rb_mysql_client_async_result(VALUE self) { MYSQL_RES * result; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); if (rb_thread_blocking_region(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) { // an error occurred, mark this connection inactive MARK_CONN_INACTIVE(self); return rb_raise_mysql2_error(wrapper->client); } result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper->client, RUBY_UBF_IO, 0); // we have our result, mark this connection inactive MARK_CONN_INACTIVE(self); if (result == NULL) { if (mysql_field_count(wrapper->client) != 0) { rb_raise_mysql2_error(wrapper->client); } return Qnil; } VALUE resultObj = rb_mysql_result_to_obj(result); // pass-through query options for result construction later rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0)); #ifdef HAVE_RUBY_ENCODING_H mysql2_result_wrapper * result_wrapper; GetMysql2Result(resultObj, result_wrapper); result_wrapper->encoding = wrapper->encoding; #endif return resultObj; }
/* call-seq: define_function(name) { |args,...| } * * Define a function named +name+ with +args+. The arity of the block * will be used as the arity for the function defined. */ static VALUE define_function(VALUE self, VALUE name) { sqlite3RubyPtr ctx; VALUE block; int status; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); block = rb_block_proc(); status = sqlite3_create_function( ctx->db, StringValuePtr(name), rb_proc_arity(block), SQLITE_UTF8, (void *)block, rb_sqlite3_func, NULL, NULL ); CHECK(ctx->db, status); return self; }
/* call-seq: db.transaction_active? * * Returns +true+ if there is a transaction active, and +false+ otherwise. * */ static VALUE transaction_active_p(VALUE self) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); return sqlite3_get_autocommit(ctx->db) ? Qfalse : Qtrue; }
/* call-seq: errcode * * Return an integer representing the last error to have occurred with this * database. */ static VALUE errcode(VALUE self) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); return INT2NUM((long)sqlite3_errcode(ctx->db)); }
static VALUE rb_mysql_client_thread_id(VALUE self) { unsigned long retVal; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); retVal = mysql_thread_id(wrapper->client); return ULL2NUM(retVal); }
/* call-seq: changes * * Returns the number of changes made to this database instance by the last * operation performed. Note that a "delete from table" without a where * clause will not affect this value. */ static VALUE changes(VALUE self) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); return INT2NUM(sqlite3_changes(ctx->db)); }
/* call-seq: last_insert_row_id * * Obtains the unique row ID of the last row to be inserted by this Database * instance. */ static VALUE last_insert_row_id(VALUE self) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); return LL2NUM(sqlite3_last_insert_rowid(ctx->db)); }
/* call-seq: errmsg * * Return a string describing the last error to have occurred with this * database. */ static VALUE errmsg(VALUE self) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); return rb_str_new2(sqlite3_errmsg(ctx->db)); }
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) { struct nogvl_send_query_args args; fd_set fdset; int fd, retval; int async = 0; VALUE opts; VALUE rb_async; MYSQL * client; if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) { if ((rb_async = rb_hash_aref(opts, sym_async)) != Qnil) { async = rb_async == Qtrue ? 1 : 0; } } Check_Type(args.sql, T_STRING); #ifdef HAVE_RUBY_ENCODING_H rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding")); // ensure the string is in the encoding the connection is expecting args.sql = rb_str_export_to_enc(args.sql, conn_enc); #endif Data_Get_Struct(self, MYSQL, client); REQUIRE_OPEN_DB(client); args.mysql = client; if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) { return rb_raise_mysql2_error(client); } if (!async) { // the below code is largely from do_mysql // http://github.com/datamapper/do fd = client->net.fd; for(;;) { FD_ZERO(&fdset); FD_SET(fd, &fdset); retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL); if (retval < 0) { rb_sys_fail(0); } if (retval > 0) { break; } } return rb_mysql_client_async_result(self); } else { return Qnil; } }
/* call-seq: db.enable_load_extension(onoff) * * Enable or disable extension loading. */ static VALUE enable_load_extension(VALUE self, VALUE onoff) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, (int)NUM2INT(onoff))); return self; }
/* call-seq: db.busy_timeout = ms * * Indicates that if a request for a resource terminates because that * resource is busy, SQLite should sleep and retry for up to the indicated * number of milliseconds. By default, SQLite does not retry * busy resources. To restore the default behavior, send 0 as the * +ms+ parameter. * * See also the mutually exclusive #busy_handler. */ static VALUE set_busy_timeout(VALUE self, VALUE timeout) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); CHECK(ctx->db, sqlite3_busy_timeout(ctx->db, (int)NUM2INT(timeout))); return self; }
/* call-seq: interrupt * * Interrupts the currently executing operation, causing it to abort. */ static VALUE interrupt(VALUE self) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); sqlite3_interrupt(ctx->db); return self; }
static VALUE rb_mysql_client_socket(VALUE self) { GET_CLIENT(self); #ifndef _WIN32 REQUIRE_OPEN_DB(wrapper); int fd_set_fd = wrapper->client->net.fd; return INT2NUM(fd_set_fd); #else rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows"); #endif }
static VALUE rb_mysql_client_affected_rows(VALUE self) { my_ulonglong retVal; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); retVal = mysql_affected_rows(wrapper->client); if (retVal == (my_ulonglong)-1) { rb_raise_mysql2_error(wrapper); } return ULL2NUM(retVal); }
/* call-seq: * client.select_db(name) * * Causes the database specified by +name+ to become the default (current) * database on the connection specified by mysql. */ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db) { struct nogvl_select_db_args args; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); args.mysql = wrapper->client; args.db = StringValuePtr(db); if (rb_thread_blocking_region(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse) rb_raise_mysql2_error(wrapper); return db; }
static VALUE rb_mysql_client_socket(VALUE self) { GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); int fd_set_fd = wrapper->client->net.fd; #ifdef _WIN32 WSAPROTOCOL_INFO wsa_pi; // dupicate the SOCKET from libmysql int r = WSADuplicateSocket(wrapper->client->net.fd, GetCurrentProcessId(), &wsa_pi); SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0); // create the CRT fd so ruby can get back to the SOCKET fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY); return INT2NUM(fd_set_fd); #else return INT2NUM(fd_set_fd); #endif }
/* call-seq: db.encoding * * Fetch the encoding set on this database */ static VALUE db_encoding(VALUE self) { sqlite3RubyPtr ctx; VALUE enc; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); enc = rb_iv_get(self, "@encoding"); if(NIL_P(enc)) { sqlite3_exec(ctx->db, "PRAGMA encoding", enc_cb, (void *)self, NULL); } return rb_iv_get(self, "@encoding"); }
/* call-seq: * client.async_result * * Returns the result for the last async issued query. */ static VALUE rb_mysql_client_async_result(VALUE self) { MYSQL_RES * result; VALUE resultObj; #ifdef HAVE_RUBY_ENCODING_H mysql2_result_wrapper * result_wrapper; #endif GET_CLIENT(self); // if we're not waiting on a result, do nothing if (!wrapper->active) return Qnil; REQUIRE_OPEN_DB(wrapper); if (rb_thread_blocking_region(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) { // an error occurred, mark this connection inactive MARK_CONN_INACTIVE(self); return rb_raise_mysql2_error(wrapper); } VALUE is_streaming = rb_hash_aref(rb_iv_get(self, "@query_options"), sym_stream); if(is_streaming == Qtrue) { result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_use_result, wrapper, RUBY_UBF_IO, 0); } else { result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0); } if (result == NULL) { if (mysql_errno(wrapper->client) != 0) { MARK_CONN_INACTIVE(self); rb_raise_mysql2_error(wrapper); } // no data and no error, so query was not a SELECT return Qnil; } resultObj = rb_mysql_result_to_obj(result); // pass-through query options for result construction later rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0)); #ifdef HAVE_RUBY_ENCODING_H GetMysql2Result(resultObj, result_wrapper); result_wrapper->encoding = wrapper->encoding; #endif return resultObj; }
/* call-seq: set_authorizer = auth * * Set the authorizer for this database. +auth+ must respond to +call+, and * +call+ must take 5 arguments. * * Installs (or removes) a block that will be invoked for every access * to the database. If the block returns 0 (or +true+), the statement * is allowed to proceed. Returning 1 or false causes an authorization error to * occur, and returning 2 or nil causes the access to be silently denied. */ static VALUE set_authorizer(VALUE self, VALUE authorizer) { sqlite3RubyPtr ctx; int status; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); status = sqlite3_set_authorizer( ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self ); CHECK(ctx->db, status); rb_iv_set(self, "@authorizer", authorizer); return self; }
/* call-seq: * trace { |sql| ... } * trace(Class.new { def call sql; end }.new) * * Installs (or removes) a block that will be invoked for every SQL * statement executed. The block receives one parameter: the SQL statement * executed. If the block is +nil+, any existing tracer will be uninstalled. */ static VALUE trace(int argc, VALUE *argv, VALUE self) { sqlite3RubyPtr ctx; VALUE block; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); rb_scan_args(argc, argv, "01", &block); if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc(); rb_iv_set(self, "@tracefunc", block); sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self); return self; }
/* call-seq: db.collation(name, comparator) * * Add a collation with name +name+, and a +comparator+ object. The * +comparator+ object should implement a method called "compare" that takes * two parameters and returns an integer less than, equal to, or greater than * 0. */ static VALUE collation(VALUE self, VALUE name, VALUE comparator) { sqlite3RubyPtr ctx; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); CHECK(ctx->db, sqlite3_create_collation( ctx->db, StringValuePtr(name), SQLITE_UTF8, (void *)comparator, NIL_P(comparator) ? NULL : rb_comparator_func)); /* Make sure our comparator doesn't get garbage collected. */ rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator); return self; }
/* call-seq: db.enable_load_extension(onoff) * * Enable or disable extension loading. */ static VALUE enable_load_extension(VALUE self, VALUE onoff) { sqlite3RubyPtr ctx; int onoffparam; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); if (Qtrue == onoff) { onoffparam = 1; } else if (Qfalse == onoff) { onoffparam = 0; } else { onoffparam = (int)NUM2INT(onoff); } CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, onoffparam)); return self; }
/* call-seq: db.load_extension(file) * * Loads an SQLite extension library from the named file. Extension * loading must be enabled using db.enable_load_extension(1) prior * to calling this API. */ static VALUE load_extension(VALUE self, VALUE file) { sqlite3RubyPtr ctx; int status; char *errMsg; VALUE errexp; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); status = sqlite3_load_extension(ctx->db, RSTRING_PTR(file), 0, &errMsg); if (status != SQLITE_OK) { errexp = rb_exc_new2(rb_eRuntimeError, errMsg); sqlite3_free(errMsg); rb_exc_raise(errexp); } return self; }
/* call-seq: * busy_handler { |count| ... } * busy_handler(Class.new { def call count; end }.new) * * Register a busy handler with this database instance. When a requested * resource is busy, this handler will be invoked. If the handler returns * +false+, the operation will be aborted; otherwise, the resource will * be requested again. * * The handler will be invoked with the name of the resource that was * busy, and the number of times it has been retried. * * See also the mutually exclusive #busy_timeout. */ static VALUE busy_handler(int argc, VALUE *argv, VALUE self) { sqlite3RubyPtr ctx; VALUE block; int status; Data_Get_Struct(self, sqlite3Ruby, ctx); REQUIRE_OPEN_DB(ctx); rb_scan_args(argc, argv, "01", &block); if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc(); rb_iv_set(self, "@busy_handler", block); status = sqlite3_busy_handler( ctx->db, NIL_P(block) ? NULL : rb_sqlite3_busy_handler, (void *)self); CHECK(ctx->db, status); return self; }
static VALUE rb_mysql_client_async_result(VALUE self) { MYSQL * client; MYSQL_RES * result; Data_Get_Struct(self, MYSQL, client); REQUIRE_OPEN_DB(client); if (rb_thread_blocking_region(nogvl_read_query_result, client, RUBY_UBF_IO, 0) == Qfalse) { return rb_raise_mysql2_error(client); } result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0); if (result == NULL) { if (mysql_field_count(client) != 0) { rb_raise_mysql2_error(client); } return Qnil; } VALUE resultObj = rb_mysql_result_to_obj(result); rb_iv_set(resultObj, "@encoding", rb_iv_get(self, "@encoding")); return resultObj; }
static VALUE rb_mysql_client_server_info(VALUE self) { MYSQL * client; VALUE version, server_info; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding")); #endif Data_Get_Struct(self, MYSQL, client); REQUIRE_OPEN_DB(client); version = rb_hash_new(); rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(client))); server_info = rb_str_new2(mysql_get_server_info(client)); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(server_info, conn_enc); if (default_internal_enc) { server_info = rb_str_export_to_enc(server_info, default_internal_enc); } #endif rb_hash_aset(version, sym_version, server_info); return version; }