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

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

E-mail: