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