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