Annotation of sql/mysql/parser3mysql.C, revision 1.64
1.1 parser 1: /** @file
2: Parser MySQL driver.
3:
1.61 moko 4: Copyright (c) 2001-2019 Art. Lebedev Studio (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:
1.36 misha 8: 2001-07-30 using MySQL 3.23.22b
1.3 paf 9:
1.36 misha 10: 2001-11-06 numrows on "HP-UX istok1 B.11.00 A 9000/869 448594332 two-user license"
1.3 paf 11: 3.23.42 & 4.0.0.alfa never worked, both subst & .sl version returned 0
1.1 parser 12: */
13:
14: #include "config_includes.h"
15:
16: #include "pa_sql_driver.h"
17:
1.64 ! moko 18: volatile const char * IDENT_PARSER3MYSQL_C="$Id: parser3mysql.C,v 1.63 2020/01/18 20:59:24 moko Exp $" IDENT_PA_SQL_DRIVER_H;
1.40 moko 19:
1.1 parser 20: #define NO_CLIENT_LONG_LONG
21: #include "mysql.h"
22: #include "ltdl.h"
23:
24: #define MAX_STRING 0x400
25: #define MAX_NUMBER 20
26:
1.62 moko 27: #ifdef _MSC_VER
1.1 parser 28: # define snprintf _snprintf
1.2 parser 29: # define strcasecmp _stricmp
1.1 parser 30: #endif
31:
1.34 misha 32: static char *lsplit(char *string, char delim){
1.32 misha 33: if(string) {
1.34 misha 34: if(char *v=strchr(string, delim)){
1.1 parser 35: *v=0;
36: return v+1;
37: }
1.32 misha 38: }
39: return 0;
1.1 parser 40: }
41:
1.34 misha 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.34 misha 49: static char* rsplit(char* string, char delim){
50: if(string){
51: if(char* v=strrchr(string, delim)){
1.26 paf 52: *v=0;
53: return v+1;
54: }
1.32 misha 55: }
56: return NULL;
1.26 paf 57: }
58:
1.34 misha 59: static void toupper_str(char *out, const char *in, size_t size){
1.19 paf 60: while(size--)
61: *out++=(char)toupper(*in++);
62: }
63:
1.38 misha 64: inline static bool is_column_transcode_required(enum_field_types type) {
65: switch(type) {
66: case MYSQL_TYPE_NULL:
67:
1.45 moko 68: #ifdef FIELD_TYPE_NEWDECIMAL
69: case MYSQL_TYPE_NEWDECIMAL:
70: #endif
1.38 misha 71: case MYSQL_TYPE_DECIMAL:
72: case MYSQL_TYPE_FLOAT:
73: case MYSQL_TYPE_DOUBLE:
74:
75: case MYSQL_TYPE_TINY:
76: case MYSQL_TYPE_SHORT:
77: case MYSQL_TYPE_LONG:
78: case MYSQL_TYPE_LONGLONG:
79: case MYSQL_TYPE_INT24:
1.45 moko 80: #ifdef FIELD_TYPE_BIT
1.38 misha 81: case MYSQL_TYPE_BIT:
1.45 moko 82: #endif
1.38 misha 83:
84: case MYSQL_TYPE_DATE:
85: case MYSQL_TYPE_NEWDATE:
86: case MYSQL_TYPE_TIME:
87: case MYSQL_TYPE_DATETIME:
88: case MYSQL_TYPE_YEAR:
89: case MYSQL_TYPE_TIMESTAMP:
90:
91: case MYSQL_TYPE_BLOB:
92: case MYSQL_TYPE_TINY_BLOB:
93: case MYSQL_TYPE_MEDIUM_BLOB:
94: case MYSQL_TYPE_LONG_BLOB:
95: return false;
96: break;
1.49 moko 97: default:
98: return true;
1.38 misha 99: }
100: }
101:
1.53 moko 102: inline static char* strdup(SQL_Driver_services& services, char* str, size_t length) {
1.38 misha 103: char *strm=(char*)services.malloc_atomic(length+1);
104: memcpy(strm, str, length);
105: strm[length]=0;
1.53 moko 106: return strm;
1.38 misha 107: }
108:
1.14 paf 109: struct Connection {
1.16 paf 110: SQL_Driver_services* services;
1.15 paf 111:
1.14 paf 112: MYSQL* handle;
1.33 misha 113: const char* client_charset;
1.14 paf 114: bool autocommit;
115: };
116:
1.29 misha 117:
1.1 parser 118: /**
119: MySQL server driver
120: */
121: class MySQL_Driver : public SQL_Driver {
122: public:
123:
1.29 misha 124: MySQL_Driver() : SQL_Driver() {}
125:
1.1 parser 126: /// get api version
127: int api_version() { return SQL_DRIVER_API_VERSION; }
1.33 misha 128:
1.1 parser 129: /// initialize driver by loading sql dynamic link library
1.4 paf 130: const char *initialize(char *dlopen_file_spec) {
1.64 ! moko 131: return dlopen_file_spec ? dlink(dlopen_file_spec) : "client library column is empty";
1.1 parser 132: }
1.33 misha 133:
1.1 parser 134: /** connect
1.23 paf 135: @param url
1.2 parser 136: format: @b user:pass@host[:port]|[/unix/socket]/database?
1.47 moko 137: charset=value& // transcode by server with command 'SET NAMES value'
1.32 misha 138: ClientCharset=charset& // transcode by parser
1.2 parser 139: timeout=3&
1.32 misha 140: compress=0&
141: named_pipe=1&
1.33 misha 142: autocommit=1&
1.35 misha 143: multi_statements=0 // allows more then one statement in one query
1.52 moko 144: 4.1+ accept not 'cp1251_koi8' but 'cp1251', 'utf8' and much more
1.33 misha 145: it is usable for transcoding using sql server
1.1 parser 146: */
1.52 moko 147: void connect(char *url, SQL_Driver_services& services, void **connection_ref /*< output: Connection* */){
1.23 paf 148: char *user=url;
1.26 paf 149: char *s=rsplit(user, '@');
1.1 parser 150: char *host=0;
151: char *unix_socket=0;
152: if(s && s[0]=='[') { // unix socket
153: unix_socket=1+s;
154: s=lsplit(unix_socket, ']');
155: } else { // IP
156: host=s;
157: }
158: char *db=lsplit(s, '/');
159: char *pwd=lsplit(user, ':');
160: char *error_pos=0;
1.2 parser 161: char *options=lsplit(db, '?');
1.33 misha 162: char *charset=0;
1.34 misha 163: int client_flag=CLIENT_MULTI_RESULTS;
1.1 parser 164:
1.33 misha 165: Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
166: *connection_ref=&connection;
1.15 paf 167: connection.services=&services;
1.32 misha 168: connection.handle=mysql_init(NULL);
1.52 moko 169: connection.client_charset=0;
1.14 paf 170: connection.autocommit=true;
1.2 parser 171:
1.53 moko 172: while(1){
173: char *next_host=lsplit(host, ',');
174: char *host_options=next_host && options ? strdup(services, options, strlen(options)) : options;
175:
176: char *port_cstr=lsplit(host, ':');
177: int port=port_cstr?strtol(port_cstr, &error_pos, 0):0;
178:
179: while(host_options){
180: char *key=lsplit(&host_options, '&');
181: if(key && *key){
1.32 misha 182: if(char *value=lsplit(key, '=')){
1.33 misha 183: if(strcmp(key, "ClientCharset")==0){ // transcoding with parser
1.21 paf 184: toupper_str(value, value, strlen(value));
1.33 misha 185: connection.client_charset=value;
1.32 misha 186: } else if(strcasecmp(key, "charset")==0){ // transcoding with server
1.33 misha 187: charset=value;
1.32 misha 188: } else if(strcasecmp(key, "timeout")==0){
1.2 parser 189: unsigned int timeout=(unsigned int)atoi(value);
1.14 paf 190: if(mysql_options(connection.handle, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0)
191: services._throw(mysql_error(connection.handle));
1.32 misha 192: } else if(strcasecmp(key, "compress")==0){
1.2 parser 193: if(atoi(value))
1.14 paf 194: if(mysql_options(connection.handle, MYSQL_OPT_COMPRESS, 0)!=0)
195: services._throw(mysql_error(connection.handle));
1.32 misha 196: } else if(strcasecmp(key, "named_pipe")==0){
1.2 parser 197: if(atoi(value))
1.33 misha 198: if(mysql_options(connection.handle, MYSQL_OPT_NAMED_PIPE, 0)!=0)
1.14 paf 199: services._throw(mysql_error(connection.handle));
1.39 misha 200: } else if(strcasecmp(key, "local_infile")==0){
201: if(atoi(value))
202: if(mysql_options(connection.handle, MYSQL_OPT_LOCAL_INFILE, 0)!=0)
203: services._throw(mysql_error(connection.handle));
1.32 misha 204: } else if(strcasecmp(key, "autocommit")==0){
1.31 misha 205: if(atoi(value)==0)
1.14 paf 206: connection.autocommit=false;
1.34 misha 207: } else if(strcasecmp(key, "multi_statements")==0){
208: if(atoi(value)!=0)
209: client_flag=CLIENT_MULTI_STATEMENTS;
1.54 moko 210: } else if(strcasecmp(key, "config_file")==0){
211: if(mysql_options(connection.handle, MYSQL_READ_DEFAULT_FILE, value)!=0)
212: services._throw(mysql_error(connection.handle));
213: } else if(strcasecmp(key, "config_group")==0){
214: if(mysql_options(connection.handle, MYSQL_READ_DEFAULT_GROUP, value)!=0)
215: services._throw(mysql_error(connection.handle));
1.2 parser 216: } else
217: services._throw("unknown connect option" /*key*/);
1.53 moko 218: } else
1.2 parser 219: services._throw("connect option without =value" /*key*/);
220: }
221: }
222:
1.53 moko 223: if(mysql_real_connect(connection.handle, host, user, pwd, db, port, unix_socket, client_flag))
224: break;
225:
226: if(!next_host)
227: services._throw(mysql_error(connection.handle));
228:
229: host=next_host;
1.33 misha 230: }
1.1 parser 231:
1.33 misha 232: if(charset){
1.64 ! moko 233: char statement[MAX_STRING];
! 234: snprintf(statement, MAX_STRING, "SET NAMES %s", charset);
1.33 misha 235: _exec(connection, statement);
1.1 parser 236: }
237:
1.14 paf 238: if(!connection.autocommit)
1.33 misha 239: _exec(connection, "SET AUTOCOMMIT=0");
1.1 parser 240: }
1.14 paf 241:
242: void disconnect(void *aconnection) {
243: Connection& connection=*static_cast<Connection*>(aconnection);
244: mysql_close(connection.handle);
1.17 paf 245: connection.handle=0;
1.1 parser 246: }
1.32 misha 247:
1.15 paf 248: void commit(void *aconnection) {
1.14 paf 249: Connection& connection=*static_cast<Connection*>(aconnection);
250: if(!connection.autocommit)
1.33 misha 251: _exec(connection, "COMMIT");
1.14 paf 252: }
1.32 misha 253:
1.15 paf 254: void rollback(void *aconnection) {
1.14 paf 255: Connection& connection=*static_cast<Connection*>(aconnection);
256: if(!connection.autocommit)
1.33 misha 257: _exec(connection, "ROLLBACK");
1.14 paf 258: }
259:
1.15 paf 260: bool ping(void *aconnection) {
1.14 paf 261: Connection& connection=*static_cast<Connection*>(aconnection);
262: return mysql_ping(connection.handle)==0;
1.1 parser 263: }
264:
1.37 moko 265: // charset here is services.request_charset(), not connection.client_charset
266: // thus we can't use the sql server quoting support
267: const char* quote(void *aconnection, const char *str, unsigned int length) {
268: const char* from;
269: const char* from_end=str+length;
270:
271: size_t quoted=0;
272:
273: for(from=str; from<from_end; from++){
274: switch (*from) {
275: case 0:
276: case '\n':
277: case '\r':
278: case '\032':
279: case '\\':
280: case '\'':
281: case '"':
282: quoted++;
283: }
284: }
285:
286: if(!quoted)
287: return str;
288:
1.15 paf 289: Connection& connection=*static_cast<Connection*>(aconnection);
1.37 moko 290: char *result=(char*)connection.services->malloc_atomic(length + quoted + 1);
291: char *to = result;
292:
293: for(from=str; from<from_end; from++){
294: char escape;
295: switch (*from) {
296: case 0:
297: escape= '0';
298: break;
299: case '\n':
300: escape= 'n';
301: break;
302: case '\r':
303: escape= 'r';
304: break;
305: case '\032':
306: escape= 'Z';
307: break;
308: case '\\':
309: case '\'':
310: case '"':
311: escape= *from;
312: break;
313: default:
314: *to++=*from;
315: continue;
316: }
317: *to++= '\\';
318: *to++= escape;
319: }
320:
321: *to=0;
1.13 paf 322: return result;
1.1 parser 323: }
1.33 misha 324:
1.24 paf 325: void query(void *aconnection,
1.33 misha 326: const char *astatement,
327: size_t placeholders_count, Placeholder* placeholders,
328: unsigned long offset, unsigned long limit,
329: SQL_Driver_query_event_handlers& handlers
330: ){
1.14 paf 331: Connection& connection=*static_cast<Connection*>(aconnection);
1.15 paf 332: SQL_Driver_services& services=*connection.services;
1.1 parser 333: MYSQL_RES *res=NULL;
1.24 paf 334:
335: if(placeholders_count>0)
1.62 moko 336: services._throw("bind variables not supported yet");
1.1 parser 337:
1.33 misha 338: bool transcode_needed=_transcode_required(connection);
339:
1.38 misha 340: size_t statement_size=0;
341:
342: if(transcode_needed) {
343: statement_size=strlen(astatement);
344: // transcode query from $request:charset to ?ClientCharset
1.56 moko 345: services.transcode(astatement, statement_size, astatement, statement_size, services.request_charset(), connection.client_charset);
1.19 paf 346: }
347:
1.1 parser 348: const char *statement;
1.38 misha 349: if(offset || limit!=SQL_NO_LIMIT) {
350: if(!statement_size)
351: statement_size=strlen(astatement);
1.13 paf 352: char *statement_limited=(char *)services.malloc_atomic(
1.33 misha 353: statement_size+MAX_NUMBER*2+8/* LIMIT #,#*/+1);
1.1 parser 354: char *cur=statement_limited;
355: memcpy(cur, astatement, statement_size); cur+=statement_size;
1.33 misha 356: cur+=sprintf(cur, " LIMIT ");
1.1 parser 357: if(offset)
1.42 moko 358: cur+=snprintf(cur, MAX_NUMBER, "%lu,", offset);
1.33 misha 359: if(limit!=SQL_NO_LIMIT)
1.41 moko 360: cur+=snprintf(cur, MAX_NUMBER, "%lu", limit);
1.1 parser 361: statement=statement_limited;
362: } else
363: statement=astatement;
364:
1.14 paf 365: if(mysql_query(connection.handle, statement))
1.33 misha 366: _throw(connection, mysql_error(connection.handle));
1.38 misha 367:
1.58 moko 368: int next_result;
369: do {
370:
371: if(res=mysql_store_result(connection.handle)){
1.1 parser 372:
1.58 moko 373: size_t column_count=mysql_num_fields(res);
374: if(!column_count) // old client
375: column_count=mysql_field_count(connection.handle);
376:
377: if(!column_count){
378: mysql_free_result(res);
379: services._throw("result contains no columns");
380: }
1.38 misha 381:
1.58 moko 382: SQL_Error sql_error;
1.38 misha 383:
1.60 moko 384: #define CHECK(afailed) \
385: if(afailed) { \
386: mysql_free_result(res); \
1.59 moko 387: services._throw(sql_error); \
388: }
1.9 paf 389:
1.60 moko 390: #define DO_FETCH_FIELDS(transcode_column_name) \
391: for(size_t i=0; i<column_count; i++) { \
392: if(MYSQL_FIELD *field = mysql_fetch_field(res)){ \
393: size_t length=field->name_length; \
394: const char* str=strdup(services, field->name, length); \
395: transcode_column_name \
396: CHECK(handlers.add_column(sql_error, str, length)); \
397: } else { \
398: /* seen broken client, that reported "44" column count for "select 2+2" */ \
399: column_count=i; \
400: break; \
401: } \
402: }
403:
404: #define DO_FETCH_ROWS(transcode_cell_value) \
405: while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) { \
406: CHECK(handlers.add_row(sql_error)); \
407: unsigned long *lengths=mysql_fetch_lengths(res); \
408: for(size_t i=0; i<column_count; i++) { \
409: const char* str=0; \
410: size_t length=lengths[i]; \
411: if(length) { \
412: str=strdup(services, mysql_row[i], length); \
413: transcode_cell_value \
414: } \
415: CHECK(handlers.add_row_cell(sql_error, str, length)); \
416: } \
417: }
1.38 misha 418:
1.58 moko 419: if(transcode_needed) {
1.62 moko 420: #ifdef _MSC_VER
421: bool* transcode_column = (bool*)services.malloc_atomic(column_count*sizeof(bool));
422: #else
1.58 moko 423: bool transcode_column[column_count];
1.62 moko 424: #endif
1.58 moko 425: DO_FETCH_FIELDS(
426: transcode_column[i] = is_column_transcode_required(field->type);
427: // transcode column's name from ?ClientCharset to $request:charset
428: services.transcode(str, length, str, length, connection.client_charset, services.request_charset());
429: )
430: CHECK(handlers.before_rows(sql_error));
431: DO_FETCH_ROWS(
432: // transcode cell's value from ?ClientCharset to $request:charset
433: if(transcode_column[i])
434: services.transcode(str, length, str, length, connection.client_charset, services.request_charset());
435: )
1.62 moko 436: #ifdef _MSC_VER
437: services.realloc(transcode_column,0);
438: #endif
1.58 moko 439: } else {
440: // without transcoding
1.63 moko 441: DO_FETCH_FIELDS({})
1.58 moko 442: CHECK(handlers.before_rows(sql_error));
1.63 moko 443: DO_FETCH_ROWS({})
1.58 moko 444: }
1.59 moko 445:
1.58 moko 446: mysql_free_result(res);
447:
448: } else {
449: if(mysql_field_count(connection.handle))
450: _throw(connection, mysql_error(connection.handle));
451: // empty result: insert|delete|update|...
452: }
453:
454: next_result = mysql_next_result(connection.handle);
455: if (next_result > 0)
456: _throw(connection, mysql_error(connection.handle));
457: } while (next_result == 0);
1.1 parser 458: }
459:
1.33 misha 460: private:
461: void _exec(Connection& connection, const char* statement) {
462: if(mysql_query(connection.handle, statement))
463: _throw(connection, mysql_error(connection.handle));
464: (*mysql_store_result)(connection.handle); // throw out the result [don't need but must call]
465: }
466:
467: void _throw(Connection& connection, const char* aerr_msg) {
468: size_t length=strlen(aerr_msg);
469: if(length && _transcode_required(connection)) {
1.56 moko 470: connection.services->transcode(aerr_msg, length, aerr_msg, length, connection.client_charset, connection.services->request_charset());
1.33 misha 471: }
472: connection.services->_throw(aerr_msg);
473: }
474:
475: bool _transcode_required(Connection& connection){
476: return (connection.client_charset && strcmp(connection.client_charset, connection.services->request_charset())!=0);
477: }
478:
1.1 parser 479: private: // mysql client library funcs
480:
1.52 moko 481: typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init;
482:
483: typedef void (STDCALL *t_mysql_server_end)(); t_mysql_server_end mysql_server_end;
484:
485: typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options;
1.29 misha 486:
1.1 parser 487: typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result;
1.52 moko 488:
489: typedef int (STDCALL *t_mysql_query)(MYSQL *, const char *q); t_mysql_query mysql_query;
490:
491: typedef char* (STDCALL *t_mysql_error)(MYSQL *); t_mysql_error mysql_error;
1.1 parser 492: static char* STDCALL subst_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; }
493:
1.52 moko 494: typedef MYSQL* (STDCALL *t_mysql_real_connect)(MYSQL *, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned int clientflag); t_mysql_real_connect mysql_real_connect;
495:
496: typedef void (STDCALL *t_mysql_close)(MYSQL *); t_mysql_close mysql_close;
497:
498: typedef int (STDCALL *t_mysql_ping)(MYSQL *); t_mysql_ping mysql_ping;
499:
500: typedef unsigned long (STDCALL *t_mysql_escape_string)(char *to,const char *from, unsigned long from_length); t_mysql_escape_string mysql_escape_string;
501:
502: typedef void (STDCALL *t_mysql_free_result)(MYSQL_RES *result); t_mysql_free_result mysql_free_result;
503:
1.1 parser 504: typedef unsigned long* (STDCALL *t_mysql_fetch_lengths)(MYSQL_RES *result); t_mysql_fetch_lengths mysql_fetch_lengths;
505:
1.52 moko 506: typedef MYSQL_ROW (STDCALL *t_mysql_fetch_row)(MYSQL_RES *result); t_mysql_fetch_row mysql_fetch_row;
507: typedef MYSQL_FIELD* (STDCALL *t_mysql_fetch_field)(MYSQL_RES *result); t_mysql_fetch_field mysql_fetch_field;
508:
509: typedef unsigned int (STDCALL *t_mysql_num_fields)(MYSQL_RES *); t_mysql_num_fields mysql_num_fields;
510: typedef unsigned int (STDCALL *t_mysql_field_count)(MYSQL *); t_mysql_field_count mysql_field_count;
1.58 moko 511: typedef unsigned int (STDCALL *t_mysql_next_result)(MYSQL *); t_mysql_next_result mysql_next_result;
1.52 moko 512:
513: static unsigned int STDCALL subst_mysql_num_fields(MYSQL_RES *res) { return res->field_count; }
514: static unsigned int STDCALL subst_mysql_field_count(MYSQL *mysql) { return mysql->field_count; }
1.58 moko 515: static unsigned int STDCALL subst_mysql_next_result(MYSQL *mysql) { return -1; }
1.1 parser 516:
517: private: // mysql client library funcs linking
518:
1.64 ! moko 519: const char *dlink(char *dlopen_file_spec) {
1.43 moko 520: if(lt_dlinit()){
521: if(const char* result=lt_dlerror())
522: return result;
523: return "can not prepare to dynamic loading";
524: }
1.25 paf 525:
1.64 ! moko 526: lt_dlhandle handle;
! 527: do {
! 528: char *next=lsplit(dlopen_file_spec, ',');
! 529: handle=lt_dlopen(dlopen_file_spec);
! 530: dlopen_file_spec=next;
! 531: } while (!handle && dlopen_file_spec);
1.33 misha 532:
533: if(!handle){
534: if(const char* result=lt_dlerror())
535: return result;
1.1 parser 536: return "can not open the dynamic link module";
1.25 paf 537: }
1.1 parser 538:
1.29 misha 539: #define GLINK(name) \
540: name=(t_##name)lt_dlsym(handle, #name);
541:
1.1 parser 542: #define DSLINK(name, action) \
1.29 misha 543: GLINK(name) \
1.1 parser 544: if(!name) \
545: action;
546:
547: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
548: #define SLINK(name) DSLINK(name, name=subst_##name)
549:
550: DLINK(mysql_init);
1.29 misha 551: GLINK(mysql_server_end);
1.2 parser 552: DLINK(mysql_options);
1.1 parser 553: DLINK(mysql_store_result);
554: DLINK(mysql_query);
555: SLINK(mysql_error);
556: DLINK(mysql_real_connect);
557: DLINK(mysql_close);
558: DLINK(mysql_ping);
559: DLINK(mysql_escape_string);
560: DLINK(mysql_free_result);
561: DLINK(mysql_fetch_lengths);
562: DLINK(mysql_fetch_row);
1.44 moko 563: DLINK(mysql_fetch_field);
1.1 parser 564: SLINK(mysql_num_fields);
565: SLINK(mysql_field_count);
1.58 moko 566: SLINK(mysql_next_result);
1.1 parser 567: return 0;
568: }
569:
570: };
571:
572: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
1.29 misha 573: static MySQL_Driver Driver;
574: return &Driver;
1.1 parser 575: }
E-mail: