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

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

E-mail: