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