Hello community, here is the log from the commit of package rubygem-mysql2 for openSUSE:Factory checked in at 2016-03-07 13:27:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-mysql2 (Old) and /work/SRC/openSUSE:Factory/.rubygem-mysql2.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "rubygem-mysql2" Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-mysql2/rubygem-mysql2.changes 2016-03-01 09:40:03.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-mysql2.new/rubygem-mysql2.changes 2016-03-07 13:28:22.000000000 +0100 @@ -1,0 +2,6 @@ +Thu Feb 25 05:36:51 UTC 2016 - coolo@suse.com + +- updated to version 0.4.3 + see installed CHANGELOG.md + +------------------------------------------------------------------- Old: ---- mysql2-0.4.2.gem New: ---- mysql2-0.4.3.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-mysql2.spec ++++++ --- /var/tmp/diff_new_pack.MHDOCh/_old 2016-03-07 13:28:23.000000000 +0100 +++ /var/tmp/diff_new_pack.MHDOCh/_new 2016-03-07 13:28:23.000000000 +0100 @@ -24,7 +24,7 @@ # Name: rubygem-mysql2 -Version: 0.4.2 +Version: 0.4.3 Release: 0 %define mod_name mysql2 %define mod_full_name %{mod_name}-%{version} @@ -32,10 +32,10 @@ BuildRequires: mysql-devel # /MANUAL BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildRequires: ruby-macros >= 5 BuildRequires: %{rubydevel} BuildRequires: %{rubygem gem2rpm} BuildRequires: %{rubygem rdoc > 3.10} +BuildRequires: ruby-macros >= 5 Url: http://github.com/brianmario/mysql2 Source: http://rubygems.org/gems/%{mod_full_name}.gem Source1: rubygem-mysql2-rpmlintrc ++++++ mysql2-0.4.2.gem -> mysql2-0.4.3.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/README.md 1970-01-01 01:00:00.000000000 +0100 @@ -490,14 +490,15 @@ This gem is tested with the following MySQL and MariaDB versions: - * MySQL 5.5, 5.7 + * MySQL 5.5, 5.6, 5.7 * MySQL Connector/C 6.0 and 6.1 (primarily on Windows) - * MariaDB 5.5, 10.0 + * MariaDB 5.5, 10.0, 10.1 -### Active Record +### Rails / Active Record - * mysql2 0.2.x includes an Active Record driver compatible with AR 2.3 and 3.0 - * mysql2 0.3.x does not include an AR driver because it is included in AR 3.1 and above + * mysql2 0.4.x works with Active Record 4.2.5 and higher. + * mysql2 0.3.x works with Active Record 3.1 and higher (the AR adapter is now included in AR proper). + * mysql2 0.2.x includes an Active Record adapter compatible with AR 2.3 and 3.0, and should not be used with AR 3.1 or higher. ### Asynchronous Active Record Files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/mysql2/client.c new/ext/mysql2/client.c --- old/ext/mysql2/client.c 1970-01-01 01:00:00.000000000 +0100 +++ new/ext/mysql2/client.c 1970-01-01 01:00:00.000000000 +0100 @@ -234,7 +234,7 @@ if (wrapper->refcount == 0) { #ifndef _WIN32 - if (wrapper->connected) { + if (wrapper->connected && !wrapper->automatic_close) { /* The client is being garbage collected while connected. Prevent * mysql_close() from sending a mysql-QUIT or from calling shutdown() on * the socket by invalidating it. invalidate_fd() will drop this @@ -259,7 +259,8 @@ mysql_client_wrapper * wrapper; obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper); wrapper->encoding = Qnil; - MARK_CONN_INACTIVE(self); + wrapper->active_thread = Qnil; + wrapper->automatic_close = 1; wrapper->server_version = 0; wrapper->reconnect_enabled = 0; wrapper->connect_timeout = 0; @@ -331,6 +332,26 @@ return rb_str; } +static VALUE rb_mysql_get_ssl_cipher(VALUE self) +{ + const char *cipher; + VALUE rb_str; + GET_CLIENT(self); + + cipher = mysql_get_ssl_cipher(wrapper->client); + + if (cipher == NULL) { + return Qnil; + } + + rb_str = rb_str_new2(cipher); +#ifdef HAVE_RUBY_ENCODING_H + rb_enc_associate(rb_str, rb_utf8_encoding()); +#endif + + return rb_str; +} + static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) { struct nogvl_connect_args args; time_t start_time, end_time, elapsed_time, connect_timeout; @@ -372,7 +393,7 @@ if (wrapper->connect_timeout) mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout); if (rv == Qfalse) - return rb_raise_mysql2_error(wrapper); + rb_raise_mysql2_error(wrapper); } wrapper->server_version = mysql_get_server_version(wrapper->client); @@ -381,13 +402,12 @@ } /* - * Terminate the connection; call this when the connection is no longer needed. - * The garbage collector can close the connection, but doing so emits an - * "Aborted connection" error on the server and increments the Aborted_clients - * status variable. + * Immediately disconnect from the server; normally the garbage collector + * will disconnect automatically when a connection is no longer needed. + * Explicitly closing this will free up server resources sooner than waiting + * for the garbage collector. * - * @see http://dev.mysql.com/doc/en/communication-errors.html - * @return [void] + * @return [nil] */ static VALUE rb_mysql_client_close(VALUE self) { GET_CLIENT(self); @@ -418,8 +438,8 @@ mysql_client_wrapper *wrapper = query_args->wrapper; if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) { /* an error occurred, we're not active anymore */ - MARK_CONN_INACTIVE(self); - return rb_raise_mysql2_error(wrapper); + wrapper->active_thread = Qnil; + rb_raise_mysql2_error(wrapper); } return Qnil; } @@ -448,7 +468,7 @@ /* once our result is stored off, this connection is ready for another command to be issued */ - MARK_CONN_INACTIVE(self); + wrapper->active_thread = Qnil; return result; } @@ -480,8 +500,8 @@ REQUIRE_CONNECTED(wrapper); if ((VALUE)rb_thread_call_without_gvl(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); + wrapper->active_thread = Qnil; + rb_raise_mysql2_error(wrapper); } is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream); @@ -493,7 +513,7 @@ if (result == NULL) { if (mysql_errno(wrapper->client) != 0) { - MARK_CONN_INACTIVE(self); + wrapper->active_thread = Qnil; rb_raise_mysql2_error(wrapper); } /* no data and no error, so query was not a SELECT */ @@ -517,7 +537,7 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) { GET_CLIENT(self); - MARK_CONN_INACTIVE(self); + wrapper->active_thread = Qnil; wrapper->connected = 0; /* Invalidate the MySQL socket to prevent further communication. @@ -588,7 +608,7 @@ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0); mysql_free_result(result); - MARK_CONN_INACTIVE(self); + wrapper->active_thread = Qnil; } return Qnil; @@ -1011,10 +1031,10 @@ static VALUE rb_mysql_client_more_results(VALUE self) { GET_CLIENT(self); - if (mysql_more_results(wrapper->client) == 0) - return Qfalse; - else - return Qtrue; + if (mysql_more_results(wrapper->client) == 0) + return Qfalse; + else + return Qtrue; } /* call-seq: @@ -1082,6 +1102,39 @@ #endif /* call-seq: + * client.automatic_close? + * + * @return [Boolean] + */ +static VALUE get_automatic_close(VALUE self) { + GET_CLIENT(self); + return wrapper->automatic_close ? Qtrue : Qfalse; +} + +/* call-seq: + * client.automatic_close = false + * + * Set this to +false+ to leave the connection open after it is garbage + * collected. To avoid "Aborted connection" errors on the server, explicitly + * call +close+ when the connection is no longer needed. + * + * @see http://dev.mysql.com/doc/en/communication-errors.html + */ +static VALUE set_automatic_close(VALUE self, VALUE value) { + GET_CLIENT(self); + if (RTEST(value)) { + wrapper->automatic_close = 1; + } else { +#ifndef _WIN32 + wrapper->automatic_close = 0; +#else + rb_warn("Connections are always closed by garbage collector on Windows"); +#endif + } + return value; +} + +/* call-seq: * client.reconnect = true * * Enable or disable the automatic reconnect behavior of libmysql. @@ -1195,7 +1248,7 @@ if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) { /* TODO: warning - not enough memory? */ - return rb_raise_mysql2_error(wrapper); + rb_raise_mysql2_error(wrapper); } wrapper->initialized = 1; @@ -1264,9 +1317,12 @@ rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0); rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0); rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0); + rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0); + rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1); rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1); rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0); rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0); + rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0); #ifdef HAVE_RUBY_ENCODING_H rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/mysql2/client.h new/ext/mysql2/client.h --- old/ext/mysql2/client.h 1970-01-01 01:00:00.000000000 +0100 +++ new/ext/mysql2/client.h 1970-01-01 01:00:00.000000000 +0100 @@ -43,6 +43,7 @@ int reconnect_enabled; unsigned int connect_timeout; int active; + int automatic_close; int connected; int initialized; int refcount; @@ -58,10 +59,6 @@ void rb_mysql_client_set_active_thread(VALUE self); -#define MARK_CONN_INACTIVE(conn) do {\ - wrapper->active_thread = Qnil; \ - } while(0) - #define GET_CLIENT(self) \ mysql_client_wrapper *wrapper; \ Data_Get_Struct(self, mysql_client_wrapper, wrapper); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/mysql2/result.c new/ext/mysql2/result.c --- old/ext/mysql2/result.c 1970-01-01 01:00:00.000000000 +0100 +++ new/ext/mysql2/result.c 1970-01-01 01:00:00.000000000 +0100 @@ -104,6 +104,10 @@ wrapper->stmt_wrapper->stmt->bind_result_done = 0; } + if (wrapper->statement != Qnil) { + decr_mysql2_stmt(wrapper->stmt_wrapper); + } + if (wrapper->result_buffers) { unsigned int i; for (i = 0; i < wrapper->numberOfFields; i++) { @@ -136,13 +140,15 @@ decr_mysql2_client(wrapper->client_wrapper); } - if (wrapper->statement != Qnil) { - decr_mysql2_stmt(wrapper->stmt_wrapper); - } - xfree(wrapper); } +static VALUE rb_mysql_result_free_(VALUE self) { + GET_RESULT(self); + rb_mysql_result_free_result(wrapper); + return Qnil; +} + /* * for small results, this won't hit the network, but there's no * reliable way for us to tell this so we'll always release the GVL @@ -511,7 +517,6 @@ return rowVal; } - static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args) { VALUE rowVal; @@ -911,6 +916,7 @@ if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) { wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result); if (wrapper->numberOfRows == 0) { + rb_mysql_result_free_result(wrapper); wrapper->rows = rb_ary_new(); return wrapper->rows; } @@ -1007,6 +1013,7 @@ cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject); rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1); rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0); + rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0); rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0); rb_define_alias(cMysql2Result, "size", "count"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/mysql2/statement.c new/ext/mysql2/statement.c --- old/ext/mysql2/statement.c 1970-01-01 01:00:00.000000000 +0100 +++ new/ext/mysql2/statement.c 1970-01-01 01:00:00.000000000 +0100 @@ -3,7 +3,7 @@ VALUE cMysql2Statement; extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate; static VALUE sym_stream, intern_new_with_args, intern_each; -static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year; +static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year, intern_to_s; #define GET_STATEMENT(self) \ mysql_stmt_wrapper *stmt_wrapper; \ @@ -11,7 +11,6 @@ if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \ if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); } - static void rb_mysql_stmt_mark(void * ptr) { mysql_stmt_wrapper *stmt_wrapper = ptr; if (!stmt_wrapper) return; @@ -42,7 +41,6 @@ } } - void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) { VALUE e; GET_CLIENT(stmt_wrapper->client); @@ -71,7 +69,6 @@ rb_exc_raise(e); } - /* * used to pass all arguments to mysql_stmt_prepare while inside * nogvl_prepare_statement_args @@ -180,14 +177,17 @@ } } -static void *nogvl_stmt_store_result(void *ptr) { - MYSQL_STMT *stmt = ptr; +static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) { + unsigned long length; - if (mysql_stmt_store_result(stmt)) { - return (void *)Qfalse; - } else { - return (void *)Qtrue; - } + bind_buffer->buffer_type = MYSQL_TYPE_STRING; + bind_buffer->buffer = RSTRING_PTR(string); + + length = RSTRING_LEN(string); + bind_buffer->buffer_length = length; + *length_buffer = length; + + bind_buffer->length = length_buffer; } /* Free each bind_buffer[i].buffer except when params_enc is non-nil, this means @@ -280,11 +280,7 @@ #ifdef HAVE_RUBY_ENCODING_H params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc); #endif - bind_buffers[i].buffer_type = MYSQL_TYPE_STRING; - bind_buffers[i].buffer = RSTRING_PTR(params_enc[i]); - bind_buffers[i].buffer_length = RSTRING_LEN(params_enc[i]); - length_buffers[i] = bind_buffers[i].buffer_length; - bind_buffers[i].length = &length_buffers[i]; + set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]); } break; default: @@ -324,6 +320,19 @@ *(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-conver... + // 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; } @@ -347,8 +356,7 @@ if (metadata == NULL) { if (mysql_stmt_errno(stmt) != 0) { // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal. - - MARK_CONN_INACTIVE(stmt_wrapper->client); + wrapper->active_thread = Qnil; rb_raise_mysql2_stmt_error(stmt_wrapper); } // no data and no error, so query was not a SELECT @@ -362,11 +370,11 @@ is_streaming = (Qtrue == rb_hash_aref(current, sym_stream)); if (!is_streaming) { // recieve the whole result set from the server - if (rb_thread_call_without_gvl(nogvl_stmt_store_result, stmt, RUBY_UBF_IO, 0) == Qfalse) { + if (mysql_stmt_store_result(stmt)) { mysql_free_result(metadata); rb_raise_mysql2_stmt_error(stmt_wrapper); } - MARK_CONN_INACTIVE(stmt_wrapper->client); + wrapper->active_thread = Qnil; } resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self); @@ -491,4 +499,6 @@ intern_day = rb_intern("day"); intern_month = rb_intern("month"); intern_year = rb_intern("year"); + + intern_to_s = rb_intern("to_s"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/mysql2/client.rb new/lib/mysql2/client.rb --- old/lib/mysql2/client.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/mysql2/client.rb 1970-01-01 01:00:00.000000000 +0100 @@ -30,13 +30,13 @@ opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout) # TODO: stricter validation rather than silent massaging - [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command].each do |key| + [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key| next unless opts.key?(key) case key - when :reconnect, :local_infile, :secure_auth + when :reconnect, :local_infile, :secure_auth, :automatic_close send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation when :connect_timeout, :read_timeout, :write_timeout - send(:"#{key}=", opts[key].to_i) + send(:"#{key}=", opts[key]) unless opts[key].nil? else send(:"#{key}=", opts[key]) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/mysql2/version.rb new/lib/mysql2/version.rb --- old/lib/mysql2/version.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/mysql2/version.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +1,3 @@ module Mysql2 - VERSION = "0.4.2" + VERSION = "0.4.3" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 1970-01-01 01:00:00.000000000 +0100 +++ new/metadata 1970-01-01 01:00:00.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: mysql2 version: !ruby/object:Gem::Version - version: 0.4.2 + version: 0.4.3 platform: ruby authors: - Brian Lopez @@ -9,7 +9,7 @@ autorequire: bindir: bin cert_chain: [] -date: 2015-11-25 00:00:00.000000000 Z +date: 2016-02-24 00:00:00.000000000 Z dependencies: [] description: email: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/mysql2/client_spec.rb new/spec/mysql2/client_spec.rb --- old/spec/mysql2/client_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/mysql2/client_spec.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,5 +1,6 @@ # encoding: UTF-8 require 'spec_helper' +require 'stringio' RSpec.describe Mysql2::Client do context "using defaults file" do @@ -145,16 +146,12 @@ # rubocop:enable Style/TrailingComma }.not_to raise_error - results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a - expect(results[0]['Variable_name']).to eql('Ssl_cipher') - expect(results[0]['Value']).not_to be_nil - expect(results[0]['Value']).to be_an_instance_of(String) - expect(results[0]['Value']).not_to be_empty - - expect(results[1]['Variable_name']).to eql('Ssl_version') - expect(results[1]['Value']).not_to be_nil - expect(results[1]['Value']).to be_an_instance_of(String) - expect(results[1]['Value']).not_to be_empty + results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }] + expect(results['Ssl_cipher']).not_to be_empty + expect(results['Ssl_version']).not_to be_empty + + expect(ssl_client.ssl_cipher).not_to be_empty + expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher) ssl_client.close end @@ -172,48 +169,97 @@ expect { Mysql2::Client.new(DatabaseCredentials['root']).close }.to_not change { - @client.query("SHOW STATUS LIKE 'Aborted_clients'").first['Value'].to_i + @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a + + @client.query("SHOW STATUS LIKE 'Threads_connected'").to_a } end it "should not leave dangling connections after garbage collection" do run_gc + expect { + expect { + 10.times do + Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1') + end + }.to change { + @client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i + }.by(10) - client = Mysql2::Client.new(DatabaseCredentials['root']) - before_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i - - 10.times do - Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1') - end - after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i - expect(after_count).to eq(before_count + 10) - - run_gc - final_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i - expect(final_count).to eq(before_count) + run_gc + }.to_not change { + @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a + + @client.query("SHOW STATUS LIKE 'Threads_connected'").to_a + } end - it "should not close connections when running in a child process" do - pending("fork is not available on this platform") unless Process.respond_to?(:fork) + context "#automatic_close" do + it "is enabled by default" do + client = Mysql2::Client.new(DatabaseCredentials['root']) + expect(client.automatic_close?).to be(true) + end + + if RUBY_PLATFORM =~ /mingw|mswin/ + it "cannot be disabled" do + stderr, $stderr = $stderr, StringIO.new + + begin + Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false)) + expect($stderr.string).to include('always closed by garbage collector') + $stderr.reopen + + client = Mysql2::Client.new(DatabaseCredentials['root']) + client.automatic_close = false + expect($stderr.string).to include('always closed by garbage collector') + $stderr.reopen + + expect { client.automatic_close = true }.to_not change { $stderr.string } + ensure + $stderr = stderr + end + end + else + it "can be configured" do + client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false)) + expect(client.automatic_close?).to be(false) + end - run_gc - client = Mysql2::Client.new(DatabaseCredentials['root']) + it "can be assigned" do + client = Mysql2::Client.new(DatabaseCredentials['root']) + client.automatic_close = false + expect(client.automatic_close?).to be(false) - # this empty `fork` call fixes this tests on RBX; without it, the next - # `fork` call hangs forever. WTF? - fork {} - - fork do - client.query('SELECT 1') - client = nil - run_gc - end + client.automatic_close = true + expect(client.automatic_close?).to be(true) + + client.automatic_close = nil + expect(client.automatic_close?).to be(false) - Process.wait + client.automatic_close = 9 + expect(client.automatic_close?).to be(true) + end - # this will throw an error if the underlying socket was shutdown by the - # child's GC - expect { client.query('SELECT 1') }.to_not raise_exception + it "should not close connections when running in a child process" do + run_gc + client = Mysql2::Client.new(DatabaseCredentials['root']) + client.automatic_close = false + + # this empty `fork` call fixes this tests on RBX; without it, the next + # `fork` call hangs forever. WTF? + fork {} + + fork do + client.query('SELECT 1') + client = nil + run_gc + end + + Process.wait + + # this will throw an error if the underlying socket was shutdown by the + # child's GC + expect { client.query('SELECT 1') }.to_not raise_exception + end + end end it "should be able to connect to database with numeric-only name" do @@ -352,6 +398,12 @@ }.to raise_error(Mysql2::Error) end + it "should allow nil read_timeout" do + client = Mysql2::Client.new(:read_timeout => nil) + + expect(client.read_timeout).to be_nil + end + context "#query" do it "should let you query again if iterating is finished when streaming" do @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).each.to_a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/mysql2/result_spec.rb new/spec/mysql2/result_spec.rb --- old/spec/mysql2/result_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/mysql2/result_spec.rb 1970-01-01 01:00:00.000000000 +0100 @@ -22,6 +22,10 @@ expect(@result).to respond_to(:each) end + it "should respond to #free" do + expect(@result).to respond_to(:free) + end + it "should raise a Mysql2::Error exception upon a bad query" do expect { @client.query "bad sql" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/mysql2/statement_spec.rb new/spec/mysql2/statement_spec.rb --- old/spec/mysql2/statement_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/mysql2/statement_spec.rb 1970-01-01 01:00:00.000000000 +0100 @@ -117,6 +117,17 @@ expect(list[1]).to eq('2') end + it "should update a DECIMAL value passing a BigDecimal" do + @client.query 'USE test' + @client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test' + @client.query 'CREATE TABLE mysql2_stmt_decimal_test (decimal_test DECIMAL(10,3))' + + @client.prepare("INSERT INTO mysql2_stmt_decimal_test VALUES (?)").execute(BigDecimal.new("123.45")) + + test_result = @client.query("SELECT * FROM mysql2_stmt_decimal_test").first + expect(test_result['decimal_test']).to eql(123.45) + end + context "utf8_db" do before(:each) do @client.query("DROP DATABASE IF EXISTS test_mysql2_stmt_utf8")