mysql2 0.4.2 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/mysql2/client.h CHANGED
@@ -1,41 +1,6 @@
1
1
  #ifndef MYSQL2_CLIENT_H
2
2
  #define MYSQL2_CLIENT_H
3
3
 
4
- #ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
5
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
6
-
7
- /* emulate rb_thread_call_without_gvl with rb_thread_blocking_region */
8
- #define rb_thread_call_without_gvl(func, data1, ubf, data2) \
9
- rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
10
-
11
- #else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
12
- /*
13
- * partial emulation of the 2.0 rb_thread_call_without_gvl under 1.8,
14
- * this is enough for dealing with blocking I/O functions in the
15
- * presence of threads.
16
- */
17
-
18
- #include <rubysig.h>
19
- #define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
20
- typedef void rb_unblock_function_t(void *);
21
- static void *
22
- rb_thread_call_without_gvl(
23
- void *(*func)(void *), void *data1,
24
- RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
25
- RB_MYSQL_UNUSED void *data2)
26
- {
27
- void *rv;
28
-
29
- TRAP_BEG;
30
- rv = func(data1);
31
- TRAP_END;
32
-
33
- return rv;
34
- }
35
-
36
- #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
37
- #endif /* ! HAVE_RB_THREAD_CALL_WITHOUT_GVL */
38
-
39
4
  typedef struct {
40
5
  VALUE encoding;
41
6
  VALUE active_thread; /* rb_thread_current() or Qnil */
@@ -43,24 +8,15 @@ typedef struct {
43
8
  int reconnect_enabled;
44
9
  unsigned int connect_timeout;
45
10
  int active;
46
- int connected;
11
+ int automatic_close;
47
12
  int initialized;
48
13
  int refcount;
49
- int freed;
14
+ int closed;
50
15
  MYSQL *client;
51
16
  } mysql_client_wrapper;
52
17
 
53
- #define REQUIRE_CONNECTED(wrapper) \
54
- REQUIRE_INITIALIZED(wrapper) \
55
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
56
- rb_raise(cMysql2Error, "closed MySQL connection"); \
57
- }
58
-
59
18
  void rb_mysql_client_set_active_thread(VALUE self);
60
-
61
- #define MARK_CONN_INACTIVE(conn) do {\
62
- wrapper->active_thread = Qnil; \
63
- } while(0)
19
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result);
64
20
 
65
21
  #define GET_CLIENT(self) \
66
22
  mysql_client_wrapper *wrapper; \
@@ -70,7 +26,3 @@ void init_mysql2_client(void);
70
26
  void decr_mysql2_client(mysql_client_wrapper *wrapper);
71
27
 
72
28
  #endif
73
-
74
- #ifndef HAVE_RB_HASH_DUP
75
- VALUE rb_hash_dup(VALUE other);
76
- #endif
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'mkmf'
3
2
  require 'English'
4
3
 
@@ -12,18 +11,26 @@ def asplode(lib)
12
11
  end
13
12
  end
14
13
 
15
- # 2.0-only
16
- have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
14
+ def add_ssl_defines(header)
15
+ all_modes_found = %w[SSL_MODE_DISABLED SSL_MODE_PREFERRED SSL_MODE_REQUIRED SSL_MODE_VERIFY_CA SSL_MODE_VERIFY_IDENTITY].inject(true) do |m, ssl_mode|
16
+ m && have_const(ssl_mode, header)
17
+ end
18
+ $CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
19
+ # if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
20
+ has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
21
+ $CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
22
+ end
17
23
 
18
- # 1.9-only
19
- have_func('rb_thread_blocking_region')
24
+ # 2.1+
25
+ have_func('rb_absint_size')
26
+ have_func('rb_absint_singlebit_p')
27
+
28
+ # Missing in RBX (https://github.com/rubinius/rubinius/issues/3771)
20
29
  have_func('rb_wait_for_single_fd')
21
- have_func('rb_hash_dup')
22
- have_func('rb_intern3')
23
30
 
24
31
  # borrowed from mysqlplus
25
32
  # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
26
- dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
33
+ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
27
34
  /opt
28
35
  /opt/local
29
36
  /opt/local/mysql
@@ -35,17 +42,19 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
35
42
  /usr/local/mysql-*
36
43
  /usr/local/lib/mysql5*
37
44
  /usr/local/opt/mysql5*
38
- ).map { |dir| dir << '/bin' }
45
+ ].map { |dir| dir << '/bin' }
46
+
47
+ # For those without HOMEBREW_ROOT in PATH
48
+ dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
39
49
 
40
- GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
50
+ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}".freeze
41
51
 
42
52
  # If the user has provided a --with-mysql-dir argument, we must respect it or fail.
43
53
  inc, lib = dir_config('mysql')
44
54
  if inc && lib
45
- # TODO: Remove when 2.0.0 is the minimum supported version
46
- # Ruby versions not incorporating the mkmf fix at
55
+ # Ruby versions below 2.0 on Unix and below 2.1 on Windows
56
+ # do not properly search for lib directories, and must be corrected:
47
57
  # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
48
- # do not properly search for lib directories, and must be corrected
49
58
  unless lib && lib[-3, 3] == 'lib'
50
59
  @libdir_basename = 'lib'
51
60
  inc, lib = dir_config('mysql')
@@ -54,6 +63,7 @@ if inc && lib
54
63
  abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
55
64
  warn "-----\nUsing --with-mysql-dir=#{File.dirname inc}\n-----"
56
65
  rpath_dir = lib
66
+ have_library('mysqlclient')
57
67
  elsif (mc = (with_config('mysql-config') || Dir[GLOB].first))
58
68
  # If the user has provided a --with-mysql-config argument, we must respect it or fail.
59
69
  # If the user gave --with-mysql-config with no argument means we should try to find it.
@@ -74,7 +84,7 @@ elsif (mc = (with_config('mysql-config') || Dir[GLOB].first))
74
84
  else
75
85
  _, usr_local_lib = dir_config('mysql', '/usr/local')
76
86
 
77
- asplode("mysql client") unless find_library('mysqlclient', 'mysql_query', usr_local_lib, "#{usr_local_lib}/mysql")
87
+ asplode("mysql client") unless find_library('mysqlclient', nil, usr_local_lib, "#{usr_local_lib}/mysql")
78
88
 
79
89
  rpath_dir = usr_local_lib
80
90
  end
@@ -87,11 +97,28 @@ else
87
97
  asplode 'mysql.h'
88
98
  end
89
99
 
90
- %w(errmsg.h mysqld_error.h).each do |h|
91
- header = [prefix, h].compact.join '/'
100
+ %w[errmsg.h].each do |h|
101
+ header = [prefix, h].compact.join('/')
92
102
  asplode h unless have_header header
93
103
  end
94
104
 
105
+ mysql_h = [prefix, 'mysql.h'].compact.join('/')
106
+ add_ssl_defines(mysql_h)
107
+ have_struct_member('MYSQL', 'net.vio', mysql_h)
108
+ have_struct_member('MYSQL', 'net.pvio', mysql_h)
109
+
110
+ # These constants are actually enums, so they cannot be detected by #ifdef in C code.
111
+ have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
112
+ have_const('SERVER_QUERY_NO_GOOD_INDEX_USED', mysql_h)
113
+ have_const('SERVER_QUERY_NO_INDEX_USED', mysql_h)
114
+ have_const('SERVER_QUERY_WAS_SLOW', mysql_h)
115
+ have_const('MYSQL_OPTION_MULTI_STATEMENTS_ON', mysql_h)
116
+ have_const('MYSQL_OPTION_MULTI_STATEMENTS_OFF', mysql_h)
117
+
118
+ # my_bool is replaced by C99 bool in MySQL 8.0, but we want
119
+ # to retain compatibility with the typedef in earlier MySQLs.
120
+ have_type('my_bool', mysql_h)
121
+
95
122
  # This is our wishlist. We use whichever flags work on the host.
96
123
  # -Wall and -Wextra are included by default.
97
124
  wishlist = [
@@ -126,7 +153,7 @@ sanitizers = with_config('sanitize')
126
153
  case sanitizers
127
154
  when true
128
155
  # Try them all, turn on whatever we can
129
- enabled_sanitizers = %w(address cfi integer memory thread undefined).select do |s|
156
+ enabled_sanitizers = %w[address cfi integer memory thread undefined].select do |s|
130
157
  try_link('int main() {return 0;}', "-Werror -fsanitize=#{s}")
131
158
  end
132
159
  abort "-----\nCould not enable any sanitizers!\n-----" if enabled_sanitizers.empty?
@@ -154,7 +181,7 @@ unless enabled_sanitizers.empty?
154
181
  $CFLAGS << ' -g -fno-omit-frame-pointer'
155
182
  end
156
183
 
157
- if RUBY_PLATFORM =~ /mswin|mingw/
184
+ if RUBY_PLATFORM =~ /mswin|mingw/ && !defined?(RubyInstaller)
158
185
  # Build libmysql.a interface link library
159
186
  require 'rake'
160
187
 
@@ -1,11 +1,12 @@
1
1
  #include <mysql2_ext.h>
2
2
 
3
- VALUE mMysql2, cMysql2Error;
3
+ VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
4
4
 
5
5
  /* Ruby Extension initializer */
6
6
  void Init_mysql2() {
7
7
  mMysql2 = rb_define_module("Mysql2");
8
8
  cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
9
+ cMysql2TimeoutError = rb_const_get(cMysql2Error, rb_intern("TimeoutError"));
9
10
 
10
11
  init_mysql2_client();
11
12
  init_mysql2_result();
@@ -11,22 +11,14 @@ void Init_mysql2(void);
11
11
 
12
12
  #ifdef HAVE_MYSQL_H
13
13
  #include <mysql.h>
14
- #include <mysql_com.h>
15
14
  #include <errmsg.h>
16
- #include <mysqld_error.h>
17
15
  #else
18
16
  #include <mysql/mysql.h>
19
- #include <mysql/mysql_com.h>
20
17
  #include <mysql/errmsg.h>
21
- #include <mysql/mysqld_error.h>
22
18
  #endif
23
19
 
24
- #ifdef HAVE_RUBY_ENCODING_H
25
20
  #include <ruby/encoding.h>
26
- #endif
27
- #ifdef HAVE_RUBY_THREAD_H
28
21
  #include <ruby/thread.h>
29
- #endif
30
22
 
31
23
  #if defined(__GNUC__) && (__GNUC__ >= 3)
32
24
  #define RB_MYSQL_NORETURN __attribute__ ((noreturn))
@@ -36,6 +28,14 @@ void Init_mysql2(void);
36
28
  #define RB_MYSQL_UNUSED
37
29
  #endif
38
30
 
31
+ /* MySQL 8.0 replaces my_bool with C99 bool. Earlier versions of MySQL had
32
+ * a typedef to char. Gem users reported failures on big endian systems when
33
+ * using C99 bool types with older MySQLs due to mismatched behavior. */
34
+ #ifndef HAVE_TYPE_MY_BOOL
35
+ #include <stdbool.h>
36
+ typedef bool my_bool;
37
+ #endif
38
+
39
39
  #include <client.h>
40
40
  #include <statement.h>
41
41
  #include <result.h>
@@ -245,5 +245,15 @@ static const char *mysql2_mysql_enc_to_rb[] = {
245
245
  "UTF-8",
246
246
  "UTF-8",
247
247
  "UTF-8",
248
+ "UTF-8",
249
+ NULL,
250
+ NULL,
251
+ NULL,
252
+ NULL,
253
+ NULL,
254
+ NULL,
255
+ NULL,
248
256
  "UTF-8"
249
257
  };
258
+
259
+ #define CHARSETNR_SIZE (sizeof(mysql2_mysql_enc_to_rb)/sizeof(mysql2_mysql_enc_to_rb[0]))
data/ext/mysql2/result.c CHANGED
@@ -2,51 +2,19 @@
2
2
 
3
3
  #include "mysql_enc_to_ruby.h"
4
4
 
5
- #ifdef HAVE_RUBY_ENCODING_H
6
5
  static rb_encoding *binaryEncoding;
7
- #endif
8
6
 
9
- #if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
10
7
  /* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
11
8
  *
12
9
  * (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
13
10
  */
14
11
  #define MYSQL2_MAX_TIME 315578267999ULL
15
- #else
16
- /**
17
- * On 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07
18
- * 2038 years + 1 month + 19 days + 3 hours + 14 minutes + 7 seconds = 64318634047 seconds
19
- *
20
- * (2038*31557600) + (1*2592000) + (19*86400) + (3*3600) + (14*60) + 7
21
- */
22
- #define MYSQL2_MAX_TIME 64318634047ULL
23
- #endif
24
12
 
25
- #if defined(HAVE_RUBY_ENCODING_H)
26
13
  /* 0000-1-1 00:00:00 UTC
27
14
  *
28
15
  * (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
29
16
  */
30
17
  #define MYSQL2_MIN_TIME 2678400ULL
31
- #elif SIZEOF_INT < SIZEOF_LONG /* 64bit Ruby 1.8 */
32
- /* 0139-1-1 00:00:00 UTC
33
- *
34
- * (139*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
35
- */
36
- #define MYSQL2_MIN_TIME 4389184800ULL
37
- #elif defined(NEGATIVE_TIME_T)
38
- /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t.
39
- *
40
- * (1901*31557600) + (12*2592000) + (13*86400) + (20*3600) + (45*60) + 52
41
- */
42
- #define MYSQL2_MIN_TIME 60023299552ULL
43
- #else
44
- /* 1970-01-01 00:00:01 UTC : The Unix epoch - the oldest time in portable time_t.
45
- *
46
- * (1970*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 1
47
- */
48
- #define MYSQL2_MIN_TIME 62171150401ULL
49
- #endif
50
18
 
51
19
  #define GET_RESULT(self) \
52
20
  mysql2_result_wrapper *wrapper; \
@@ -64,14 +32,14 @@ typedef struct {
64
32
  VALUE block_given;
65
33
  } result_each_args;
66
34
 
67
- VALUE cBigDecimal, cDateTime, cDate;
68
- static VALUE cMysql2Result;
69
- static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
70
35
  extern VALUE mMysql2, cMysql2Client, cMysql2Error;
71
- static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
72
- static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
73
- sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name;
74
- static ID intern_merge;
36
+ static VALUE cMysql2Result, cDateTime, cDate;
37
+ static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
38
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
39
+ intern_civil, intern_new_offset, intern_merge, intern_BigDecimal;
40
+ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
41
+ sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
42
+ sym_cache_rows, sym_cast, sym_stream, sym_name;
75
43
 
76
44
  /* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
77
45
  static void rb_mysql_result_mark(void * wrapper) {
@@ -104,6 +72,10 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
104
72
  wrapper->stmt_wrapper->stmt->bind_result_done = 0;
105
73
  }
106
74
 
75
+ if (wrapper->statement != Qnil) {
76
+ decr_mysql2_stmt(wrapper->stmt_wrapper);
77
+ }
78
+
107
79
  if (wrapper->result_buffers) {
108
80
  unsigned int i;
109
81
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -136,13 +108,15 @@ static void rb_mysql_result_free(void *ptr) {
136
108
  decr_mysql2_client(wrapper->client_wrapper);
137
109
  }
138
110
 
139
- if (wrapper->statement != Qnil) {
140
- decr_mysql2_stmt(wrapper->stmt_wrapper);
141
- }
142
-
143
111
  xfree(wrapper);
144
112
  }
145
113
 
114
+ static VALUE rb_mysql_result_free_(VALUE self) {
115
+ GET_RESULT(self);
116
+ rb_mysql_result_free_result(wrapper);
117
+ return Qnil;
118
+ }
119
+
146
120
  /*
147
121
  * for small results, this won't hit the network, but there's no
148
122
  * reliable way for us to tell this so we'll always release the GVL
@@ -173,29 +147,19 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
173
147
  rb_field = rb_ary_entry(wrapper->fields, idx);
174
148
  if (rb_field == Qnil) {
175
149
  MYSQL_FIELD *field = NULL;
176
- #ifdef HAVE_RUBY_ENCODING_H
177
150
  rb_encoding *default_internal_enc = rb_default_internal_encoding();
178
151
  rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
179
- #endif
180
152
 
181
153
  field = mysql_fetch_field_direct(wrapper->result, idx);
182
154
  if (symbolize_keys) {
183
- #ifdef HAVE_RB_INTERN3
184
155
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
185
156
  rb_field = ID2SYM(rb_field);
186
- #else
187
- VALUE colStr;
188
- colStr = rb_str_new(field->name, field->name_length);
189
- rb_field = ID2SYM(rb_to_id(colStr));
190
- #endif
191
157
  } else {
192
158
  rb_field = rb_str_new(field->name, field->name_length);
193
- #ifdef HAVE_RUBY_ENCODING_H
194
159
  rb_enc_associate(rb_field, conn_enc);
195
160
  if (default_internal_enc) {
196
161
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
197
162
  }
198
- #endif
199
163
  }
200
164
  rb_ary_store(wrapper->fields, idx, rb_field);
201
165
  }
@@ -203,7 +167,6 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
203
167
  return rb_field;
204
168
  }
205
169
 
206
- #ifdef HAVE_RUBY_ENCODING_H
207
170
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
208
171
  /* if binary flag is set, respect its wishes */
209
172
  if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
@@ -216,7 +179,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
216
179
  const char *enc_name;
217
180
  int enc_index;
218
181
 
219
- enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
182
+ enc_name = (field.charsetnr-1 < CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
183
+
220
184
  if (enc_name != NULL) {
221
185
  /* use the field encoding we were able to match */
222
186
  enc_index = rb_enc_find_index(enc_name);
@@ -232,7 +196,6 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
232
196
  }
233
197
  return val;
234
198
  }
235
- #endif
236
199
 
237
200
  /* Interpret microseconds digits left-aligned in fixed-width field.
238
201
  * e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
@@ -272,12 +235,12 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
272
235
  wrapper->result_buffers[i].buffer_length = sizeof(signed char);
273
236
  break;
274
237
  case MYSQL_TYPE_SHORT: // short int
238
+ case MYSQL_TYPE_YEAR: // short int
275
239
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
276
240
  wrapper->result_buffers[i].buffer_length = sizeof(short int);
277
241
  break;
278
242
  case MYSQL_TYPE_INT24: // int
279
243
  case MYSQL_TYPE_LONG: // int
280
- case MYSQL_TYPE_YEAR: // int
281
244
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
282
245
  wrapper->result_buffers[i].buffer_length = sizeof(int);
283
246
  break;
@@ -329,26 +292,22 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
329
292
  VALUE rowVal;
330
293
  unsigned int i = 0;
331
294
 
332
- #ifdef HAVE_RUBY_ENCODING_H
333
295
  rb_encoding *default_internal_enc;
334
296
  rb_encoding *conn_enc;
335
- #endif
336
297
  GET_RESULT(self);
337
298
 
338
- #ifdef HAVE_RUBY_ENCODING_H
339
299
  default_internal_enc = rb_default_internal_encoding();
340
300
  conn_enc = rb_to_encoding(wrapper->encoding);
341
- #endif
342
301
 
302
+ if (wrapper->fields == Qnil) {
303
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
304
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
305
+ }
343
306
  if (args->asArray) {
344
307
  rowVal = rb_ary_new2(wrapper->numberOfFields);
345
308
  } else {
346
309
  rowVal = rb_hash_new();
347
310
  }
348
- if (wrapper->fields == Qnil) {
349
- wrapper->numberOfFields = mysql_num_fields(wrapper->result);
350
- wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
351
- }
352
311
 
353
312
  if (wrapper->result_buffers == NULL) {
354
313
  rb_mysql_result_alloc_result_buffers(self, fields);
@@ -399,7 +358,15 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
399
358
  val = INT2NUM(*((signed char*)result_buffer->buffer));
400
359
  }
401
360
  break;
361
+ case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
362
+ if (args->castBool && fields[i].length == 1) {
363
+ val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
364
+ }else{
365
+ val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
366
+ }
367
+ break;
402
368
  case MYSQL_TYPE_SHORT: // short int
369
+ case MYSQL_TYPE_YEAR: // short int
403
370
  if (result_buffer->is_unsigned) {
404
371
  val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
405
372
  } else {
@@ -408,7 +375,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
408
375
  break;
409
376
  case MYSQL_TYPE_INT24: // int
410
377
  case MYSQL_TYPE_LONG: // int
411
- case MYSQL_TYPE_YEAR: // int
412
378
  if (result_buffer->is_unsigned) {
413
379
  val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
414
380
  } else {
@@ -479,7 +445,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
479
445
  }
480
446
  case MYSQL_TYPE_DECIMAL: // char[]
481
447
  case MYSQL_TYPE_NEWDECIMAL: // char[]
482
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
448
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
483
449
  break;
484
450
  case MYSQL_TYPE_STRING: // char[]
485
451
  case MYSQL_TYPE_VAR_STRING: // char[]
@@ -488,15 +454,12 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
488
454
  case MYSQL_TYPE_BLOB: // char[]
489
455
  case MYSQL_TYPE_MEDIUM_BLOB: // char[]
490
456
  case MYSQL_TYPE_LONG_BLOB: // char[]
491
- case MYSQL_TYPE_BIT: // char[]
492
457
  case MYSQL_TYPE_SET: // char[]
493
458
  case MYSQL_TYPE_ENUM: // char[]
494
459
  case MYSQL_TYPE_GEOMETRY: // char[]
495
460
  default:
496
461
  val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
497
- #ifdef HAVE_RUBY_ENCODING_H
498
462
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
499
- #endif
500
463
  break;
501
464
  }
502
465
  }
@@ -511,7 +474,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
511
474
  return rowVal;
512
475
  }
513
476
 
514
-
515
477
  static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
516
478
  {
517
479
  VALUE rowVal;
@@ -519,16 +481,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
519
481
  unsigned int i = 0;
520
482
  unsigned long * fieldLengths;
521
483
  void * ptr;
522
- #ifdef HAVE_RUBY_ENCODING_H
523
484
  rb_encoding *default_internal_enc;
524
485
  rb_encoding *conn_enc;
525
- #endif
526
486
  GET_RESULT(self);
527
487
 
528
- #ifdef HAVE_RUBY_ENCODING_H
529
488
  default_internal_enc = rb_default_internal_encoding();
530
489
  conn_enc = rb_to_encoding(wrapper->encoding);
531
- #endif
532
490
 
533
491
  ptr = wrapper->result;
534
492
  row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
@@ -536,16 +494,16 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
536
494
  return Qnil;
537
495
  }
538
496
 
497
+ if (wrapper->fields == Qnil) {
498
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
499
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
500
+ }
539
501
  if (args->asArray) {
540
502
  rowVal = rb_ary_new2(wrapper->numberOfFields);
541
503
  } else {
542
504
  rowVal = rb_hash_new();
543
505
  }
544
506
  fieldLengths = mysql_fetch_lengths(wrapper->result);
545
- if (wrapper->fields == Qnil) {
546
- wrapper->numberOfFields = mysql_num_fields(wrapper->result);
547
- wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
548
- }
549
507
 
550
508
  for (i = 0; i < wrapper->numberOfFields; i++) {
551
509
  VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
@@ -558,9 +516,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
558
516
  val = Qnil;
559
517
  } else {
560
518
  val = rb_str_new(row[i], fieldLengths[i]);
561
- #ifdef HAVE_RUBY_ENCODING_H
562
519
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
563
- #endif
564
520
  }
565
521
  } else {
566
522
  switch(type) {
@@ -591,9 +547,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
591
547
  if (fields[i].decimals == 0) {
592
548
  val = rb_cstr2inum(row[i], 10);
593
549
  } else if (strtod(row[i], NULL) == 0.000000){
594
- val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
550
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
595
551
  }else{
596
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
552
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
597
553
  }
598
554
  break;
599
555
  case MYSQL_TYPE_FLOAT: /* FLOAT field */
@@ -711,9 +667,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
711
667
  case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
712
668
  default:
713
669
  val = rb_str_new(row[i], fieldLengths[i]);
714
- #ifdef HAVE_RUBY_ENCODING_H
715
670
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
716
- #endif
717
671
  break;
718
672
  }
719
673
  }
@@ -829,7 +783,9 @@ static VALUE rb_mysql_result_each_(VALUE self,
829
783
 
830
784
  if (row == Qnil) {
831
785
  /* we don't need the mysql C dataset around anymore, peace it */
832
- rb_mysql_result_free_result(wrapper);
786
+ if (args->cacheRows) {
787
+ rb_mysql_result_free_result(wrapper);
788
+ }
833
789
  return Qnil;
834
790
  }
835
791
 
@@ -837,7 +793,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
837
793
  rb_yield(row);
838
794
  }
839
795
  }
840
- if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
796
+ if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
841
797
  /* we don't need the mysql C dataset around anymore, peace it */
842
798
  rb_mysql_result_free_result(wrapper);
843
799
  }
@@ -881,6 +837,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
881
837
 
882
838
  if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
883
839
  rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
840
+ cacheRows = 1;
884
841
  }
885
842
 
886
843
  if (wrapper->stmt_wrapper && !cast) {
@@ -908,12 +865,15 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
908
865
  app_timezone = Qnil;
909
866
  }
910
867
 
911
- if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
868
+ if (wrapper->rows == Qnil && !wrapper->is_streaming) {
912
869
  wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
913
- if (wrapper->numberOfRows == 0) {
914
- wrapper->rows = rb_ary_new();
915
- return wrapper->rows;
870
+ wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
871
+ } else if (wrapper->rows && !cacheRows) {
872
+ if (wrapper->resultFreed) {
873
+ rb_raise(cMysql2Error, "Result set has already been freed");
916
874
  }
875
+ mysql_data_seek(wrapper->result, 0);
876
+ wrapper->lastRowProcessed = 0;
917
877
  wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
918
878
  }
919
879
 
@@ -1000,13 +960,13 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
1000
960
  }
1001
961
 
1002
962
  void init_mysql2_result() {
1003
- cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1004
963
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1005
964
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1006
965
 
1007
966
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1008
967
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
1009
968
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
969
+ rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
1010
970
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
1011
971
  rb_define_alias(cMysql2Result, "size", "count");
1012
972
 
@@ -1018,6 +978,7 @@ void init_mysql2_result() {
1018
978
  intern_local_offset = rb_intern("local_offset");
1019
979
  intern_civil = rb_intern("civil");
1020
980
  intern_new_offset = rb_intern("new_offset");
981
+ intern_BigDecimal = rb_intern("BigDecimal");
1021
982
 
1022
983
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1023
984
  sym_as = ID2SYM(rb_intern("as"));
@@ -1040,7 +1001,5 @@ void init_mysql2_result() {
1040
1001
  opt_time_month = INT2NUM(1);
1041
1002
  opt_utc_offset = INT2NUM(0);
1042
1003
 
1043
- #ifdef HAVE_RUBY_ENCODING_H
1044
1004
  binaryEncoding = rb_enc_find("binary");
1045
- #endif
1046
1005
  }