diff --git a/.rubocop.yml b/.rubocop.yml index a9cd803dc..d41d8d923 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -15,9 +15,6 @@ Layout/CaseIndentation: Layout/IndentHash: EnforcedStyle: consistent -Layout/IndentHeredoc: - EnforcedStyle: powerpack - Lint/EndAlignment: EnforcedStyleAlignWith: variable diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e675d69af..3da4399a2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,6 +6,15 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent +Layout/IndentHeredoc: + Exclude: + - 'support/ruby_enc_to_mysql.rb' + - 'tasks/compile.rake' + # Offense count: 2 Metrics/AbcSize: Max: 90 @@ -13,7 +22,7 @@ Metrics/AbcSize: # Offense count: 31 # Configuration parameters: CountComments, ExcludedMethods. Metrics/BlockLength: - Max: 825 + Max: 850 # Offense count: 1 # Configuration parameters: CountBlocks. diff --git a/.travis.yml b/.travis.yml index 9a3bb367b..a8661f785 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.7.3 + - gem update --system 2.7.6 --quiet - gem update bundler - gem --version - bash .travis_setup.sh @@ -18,6 +18,7 @@ addons: - mysql-client-core-5.6 - mysql-client-5.6 rvm: + - 2.6 - 2.5 - 2.4 - 2.3 @@ -45,6 +46,12 @@ matrix: mariadb: 10.2 hosts: - mysql2gem.example.com + - rvm: 2.4 + env: DB=mariadb10.3 + addons: + mariadb: 10.3 + hosts: + - mysql2gem.example.com - rvm: 2.4 env: DB=mysql55 addons: diff --git a/.travis_mysql55.sh b/.travis_mysql55.sh index 771250dc0..ffc2f9571 100644 --- a/.travis_mysql55.sh +++ b/.travis_mysql55.sh @@ -5,4 +5,5 @@ set -eux apt-get purge -qq '^mysql*' '^libmysql*' rm -fr /etc/mysql rm -fr /var/lib/mysql +apt-get update -qq apt-get install -qq mysql-server-5.5 mysql-client-core-5.5 mysql-client-5.5 libmysqlclient-dev diff --git a/README.md b/README.md index a8bb2085a..bd7b5dc64 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,11 @@ To see line numbers in backtraces, declare these environment variables ### Linux and other Unixes -You may need to install a package such as `libmysqlclient-dev` or `mysql-devel`; -refer to your distribution's package guide to find the particular package. -The most common issue we see is a user who has the library file `libmysqlclient.so` but is -missing the header file `mysql.h` -- double check that you have the _-dev_ packages installed. +You may need to install a package such as `libmysqlclient-dev`, `mysql-devel`, +or `default-libmysqlclient-dev`; refer to your distribution's package guide to +find the particular package. The most common issue we see is a user who has +the library file `libmysqlclient.so` but is missing the header file `mysql.h` +-- double check that you have the _-dev_ packages installed. ### Mac OS X @@ -138,7 +139,7 @@ results.each do |row| # conveniently, row is a hash # the keys are the fields, as you'd expect # the values are pre-built ruby primitives mapped from their corresponding field types in MySQL - puts row["id"] # row["id"].class == Fixnum + puts row["id"] # row["id"].is_a? Integer if row["dne"] # non-existant hash entry is nil puts row["dne"] end @@ -178,6 +179,9 @@ Pass your arguments to the execute method in the same number and order as the question marks in the statement. Query options can be passed as keyword arguments to the execute method. +Be sure to read about the known limitations of prepared statements at +https://dev.mysql.com/doc/refman/5.6/en/c-api-prepared-statement-problems.html + ``` ruby statement = @client.prepare("SELECT * FROM users WHERE login_count = ?") result1 = statement.execute(1) @@ -523,17 +527,18 @@ 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 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x + * Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.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: * MySQL 5.5, 5.6, 5.7, 8.0 * MySQL Connector/C 6.0 and 6.1 (primarily on Windows) - * MariaDB 5.5, 10.0, 10.1, 10.2 + * MariaDB 5.5, 10.0, 10.1, 10.2, 10.3 ### Ruby on Rails / Active Record + * mysql2 0.5.x works with Rails / Active Record 5.0.7, 5.1.6, and higher. * mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher. * mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0. * mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0. diff --git a/ext/mysql2/client.c b/ext/mysql2/client.c index 937077964..d12f2c180 100644 --- a/ext/mysql2/client.c +++ b/ext/mysql2/client.c @@ -114,7 +114,7 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) { int val = NUM2INT( setting ); if (version >= 50703 && version < 50711) { if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) { - bool b = ( val == SSL_MODE_REQUIRED ); + my_bool b = ( val == SSL_MODE_REQUIRED ); int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b ); return INT2NUM(result); } else { @@ -526,7 +526,7 @@ static VALUE do_send_query(void *args) { */ static void *nogvl_read_query_result(void *ptr) { MYSQL * client = ptr; - bool res = mysql_read_query_result(client); + my_bool res = mysql_read_query_result(client); return (void *)(res == 0 ? Qtrue : Qfalse); } @@ -846,7 +846,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) { const void *retval = NULL; unsigned int intval = 0; const char * charval = NULL; - bool boolval; + my_bool boolval; GET_CLIENT(self); @@ -1098,6 +1098,23 @@ static VALUE rb_mysql_client_ping(VALUE self) { } } +/* call-seq: + * client.set_server_option(value) + * + * Enables or disables an option for the connection. + * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html + * for more information. + */ +static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) { + GET_CLIENT(self); + + if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) { + return Qtrue; + } else { + return Qfalse; + } +} + /* call-seq: * client.more_results? * @@ -1399,6 +1416,7 @@ void init_mysql2_client() { rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0); rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0); rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1); + rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1); 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); @@ -1528,6 +1546,16 @@ void init_mysql2_client() { rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0)); #endif +#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON + rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"), + LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON)); +#endif + +#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF + rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"), + LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF)); +#endif + #ifdef CLIENT_MULTI_STATEMENTS rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"), LONG2NUM(CLIENT_MULTI_STATEMENTS)); diff --git a/ext/mysql2/extconf.rb b/ext/mysql2/extconf.rb index 8755cb091..190e930e1 100644 --- a/ext/mysql2/extconf.rb +++ b/ext/mysql2/extconf.rb @@ -63,6 +63,7 @@ def add_ssl_defines(header) 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-----" rpath_dir = lib + have_library('mysqlclient') elsif (mc = (with_config('mysql-config') || Dir[GLOB].first)) # If the user has provided a --with-mysql-config argument, we must respect it or fail. # If the user gave --with-mysql-config with no argument means we should try to find it. @@ -105,11 +106,18 @@ def add_ssl_defines(header) add_ssl_defines(mysql_h) have_struct_member('MYSQL', 'net.vio', mysql_h) have_struct_member('MYSQL', 'net.pvio', mysql_h) + # These constants are actually enums, so they cannot be detected by #ifdef in C code. have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h) have_const('SERVER_QUERY_NO_GOOD_INDEX_USED', mysql_h) have_const('SERVER_QUERY_NO_INDEX_USED', mysql_h) have_const('SERVER_QUERY_WAS_SLOW', mysql_h) +have_const('MYSQL_OPTION_MULTI_STATEMENTS_ON', mysql_h) +have_const('MYSQL_OPTION_MULTI_STATEMENTS_OFF', mysql_h) + +# my_bool is replaced by C99 bool in MySQL 8.0, but we want +# to retain compatibility with the typedef in earlier MySQLs. +have_type('my_bool', mysql_h) # This is our wishlist. We use whichever flags work on the host. # -Wall and -Wextra are included by default. diff --git a/ext/mysql2/mysql2_ext.h b/ext/mysql2/mysql2_ext.h index f76ea45ec..e1ce0e943 100644 --- a/ext/mysql2/mysql2_ext.h +++ b/ext/mysql2/mysql2_ext.h @@ -28,6 +28,14 @@ void Init_mysql2(void); #define RB_MYSQL_UNUSED #endif +/* MySQL 8.0 replaces my_bool with C99 bool. Earlier versions of MySQL had + * a typedef to char. Gem users reported failures on big endian systems when + * using C99 bool types with older MySQLs due to mismatched behavior. */ +#ifndef HAVE_TYPE_MY_BOOL +#include +typedef bool my_bool; +#endif + #include #include #include diff --git a/ext/mysql2/mysql_enc_to_ruby.h b/ext/mysql2/mysql_enc_to_ruby.h index 20d545c98..b32b6925f 100644 --- a/ext/mysql2/mysql_enc_to_ruby.h +++ b/ext/mysql2/mysql_enc_to_ruby.h @@ -245,5 +245,15 @@ static const char *mysql2_mysql_enc_to_rb[] = { "UTF-8", "UTF-8", "UTF-8", + "UTF-8", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, "UTF-8" }; + +#define CHARSETNR_SIZE (sizeof(mysql2_mysql_enc_to_rb)/sizeof(mysql2_mysql_enc_to_rb[0])) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 857de908e..0ab38aabe 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -179,7 +179,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e const char *enc_name; int enc_index; - enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1]; + enc_name = (field.charsetnr-1 < CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL; + if (enc_name != NULL) { /* use the field encoding we were able to match */ enc_index = rb_enc_find_index(enc_name); @@ -218,8 +219,8 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields if (wrapper->result_buffers != NULL) return; wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND)); - wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool)); - wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool)); + wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool)); + wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool)); wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long)); for (i = 0; i < wrapper->numberOfFields; i++) { diff --git a/ext/mysql2/result.h b/ext/mysql2/result.h index 525a2188b..0c25b24b6 100644 --- a/ext/mysql2/result.h +++ b/ext/mysql2/result.h @@ -1,6 +1,5 @@ #ifndef MYSQL2_RESULT_H #define MYSQL2_RESULT_H -#include void init_mysql2_result(void); VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement); @@ -22,8 +21,8 @@ typedef struct { mysql_client_wrapper *client_wrapper; /* statement result bind buffers */ MYSQL_BIND *result_buffers; - bool *is_null; - bool *error; + my_bool *is_null; + my_bool *error; unsigned long *length; } mysql2_result_wrapper; diff --git a/ext/mysql2/statement.c b/ext/mysql2/statement.c index 3bfba62d5..22e22ecfd 100644 --- a/ext/mysql2/statement.c +++ b/ext/mysql2/statement.c @@ -115,7 +115,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) { // set STMT_ATTR_UPDATE_MAX_LENGTH attr { - bool truth = 1; + my_bool truth = 1; if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) { rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH"); } @@ -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 < c; i++) { \ + for (i = 0; i < bind_count; i++) { \ if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \ xfree(bind_buffers[i].buffer); \ } \ @@ -247,14 +247,13 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { MYSQL_BIND *bind_buffers = NULL; unsigned long *length_buffers = NULL; unsigned long bind_count; - long i; - int c; + unsigned long i; MYSQL_STMT *stmt; MYSQL_RES *metadata; VALUE opts; VALUE current; VALUE resultObj; - VALUE *params_enc; + VALUE *params_enc = NULL; int is_streaming; rb_encoding *conn_enc; @@ -263,24 +262,26 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { conn_enc = rb_to_encoding(wrapper->encoding); - // 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 (c != (long)bind_count) { - rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c); + + // Get count of ordinary arguments, and extract hash opts/keyword arguments + // Use a local scope to avoid leaking the temporary count variable + { + int c = rb_scan_args(argc, argv, "*:", NULL, &opts); + if (c != (long)bind_count) { + rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c); + } } // setup any bind variables in the query if (bind_count > 0) { // Scratch space for string encoding exports, allocate on the stack - params_enc = alloca(sizeof(VALUE) * c); + params_enc = alloca(sizeof(VALUE) * bind_count); bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND)); length_buffers = xcalloc(bind_count, sizeof(unsigned long)); - for (i = 0; i < c; i++) { + for (i = 0; i < bind_count; i++) { bind_buffers[i].buffer = NULL; params_enc[i] = Qnil; @@ -402,6 +403,39 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) { } } + // 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)); + + // From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no + // Ruby API calls are allowed so that GC is not invoked. If the connection is + // in results-streaming-mode for Statement A, and in the middle Statement B + // gets garbage collected, a message will be sent to the server notifying it + // to release Statement B, resulting in the following error: + // Commands out of sync; you can't run this command now + // + // In streaming mode, statement execute must return a cursor because we + // cannot prevent other Statement objects from being garbage collected + // between fetches of each row of the result set. The following error + // occurs if cursor mode is not set: + // Row retrieval was canceled by mysql_stmt_close + + if (is_streaming) { + unsigned long type = CURSOR_TYPE_READ_ONLY; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) { + FREE_BINDS; + rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY"); + } + } + if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) { FREE_BINDS; rb_raise_mysql2_stmt_error(stmt_wrapper); @@ -420,17 +454,6 @@ 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 if (mysql_stmt_store_result(stmt)) { diff --git a/lib/mysql2/version.rb b/lib/mysql2/version.rb index d7a169357..474616c3f 100644 --- a/lib/mysql2/version.rb +++ b/lib/mysql2/version.rb @@ -1,3 +1,3 @@ module Mysql2 - VERSION = "0.4.10".freeze + VERSION = "0.5.1".freeze end diff --git a/spec/mysql2/client_spec.rb b/spec/mysql2/client_spec.rb index f82f13b3d..00d9c17db 100644 --- a/spec/mysql2/client_spec.rb +++ b/spec/mysql2/client_spec.rb @@ -205,6 +205,40 @@ def run_gc # rubocop:enable Lint/AmbiguousBlockAssociation end + context "#set_server_option" do + let(:client) do + new_client.tap do |client| + client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON) + end + end + + it 'returns true when multi_statements is enable' do + expect(client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)).to be true + end + + it 'returns true when multi_statements is disable' do + expect(client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)).to be true + end + + it 'returns false when multi_statements is neither OPTION_MULTI_STATEMENTS_OFF or OPTION_MULTI_STATEMENTS_ON' do + expect(client.set_server_option(344)).to be false + end + + it 'enables multiple-statement' do + client.query("SELECT 1;SELECT 2;") + + expect(client.next_result).to be true + expect(client.store_result.first).to eql('2' => 2) + expect(client.next_result).to be false + end + + it 'disables multiple-statement' do + client.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF) + + expect { client.query("SELECT 1;SELECT 2;") }.to raise_error(Mysql2::Error) + end + end + context "#automatic_close" do it "is enabled by default" do expect(new_client.automatic_close?).to be(true) diff --git a/spec/mysql2/statement_spec.rb b/spec/mysql2/statement_spec.rb index 6d617eab0..dbc185e6b 100644 --- a/spec/mysql2/statement_spec.rb +++ b/spec/mysql2/statement_spec.rb @@ -6,6 +6,10 @@ end def stmt_count + # Use the performance schema in MySQL 5.7 and above + @client.query("SELECT COUNT(1) AS count FROM performance_schema.prepared_statements_instances").first['count'].to_i + rescue Mysql2::Error + # Fall back to the global prepapred statement counter @client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i end diff --git a/support/ruby_enc_to_mysql.rb b/support/ruby_enc_to_mysql.rb index b4e9444e5..96f6f056c 100644 --- a/support/ruby_enc_to_mysql.rb +++ b/support/ruby_enc_to_mysql.rb @@ -40,14 +40,14 @@ "eucjpms" => "eucJP-ms", } -puts <<-HEADER.strip_indent - %readonly-tables - %enum - %define lookup-function-name mysql2_mysql_enc_name_to_rb - %define hash-function-name mysql2_mysql_enc_name_to_rb_hash - %struct-type - struct mysql2_mysql_enc_name_to_rb_map { const char *name; const char *rb_name; } - %% +puts <<-HEADER +%readonly-tables +%enum +%define lookup-function-name mysql2_mysql_enc_name_to_rb +%define hash-function-name mysql2_mysql_enc_name_to_rb_hash +%struct-type +struct mysql2_mysql_enc_name_to_rb_map { const char *name; const char *rb_name; } +%% HEADER mysql_to_rb.each do |mysql, ruby| diff --git a/tasks/compile.rake b/tasks/compile.rake index a9b7eb8a6..07aa1abea 100644 --- a/tasks/compile.rake +++ b/tasks/compile.rake @@ -33,20 +33,20 @@ Rake::ExtensionTask.new("mysql2", Mysql2::GEMSPEC) do |ext| spec.files << 'lib/mysql2/mysql2.rb' spec.files << 'vendor/libmysql.dll' spec.files << 'vendor/README' - spec.post_install_message = <<-POST_INSTALL_MESSAGE.strip_indent + spec.post_install_message = <<-POST_INSTALL_MESSAGE - ====================================================================================================== +====================================================================================================== - You've installed the binary version of #{spec.name}. - It was built using MySQL Connector/C version #{CONNECTOR_VERSION}. - It's recommended to use the exact same version to avoid potential issues. + You've installed the binary version of #{spec.name}. + It was built using MySQL Connector/C version #{CONNECTOR_VERSION}. + It's recommended to use the exact same version to avoid potential issues. - At the time of building this gem, the necessary DLL files were retrieved from: - #{vendor_mysql_url(spec.platform)} + At the time of building this gem, the necessary DLL files were retrieved from: + #{vendor_mysql_url(spec.platform)} - This gem *includes* vendor/libmysql.dll with redistribution notice in vendor/README. + This gem *includes* vendor/libmysql.dll with redistribution notice in vendor/README. - ====================================================================================================== +====================================================================================================== POST_INSTALL_MESSAGE end @@ -64,9 +64,9 @@ end file 'lib/mysql2/mysql2.rb' do |t| name = Mysql2::GEMSPEC.name File.open(t.name, 'wb') do |f| - f.write <<-END_OF_RUBY.strip_indent - RUBY_VERSION =~ /(\\d+.\\d+)/ - require "#{name}/\#{$1}/#{name}" + f.write <<-END_OF_RUBY +RUBY_VERSION =~ /(\\d+.\\d+)/ +require "#{name}/\#{$1}/#{name}" END_OF_RUBY end end