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