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