diff --git a/.rubocop.yml b/.rubocop.yml index 8f634d507..a9cd803dc 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,7 +1,7 @@ inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 1.9 + TargetRubyVersion: 2.0 DisplayCopNames: true Exclude: @@ -21,10 +21,6 @@ Layout/IndentHeredoc: Lint/EndAlignment: EnforcedStyleAlignWith: variable -Style/Encoding: - AutoCorrectEncodingComment: '# encoding: UTF-8' - EnforcedStyle: always - Style/TrailingCommaInArguments: EnforcedStyleForMultiline: consistent_comma diff --git a/.travis.yml b/.travis.yml index 92f10ad4a..768848f2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ bundler_args: --without benchmarks development # Pin Rubygems to a working version. Sometimes it breaks upstream. Update now and then. before_install: - gem --version - - gem update --system 2.6.12 + - gem update --system 2.7.3 - gem update bundler - gem --version - bash .travis_setup.sh @@ -18,12 +18,12 @@ addons: - mysql-client-core-5.6 - mysql-client-5.6 rvm: + - 2.5 - 2.4 - 2.3 - 2.2 - 2.1 - 2.0.0 - - 1.9.3 - ruby-head matrix: include: @@ -73,21 +73,16 @@ matrix: hosts: - mysql2gem.example.com - os: osx - rvm: system + rvm: 2.3 env: DB=mysql56 addons: hosts: - mysql2gem.example.com - before_install: - # Use the system RubyGem with the system Ruby - - gem --version - - sudo gem install bundler - - bash .travis_setup.sh fast_finish: true allow_failures: - rvm: ruby-head - os: osx - rvm: system + rvm: 2.3 env: DB=mysql56 - rvm: 2.0.0 env: DB=mysql51 diff --git a/Gemfile b/Gemfile index 3f64bcc33..5d9c98491 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,3 @@ -# encoding: UTF-8 - source 'https://rubygems.org' gemspec @@ -10,9 +8,8 @@ gem 'rake-compiler', '~> 1.0' group :test do gem 'eventmachine' unless RUBY_PLATFORM =~ /mswin|mingw/ gem 'rspec', '~> 3.2' - # https://github.com/bbatsov/rubocop/pull/3328 # https://github.com/bbatsov/rubocop/pull/4789 - gem 'rubocop', '~> 0.50.0' unless RUBY_VERSION =~ /1.9/ + gem 'rubocop', '~> 0.50.0' end group :benchmarks do diff --git a/README.md b/README.md index 126359455..822cadfdc 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The Mysql2 gem is meant to serve the extremely common use-case of connecting, qu Some database libraries out there serve as direct 1:1 mappings of the already complex C APIs available. This one is not. -It also forces the use of UTF-8 [or binary] for the connection [and all strings in 1.9, unless Encoding.default_internal is set then it'll convert from UTF-8 to that encoding] and uses encoding-aware MySQL API calls where it can. +It also forces the use of UTF-8 [or binary] for the connection and uses encoding-aware MySQL API calls where it can. The API consists of three classes: @@ -185,7 +185,8 @@ end Prepared statements are supported, as well. In a prepared statement, use a `?` in place of each value and then execute the statement to retrieve a result set. Pass your arguments to the execute method in the same number and order as the -question marks in the statement. +question marks in the statement. Query options can be passed as keyword arguments +to the execute method. ``` ruby statement = @client.prepare("SELECT * FROM users WHERE login_count = ?") @@ -194,6 +195,9 @@ result2 = statement.execute(2) statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?") result = statement.execute(1, "CA") + +statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?") +result = statement.execute(1, "CA", :as => :array) ``` ## Connection options @@ -392,6 +396,15 @@ c = Mysql2::Client.new c.query(sql, :symbolize_keys => true) ``` +or + +``` ruby +# this will set the options for the Mysql2::Result instance returned from the #execute method +c = Mysql2::Client.new +s = c.prepare(sql) +s.execute(arg1, args2, :symbolize_keys => true) +``` + ## Result types ### Array of Arrays @@ -520,7 +533,7 @@ As for field values themselves, I'm workin on it - but expect that soon. This gem is tested with the following Ruby versions on Linux and Mac OS X: - * Ruby MRI 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x + * Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x * Rubinius 2.x and 3.x do work but may fail under some workloads This gem is tested with the following MySQL and MariaDB versions: diff --git a/Rakefile b/Rakefile index 1b7a093cf..4164a7ad2 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'rake' # Load custom tasks (careful attention to define tasks before prerequisites) @@ -12,7 +10,7 @@ load 'tasks/benchmarks.rake' begin require 'rubocop/rake_task' RuboCop::RakeTask.new - task default: [:spec, :rubocop] + task default: %i[spec rubocop] rescue LoadError warn 'RuboCop is not available' task default: :spec diff --git a/benchmark/active_record.rb b/benchmark/active_record.rb index 90191dcff..0ae442c25 100644 --- a/benchmark/active_record.rb +++ b/benchmark/active_record.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/active_record_threaded.rb b/benchmark/active_record_threaded.rb index 013b2d57c..22c3f01be 100644 --- a/benchmark/active_record_threaded.rb +++ b/benchmark/active_record_threaded.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/allocations.rb b/benchmark/allocations.rb index 372ffb9a2..7926a837d 100644 --- a/benchmark/allocations.rb +++ b/benchmark/allocations.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/escape.rb b/benchmark/escape.rb index fad2190e6..0e2320e2b 100644 --- a/benchmark/escape.rb +++ b/benchmark/escape.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/query_with_mysql_casting.rb b/benchmark/query_with_mysql_casting.rb index 33fa024f2..caf7eb011 100644 --- a/benchmark/query_with_mysql_casting.rb +++ b/benchmark/query_with_mysql_casting.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/query_without_mysql_casting.rb b/benchmark/query_without_mysql_casting.rb index 1fc6af235..4080e22d1 100644 --- a/benchmark/query_without_mysql_casting.rb +++ b/benchmark/query_without_mysql_casting.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/sequel.rb b/benchmark/sequel.rb index afd67654c..a9555fa6e 100644 --- a/benchmark/sequel.rb +++ b/benchmark/sequel.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'rubygems' diff --git a/benchmark/setup_db.rb b/benchmark/setup_db.rb index 6ba3033dd..497406700 100644 --- a/benchmark/setup_db.rb +++ b/benchmark/setup_db.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') # This script is for generating psudo-random data into a single table consisting of nearly every diff --git a/examples/eventmachine.rb b/examples/eventmachine.rb index 273f1bc52..dd8419cf2 100644 --- a/examples/eventmachine.rb +++ b/examples/eventmachine.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - $LOAD_PATH.unshift 'lib' require 'rubygems' diff --git a/examples/threaded.rb b/examples/threaded.rb index eaf769f8b..440a0081b 100644 --- a/examples/threaded.rb +++ b/examples/threaded.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - $LOAD_PATH.unshift 'lib' require 'mysql2' require 'timeout' diff --git a/ext/mysql2/client.c b/ext/mysql2/client.c index f2e289d4c..665147a2a 100644 --- a/ext/mysql2/client.c +++ b/ext/mysql2/client.c @@ -15,7 +15,7 @@ #include "mysql_enc_name_to_ruby.h" VALUE cMysql2Client; -extern VALUE mMysql2, cMysql2Error; +extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError; static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream; static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow; static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args; @@ -595,6 +595,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) { return Qnil; } + // Duplicate the options hash and put the copy in the Result object current = rb_hash_dup(rb_iv_get(self, "@current_query_options")); (void)RB_GC_GUARD(current); Check_Type(current, T_HASH); @@ -659,7 +660,7 @@ static VALUE do_query(void *args) { retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp); if (retval == 0) { - rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout)); + rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout)); } if (retval < 0) { @@ -1155,6 +1156,7 @@ static VALUE rb_mysql_client_store_result(VALUE self) return Qnil; } + // Duplicate the options hash and put the copy in the Result object current = rb_hash_dup(rb_iv_get(self, "@current_query_options")); (void)RB_GC_GUARD(current); Check_Type(current, T_HASH); diff --git a/ext/mysql2/client.h b/ext/mysql2/client.h index e5afe985f..5e0ebe3f0 100644 --- a/ext/mysql2/client.h +++ b/ext/mysql2/client.h @@ -1,12 +1,6 @@ #ifndef MYSQL2_CLIENT_H #define MYSQL2_CLIENT_H -#ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL -/* emulate rb_thread_call_without_gvl with rb_thread_blocking_region */ -#define rb_thread_call_without_gvl(func, data1, ubf, data2) \ - rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2) -#endif /* ! HAVE_RB_THREAD_CALL_WITHOUT_GVL */ - typedef struct { VALUE encoding; VALUE active_thread; /* rb_thread_current() or Qnil */ diff --git a/ext/mysql2/extconf.rb b/ext/mysql2/extconf.rb index bb08556a2..b73774536 100644 --- a/ext/mysql2/extconf.rb +++ b/ext/mysql2/extconf.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'mkmf' require 'English' @@ -27,9 +25,6 @@ def add_ssl_defines(header) have_func('rb_absint_size') have_func('rb_absint_singlebit_p') -# 2.0-only -have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h') - # Missing in RBX (https://github.com/rubinius/rubinius/issues/3771) have_func('rb_wait_for_single_fd') @@ -57,14 +52,6 @@ def add_ssl_defines(header) # If the user has provided a --with-mysql-dir argument, we must respect it or fail. inc, lib = dir_config('mysql') if inc && lib - # TODO: Remove when 2.0.0 is the minimum supported version - # Ruby versions not incorporating the mkmf fix at - # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717 - # do not properly search for lib directories, and must be corrected - unless lib && lib[-3, 3] == 'lib' - @libdir_basename = 'lib' - inc, lib = dir_config('mysql') - end abort "-----\nCannot find include dir(s) #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) } abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) } warn "-----\nUsing --with-mysql-dir=#{File.dirname inc}\n-----" diff --git a/ext/mysql2/mysql2_ext.c b/ext/mysql2/mysql2_ext.c index a6fe365ac..2eb8b6d94 100644 --- a/ext/mysql2/mysql2_ext.c +++ b/ext/mysql2/mysql2_ext.c @@ -1,11 +1,12 @@ #include -VALUE mMysql2, cMysql2Error; +VALUE mMysql2, cMysql2Error, cMysql2TimeoutError; /* Ruby Extension initializer */ void Init_mysql2() { mMysql2 = rb_define_module("Mysql2"); cMysql2Error = rb_const_get(mMysql2, rb_intern("Error")); + cMysql2TimeoutError = rb_const_get(cMysql2Error, rb_intern("TimeoutError")); init_mysql2_client(); init_mysql2_result(); diff --git a/ext/mysql2/mysql2_ext.h b/ext/mysql2/mysql2_ext.h index d94f29483..f76ea45ec 100644 --- a/ext/mysql2/mysql2_ext.h +++ b/ext/mysql2/mysql2_ext.h @@ -18,15 +18,7 @@ void Init_mysql2(void); #endif #include -// ruby/thread.h was added in 2.0.0. See: -// https://github.com/ruby/ruby/commit/c51a826 -// -// Rubinius doesn't define this, but it ships an empty thread.h (the symbols we -// care about are in ruby.h); this is safe to remove when < 2.0.0 is no longer -// supported. -#ifdef HAVE_RUBY_THREAD_H #include -#endif #if defined(__GNUC__) && (__GNUC__ >= 3) #define RB_MYSQL_NORETURN __attribute__ ((noreturn)) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 9224e6548..f93a52f84 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -234,12 +234,12 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields wrapper->result_buffers[i].buffer_length = sizeof(signed char); break; case MYSQL_TYPE_SHORT: // short int + case MYSQL_TYPE_YEAR: // short int wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int)); wrapper->result_buffers[i].buffer_length = sizeof(short int); break; case MYSQL_TYPE_INT24: // int case MYSQL_TYPE_LONG: // int - case MYSQL_TYPE_YEAR: // int wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int)); wrapper->result_buffers[i].buffer_length = sizeof(int); break; @@ -365,6 +365,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co } break; case MYSQL_TYPE_SHORT: // short int + case MYSQL_TYPE_YEAR: // short int if (result_buffer->is_unsigned) { val = UINT2NUM(*((unsigned short int*)result_buffer->buffer)); } else { @@ -373,7 +374,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co break; case MYSQL_TYPE_INT24: // int case MYSQL_TYPE_LONG: // int - case MYSQL_TYPE_YEAR: // int if (result_buffer->is_unsigned) { val = UINT2NUM(*((unsigned int*)result_buffer->buffer)); } else { diff --git a/ext/mysql2/statement.c b/ext/mysql2/statement.c index 3a022e661..fec9aeba8 100644 --- a/ext/mysql2/statement.c +++ b/ext/mysql2/statement.c @@ -1,8 +1,8 @@ #include VALUE cMysql2Statement; -extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate; -static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s; +extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError, cBigDecimal, cDateTime, cDate; +static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_merge_bang; static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year; #define GET_STATEMENT(self) \ @@ -184,7 +184,7 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length * the buffer is a Ruby string pointer and not our memory to manage. */ #define FREE_BINDS \ - for (i = 0; i < argc; i++) { \ + for (i = 0; i < c; i++) { \ if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \ xfree(bind_buffers[i].buffer); \ } \ @@ -248,8 +248,10 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { unsigned long *length_buffers = NULL; unsigned long bind_count; long i; + int c; MYSQL_STMT *stmt; MYSQL_RES *metadata; + VALUE opts; VALUE current; VALUE resultObj; VALUE *params_enc; @@ -261,22 +263,24 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { conn_enc = rb_to_encoding(wrapper->encoding); - /* Scratch space for string encoding exports, allocate on the stack. */ - params_enc = alloca(sizeof(VALUE) * argc); + // Get count of ordinary arguments, and extract hash opts/keyword arguments + c = rb_scan_args(argc, argv, "*:", NULL, &opts); 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); + 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) * c); bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND)); length_buffers = xcalloc(bind_count, sizeof(unsigned long)); - for (i = 0; i < argc; i++) { + for (i = 0; i < c; i++) { bind_buffers[i].buffer = NULL; params_enc[i] = Qnil; @@ -416,10 +420,16 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { return Qnil; } + // Duplicate the options hash, merge! extra opts, put the copy into the Result object current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@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)); if (!is_streaming) { // recieve the whole result set from the server @@ -562,4 +572,5 @@ void init_mysql2_statement() { intern_year = rb_intern("year"); intern_to_s = rb_intern("to_s"); + intern_merge_bang = rb_intern("merge!"); } diff --git a/lib/mysql2.rb b/lib/mysql2.rb index 4ec3198f6..4bf75364a 100644 --- a/lib/mysql2.rb +++ b/lib/mysql2.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'date' require 'bigdecimal' @@ -75,13 +73,11 @@ def self.key_hash_as_symbols(hash) # Timeout::ExitException was removed in Ruby 2.3.0, 2.2.3, and 2.1.8, # but is present in earlier 2.1.x and 2.2.x, so we provide a shim. # - if Thread.respond_to?(:handle_interrupt) - require 'timeout' - TIMEOUT_ERROR_CLASS = if defined?(::Timeout::ExitException) - ::Timeout::ExitException - else - ::Timeout::Error - end + require 'timeout' + TIMEOUT_ERROR_CLASS = if defined?(::Timeout::ExitException) + ::Timeout::ExitException + else + ::Timeout::Error end end end diff --git a/lib/mysql2/client.rb b/lib/mysql2/client.rb index 2c3a92d72..2cc0953cf 100644 --- a/lib/mysql2/client.rb +++ b/lib/mysql2/client.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module Mysql2 class Client attr_reader :query_options, :read_timeout @@ -33,7 +31,7 @@ def initialize(opts = {}) 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, :automatic_close, :enable_cleartext_plugin].each do |key| + %i[reconnect connect_timeout local_infile read_timeout write_timeout default_file default_group secure_auth init_command automatic_close enable_cleartext_plugin].each do |key| next unless opts.key?(key) case key when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin @@ -71,7 +69,7 @@ def initialize(opts = {}) conn_attrs = opts[:connect_attrs] || {} conn_attrs[:program_name] = $PROGRAM_NAME unless conn_attrs.key?(:program_name) - if [:user, :pass, :hostname, :dbname, :db, :sock].any? { |k| @query_options.key?(k) } + if %i[user pass hostname dbname db sock].any? { |k| @query_options.key?(k) } warn "============= WARNING FROM mysql2 =============" warn "The options :user, :pass, :hostname, :dbname, :db, and :sock are deprecated and will be removed at some point in the future." warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options." @@ -124,14 +122,8 @@ def parse_flags_array(flags, initial = 0) end end - if Thread.respond_to?(:handle_interrupt) - def query(sql, options = {}) - Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do - _query(sql, @query_options.merge(options)) - end - end - else - def query(sql, options = {}) + def query(sql, options = {}) + Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do _query(sql, @query_options.merge(options)) end end diff --git a/lib/mysql2/console.rb b/lib/mysql2/console.rb index 1e3885ffa..d8fb9e324 100644 --- a/lib/mysql2/console.rb +++ b/lib/mysql2/console.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - # Loaded by script/console. Land helpers here. Pry.config.prompt = lambda do |context, *| diff --git a/lib/mysql2/em.rb b/lib/mysql2/em.rb index 381a2a739..329b3080e 100644 --- a/lib/mysql2/em.rb +++ b/lib/mysql2/em.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - require 'eventmachine' require 'mysql2' diff --git a/lib/mysql2/error.rb b/lib/mysql2/error.rb index a66fce45d..758f01a98 100644 --- a/lib/mysql2/error.rb +++ b/lib/mysql2/error.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module Mysql2 class Error < StandardError ENCODE_OPTS = { @@ -8,25 +6,60 @@ class Error < StandardError replace: '?'.freeze, }.freeze + ConnectionError = Class.new(Error) + TimeoutError = Class.new(Error) + + CODES = { + 1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT + + 1044 => ConnectionError, # ER_DBACCESS_DENIED_ERROR + 1045 => ConnectionError, # ER_ACCESS_DENIED_ERROR + 1152 => ConnectionError, # ER_ABORTING_CONNECTION + 1153 => ConnectionError, # ER_NET_PACKET_TOO_LARGE + 1154 => ConnectionError, # ER_NET_READ_ERROR_FROM_PIPE + 1155 => ConnectionError, # ER_NET_FCNTL_ERROR + 1156 => ConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER + 1157 => ConnectionError, # ER_NET_UNCOMPRESS_ERROR + 1158 => ConnectionError, # ER_NET_READ_ERROR + 1159 => ConnectionError, # ER_NET_READ_INTERRUPTED + 1160 => ConnectionError, # ER_NET_ERROR_ON_WRITE + 1161 => ConnectionError, # ER_NET_WRITE_INTERRUPTED + + 2001 => ConnectionError, # CR_SOCKET_CREATE_ERROR + 2002 => ConnectionError, # CR_CONNECTION_ERROR + 2003 => ConnectionError, # CR_CONN_HOST_ERROR + 2004 => ConnectionError, # CR_IPSOCK_ERROR + 2005 => ConnectionError, # CR_UNKNOWN_HOST + 2006 => ConnectionError, # CR_SERVER_GONE_ERROR + 2007 => ConnectionError, # CR_VERSION_ERROR + 2009 => ConnectionError, # CR_WRONG_HOST_INFO + 2012 => ConnectionError, # CR_SERVER_HANDSHAKE_ERR + 2013 => ConnectionError, # CR_SERVER_LOST + 2020 => ConnectionError, # CR_NET_PACKET_TOO_LARGE + 2026 => ConnectionError, # CR_SSL_CONNECTION_ERROR + 2027 => ConnectionError, # CR_MALFORMED_PACKET + 2047 => ConnectionError, # CR_CONN_UNKNOW_PROTOCOL + 2048 => ConnectionError, # CR_INVALID_CONN_HANDLE + 2049 => ConnectionError, # CR_UNUSED_1 + }.freeze + attr_reader :error_number, :sql_state # Mysql gem compatibility alias errno error_number alias error message - def initialize(msg) - @server_version ||= nil + def initialize(msg, server_version = nil, error_number = nil, sql_state = nil) + @server_version = server_version + @error_number = error_number + @sql_state = sql_state ? sql_state.encode(ENCODE_OPTS) : nil super(clean_message(msg)) end def self.new_with_args(msg, server_version, error_number, sql_state) - err = allocate - err.instance_variable_set('@server_version', server_version) - err.instance_variable_set('@error_number', error_number) - err.instance_variable_set('@sql_state', sql_state.encode(ENCODE_OPTS)) - err.send(:initialize, msg) - err + error_class = CODES.fetch(error_number, self) + error_class.new(msg, server_version, error_number, sql_state) end private diff --git a/lib/mysql2/field.rb b/lib/mysql2/field.rb index 94a006739..516ec17c2 100644 --- a/lib/mysql2/field.rb +++ b/lib/mysql2/field.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module Mysql2 Field = Struct.new(:name, :type) end diff --git a/lib/mysql2/result.rb b/lib/mysql2/result.rb index 255026a2b..585104e0b 100644 --- a/lib/mysql2/result.rb +++ b/lib/mysql2/result.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module Mysql2 class Result attr_reader :server_flags diff --git a/lib/mysql2/statement.rb b/lib/mysql2/statement.rb index 482ccce19..2f9f09a05 100644 --- a/lib/mysql2/statement.rb +++ b/lib/mysql2/statement.rb @@ -1,18 +1,10 @@ -# encoding: UTF-8 - module Mysql2 class Statement include Enumerable - if Thread.respond_to?(:handle_interrupt) - def execute(*args) - Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do - _execute(*args) - end - end - else - def execute(*args) - _execute(*args) + def execute(*args, **kwargs) + Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do + _execute(*args, **kwargs) end end end diff --git a/lib/mysql2/version.rb b/lib/mysql2/version.rb index 32a02ba3e..4ebca1dd0 100644 --- a/lib/mysql2/version.rb +++ b/lib/mysql2/version.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - module Mysql2 - VERSION = "0.4.11.latin1utf8" + VERSION = "0.4.11.latin1utf8".freeze end diff --git a/mysql2.gemspec b/mysql2.gemspec index 3e9d4e5e3..98a89b95a 100644 --- a/mysql2.gemspec +++ b/mysql2.gemspec @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require File.expand_path('../lib/mysql2/version', __FILE__) Mysql2::GEMSPEC = Gem::Specification.new do |s| @@ -9,10 +7,10 @@ Mysql2::GEMSPEC = Gem::Specification.new do |s| s.license = "MIT" s.email = ['seniorlopez@gmail.com', 'aaron@serendipity.cx'] s.extensions = ["ext/mysql2/extconf.rb"] - s.homepage = 'http://github.com/brianmario/mysql2' + s.homepage = 'https://github.com/brianmario/mysql2' s.rdoc_options = ["--charset=UTF-8"] s.summary = 'A simple, fast Mysql library for Ruby, binding to libmysql' - s.required_ruby_version = '>= 1.9.3' + s.required_ruby_version = '>= 2.0.0' s.files = `git ls-files README.md CHANGELOG.md LICENSE ext lib support`.split s.test_files = `git ls-files spec examples`.split diff --git a/spec/em/em_spec.rb b/spec/em/em_spec.rb index 4daab27bc..c57d474dd 100644 --- a/spec/em/em_spec.rb +++ b/spec/em/em_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'spec_helper' begin require 'eventmachine' diff --git a/spec/mysql2/client_spec.rb b/spec/mysql2/client_spec.rb index 3c72c2a99..b61bd7e03 100644 --- a/spec/mysql2/client_spec.rb +++ b/spec/mysql2/client_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'spec_helper' RSpec.describe Mysql2::Client do @@ -19,12 +17,12 @@ end end - it "should raise an exception upon connection failure" do + it "should raise a Mysql::Error::ConnectionError upon connection failure" do expect do # The odd local host IP address forces the mysql client library to # use a TCP socket rather than a domain socket. new_client('host' => '127.0.0.2', 'port' => 999999) - end.to raise_error(Mysql2::Error) + end.to raise_error(Mysql2::Error::ConnectionError) end it "should raise an exception on create for invalid encodings" do @@ -561,7 +559,7 @@ def run_gc client = new_client(read_timeout: 0) expect do client.query('SELECT SLEEP(0.1)') - end.to raise_error(Mysql2::Error) + end.to raise_error(Mysql2::Error::TimeoutError) end # XXX this test is not deterministic (because Unix signal handling is not) @@ -607,10 +605,6 @@ def run_gc end it 'should be impervious to connection-corrupting timeouts in #execute' do - # the statement handle gets corrupted and will segfault the tests if interrupted, - # so we can't even use pending on this test, really have to skip it on older Rubies. - skip('`Thread.handle_interrupt` is not defined') unless Thread.respond_to?(:handle_interrupt) - # attempt to break the connection stmt = @client.prepare('SELECT SLEEP(?)') expect { Timeout.timeout(0.1) { stmt.execute(0.2) } }.to raise_error(Timeout::Error) @@ -924,10 +918,10 @@ def run_gc end end - it "should raise a Mysql2::Error exception upon connection failure" do + it "should raise a Mysql2::Error::ConnectionError exception upon connection failure due to invalid credentials" do expect do - new_client(host: "localhost", username: 'asdfasdf8d2h', password: 'asdfasdfw42') - end.to raise_error(Mysql2::Error) + new_client(host: 'localhost', username: 'asdfasdf8d2h', password: 'asdfasdfw42') + end.to raise_error(Mysql2::Error::ConnectionError) expect do new_client(DatabaseCredentials['root']) diff --git a/spec/mysql2/error_spec.rb b/spec/mysql2/error_spec.rb index be3154be9..c94a31961 100644 --- a/spec/mysql2/error_spec.rb +++ b/spec/mysql2/error_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'spec_helper' RSpec.describe Mysql2::Error do diff --git a/spec/mysql2/result_spec.rb b/spec/mysql2/result_spec.rb index f1386b16c..89744fc2f 100644 --- a/spec/mysql2/result_spec.rb +++ b/spec/mysql2/result_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'spec_helper' RSpec.describe Mysql2::Result do @@ -109,12 +107,10 @@ end context "#fields" do - before(:each) do - @test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1") - end + let(:test_result) { @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1") } it "method should exist" do - expect(@test_result).to respond_to(:fields) + expect(test_result).to respond_to(:fields) end it "should return an array of field names in proper order" do @@ -175,9 +171,7 @@ end context "row data type mapping" do - before(:each) do - @test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first - end + let(:test_result) { @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first } it "should return nil values for NULL and strings for everything else when :cast is false" do result = @client.query('SELECT null_test, tiny_int_test, bool_cast_test, int_test, date_test, enum_test FROM mysql2_test WHERE bool_cast_test = 1 LIMIT 1', cast: false).first @@ -190,23 +184,23 @@ end it "should return nil for a NULL value" do - expect(@test_result['null_test']).to be_an_instance_of(NilClass) - expect(@test_result['null_test']).to eql(nil) + expect(test_result['null_test']).to be_an_instance_of(NilClass) + expect(test_result['null_test']).to eql(nil) end it "should return String for a BIT(64) value" do - expect(@test_result['bit_test']).to be_an_instance_of(String) - expect(@test_result['bit_test']).to eql("\000\000\000\000\000\000\000\005") + expect(test_result['bit_test']).to be_an_instance_of(String) + expect(test_result['bit_test']).to eql("\000\000\000\000\000\000\000\005") end it "should return String for a BIT(1) value" do - expect(@test_result['single_bit_test']).to be_an_instance_of(String) - expect(@test_result['single_bit_test']).to eql("\001") + expect(test_result['single_bit_test']).to be_an_instance_of(String) + expect(test_result['single_bit_test']).to eql("\001") end it "should return Fixnum for a TINYINT value" do - expect(num_classes).to include(@test_result['tiny_int_test'].class) - expect(@test_result['tiny_int_test']).to eql(1) + expect(num_classes).to include(test_result['tiny_int_test'].class) + expect(test_result['tiny_int_test']).to eql(1) end context "cast booleans for TINYINT if :cast_booleans is enabled" do @@ -249,48 +243,48 @@ end it "should return Fixnum for a SMALLINT value" do - expect(num_classes).to include(@test_result['small_int_test'].class) - expect(@test_result['small_int_test']).to eql(10) + expect(num_classes).to include(test_result['small_int_test'].class) + expect(test_result['small_int_test']).to eql(10) end it "should return Fixnum for a MEDIUMINT value" do - expect(num_classes).to include(@test_result['medium_int_test'].class) - expect(@test_result['medium_int_test']).to eql(10) + expect(num_classes).to include(test_result['medium_int_test'].class) + expect(test_result['medium_int_test']).to eql(10) end it "should return Fixnum for an INT value" do - expect(num_classes).to include(@test_result['int_test'].class) - expect(@test_result['int_test']).to eql(10) + expect(num_classes).to include(test_result['int_test'].class) + expect(test_result['int_test']).to eql(10) end it "should return Fixnum for a BIGINT value" do - expect(num_classes).to include(@test_result['big_int_test'].class) - expect(@test_result['big_int_test']).to eql(10) + expect(num_classes).to include(test_result['big_int_test'].class) + expect(test_result['big_int_test']).to eql(10) end it "should return Fixnum for a YEAR value" do - expect(num_classes).to include(@test_result['year_test'].class) - expect(@test_result['year_test']).to eql(2009) + expect(num_classes).to include(test_result['year_test'].class) + expect(test_result['year_test']).to eql(2009) end it "should return BigDecimal for a DECIMAL value" do - expect(@test_result['decimal_test']).to be_an_instance_of(BigDecimal) - expect(@test_result['decimal_test']).to eql(10.3) + expect(test_result['decimal_test']).to be_an_instance_of(BigDecimal) + expect(test_result['decimal_test']).to eql(10.3) end it "should return Float for a FLOAT value" do - expect(@test_result['float_test']).to be_an_instance_of(Float) - expect(@test_result['float_test']).to eql(10.3) + expect(test_result['float_test']).to be_an_instance_of(Float) + expect(test_result['float_test']).to eql(10.3) end it "should return Float for a DOUBLE value" do - expect(@test_result['double_test']).to be_an_instance_of(Float) - expect(@test_result['double_test']).to eql(10.3) + expect(test_result['double_test']).to be_an_instance_of(Float) + expect(test_result['double_test']).to eql(10.3) end it "should return Time for a DATETIME value when within the supported range" do - expect(@test_result['date_time_test']).to be_an_instance_of(Time) - expect(@test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') + expect(test_result['date_time_test']).to be_an_instance_of(Time) + expect(test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') end it "should return Time when timestamp is < 1901-12-13 20:45:52" do @@ -304,23 +298,23 @@ end it "should return Time for a TIMESTAMP value when within the supported range" do - expect(@test_result['timestamp_test']).to be_an_instance_of(Time) - expect(@test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') + expect(test_result['timestamp_test']).to be_an_instance_of(Time) + expect(test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') end it "should return Time for a TIME value" do - expect(@test_result['time_test']).to be_an_instance_of(Time) - expect(@test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2000-01-01 11:44:00') + expect(test_result['time_test']).to be_an_instance_of(Time) + expect(test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2000-01-01 11:44:00') end it "should return Date for a DATE value" do - expect(@test_result['date_test']).to be_an_instance_of(Date) - expect(@test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04') + expect(test_result['date_test']).to be_an_instance_of(Date) + expect(test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04') end it "should return String for an ENUM value" do - expect(@test_result['enum_test']).to be_an_instance_of(String) - expect(@test_result['enum_test']).to eql('val1') + expect(test_result['enum_test']).to be_an_instance_of(String) + expect(test_result['enum_test']).to eql('val1') end it "should raise an error given an invalid DATETIME" do @@ -354,8 +348,8 @@ end it "should return String for a SET value" do - expect(@test_result['set_test']).to be_an_instance_of(String) - expect(@test_result['set_test']).to eql('val1,val2') + expect(test_result['set_test']).to be_an_instance_of(String) + expect(test_result['set_test']).to eql('val1,val2') end context "string encoding for SET values" do @@ -384,8 +378,8 @@ end it "should return String for a BINARY value" do - expect(@test_result['binary_test']).to be_an_instance_of(String) - expect(@test_result['binary_test']).to eql("test#{"\000" * 6}") + expect(test_result['binary_test']).to be_an_instance_of(String) + expect(test_result['binary_test']).to eql("test#{"\000" * 6}") end context "string encoding for BINARY values" do @@ -423,8 +417,8 @@ 'long_text_test' => 'LONGTEXT', }.each do |field, type| it "should return a String for #{type}" do - expect(@test_result[field]).to be_an_instance_of(String) - expect(@test_result[field]).to eql("test") + expect(test_result[field]).to be_an_instance_of(String) + expect(test_result[field]).to eql("test") end context "string encoding for #{type} values" do @@ -476,18 +470,16 @@ end context "server flags" do - before(:each) do - @test_result = @client.query("SELECT * FROM mysql2_test ORDER BY null_test DESC LIMIT 1") - end + let(:test_result) { @client.query("SELECT * FROM mysql2_test ORDER BY null_test DESC LIMIT 1") } it "should set a definitive value for query_was_slow" do - expect(@test_result.server_flags[:query_was_slow]).to eql(false) + expect(test_result.server_flags[:query_was_slow]).to eql(false) end it "should set a definitive value for no_index_used" do - expect(@test_result.server_flags[:no_index_used]).to eql(true) + expect(test_result.server_flags[:no_index_used]).to eql(true) end it "should set a definitive value for no_good_index_used" do - expect(@test_result.server_flags[:no_good_index_used]).to eql(false) + expect(test_result.server_flags[:no_good_index_used]).to eql(false) end end end diff --git a/spec/mysql2/statement_spec.rb b/spec/mysql2/statement_spec.rb index 7d856cbd3..5259c8076 100644 --- a/spec/mysql2/statement_spec.rb +++ b/spec/mysql2/statement_spec.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require './spec/spec_helper.rb' RSpec.describe Mysql2::Statement do @@ -88,6 +86,20 @@ def stmt_count expect(result.to_a).to eq(['max1' => int64_max1, 'max2' => int64_max2, 'max3' => int64_max3, 'min1' => int64_min1, 'min2' => int64_min2, 'min3' => int64_min3]) end + it "should accept keyword arguments on statement execute" do + stmt = @client.prepare 'SELECT 1 AS a' + + expect(stmt.execute(as: :hash).first).to eq("a" => 1) + expect(stmt.execute(as: :array).first).to eq([1]) + end + + it "should accept bind arguments and keyword arguments on statement execute" do + stmt = @client.prepare 'SELECT ? AS a' + + expect(stmt.execute(1, as: :hash).first).to eq("a" => 1) + expect(stmt.execute(1, as: :array).first).to eq([1]) + end + it "should keep its result after other query" do @client.query 'USE test' @client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int)' @@ -188,10 +200,9 @@ def stmt_count end it "should warn but still work if cache_rows is set to false" do - @client.query_options[:cache_rows] = false statement = @client.prepare 'SELECT 1' result = nil - expect { result = statement.execute.to_a }.to output(/:cache_rows is forced for prepared statements/).to_stderr + expect { result = statement.execute(cache_rows: false).to_a }.to output(/:cache_rows is forced for prepared statements/).to_stderr expect(result.length).to eq(1) end @@ -240,10 +251,7 @@ def stmt_count it "should be able to stream query result" do n = 1 stmt = @client.prepare("SELECT 1 UNION SELECT 2") - - @client.query_options.merge!(stream: true, cache_rows: false, as: :array) - - stmt.execute.each do |r| + stmt.execute(stream: true, cache_rows: false, as: :array).each do |r| case n when 1 expect(r).to eq([1]) @@ -269,23 +277,17 @@ def stmt_count end it "should yield rows as hash's with symbol keys if :symbolize_keys was set to true" do - @client.query_options[:symbolize_keys] = true - @result = @client.prepare("SELECT 1").execute + @result = @client.prepare("SELECT 1").execute(symbolize_keys: true) @result.each do |row| expect(row.keys.first).to be_an_instance_of(Symbol) end - @client.query_options[:symbolize_keys] = false end it "should be able to return results as an array" do - @client.query_options[:as] = :array - - @result = @client.prepare("SELECT 1").execute + @result = @client.prepare("SELECT 1").execute(as: :array) @result.each do |row| expect(row).to be_an_instance_of(Array) end - - @client.query_options[:as] = :hash end it "should cache previously yielded results by default" do @@ -294,35 +296,21 @@ def stmt_count end it "should yield different value for #first if streaming" do - @client.query_options[:stream] = true - @client.query_options[:cache_rows] = false - - result = @client.prepare("SELECT 1 UNION SELECT 2").execute + result = @client.prepare("SELECT 1 UNION SELECT 2").execute(stream: true, cache_rows: true) expect(result.first).not_to eql(result.first) - - @client.query_options[:stream] = false - @client.query_options[:cache_rows] = true end it "should yield the same value for #first if streaming is disabled" do - @client.query_options[:stream] = false - result = @client.prepare("SELECT 1 UNION SELECT 2").execute + result = @client.prepare("SELECT 1 UNION SELECT 2").execute(stream: false) expect(result.first).to eql(result.first) end it "should throw an exception if we try to iterate twice when streaming is enabled" do - @client.query_options[:stream] = true - @client.query_options[:cache_rows] = false - - result = @client.prepare("SELECT 1 UNION SELECT 2").execute - + result = @client.prepare("SELECT 1 UNION SELECT 2").execute(stream: true, cache_rows: false) expect do result.each {} result.each {} end.to raise_exception(Mysql2::Error) - - @client.query_options[:stream] = false - @client.query_options[:cache_rows] = true end end @@ -344,48 +332,44 @@ def stmt_count end context "row data type mapping" do - before(:each) do - @client.query "USE test" - @test_result = @client.prepare("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").execute.first - end + let(:test_result) { @client.prepare("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").execute.first } it "should return nil for a NULL value" do - expect(@test_result['null_test']).to be_an_instance_of(NilClass) - expect(@test_result['null_test']).to eql(nil) + expect(test_result['null_test']).to be_an_instance_of(NilClass) + expect(test_result['null_test']).to eql(nil) end it "should return String for a BIT(64) value" do - expect(@test_result['bit_test']).to be_an_instance_of(String) - expect(@test_result['bit_test']).to eql("\000\000\000\000\000\000\000\005") + expect(test_result['bit_test']).to be_an_instance_of(String) + expect(test_result['bit_test']).to eql("\000\000\000\000\000\000\000\005") end it "should return String for a BIT(1) value" do - expect(@test_result['single_bit_test']).to be_an_instance_of(String) - expect(@test_result['single_bit_test']).to eql("\001") + expect(test_result['single_bit_test']).to be_an_instance_of(String) + expect(test_result['single_bit_test']).to eql("\001") end it "should return Fixnum for a TINYINT value" do - expect(num_classes).to include(@test_result['tiny_int_test'].class) - expect(@test_result['tiny_int_test']).to eql(1) + expect(num_classes).to include(test_result['tiny_int_test'].class) + expect(test_result['tiny_int_test']).to eql(1) end context "cast booleans for TINYINT if :cast_booleans is enabled" do # rubocop:disable Style/Semicolon - let(:client) { new_client(cast_booleans: true) } - let(:id1) { client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES ( 1)'; client.last_id } - let(:id2) { client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES ( 0)'; client.last_id } - let(:id3) { client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (-1)'; client.last_id } + let(:id1) { @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES ( 1)'; @client.last_id } + let(:id2) { @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES ( 0)'; @client.last_id } + let(:id3) { @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (-1)'; @client.last_id } # rubocop:enable Style/Semicolon after do - client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2},#{id3})" + @client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2},#{id3})" end it "should return TrueClass or FalseClass for a TINYINT value if :cast_booleans is enabled" do - query = client.prepare 'SELECT bool_cast_test FROM mysql2_test WHERE id = ?' - result1 = query.execute id1 - result2 = query.execute id2 - result3 = query.execute id3 + query = @client.prepare 'SELECT bool_cast_test FROM mysql2_test WHERE id = ?' + result1 = query.execute id1, cast_booleans: true + result2 = query.execute id2, cast_booleans: true + result3 = query.execute id3, cast_booleans: true expect(result1.first['bool_cast_test']).to be true expect(result2.first['bool_cast_test']).to be false expect(result3.first['bool_cast_test']).to be true @@ -394,67 +378,66 @@ def stmt_count context "cast booleans for BIT(1) if :cast_booleans is enabled" do # rubocop:disable Style/Semicolon - let(:client) { new_client(cast_booleans: true) } - let(:id1) { client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (1)'; client.last_id } - let(:id2) { client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (0)'; client.last_id } + let(:id1) { @client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (1)'; @client.last_id } + let(:id2) { @client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (0)'; @client.last_id } # rubocop:enable Style/Semicolon after do - client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2})" + @client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2})" end it "should return TrueClass or FalseClass for a BIT(1) value if :cast_booleans is enabled" do - query = client.prepare 'SELECT single_bit_test FROM mysql2_test WHERE id = ?' - result1 = query.execute id1 - result2 = query.execute id2 + query = @client.prepare 'SELECT single_bit_test FROM mysql2_test WHERE id = ?' + result1 = query.execute id1, cast_booleans: true + result2 = query.execute id2, cast_booleans: true expect(result1.first['single_bit_test']).to be true expect(result2.first['single_bit_test']).to be false end end it "should return Fixnum for a SMALLINT value" do - expect(num_classes).to include(@test_result['small_int_test'].class) - expect(@test_result['small_int_test']).to eql(10) + expect(num_classes).to include(test_result['small_int_test'].class) + expect(test_result['small_int_test']).to eql(10) end it "should return Fixnum for a MEDIUMINT value" do - expect(num_classes).to include(@test_result['medium_int_test'].class) - expect(@test_result['medium_int_test']).to eql(10) + expect(num_classes).to include(test_result['medium_int_test'].class) + expect(test_result['medium_int_test']).to eql(10) end it "should return Fixnum for an INT value" do - expect(num_classes).to include(@test_result['int_test'].class) - expect(@test_result['int_test']).to eql(10) + expect(num_classes).to include(test_result['int_test'].class) + expect(test_result['int_test']).to eql(10) end it "should return Fixnum for a BIGINT value" do - expect(num_classes).to include(@test_result['big_int_test'].class) - expect(@test_result['big_int_test']).to eql(10) + expect(num_classes).to include(test_result['big_int_test'].class) + expect(test_result['big_int_test']).to eql(10) end it "should return Fixnum for a YEAR value" do - expect(num_classes).to include(@test_result['year_test'].class) - expect(@test_result['year_test']).to eql(2009) + expect(num_classes).to include(test_result['year_test'].class) + expect(test_result['year_test']).to eql(2009) end it "should return BigDecimal for a DECIMAL value" do - expect(@test_result['decimal_test']).to be_an_instance_of(BigDecimal) - expect(@test_result['decimal_test']).to eql(10.3) + expect(test_result['decimal_test']).to be_an_instance_of(BigDecimal) + expect(test_result['decimal_test']).to eql(10.3) end it "should return Float for a FLOAT value" do - expect(@test_result['float_test']).to be_an_instance_of(Float) - expect(@test_result['float_test']).to be_within(1e-5).of(10.3) + expect(test_result['float_test']).to be_an_instance_of(Float) + expect(test_result['float_test']).to be_within(1e-5).of(10.3) end it "should return Float for a DOUBLE value" do - expect(@test_result['double_test']).to be_an_instance_of(Float) - expect(@test_result['double_test']).to eql(10.3) + expect(test_result['double_test']).to be_an_instance_of(Float) + expect(test_result['double_test']).to eql(10.3) end it "should return Time for a DATETIME value when within the supported range" do - expect(@test_result['date_time_test']).to be_an_instance_of(Time) - expect(@test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') + expect(test_result['date_time_test']).to be_an_instance_of(Time) + expect(test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') end it "should return Time when timestamp is < 1901-12-13 20:45:52" do @@ -468,23 +451,23 @@ def stmt_count end it "should return Time for a TIMESTAMP value when within the supported range" do - expect(@test_result['timestamp_test']).to be_an_instance_of(Time) - expect(@test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') + expect(test_result['timestamp_test']).to be_an_instance_of(Time) + expect(test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00') end it "should return Time for a TIME value" do - expect(@test_result['time_test']).to be_an_instance_of(Time) - expect(@test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2000-01-01 11:44:00') + expect(test_result['time_test']).to be_an_instance_of(Time) + expect(test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2000-01-01 11:44:00') end it "should return Date for a DATE value" do - expect(@test_result['date_test']).to be_an_instance_of(Date) - expect(@test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04') + expect(test_result['date_test']).to be_an_instance_of(Date) + expect(test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04') end it "should return String for an ENUM value" do - expect(@test_result['enum_test']).to be_an_instance_of(String) - expect(@test_result['enum_test']).to eql('val1') + expect(test_result['enum_test']).to be_an_instance_of(String) + expect(test_result['enum_test']).to eql('val1') end it "should raise an error given an invalid DATETIME" do @@ -518,8 +501,8 @@ def stmt_count end it "should return String for a SET value" do - expect(@test_result['set_test']).to be_an_instance_of(String) - expect(@test_result['set_test']).to eql('val1,val2') + expect(test_result['set_test']).to be_an_instance_of(String) + expect(test_result['set_test']).to eql('val1,val2') end context "string encoding for SET values" do @@ -548,8 +531,8 @@ def stmt_count end it "should return String for a BINARY value" do - expect(@test_result['binary_test']).to be_an_instance_of(String) - expect(@test_result['binary_test']).to eql("test#{"\000" * 6}") + expect(test_result['binary_test']).to be_an_instance_of(String) + expect(test_result['binary_test']).to eql("test#{"\000" * 6}") end context "string encoding for BINARY values" do @@ -587,8 +570,8 @@ def stmt_count 'long_text_test' => 'LONGTEXT', }.each do |field, type| it "should return a String for #{type}" do - expect(@test_result[field]).to be_an_instance_of(String) - expect(@test_result[field]).to eql("test") + expect(test_result[field]).to be_an_instance_of(String) + expect(test_result[field]).to eql("test") end context "string encoding for #{type} values" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 32e3b0069..12b48df67 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'rspec' require 'mysql2' require 'timeout' diff --git a/support/mysql_enc_to_ruby.rb b/support/mysql_enc_to_ruby.rb index fbac70212..0ca73dad8 100644 --- a/support/mysql_enc_to_ruby.rb +++ b/support/mysql_enc_to_ruby.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'mysql2' diff --git a/support/ruby_enc_to_mysql.rb b/support/ruby_enc_to_mysql.rb index eb9463e6f..b4e9444e5 100644 --- a/support/ruby_enc_to_mysql.rb +++ b/support/ruby_enc_to_mysql.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - mysql_to_rb = { "big5" => "Big5", "dec8" => nil, diff --git a/tasks/benchmarks.rake b/tasks/benchmarks.rake index 8302f62a3..b587ecdc0 100644 --- a/tasks/benchmarks.rake +++ b/tasks/benchmarks.rake @@ -1,5 +1,3 @@ -# encoding: UTF-8 - BENCHMARKS = Dir["#{File.dirname(__FILE__)}/../benchmark/*.rb"].map do |path| File.basename(path, '.rb') end - ['setup_db'] diff --git a/tasks/compile.rake b/tasks/compile.rake index ce33554f5..a9b7eb8a6 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require "rake/extensiontask" load File.expand_path('../../mysql2.gemspec', __FILE__) unless defined? Mysql2::GEMSPEC diff --git a/tasks/generate.rake b/tasks/generate.rake index 2712eeaee..6e5eebd99 100644 --- a/tasks/generate.rake +++ b/tasks/generate.rake @@ -1,5 +1,3 @@ -# encoding: UTF-8 - task :encodings do sh "ruby support/mysql_enc_to_ruby.rb > ./ext/mysql2/mysql_enc_to_ruby.h" sh "ruby support/ruby_enc_to_mysql.rb | gperf > ./ext/mysql2/mysql_enc_name_to_ruby.h" diff --git a/tasks/rspec.rake b/tasks/rspec.rake index a7bbfb098..ea8dc2258 100644 --- a/tasks/rspec.rake +++ b/tasks/rspec.rake @@ -1,5 +1,3 @@ -# encoding: UTF-8 - begin require 'rspec' require 'rspec/core/rake_task' diff --git a/tasks/vendor_mysql.rake b/tasks/vendor_mysql.rake index 6d77be208..85e88fe68 100644 --- a/tasks/vendor_mysql.rake +++ b/tasks/vendor_mysql.rake @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'rake/clean' require 'rake/extensioncompiler'