|
|
1.1 parser 1: /** @file
2: Parser MySQL driver.
3:
1.11 paf 4: Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
1.1 parser 5:
1.7 paf 6: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1 parser 7:
8: 2001.07.30 using MySQL 3.23.22b
1.3 paf 9:
10: 2001.11.06 numrows on "HP-UX istok1 B.11.00 A 9000/869 448594332 two-user license"
11: 3.23.42 & 4.0.0.alfa never worked, both subst & .sl version returned 0
1.1 parser 12: */
1.12.6.1! paf 13: static const char *RCSId="$Id: parser3mysql.C,v 1.13 2003/03/25 14:07:29 paf Exp $";
1.1 parser 14:
15: #include "config_includes.h"
16:
17: #include "pa_sql_driver.h"
18:
19: #define NO_CLIENT_LONG_LONG
20: #include "mysql.h"
21: #include "ltdl.h"
22:
23: #define MAX_STRING 0x400
24: #define MAX_NUMBER 20
25:
26: #if _MSC_VER
27: # define snprintf _snprintf
1.2 parser 28: # define strcasecmp _stricmp
1.1 parser 29: #endif
30:
31: static char *lsplit(char *string, char delim) {
32: if(string) {
33: char *v=strchr(string, delim);
34: if(v) {
35: *v=0;
36: return v+1;
37: }
38: }
39: return 0;
40: }
41:
1.2 parser 42: static char *lsplit(char **string_ref, char delim) {
43: char *result=*string_ref;
44: char *next=lsplit(*string_ref, delim);
45: *string_ref=next;
46: return result;
47: }
48:
1.1 parser 49: /**
50: MySQL server driver
51: */
52: class MySQL_Driver : public SQL_Driver {
53: public:
54:
55: MySQL_Driver() : SQL_Driver() {
56: }
57:
58: /// get api version
59: int api_version() { return SQL_DRIVER_API_VERSION; }
60: /// initialize driver by loading sql dynamic link library
1.4 paf 61: const char *initialize(char *dlopen_file_spec) {
1.1 parser 62: return dlopen_file_spec?
63: dlink(dlopen_file_spec):"client library column is empty";
64: }
65: /** connect
66: @param used_only_in_connect_url
1.2 parser 67: format: @b user:pass@host[:port]|[/unix/socket]/database?
68: charset=cp1251_koi8&
69: timeout=3&
70: compress=1&
71: named_pipe=1
1.1 parser 72: 3.23.22b
73: Currently the only option for @b character_set_name is cp1251_koi8.
74: WARNING: must be used only to connect, for buffer doesn't live long
75: */
76: void connect(
77: char *used_only_in_connect_url,
78: SQL_Driver_services& services,
79: void **connection ///< output: MYSQL *
80: ) {
81: char *user=used_only_in_connect_url;
82: char *s=lsplit(user, '@');
83: char *host=0;
84: char *unix_socket=0;
85: if(s && s[0]=='[') { // unix socket
86: unix_socket=1+s;
87: s=lsplit(unix_socket, ']');
88: } else { // IP
89: host=s;
90: }
91: char *db=lsplit(s, '/');
92: char *pwd=lsplit(user, ':');
93: char *error_pos=0;
94: char *port_cstr=lsplit(host, ':');
95: int port=port_cstr?strtol(port_cstr, &error_pos, 0):0;
1.2 parser 96: char *options=lsplit(db, '?');
97:
98: char *charset=0;
1.1 parser 99:
100: MYSQL *mysql=mysql_init(NULL);
1.2 parser 101:
102: while(options) {
103: if(char *key=lsplit(&options, '&')) {
104: if(*key) {
105: if(char *value=lsplit(key, '=')) {
106: if(strcasecmp(key, "charset")==0) {
107: charset=value;
108: } else if(strcasecmp(key, "timeout")==0) {
109: unsigned int timeout=(unsigned int)atoi(value);
110: if(mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0)
111: services._throw(mysql_error(mysql));
112: } else if(strcasecmp(key, "compress")==0) {
113: if(atoi(value))
114: if(mysql_options(mysql, MYSQL_OPT_COMPRESS, 0)!=0)
115: services._throw(mysql_error(mysql));
116: } else if(strcasecmp(key, "named_pipe")==0) {
117: if(atoi(value))
118: if(mysql_options(mysql, MYSQL_OPT_NAMED_PIPE , 0)!=0)
119: services._throw(mysql_error(mysql));
120: } else
121: services._throw("unknown connect option" /*key*/);
122: } else
123: services._throw("connect option without =value" /*key*/);
124: }
125: }
126: }
127:
1.1 parser 128: if(!mysql_real_connect(mysql,
129: host, user, pwd, db, port?port:MYSQL_PORT, unix_socket, 0))
130: services._throw(mysql_error(mysql));
131:
1.2 parser 132: if(charset) {
1.1 parser 133: // set charset
134: char statement[MAX_STRING]="set CHARACTER SET "; // cp1251_koi8
135: strncat(statement, charset, MAX_STRING);
136:
137: if(mysql_query(mysql, statement))
138: services._throw(mysql_error(mysql));
139: (*mysql_store_result)(mysql); // throw out the result [don't need but must call]
140: }
141:
142: *(MYSQL **)connection=mysql;
143: }
144: void disconnect(void *connection) {
145: mysql_close((MYSQL *)connection);
146: }
147: void commit(SQL_Driver_services&, void *) {}
148: void rollback(SQL_Driver_services&, void *) {}
149:
150: bool ping(SQL_Driver_services&, void *connection) {
151: return mysql_ping((MYSQL *)connection)==0;
152: }
153:
1.12.6.1! paf 154: const char* quote(
! 155: SQL_Driver_services& services, void *connection,
! 156: const char *from, unsigned int length) {
1.1 parser 157: /*
158: 3.23.22b
159: You must allocate the to buffer to be at least length*2+1 bytes long.
160: (In the worse case, each character may need to be encoded as using two bytes,
161: and you need room for the terminating null byte.)
162: */
1.12.6.1! paf 163: char *result=(char*)services.malloc_atomic(length*2+1);
! 164: mysql_escape_string(result, from, length);
! 165: return result;
1.1 parser 166: }
167: void query(
168: SQL_Driver_services& services, void *connection,
169: const char *astatement, unsigned long offset, unsigned long limit,
170: SQL_Driver_query_event_handlers& handlers) {
171:
172: MYSQL *mysql=(MYSQL *)connection;
173: MYSQL_RES *res=NULL;
174:
175: const char *statement;
176: if(offset || limit) {
177: size_t statement_size=strlen(astatement);
1.12.6.1! paf 178: char *statement_limited=(char *)services.malloc_atomic(
1.1 parser 179: statement_size+MAX_NUMBER*2+8/* limit #,#*/+1);
180: char *cur=statement_limited;
181: memcpy(cur, astatement, statement_size); cur+=statement_size;
182: cur+=sprintf(cur, " limit ");
183: if(offset)
184: cur+=snprintf(cur, MAX_NUMBER+1, "%u,", offset);
185: if(limit)
186: cur+=snprintf(cur, MAX_NUMBER, "%u", limit);
187: statement=statement_limited;
188: } else
189: statement=astatement;
190:
191: if(mysql_query(mysql, statement))
192: services._throw(mysql_error(mysql));
193: if(!(res=mysql_store_result(mysql)) && mysql_field_count(mysql))
194: services._throw(mysql_error(mysql));
195: if(!res) // empty result: insert|delete|update|...
196: return;
197:
198: int column_count=mysql_num_fields(res);
199: if(!column_count) // old client
200: column_count=mysql_field_count(mysql);
201:
202: if(!column_count) {
203: mysql_free_result(res);
204: services._throw("result contains no columns");
205: }
1.8 paf 206:
1.9 paf 207: bool failed=false;
208: SQL_Error sql_error;
209: #define CHECK(afailed) \
210: if(afailed) { \
211: failed=true; \
212: goto cleanup; \
213: }
214:
215: for(int i=0; i<column_count; i++){
1.12 paf 216: if(MYSQL_FIELD *field=mysql_fetch_field(res)) {
217: size_t size=strlen(field->name);
1.12.6.1! paf 218: void *ptr=services.malloc_atomic(size);
1.12 paf 219: memcpy(ptr, field->name, size);
220: CHECK(handlers.add_column(sql_error, ptr, size));
221: } else {
222: // seen some broken client,
223: // which reported "44" for column count of response to "select 2+2"
224: column_count=i;
225: break;
226: }
1.9 paf 227: }
1.1 parser 228:
1.9 paf 229: CHECK(handlers.before_rows(sql_error));
230:
231: while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) {
232: CHECK(handlers.add_row(sql_error));
233: unsigned long *lengths=mysql_fetch_lengths(res);
234: for(int i=0; i<column_count; i++){
235: size_t size=(size_t)lengths[i];
236: void *ptr;
237: if(size) {
1.12.6.1! paf 238: ptr=services.malloc_atomic(size);
1.9 paf 239: memcpy(ptr, mysql_row[i], size);
240: } else
241: ptr=0;
242: CHECK(handlers.add_row_cell(sql_error, ptr, size));
1.3 paf 243: }
1.9 paf 244: }
245: cleanup:
1.1 parser 246: mysql_free_result(res);
1.9 paf 247: if(failed)
248: services._throw(sql_error);
1.1 parser 249: }
250:
251: private: // mysql client library funcs
252:
253: typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init;
254:
1.2 parser 255: typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options;
256:
1.1 parser 257: typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result;
258:
259: typedef int (STDCALL *t_mysql_query)(MYSQL *, const char *q); t_mysql_query mysql_query;
260:
261: typedef char * (STDCALL *t_mysql_error)(MYSQL *); t_mysql_error mysql_error;
262: static char* STDCALL subst_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; }
263:
264: typedef MYSQL* (STDCALL *t_mysql_real_connect)(MYSQL *, const char *host,
265: const char *user,
266: const char *passwd,
267: const char *db,
268: unsigned int port,
269: const char *unix_socket,
270: unsigned int clientflag); t_mysql_real_connect mysql_real_connect;
271:
272: typedef void (STDCALL *t_mysql_close)(MYSQL *); t_mysql_close mysql_close;
273:
274: typedef int (STDCALL *t_mysql_ping)(MYSQL *); t_mysql_ping mysql_ping;
275:
276: typedef unsigned long (STDCALL *t_mysql_escape_string)(char *to,const char *from,
277: unsigned long from_length); t_mysql_escape_string mysql_escape_string;
278:
279: typedef void (STDCALL *t_mysql_free_result)(MYSQL_RES *result); t_mysql_free_result mysql_free_result;
280:
281: typedef unsigned long* (STDCALL *t_mysql_fetch_lengths)(MYSQL_RES *result); t_mysql_fetch_lengths mysql_fetch_lengths;
282:
283: typedef MYSQL_ROW (STDCALL *t_mysql_fetch_row)(MYSQL_RES *result); t_mysql_fetch_row mysql_fetch_row;
284:
285: typedef MYSQL_FIELD* (STDCALL *t_mysql_fetch_field)(MYSQL_RES *result); t_mysql_fetch_field mysql_fetch_field;
286:
287: typedef unsigned int (STDCALL *t_mysql_num_fields)(MYSQL_RES *); t_mysql_num_fields mysql_num_fields;
288: static unsigned int STDCALL subst_mysql_num_fields(MYSQL_RES *res) { return res->field_count; }
289:
290: typedef unsigned int (STDCALL *t_mysql_field_count)(MYSQL *); t_mysql_field_count mysql_field_count;
291: static unsigned int STDCALL subst_mysql_field_count(MYSQL *mysql) { return mysql->field_count; }
292:
293: private: // mysql client library funcs linking
294:
295: const char *dlink(const char *dlopen_file_spec) {
1.10 paf 296: if(lt_dlinit())
297: return lt_dlerror();
1.1 parser 298: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
299: if (!handle)
300: return "can not open the dynamic link module";
301:
302: #define DSLINK(name, action) \
303: name=(t_##name)lt_dlsym(handle, #name); \
304: if(!name) \
305: action;
306:
307: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
308: #define SLINK(name) DSLINK(name, name=subst_##name)
309:
310: DLINK(mysql_init);
1.2 parser 311: DLINK(mysql_options);
1.1 parser 312: DLINK(mysql_store_result);
313: DLINK(mysql_query);
314: SLINK(mysql_error);
315: DLINK(mysql_real_connect);
316: DLINK(mysql_close);
317: DLINK(mysql_ping);
318: DLINK(mysql_escape_string);
319: DLINK(mysql_free_result);
320: DLINK(mysql_fetch_lengths);
321: DLINK(mysql_fetch_row);
322: DLINK(mysql_fetch_field);
323: SLINK(mysql_num_fields);
324: SLINK(mysql_field_count);
325: return 0;
326: }
327:
328: };
329:
330: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
331: return new MySQL_Driver();
332: }