mysql2 0.2.21 → 0.2.22
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.
- checksums.yaml +4 -4
- data/README.md +65 -25
- data/ext/mysql2/client.c +20 -15
- data/ext/mysql2/client.h +4 -2
- data/ext/mysql2/infile.c +119 -0
- data/ext/mysql2/infile.h +1 -0
- data/ext/mysql2/mysql2_ext.h +1 -0
- data/ext/mysql2/result.c +14 -13
- data/lib/mysql2/error.rb +72 -6
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +114 -20
- data/spec/mysql2/error_spec.rb +57 -47
- data/spec/mysql2/result_spec.rb +131 -94
- data/spec/spec_helper.rb +9 -0
- data/spec/test_data +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcca31d92606bbe214af44d546de724f590a1136
|
4
|
+
data.tar.gz: c927057bf2398485dee1bb4a705972f8d635ccf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8de2036b42828457b7fa485dfbdeec79dd3354c5ff32f81a08f6bdfaacbc51ae80d1549c98d821f8e35aec339f0769a6af6708c75d38614e3c5b1c92e32140f3
|
7
|
+
data.tar.gz: 4d8bd2f85a3b5b3d30e1f1062ed6e351df444e8adbe9fc9e0758854be870580caf1942b4f0923b08b18e6c27e9a7043cd803fb8353aa3d72174e0d8a40f9dbfd
|
data/README.md
CHANGED
@@ -66,7 +66,7 @@ files are. For example, if you unzipped the connector to c:\mysql-connector-c-6.
|
|
66
66
|
the gem like this:
|
67
67
|
|
68
68
|
gem install mysql2 -- --with-mysql-dir=c:\mysql-connector-c-6.1.1-win32
|
69
|
-
|
69
|
+
|
70
70
|
Finally, you must copy libmysql.dll from the lib subdirectory of your MySQL or MySQL connector directory into
|
71
71
|
your ruby\bin directory. In the above example, libmysql.dll would be located at
|
72
72
|
c:\mysql-connector-c-6.1.1-win32\lib .
|
@@ -160,22 +160,67 @@ Mysql2::Client.new(
|
|
160
160
|
:default_group = 'my.cfg section'
|
161
161
|
)
|
162
162
|
```
|
163
|
+
|
164
|
+
### SSL options
|
165
|
+
|
166
|
+
Setting any of the following options will enable an SSL connection, but only if
|
167
|
+
your MySQL client library and server have been compiled with SSL support.
|
168
|
+
MySQL client library defaults will be used for any parameters that are left out
|
169
|
+
or set to nil. Relative paths are allowed, and may be required by managed
|
170
|
+
hosting providers such as Heroku.
|
171
|
+
|
172
|
+
``` ruby
|
173
|
+
Mysql2::Client.new(
|
174
|
+
# ...options as above...,
|
175
|
+
:sslkey => '/path/to/client-key.pem',
|
176
|
+
:sslcert => '/path/to/client-cert.pem',
|
177
|
+
:sslca => '/path/to/ca-cert.pem',
|
178
|
+
:sslcapath => '/path/to/cacerts',
|
179
|
+
:sslcipher => 'DHE-RSA-AES256-SHA'
|
180
|
+
)
|
181
|
+
```
|
182
|
+
|
163
183
|
### Multiple result sets
|
164
184
|
|
165
|
-
You can also retrieve multiple result sets. For this to work you need to
|
166
|
-
flags `Mysql2::Client::MULTI_STATEMENTS`.
|
167
|
-
|
185
|
+
You can also retrieve multiple result sets. For this to work you need to
|
186
|
+
connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
|
187
|
+
be used with stored procedures that return more than one result set, and for
|
188
|
+
bundling several SQL statements into a single call to `client.query`.
|
168
189
|
|
169
190
|
``` ruby
|
170
|
-
client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS
|
171
|
-
result = client.query(
|
191
|
+
client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
|
192
|
+
result = client.query('CALL sp_customer_list( 25, 10 )')
|
172
193
|
# result now contains the first result set
|
173
|
-
while
|
174
|
-
|
175
|
-
|
194
|
+
while client.next_result
|
195
|
+
result = client.store_result
|
196
|
+
# result now contains the next result set
|
176
197
|
end
|
177
198
|
```
|
178
199
|
|
200
|
+
Repeated calls to `client.next_result` will return true, false, or raise an
|
201
|
+
exception if the respective query erred. When `client.next_result` returns true,
|
202
|
+
call `client.store_result` to retrieve a result object. Exceptions are not
|
203
|
+
raised until `client.next_result` is called to find the status of the respective
|
204
|
+
query. Subsequent queries are not executed if an earlier query raised an
|
205
|
+
exception. Subsequent calls to `client.next_result` will return false.
|
206
|
+
|
207
|
+
``` ruby
|
208
|
+
result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
|
209
|
+
p result.first
|
210
|
+
|
211
|
+
while client.next_result
|
212
|
+
result = client.store_result
|
213
|
+
p result.first
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
Yields:
|
218
|
+
```
|
219
|
+
{"1"=>1}
|
220
|
+
{"2"=>2}
|
221
|
+
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
222
|
+
```
|
223
|
+
|
179
224
|
See https://gist.github.com/1367987 for using MULTI_STATEMENTS with Active Record.
|
180
225
|
|
181
226
|
### Secure auth
|
@@ -186,7 +231,7 @@ The MySQL 5.6.5 client library may also refuse to attempt a connection if provid
|
|
186
231
|
To bypass this restriction in the client, pass the option :secure_auth => false to Mysql2::Client.new().
|
187
232
|
If using ActiveRecord, your database.yml might look something like this:
|
188
233
|
|
189
|
-
```
|
234
|
+
``` yaml
|
190
235
|
development:
|
191
236
|
adapter: mysql2
|
192
237
|
encoding: utf8
|
@@ -199,11 +244,12 @@ development:
|
|
199
244
|
```
|
200
245
|
|
201
246
|
### Reading a MySQL config file
|
247
|
+
|
202
248
|
You may read configuration options from a MySQL configuration file by passing
|
203
249
|
the `:default_file` and `:default_group` paramters. For example:
|
204
250
|
|
205
|
-
```
|
206
|
-
|
251
|
+
``` ruby
|
252
|
+
Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
|
207
253
|
```
|
208
254
|
|
209
255
|
|
@@ -254,11 +300,6 @@ Pass the `:as => :array` option to any of the above methods of configuration
|
|
254
300
|
|
255
301
|
The default result type is set to :hash, but you can override a previous setting to something else with :as => :hash
|
256
302
|
|
257
|
-
### Others...
|
258
|
-
|
259
|
-
I may add support for `:as => :csv` or even `:as => :json` to allow for *much* more efficient generation of those data types from result sets.
|
260
|
-
If you'd like to see either of these (or others), open an issue and start bugging me about it ;)
|
261
|
-
|
262
303
|
### Timezones
|
263
304
|
|
264
305
|
Mysql2 now supports two timezone options:
|
@@ -405,15 +446,15 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
405
446
|
|
406
447
|
## Compatibility
|
407
448
|
|
408
|
-
|
449
|
+
This gem is regularly tested against the following Ruby versions on Linux and Mac OS X:
|
409
450
|
|
410
|
-
* 1.8.7
|
411
|
-
*
|
412
|
-
* 1.9.
|
413
|
-
* ruby-trunk
|
414
|
-
* rbx-head - broken at the moment, working with the rbx team for a solution
|
451
|
+
* Ruby MRI 1.8.7, 1.9.2, 1.9.3, 2.0.0 (ongoing patch releases).
|
452
|
+
* Ruby Enterprise Edition (based on MRI 1.8.7).
|
453
|
+
* Rubinius 2.0 in compatibility modes 1.8, 1.9, 2.0.
|
415
454
|
|
416
|
-
The Active Record driver
|
455
|
+
The mysql2 gem 0.2.x series includes an Active Record driver that works with AR
|
456
|
+
2.3.x and 3.0.x. Starting in Active Record 3.1, a mysql2 driver is included in
|
457
|
+
the Active Record codebase and no longer provided in mysql2 gem 0.3 and above.
|
417
458
|
|
418
459
|
## Yeah... but why?
|
419
460
|
|
@@ -422,7 +463,6 @@ Someone: Dude, the Mysql gem works fiiiiiine.
|
|
422
463
|
Me: It sure does, but it only hands you nil and strings for field values. Leaving you to convert
|
423
464
|
them into proper Ruby types in Ruby-land - which is slow as balls.
|
424
465
|
|
425
|
-
|
426
466
|
Someone: OK fine, but do_mysql can already give me back values with Ruby objects mapped to MySQL types.
|
427
467
|
|
428
468
|
Me: Yep, but it's API is considerably more complex *and* can be ~2x slower.
|
data/ext/mysql2/client.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
|
-
|
2
|
+
|
3
3
|
#include <errno.h>
|
4
4
|
#ifndef _WIN32
|
5
5
|
#include <sys/socket.h>
|
@@ -12,7 +12,7 @@
|
|
12
12
|
VALUE cMysql2Client;
|
13
13
|
extern VALUE mMysql2, cMysql2Error;
|
14
14
|
static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
15
|
-
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
|
15
|
+
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql, intern_server_version;
|
16
16
|
|
17
17
|
#ifndef HAVE_RB_HASH_DUP
|
18
18
|
static VALUE rb_hash_dup(VALUE other) {
|
@@ -125,19 +125,13 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
125
125
|
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
|
126
126
|
VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
|
127
127
|
VALUE e;
|
128
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
129
|
-
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
130
|
-
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
131
128
|
|
132
|
-
|
133
|
-
rb_enc_associate(
|
134
|
-
|
135
|
-
rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
|
136
|
-
rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
|
137
|
-
}
|
129
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
130
|
+
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
131
|
+
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
138
132
|
#endif
|
139
133
|
|
140
|
-
e =
|
134
|
+
e = rb_funcall(cMysql2Error, rb_intern("new"), 2, rb_error_msg, LONG2FIX(wrapper->server_version));
|
141
135
|
rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
|
142
136
|
rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
|
143
137
|
rb_exc_raise(e);
|
@@ -146,9 +140,13 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
146
140
|
|
147
141
|
static void *nogvl_init(void *ptr) {
|
148
142
|
MYSQL *client;
|
143
|
+
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
149
144
|
|
150
145
|
/* may initialize embedded server and read /etc/services off disk */
|
151
|
-
client = mysql_init(
|
146
|
+
client = mysql_init(wrapper->client);
|
147
|
+
|
148
|
+
if (client) mysql2_set_local_infile(client, wrapper);
|
149
|
+
|
152
150
|
return (void*)(client ? Qtrue : Qfalse);
|
153
151
|
}
|
154
152
|
|
@@ -196,7 +194,11 @@ static void *nogvl_close(void *ptr) {
|
|
196
194
|
|
197
195
|
static void rb_mysql_client_free(void *ptr) {
|
198
196
|
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
197
|
+
decr_mysql2_client(wrapper);
|
198
|
+
}
|
199
199
|
|
200
|
+
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
201
|
+
{
|
200
202
|
wrapper->refcount--;
|
201
203
|
if (wrapper->refcount == 0) {
|
202
204
|
nogvl_close(wrapper);
|
@@ -211,6 +213,7 @@ static VALUE allocate(VALUE klass) {
|
|
211
213
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
212
214
|
wrapper->encoding = Qnil;
|
213
215
|
wrapper->active_thread = Qnil;
|
216
|
+
wrapper->server_version = 0;
|
214
217
|
wrapper->reconnect_enabled = 0;
|
215
218
|
wrapper->connected = 0; /* means that a database connection is open */
|
216
219
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
@@ -303,6 +306,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
303
306
|
return rb_raise_mysql2_error(wrapper);
|
304
307
|
}
|
305
308
|
|
309
|
+
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
306
310
|
wrapper->connected = 1;
|
307
311
|
return self;
|
308
312
|
}
|
@@ -1120,7 +1124,7 @@ static VALUE set_read_default_group(VALUE self, VALUE value) {
|
|
1120
1124
|
static VALUE initialize_ext(VALUE self) {
|
1121
1125
|
GET_CLIENT(self);
|
1122
1126
|
|
1123
|
-
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper
|
1127
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1124
1128
|
/* TODO: warning - not enough memory? */
|
1125
1129
|
return rb_raise_mysql2_error(wrapper);
|
1126
1130
|
}
|
@@ -1135,7 +1139,7 @@ void init_mysql2_client() {
|
|
1135
1139
|
int i;
|
1136
1140
|
int dots = 0;
|
1137
1141
|
const char *lib = mysql_get_client_info();
|
1138
|
-
|
1142
|
+
|
1139
1143
|
for (i = 0; lib[i] != 0 && MYSQL_LINK_VERSION[i] != 0; i++) {
|
1140
1144
|
if (lib[i] == '.') {
|
1141
1145
|
dots++;
|
@@ -1204,6 +1208,7 @@ void init_mysql2_client() {
|
|
1204
1208
|
intern_merge_bang = rb_intern("merge!");
|
1205
1209
|
intern_error_number_eql = rb_intern("error_number=");
|
1206
1210
|
intern_sql_state_eql = rb_intern("sql_state=");
|
1211
|
+
intern_server_version = rb_intern("server_version=");
|
1207
1212
|
|
1208
1213
|
#ifdef CLIENT_LONG_PASSWORD
|
1209
1214
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
data/ext/mysql2/client.h
CHANGED
@@ -36,11 +36,10 @@ rb_thread_call_without_gvl(
|
|
36
36
|
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
37
37
|
#endif /* ! HAVE_RB_THREAD_CALL_WITHOUT_GVL */
|
38
38
|
|
39
|
-
void init_mysql2_client();
|
40
|
-
|
41
39
|
typedef struct {
|
42
40
|
VALUE encoding;
|
43
41
|
VALUE active_thread; /* rb_thread_current() or Qnil */
|
42
|
+
long server_version;
|
44
43
|
int reconnect_enabled;
|
45
44
|
int active;
|
46
45
|
int connected;
|
@@ -50,4 +49,7 @@ typedef struct {
|
|
50
49
|
MYSQL *client;
|
51
50
|
} mysql_client_wrapper;
|
52
51
|
|
52
|
+
void init_mysql2_client();
|
53
|
+
void decr_mysql2_client(mysql_client_wrapper *wrapper);
|
54
|
+
|
53
55
|
#endif
|
data/ext/mysql2/infile.c
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
#include <mysql2_ext.h>
|
2
|
+
|
3
|
+
#include <errno.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
|
6
|
+
#define ERROR_LEN 1024
|
7
|
+
typedef struct
|
8
|
+
{
|
9
|
+
int fd;
|
10
|
+
char *filename;
|
11
|
+
char error[ERROR_LEN];
|
12
|
+
mysql_client_wrapper *wrapper;
|
13
|
+
} mysql2_local_infile_data;
|
14
|
+
|
15
|
+
/* MySQL calls this function when a user begins a LOAD DATA LOCAL INFILE query.
|
16
|
+
*
|
17
|
+
* Allocate a data struct and pass it back through the data pointer.
|
18
|
+
*
|
19
|
+
* Returns:
|
20
|
+
* 0 on success
|
21
|
+
* 1 on error
|
22
|
+
*/
|
23
|
+
static int
|
24
|
+
mysql2_local_infile_init(void **ptr, const char *filename, void *userdata)
|
25
|
+
{
|
26
|
+
mysql2_local_infile_data *data = malloc(sizeof(mysql2_local_infile_data));
|
27
|
+
if (!data) return 1;
|
28
|
+
|
29
|
+
*ptr = data;
|
30
|
+
data->error[0] = 0;
|
31
|
+
data->wrapper = userdata;
|
32
|
+
|
33
|
+
data->filename = strdup(filename);
|
34
|
+
if (!data->filename) {
|
35
|
+
snprintf(data->error, ERROR_LEN, "%s: %s", strerror(errno), filename);
|
36
|
+
return 1;
|
37
|
+
}
|
38
|
+
|
39
|
+
data->fd = open(filename, O_RDONLY);
|
40
|
+
if (data->fd < 0) {
|
41
|
+
snprintf(data->error, ERROR_LEN, "%s: %s", strerror(errno), filename);
|
42
|
+
return 1;
|
43
|
+
}
|
44
|
+
|
45
|
+
return 0;
|
46
|
+
}
|
47
|
+
|
48
|
+
/* MySQL calls this function to read data from the local file.
|
49
|
+
*
|
50
|
+
* Returns:
|
51
|
+
* > 0 number of bytes read
|
52
|
+
* == 0 end of file
|
53
|
+
* < 0 error
|
54
|
+
*/
|
55
|
+
static int
|
56
|
+
mysql2_local_infile_read(void *ptr, char *buf, uint buf_len)
|
57
|
+
{
|
58
|
+
int count;
|
59
|
+
mysql2_local_infile_data *data = (mysql2_local_infile_data *)ptr;
|
60
|
+
|
61
|
+
count = (int)read(data->fd, buf, buf_len);
|
62
|
+
if (count < 0) {
|
63
|
+
snprintf(data->error, ERROR_LEN, "%s: %s", strerror(errno), data->filename);
|
64
|
+
}
|
65
|
+
|
66
|
+
return count;
|
67
|
+
}
|
68
|
+
|
69
|
+
/* MySQL calls this function when we're done with the LOCAL INFILE query.
|
70
|
+
*
|
71
|
+
* ptr will be null if the init function failed.
|
72
|
+
*/
|
73
|
+
static void
|
74
|
+
mysql2_local_infile_end(void *ptr)
|
75
|
+
{
|
76
|
+
mysql2_local_infile_data *data = (mysql2_local_infile_data *)ptr;
|
77
|
+
if (data) {
|
78
|
+
if (data->fd >= 0)
|
79
|
+
close(data->fd);
|
80
|
+
if (data->filename)
|
81
|
+
free(data->filename);
|
82
|
+
free(data);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
/* MySQL calls this function if any of the functions above returned an error.
|
87
|
+
*
|
88
|
+
* This function is called even if init failed, with whatever ptr value
|
89
|
+
* init has set, regardless of the return value of the init function.
|
90
|
+
*
|
91
|
+
* Returns:
|
92
|
+
* Error message number (see http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html)
|
93
|
+
*/
|
94
|
+
static int
|
95
|
+
mysql2_local_infile_error(void *ptr, char *error_msg, uint error_msg_len)
|
96
|
+
{
|
97
|
+
mysql2_local_infile_data *data = (mysql2_local_infile_data *) ptr;
|
98
|
+
|
99
|
+
if (data) {
|
100
|
+
snprintf(error_msg, error_msg_len, "%s", data->error);
|
101
|
+
return CR_UNKNOWN_ERROR;
|
102
|
+
}
|
103
|
+
|
104
|
+
snprintf(error_msg, error_msg_len, "Out of memory");
|
105
|
+
return CR_OUT_OF_MEMORY;
|
106
|
+
}
|
107
|
+
|
108
|
+
/* Tell MySQL Client to use our own local_infile functions.
|
109
|
+
* This is both due to bugginess in the default handlers,
|
110
|
+
* and to improve the Rubyness of the handlers here.
|
111
|
+
*/
|
112
|
+
void mysql2_set_local_infile(MYSQL *mysql, void *userdata)
|
113
|
+
{
|
114
|
+
mysql_set_local_infile_handler(mysql,
|
115
|
+
mysql2_local_infile_init,
|
116
|
+
mysql2_local_infile_read,
|
117
|
+
mysql2_local_infile_end,
|
118
|
+
mysql2_local_infile_error, userdata);
|
119
|
+
}
|
data/ext/mysql2/infile.h
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
void mysql2_set_local_infile(MYSQL *mysql, void *userdata);
|
data/ext/mysql2/mysql2_ext.h
CHANGED
data/ext/mysql2/result.c
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
|
+
|
2
3
|
#include <stdint.h>
|
3
4
|
|
4
5
|
#include "mysql_enc_to_ruby.h"
|
@@ -84,11 +85,7 @@ static void rb_mysql_result_free(void *ptr) {
|
|
84
85
|
|
85
86
|
// If the GC gets to client first it will be nil
|
86
87
|
if (wrapper->client != Qnil) {
|
87
|
-
wrapper->client_wrapper
|
88
|
-
if (wrapper->client_wrapper->refcount == 0) {
|
89
|
-
xfree(wrapper->client_wrapper->client);
|
90
|
-
xfree(wrapper->client_wrapper);
|
91
|
-
}
|
88
|
+
decr_mysql2_client(wrapper->client_wrapper);
|
92
89
|
}
|
93
90
|
|
94
91
|
xfree(wrapper);
|
@@ -125,16 +122,12 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
125
122
|
|
126
123
|
field = mysql_fetch_field_direct(wrapper->result, idx);
|
127
124
|
if (symbolize_keys) {
|
128
|
-
char buf[field->name_length+1];
|
129
|
-
memcpy(buf, field->name, field->name_length);
|
130
|
-
buf[field->name_length] = 0;
|
131
|
-
|
132
125
|
#ifdef HAVE_RB_INTERN3
|
133
|
-
rb_field = rb_intern3(
|
126
|
+
rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
|
134
127
|
rb_field = ID2SYM(rb_field);
|
135
128
|
#else
|
136
129
|
VALUE colStr;
|
137
|
-
colStr =
|
130
|
+
colStr = rb_str_new(field->name, field->name_length);
|
138
131
|
rb_field = ID2SYM(rb_to_id(colStr));
|
139
132
|
#endif
|
140
133
|
} else {
|
@@ -433,6 +426,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
433
426
|
ID db_timezone, app_timezone, dbTz, appTz;
|
434
427
|
mysql2_result_wrapper * wrapper;
|
435
428
|
unsigned long i;
|
429
|
+
const char * errstr;
|
436
430
|
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1, streaming = 0;
|
437
431
|
MYSQL_FIELD * fields = NULL;
|
438
432
|
|
@@ -496,7 +490,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
496
490
|
}
|
497
491
|
|
498
492
|
if (wrapper->lastRowProcessed == 0) {
|
499
|
-
if(streaming) {
|
493
|
+
if (streaming) {
|
500
494
|
/* We can't get number of rows if we're streaming, */
|
501
495
|
/* until we've finished fetching all rows */
|
502
496
|
wrapper->numberOfRows = 0;
|
@@ -512,7 +506,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
512
506
|
}
|
513
507
|
|
514
508
|
if (streaming) {
|
515
|
-
if(!wrapper->streamingComplete) {
|
509
|
+
if (!wrapper->streamingComplete) {
|
516
510
|
VALUE row;
|
517
511
|
|
518
512
|
fields = mysql_fetch_fields(wrapper->result);
|
@@ -530,6 +524,13 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
530
524
|
|
531
525
|
wrapper->numberOfRows = wrapper->lastRowProcessed;
|
532
526
|
wrapper->streamingComplete = 1;
|
527
|
+
|
528
|
+
// Check for errors, the connection might have gone out from under us
|
529
|
+
// mysql_error returns an empty string if there is no error
|
530
|
+
errstr = mysql_error(wrapper->client_wrapper->client);
|
531
|
+
if (errstr[0]) {
|
532
|
+
rb_raise(cMysql2Error, "%s", errstr);
|
533
|
+
}
|
533
534
|
} else {
|
534
535
|
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
535
536
|
}
|