Annotation of sql/mysql/parser3mysql.C, revision 1.30

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

E-mail: