mysql2 0.3.14 → 0.3.15
Sign up to get free protection for your applications and to get access to all the features.
- 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: 1895b2fe6d621bc0608c1c16c7dde5872aff9af9
|
4
|
+
data.tar.gz: 4757365df13127c09c35c3b2cbf84ea35c9ab872
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9feaf2446a8f5c72594fb3958d2675b94b64849f1a8aa0054a7d2fd31191ac82566ae5d008d7e3cc84af329bf009461010e3e8617919abefeede90567ea6f571
|
7
|
+
data.tar.gz: 114eb344643a6ec32cf8624e268551334776eb0196105888dfff3a21f42b4889123a00a32f5692015c1ab1638b02b565c7a20a14a6df3d8371f471d9ad0ba702
|
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
|
}
|