Skip to content

Commit a9571b6

Browse files
committed
Local local infile handler
1 parent e99b11b commit a9571b6

File tree

5 files changed

+130
-4
lines changed

5 files changed

+130
-4
lines changed

ext/mysql2/client.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include <mysql2_ext.h>
2-
#include <client.h>
2+
33
#include <errno.h>
44
#ifndef _WIN32
55
#include <sys/socket.h>
@@ -146,9 +146,13 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
146146

147147
static void *nogvl_init(void *ptr) {
148148
MYSQL *client;
149+
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
149150

150151
/* may initialize embedded server and read /etc/services off disk */
151-
client = mysql_init((MYSQL *)ptr);
152+
client = mysql_init(wrapper->client);
153+
154+
if (client) mysql2_set_local_infile(client, wrapper);
155+
152156
return (void*)(client ? Qtrue : Qfalse);
153157
}
154158

@@ -1124,7 +1128,7 @@ static VALUE set_read_default_group(VALUE self, VALUE value) {
11241128
static VALUE initialize_ext(VALUE self) {
11251129
GET_CLIENT(self);
11261130

1127-
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
1131+
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
11281132
/* TODO: warning - not enough memory? */
11291133
return rb_raise_mysql2_error(wrapper);
11301134
}
@@ -1139,7 +1143,7 @@ void init_mysql2_client() {
11391143
int i;
11401144
int dots = 0;
11411145
const char *lib = mysql_get_client_info();
1142-
1146+
11431147
for (i = 0; lib[i] != 0 && MYSQL_LINK_VERSION[i] != 0; i++) {
11441148
if (lib[i] == '.') {
11451149
dots++;

ext/mysql2/infile.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

ext/mysql2/infile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void mysql2_set_local_infile(MYSQL *mysql, void *userdata);

ext/mysql2/mysql2_ext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ typedef unsigned int uint;
4141

4242
#include <client.h>
4343
#include <result.h>
44+
#include <infile.h>
4445

4546
#endif

ext/mysql2/result.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <mysql2_ext.h>
2+
23
#include <stdint.h>
34

45
#include "mysql_enc_to_ruby.h"

0 commit comments

Comments
 (0)