Annotation of sql/mysql/parser3mysql.C, revision 1.31
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.31 ! misha 13: static const char *RCSId="$Id: parser3mysql.C,v 1.30 2008-05-04 07:37:31 misha 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.26 paf 49: static char* rsplit(char* string, char delim) {
50: if(string) {
51: char* v=strrchr(string, delim);
52: if(v) {
53: *v=0;
54: return v+1;
55: }
56: }
57: return NULL;
58: }
59:
1.21 paf 60: static void toupper_str(char *out, const char *in, size_t size) {
1.19 paf 61: while(size--)
62: *out++=(char)toupper(*in++);
63: }
64:
1.14 paf 65: struct Connection {
1.16 paf 66: SQL_Driver_services* services;
1.15 paf 67:
1.14 paf 68: MYSQL* handle;
1.19 paf 69: const char* cstrClientCharset;
1.14 paf 70: bool autocommit;
1.31 ! misha 71: bool multi_statements;
1.14 paf 72: };
73:
1.29 misha 74:
1.1 parser 75: /**
76: MySQL server driver
77: */
78: class MySQL_Driver : public SQL_Driver {
79: public:
80:
1.29 misha 81: MySQL_Driver() : SQL_Driver() {}
82:
83: ~MySQL_Driver() {
84: if(mysql_server_end)
85: mysql_server_end();
1.1 parser 86: }
87:
88: /// get api version
89: int api_version() { return SQL_DRIVER_API_VERSION; }
90: /// initialize driver by loading sql dynamic link library
1.4 paf 91: const char *initialize(char *dlopen_file_spec) {
1.1 parser 92: return dlopen_file_spec?
93: dlink(dlopen_file_spec):"client library column is empty";
94: }
95: /** connect
1.23 paf 96: @param url
1.2 parser 97: format: @b user:pass@host[:port]|[/unix/socket]/database?
98: charset=cp1251_koi8&
99: timeout=3&
100: compress=1&
101: named_pipe=1
1.1 parser 102: 3.23.22b
1.28 misha 103: Currently the only option for @b character_set_name is cp1251_koi8.
104: WARNING: must be used only to connect, for buffer doesn't live long
105: 4.1+ accept not only 'cp1251_koi8' but 'cp1251', 'utf8' and much more
106: it can be usable for transcoding using sql server
1.1 parser 107: */
108: void connect(
1.23 paf 109: char *url,
1.1 parser 110: SQL_Driver_services& services,
1.14 paf 111: void **connection_ref ///< output: Connection*
1.1 parser 112: ) {
1.23 paf 113: char *user=url;
1.26 paf 114: char *s=rsplit(user, '@');
1.1 parser 115: char *host=0;
116: char *unix_socket=0;
117: if(s && s[0]=='[') { // unix socket
118: unix_socket=1+s;
119: s=lsplit(unix_socket, ']');
120: } else { // IP
121: host=s;
122: }
123: char *db=lsplit(s, '/');
124: char *pwd=lsplit(user, ':');
125: char *error_pos=0;
126: char *port_cstr=lsplit(host, ':');
127: int port=port_cstr?strtol(port_cstr, &error_pos, 0):0;
1.2 parser 128: char *options=lsplit(db, '?');
129:
1.19 paf 130: char *cstrBackwardCompAskServerToTranscode=0;
1.1 parser 131:
1.18 paf 132: Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
1.15 paf 133: connection.services=&services;
1.14 paf 134: connection.handle=mysql_init(NULL);
1.19 paf 135: connection.cstrClientCharset=0;
1.14 paf 136: connection.autocommit=true;
1.31 ! misha 137: connection.multi_statements=false;
1.18 paf 138: *connection_ref=&connection;
1.2 parser 139:
140: while(options) {
141: if(char *key=lsplit(&options, '&')) {
142: if(*key) {
143: if(char *value=lsplit(key, '=')) {
1.31 ! misha 144: if(strcmp(key, "ClientCharset" ) == 0) { // transcoding with parser
1.21 paf 145: toupper_str(value, value, strlen(value));
1.19 paf 146: connection.cstrClientCharset=value;
1.31 ! misha 147: } else if(strcasecmp(key, "charset")==0) { // transcoding with mysql server
1.19 paf 148: cstrBackwardCompAskServerToTranscode=value;
1.2 parser 149: } else if(strcasecmp(key, "timeout")==0) {
150: unsigned int timeout=(unsigned int)atoi(value);
1.14 paf 151: if(mysql_options(connection.handle, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0)
152: services._throw(mysql_error(connection.handle));
1.2 parser 153: } else if(strcasecmp(key, "compress")==0) {
154: if(atoi(value))
1.14 paf 155: if(mysql_options(connection.handle, MYSQL_OPT_COMPRESS, 0)!=0)
156: services._throw(mysql_error(connection.handle));
1.2 parser 157: } else if(strcasecmp(key, "named_pipe")==0) {
158: if(atoi(value))
1.14 paf 159: if(mysql_options(connection.handle, MYSQL_OPT_NAMED_PIPE , 0)!=0)
160: services._throw(mysql_error(connection.handle));
1.31 ! misha 161: } else if(strcasecmp(key, "multi_statements")==0) {
! 162: if(atoi(value)!=0)
! 163: connection.multi_statements=true;
1.14 paf 164: } else if(strcasecmp(key, "autocommit")==0) {
1.31 ! misha 165: if(atoi(value)==0)
1.14 paf 166: connection.autocommit=false;
1.2 parser 167: } else
168: services._throw("unknown connect option" /*key*/);
169: } else
170: services._throw("connect option without =value" /*key*/);
171: }
172: }
173: }
174:
1.28 misha 175: // if(connection.cstrClientCharset && cstrBackwardCompAskServerToTranscode)
176: // services._throw("use 'ClientCharset' option only, "
177: // "'charset' option is obsolete and should not be used with new 'ClientCharset' option");
1.31 ! misha 178:
1.14 paf 179: if(!mysql_real_connect(connection.handle,
1.31 ! misha 180: host, user, pwd, db, port?port:MYSQL_PORT, unix_socket, (connection.multi_statements) ? CLIENT_MULTI_STATEMENTS : CLIENT_MULTI_RESULTS))
1.14 paf 181: services._throw(mysql_error(connection.handle));
1.1 parser 182:
1.19 paf 183: if(cstrBackwardCompAskServerToTranscode) {
1.1 parser 184: // set charset
1.28 misha 185: char statement[MAX_STRING]="set CHARACTER SET ";
1.19 paf 186: strncat(statement, cstrBackwardCompAskServerToTranscode, MAX_STRING);
1.1 parser 187:
1.15 paf 188: exec(connection, statement);
1.1 parser 189: }
190:
1.14 paf 191: if(!connection.autocommit)
1.15 paf 192: exec(connection, "set autocommit=0");
1.14 paf 193: }
194:
1.15 paf 195: void exec(Connection& connection, const char* statement) {
1.14 paf 196: if(mysql_query(connection.handle, statement))
1.15 paf 197: connection.services->_throw(mysql_error(connection.handle));
1.14 paf 198: (*mysql_store_result)(connection.handle); // throw out the result [don't need but must call]
1.1 parser 199: }
1.14 paf 200:
201: void disconnect(void *aconnection) {
202: Connection& connection=*static_cast<Connection*>(aconnection);
203:
204: mysql_close(connection.handle);
1.17 paf 205: connection.handle=0;
1.1 parser 206: }
1.15 paf 207: void commit(void *aconnection) {
1.22 paf 208: //_asm int 3;
1.14 paf 209: Connection& connection=*static_cast<Connection*>(aconnection);
1.1 parser 210:
1.14 paf 211: if(!connection.autocommit)
1.15 paf 212: exec(connection, "commit");
1.14 paf 213: }
1.15 paf 214: void rollback(void *aconnection) {
1.14 paf 215: Connection& connection=*static_cast<Connection*>(aconnection);
216:
217: if(!connection.autocommit)
1.15 paf 218: exec(connection, "rollback");
1.14 paf 219: }
220:
1.15 paf 221: bool ping(void *aconnection) {
1.14 paf 222: Connection& connection=*static_cast<Connection*>(aconnection);
223:
224: return mysql_ping(connection.handle)==0;
1.1 parser 225: }
226:
1.15 paf 227: const char* quote(void *aconnection, const char *from, unsigned int length) {
228: Connection& connection=*static_cast<Connection*>(aconnection);
1.1 parser 229: /*
230: 3.23.22b
231: You must allocate the to buffer to be at least length*2+1 bytes long.
232: (In the worse case, each character may need to be encoded as using two bytes,
233: and you need room for the terminating null byte.)
234: */
1.15 paf 235: char *result=(char*)connection.services->malloc_atomic(length*2+1);
1.13 paf 236: mysql_escape_string(result, from, length);
237: return result;
1.1 parser 238: }
1.24 paf 239: void query(void *aconnection,
240: const char *astatement,
241: size_t placeholders_count, Placeholder* placeholders,
242: unsigned long offset, unsigned long limit,
1.1 parser 243: SQL_Driver_query_event_handlers& handlers) {
1.14 paf 244: Connection& connection=*static_cast<Connection*>(aconnection);
1.15 paf 245: SQL_Driver_services& services=*connection.services;
1.20 paf 246: const char* cstrClientCharset=connection.cstrClientCharset;
1.1 parser 247: MYSQL_RES *res=NULL;
1.24 paf 248:
249: if(placeholders_count>0)
250: services._throw("bind variables not supported (yet)");
1.1 parser 251:
1.19 paf 252: // transcode from $request:charset to connect-string?client_charset
1.20 paf 253: if(cstrClientCharset) {
1.19 paf 254: size_t transcoded_statement_size;
255: services.transcode(astatement, strlen(astatement),
256: astatement, transcoded_statement_size,
257: services.request_charset(),
258: cstrClientCharset);
259: }
260:
1.1 parser 261: const char *statement;
262: if(offset || limit) {
263: size_t statement_size=strlen(astatement);
1.13 paf 264: char *statement_limited=(char *)services.malloc_atomic(
1.1 parser 265: statement_size+MAX_NUMBER*2+8/* limit #,#*/+1);
266: char *cur=statement_limited;
267: memcpy(cur, astatement, statement_size); cur+=statement_size;
268: cur+=sprintf(cur, " limit ");
269: if(offset)
270: cur+=snprintf(cur, MAX_NUMBER+1, "%u,", offset);
271: if(limit)
272: cur+=snprintf(cur, MAX_NUMBER, "%u", limit);
273: statement=statement_limited;
274: } else
275: statement=astatement;
276:
1.14 paf 277: if(mysql_query(connection.handle, statement))
278: services._throw(mysql_error(connection.handle));
279: if(!(res=mysql_store_result(connection.handle)) && mysql_field_count(connection.handle))
280: services._throw(mysql_error(connection.handle));
1.1 parser 281: if(!res) // empty result: insert|delete|update|...
282: return;
283:
284: int column_count=mysql_num_fields(res);
285: if(!column_count) // old client
1.14 paf 286: column_count=mysql_field_count(connection.handle);
1.1 parser 287:
288: if(!column_count) {
289: mysql_free_result(res);
290: services._throw("result contains no columns");
291: }
1.8 paf 292:
1.9 paf 293: bool failed=false;
294: SQL_Error sql_error;
295: #define CHECK(afailed) \
296: if(afailed) { \
297: failed=true; \
298: goto cleanup; \
299: }
300:
301: for(int i=0; i<column_count; i++){
1.12 paf 302: if(MYSQL_FIELD *field=mysql_fetch_field(res)) {
1.20 paf 303: size_t length=strlen(field->name);
304: char* strm=(char*)services.malloc_atomic(length+1);
305: memcpy(strm, field->name, length+1);
306: const char* str=strm;
1.19 paf 307:
308: // transcode to $request:charset from connect-string?client_charset
1.20 paf 309: if(cstrClientCharset) {
1.19 paf 310: services.transcode(str, length,
311: str, length,
312: cstrClientCharset,
313: services.request_charset());
314: }
315:
316: CHECK(handlers.add_column(sql_error, str, length));
1.12 paf 317: } else {
318: // seen some broken client,
319: // which reported "44" for column count of response to "select 2+2"
320: column_count=i;
321: break;
322: }
1.9 paf 323: }
1.1 parser 324:
1.9 paf 325: CHECK(handlers.before_rows(sql_error));
326:
327: while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) {
328: CHECK(handlers.add_row(sql_error));
329: unsigned long *lengths=mysql_fetch_lengths(res);
330: for(int i=0; i<column_count; i++){
1.13 paf 331: size_t length=(size_t)lengths[i];
1.20 paf 332: const char* str;
1.13 paf 333: if(length) {
1.20 paf 334: char *strm=(char*)services.malloc_atomic(length+1);
335: memcpy(strm, mysql_row[i], length);
336: strm[length]=0;
337: str=strm;
1.19 paf 338:
339: // transcode to $request:charset from connect-string?client_charset
1.20 paf 340: if(cstrClientCharset)
1.19 paf 341: services.transcode(str, length,
342: str, length,
343: cstrClientCharset,
344: services.request_charset());
1.9 paf 345: } else
1.13 paf 346: str=0;
347: CHECK(handlers.add_row_cell(sql_error, str, length));
1.3 paf 348: }
1.9 paf 349: }
350: cleanup:
1.1 parser 351: mysql_free_result(res);
1.9 paf 352: if(failed)
353: services._throw(sql_error);
1.1 parser 354: }
355:
356: private: // mysql client library funcs
357:
358: typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init;
359:
1.29 misha 360: typedef void (STDCALL *t_mysql_server_end)(); t_mysql_server_end mysql_server_end;
361:
1.2 parser 362: typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options;
363:
1.1 parser 364: typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result;
365:
366: typedef int (STDCALL *t_mysql_query)(MYSQL *, const char *q); t_mysql_query mysql_query;
367:
368: typedef char * (STDCALL *t_mysql_error)(MYSQL *); t_mysql_error mysql_error;
369: static char* STDCALL subst_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; }
370:
371: typedef MYSQL* (STDCALL *t_mysql_real_connect)(MYSQL *, const char *host,
372: const char *user,
373: const char *passwd,
374: const char *db,
375: unsigned int port,
376: const char *unix_socket,
377: unsigned int clientflag); t_mysql_real_connect mysql_real_connect;
378:
379: typedef void (STDCALL *t_mysql_close)(MYSQL *); t_mysql_close mysql_close;
380:
381: typedef int (STDCALL *t_mysql_ping)(MYSQL *); t_mysql_ping mysql_ping;
382:
383: typedef unsigned long (STDCALL *t_mysql_escape_string)(char *to,const char *from,
384: unsigned long from_length); t_mysql_escape_string mysql_escape_string;
385:
386: typedef void (STDCALL *t_mysql_free_result)(MYSQL_RES *result); t_mysql_free_result mysql_free_result;
387:
388: typedef unsigned long* (STDCALL *t_mysql_fetch_lengths)(MYSQL_RES *result); t_mysql_fetch_lengths mysql_fetch_lengths;
389:
390: typedef MYSQL_ROW (STDCALL *t_mysql_fetch_row)(MYSQL_RES *result); t_mysql_fetch_row mysql_fetch_row;
391:
392: typedef MYSQL_FIELD* (STDCALL *t_mysql_fetch_field)(MYSQL_RES *result); t_mysql_fetch_field mysql_fetch_field;
393:
394: typedef unsigned int (STDCALL *t_mysql_num_fields)(MYSQL_RES *); t_mysql_num_fields mysql_num_fields;
395: static unsigned int STDCALL subst_mysql_num_fields(MYSQL_RES *res) { return res->field_count; }
396:
397: typedef unsigned int (STDCALL *t_mysql_field_count)(MYSQL *); t_mysql_field_count mysql_field_count;
398: static unsigned int STDCALL subst_mysql_field_count(MYSQL *mysql) { return mysql->field_count; }
399:
400: private: // mysql client library funcs linking
401:
402: const char *dlink(const char *dlopen_file_spec) {
1.10 paf 403: if(lt_dlinit())
404: return lt_dlerror();
1.1 parser 405: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
1.25 paf 406: if (!handle) {
407: if(const char* result=lt_dlerror())
408: return result;
409:
1.1 parser 410: return "can not open the dynamic link module";
1.25 paf 411: }
1.1 parser 412:
1.29 misha 413: #define GLINK(name) \
414: name=(t_##name)lt_dlsym(handle, #name);
415:
1.1 parser 416: #define DSLINK(name, action) \
1.29 misha 417: GLINK(name) \
1.1 parser 418: if(!name) \
419: action;
420:
421: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
422: #define SLINK(name) DSLINK(name, name=subst_##name)
423:
424: DLINK(mysql_init);
1.29 misha 425: GLINK(mysql_server_end);
1.2 parser 426: DLINK(mysql_options);
1.1 parser 427: DLINK(mysql_store_result);
428: DLINK(mysql_query);
429: SLINK(mysql_error);
430: DLINK(mysql_real_connect);
431: DLINK(mysql_close);
432: DLINK(mysql_ping);
433: DLINK(mysql_escape_string);
434: DLINK(mysql_free_result);
435: DLINK(mysql_fetch_lengths);
436: DLINK(mysql_fetch_row);
437: DLINK(mysql_fetch_field);
438: SLINK(mysql_num_fields);
439: SLINK(mysql_field_count);
440: return 0;
441: }
442:
443: };
444:
445: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
1.29 misha 446: static MySQL_Driver Driver;
447: return &Driver;
1.1 parser 448: }
E-mail: