mysql2 0.4.2 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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
  }