Annotation of embedaddon/strongswan/src/libstrongswan/plugins/mysql/mysql_database.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2013 Tobias Brunner
                      3:  * Copyright (C) 2007 Martin Willi
                      4:  * HSR Hochschule fuer Technik Rapperswil
                      5:  *
                      6:  * This program is free software; you can redistribute it and/or modify it
                      7:  * under the terms of the GNU General Public License as published by the
                      8:  * Free Software Foundation; either version 2 of the License, or (at your
                      9:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                     10:  *
                     11:  * This program is distributed in the hope that it will be useful, but
                     12:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     13:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     14:  * for more details.
                     15:  */
                     16: 
                     17: #include "mysql_database.h"
                     18: 
                     19: #define _GNU_SOURCE
                     20: #include <string.h>
                     21: #include <mysql.h>
                     22: 
                     23: #include <utils/debug.h>
                     24: #include <utils/chunk.h>
                     25: #include <threading/thread_value.h>
                     26: #include <threading/mutex.h>
                     27: #include <collections/linked_list.h>
                     28: 
                     29: /* Older mysql.h headers do not define it, but we need it. It is not returned
                     30:  * in in MySQL 4 by default, but by MySQL 5. To avoid this problem, we catch
                     31:  * it in all cases. */
                     32: #ifndef MYSQL_DATA_TRUNCATED
                     33: #define MYSQL_DATA_TRUNCATED 101
                     34: #endif
                     35: 
                     36: typedef struct private_mysql_database_t private_mysql_database_t;
                     37: 
                     38: /**
                     39:  * private data of mysql_database
                     40:  */
                     41: struct private_mysql_database_t {
                     42: 
                     43:        /**
                     44:         * public functions
                     45:         */
                     46:        mysql_database_t public;
                     47: 
                     48:        /**
                     49:         * connection pool, contains conn_t
                     50:         */
                     51:        linked_list_t *pool;
                     52: 
                     53:        /**
                     54:         * thread-specific transaction, as transaction_t
                     55:         */
                     56:        thread_value_t *transaction;
                     57: 
                     58:        /**
                     59:         * mutex to lock pool
                     60:         */
                     61:        mutex_t *mutex;
                     62: 
                     63:        /**
                     64:         * hostname to connect to
                     65:         */
                     66:        char *host;
                     67: 
                     68:        /**
                     69:         * username to use
                     70:         */
                     71:        char *username;
                     72: 
                     73:        /**
                     74:         * password
                     75:         */
                     76:        char *password;
                     77: 
                     78:        /**
                     79:         * database name
                     80:         */
                     81:        char *database;
                     82: 
                     83:        /**
                     84:         * tcp port
                     85:         */
                     86:        int port;
                     87: };
                     88: 
                     89: typedef struct conn_t conn_t;
                     90: 
                     91: /**
                     92:  * connection pool entry
                     93:  */
                     94: struct conn_t {
                     95: 
                     96:        /**
                     97:         * MySQL database connection
                     98:         */
                     99:        MYSQL *mysql;
                    100: 
                    101:        /**
                    102:         * connection in use?
                    103:         */
                    104:        bool in_use;
                    105: };
                    106: 
                    107: /**
                    108:  * database transaction
                    109:  */
                    110: typedef struct {
                    111: 
                    112:        /**
                    113:         * Reference to the specific connection we started the transaction on
                    114:         */
                    115:        conn_t *conn;
                    116: 
                    117:        /**
                    118:         * Refcounter if transaction() is called multiple times
                    119:         */
                    120:        refcount_t refs;
                    121: 
                    122:        /**
                    123:         * TRUE if transaction was rolled back
                    124:         */
                    125:        bool rollback;
                    126: 
                    127: } transaction_t;
                    128: 
                    129: /**
                    130:  * Release a mysql connection
                    131:  */
                    132: static void conn_release(private_mysql_database_t *this, conn_t *conn)
                    133: {
                    134:        /* do not release the connection while transactions are using it */
                    135:        if (!this->transaction->get(this->transaction))
                    136:        {
                    137:                this->mutex->lock(this->mutex);
                    138:                conn->in_use = FALSE;
                    139:                this->mutex->unlock(this->mutex);
                    140:        }
                    141: }
                    142: 
                    143: /**
                    144:  * Destroy a transaction and release the connection
                    145:  */
                    146: static void transaction_destroy(private_mysql_database_t *this,
                    147:                                                                transaction_t *trans)
                    148: {
                    149:        conn_release(this, trans->conn);
                    150:        free(trans);
                    151: }
                    152: 
                    153: /**
                    154:  * thread specific initialization flag
                    155:  */
                    156: thread_value_t *initialized;
                    157: 
                    158: /**
                    159:  * Initialize a thread for mysql usage
                    160:  */
                    161: static void thread_initialize()
                    162: {
                    163:        if (initialized->get(initialized) == NULL)
                    164:        {
                    165:                initialized->set(initialized, (void*)TRUE);
                    166:                mysql_thread_init();
                    167:        }
                    168: }
                    169: 
                    170: /**
                    171:  * mysql library initialization function
                    172:  */
                    173: bool mysql_database_init()
                    174: {
                    175:        if (mysql_library_init(0, NULL, NULL))
                    176:        {
                    177:                return FALSE;
                    178:        }
                    179:        initialized = thread_value_create((thread_cleanup_t)mysql_thread_end);
                    180:        return TRUE;
                    181: }
                    182: 
                    183: /**
                    184:  * mysql library cleanup function
                    185:  */
                    186: void mysql_database_deinit()
                    187: {
                    188:        initialized->destroy(initialized);
                    189:        mysql_thread_end();
                    190:        mysql_library_end();
                    191: }
                    192: 
                    193: /**
                    194:  * Destroy a mysql connection
                    195:  */
                    196: static void conn_destroy(conn_t *this)
                    197: {
                    198:        mysql_close(this->mysql);
                    199:        free(this);
                    200: }
                    201: 
                    202: /**
                    203:  * Acquire/Reuse a mysql connection
                    204:  */
                    205: static conn_t *conn_get(private_mysql_database_t *this, transaction_t **trans)
                    206: {
                    207:        conn_t *current, *found = NULL;
                    208:        enumerator_t *enumerator;
                    209:        transaction_t *transaction;
                    210: 
                    211:        thread_initialize();
                    212: 
                    213:        transaction = this->transaction->get(this->transaction);
                    214:        if (transaction)
                    215:        {
                    216:                if (trans)
                    217:                {
                    218:                        *trans = transaction;
                    219:                }
                    220:                return transaction->conn;
                    221:        }
                    222: 
                    223:        while (TRUE)
                    224:        {
                    225:                this->mutex->lock(this->mutex);
                    226:                enumerator = this->pool->create_enumerator(this->pool);
                    227:                while (enumerator->enumerate(enumerator, &current))
                    228:                {
                    229:                        if (!current->in_use)
                    230:                        {
                    231:                                found = current;
                    232:                                found->in_use = TRUE;
                    233:                                break;
                    234:                        }
                    235:                }
                    236:                enumerator->destroy(enumerator);
                    237:                this->mutex->unlock(this->mutex);
                    238:                if (found)
                    239:                {       /* check connection if found, release if ping fails */
                    240:                        if (mysql_ping(found->mysql) == 0)
                    241:                        {
                    242:                                break;
                    243:                        }
                    244:                        this->mutex->lock(this->mutex);
                    245:                        this->pool->remove(this->pool, found, NULL);
                    246:                        this->mutex->unlock(this->mutex);
                    247:                        conn_destroy(found);
                    248:                        found = NULL;
                    249:                        continue;
                    250:                }
                    251:                break;
                    252:        }
                    253:        if (found == NULL)
                    254:        {
                    255:                INIT(found,
                    256:                        .in_use = TRUE,
                    257:                        .mysql = mysql_init(NULL),
                    258:                );
                    259:                if (!mysql_real_connect(found->mysql, this->host, this->username,
                    260:                                                                this->password, this->database, this->port,
                    261:                                                                NULL, 0))
                    262:                {
                    263:                        DBG1(DBG_LIB, "connecting to mysql://%s:***@%s:%d/%s failed: %s",
                    264:                                 this->username, this->host, this->port, this->database,
                    265:                                 mysql_error(found->mysql));
                    266:                        conn_destroy(found);
                    267:                        found = NULL;
                    268:                }
                    269:                else
                    270:                {
                    271:                        this->mutex->lock(this->mutex);
                    272:                        this->pool->insert_last(this->pool, found);
                    273:                        DBG2(DBG_LIB, "increased MySQL connection pool size to %d",
                    274:                                 this->pool->get_count(this->pool));
                    275:                        this->mutex->unlock(this->mutex);
                    276:                }
                    277:        }
                    278:        return found;
                    279: }
                    280: 
                    281: /**
                    282:  * Create and run a MySQL stmt using a sql string and args
                    283:  */
                    284: static MYSQL_STMT* run(MYSQL *mysql, char *sql, va_list *args)
                    285: {
                    286:        MYSQL_STMT *stmt;
                    287:        int params;
                    288: 
                    289:        stmt = mysql_stmt_init(mysql);
                    290:        if (stmt == NULL)
                    291:        {
                    292:                DBG1(DBG_LIB, "creating MySQL statement failed: %s",
                    293:                         mysql_error(mysql));
                    294:                return NULL;
                    295:        }
                    296:        if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
                    297:        {
                    298:                DBG1(DBG_LIB, "preparing MySQL statement failed: %s",
                    299:                         mysql_stmt_error(stmt));
                    300:                mysql_stmt_close(stmt);
                    301:                return NULL;
                    302:        }
                    303:        params = mysql_stmt_param_count(stmt);
                    304:        if (params > 0)
                    305:        {
                    306:                int i;
                    307:                MYSQL_BIND *bind;
                    308: 
                    309:                bind = alloca(sizeof(MYSQL_BIND) * params);
                    310:                memset(bind, 0, sizeof(MYSQL_BIND) * params);
                    311: 
                    312:                for (i = 0; i < params; i++)
                    313:                {
                    314:                        switch (va_arg(*args, db_type_t))
                    315:                        {
                    316:                                case DB_INT:
                    317:                                {
                    318:                                        bind[i].buffer_type = MYSQL_TYPE_LONG;
                    319:                                        bind[i].buffer = (char*)alloca(sizeof(int));
                    320:                                        *(int*)bind[i].buffer = va_arg(*args, int);
                    321:                                        bind[i].buffer_length = sizeof(int);
                    322:                                        break;
                    323:                                }
                    324:                                case DB_UINT:
                    325:                                {
                    326:                                        bind[i].buffer_type = MYSQL_TYPE_LONG;
                    327:                                        bind[i].buffer = (char*)alloca(sizeof(u_int));
                    328:                                        *(u_int*)bind[i].buffer = va_arg(*args, u_int);
                    329:                                        bind[i].buffer_length = sizeof(u_int);
                    330:                                        bind[i].is_unsigned = TRUE;
                    331:                                        break;
                    332:                                }
                    333:                                case DB_TEXT:
                    334:                                {
                    335:                                        bind[i].buffer_type = MYSQL_TYPE_STRING;;
                    336:                                        bind[i].buffer = va_arg(*args, char*);
                    337:                                        if (bind[i].buffer)
                    338:                                        {
                    339:                                                bind[i].buffer_length = strlen(bind[i].buffer);
                    340:                                        }
                    341:                                        break;
                    342:                                }
                    343:                                case DB_BLOB:
                    344:                                {
                    345:                                        chunk_t chunk = va_arg(*args, chunk_t);
                    346:                                        bind[i].buffer_type = MYSQL_TYPE_BLOB;
                    347:                                        bind[i].buffer = chunk.ptr;
                    348:                                        bind[i].buffer_length = chunk.len;
                    349:                                        break;
                    350:                                }
                    351:                                case DB_DOUBLE:
                    352:                                {
                    353:                                        bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
                    354:                                        bind[i].buffer = (char*)alloca(sizeof(double));
                    355:                                        *(double*)bind[i].buffer = va_arg(*args, double);
                    356:                                        bind[i].buffer_length = sizeof(double);
                    357:                                        break;
                    358:                                }
                    359:                                case DB_NULL:
                    360:                                {
                    361:                                        bind[i].buffer_type = MYSQL_TYPE_NULL;
                    362:                                        break;
                    363:                                }
                    364:                                default:
                    365:                                        DBG1(DBG_LIB, "invalid data type supplied");
                    366:                                        mysql_stmt_close(stmt);
                    367:                                        return NULL;
                    368:                        }
                    369:                }
                    370:                if (mysql_stmt_bind_param(stmt, bind))
                    371:                {
                    372:                        DBG1(DBG_LIB, "binding MySQL param failed: %s",
                    373:                                 mysql_stmt_error(stmt));
                    374:                        mysql_stmt_close(stmt);
                    375:                        return NULL;
                    376:                }
                    377:        }
                    378:        if (mysql_stmt_execute(stmt))
                    379:        {
                    380:                DBG1(DBG_LIB, "executing MySQL statement failed: %s",
                    381:                         mysql_stmt_error(stmt));
                    382:                mysql_stmt_close(stmt);
                    383:                return NULL;
                    384:        }
                    385:        return stmt;
                    386: }
                    387: 
                    388: typedef struct {
                    389:        /** implements enumerator_t */
                    390:        enumerator_t public;
                    391:        /** mysql database */
                    392:        private_mysql_database_t *db;
                    393:        /** associated MySQL statement */
                    394:        MYSQL_STMT *stmt;
                    395:        /** result bindings */
                    396:        MYSQL_BIND *bind;
                    397:        /** pooled connection handle */
                    398:        conn_t *conn;
                    399:        /** value for INT, UINT, double */
                    400:        union {
                    401:                void *p_void;;
                    402:                int *p_int;
                    403:                u_int *p_uint;
                    404:                double *p_double;
                    405:        } val;
                    406:        /* length for TEXT and BLOB */
                    407:        unsigned long *length;
                    408: } mysql_enumerator_t;
                    409: 
                    410: METHOD(enumerator_t, mysql_enumerator_destroy, void,
                    411:        mysql_enumerator_t *this)
                    412: {
                    413:        int columns, i;
                    414: 
                    415:        columns = mysql_stmt_field_count(this->stmt);
                    416: 
                    417:        for (i = 0; i < columns; i++)
                    418:        {
                    419:                switch (this->bind[i].buffer_type)
                    420:                {
                    421:                        case MYSQL_TYPE_STRING:
                    422:                        case MYSQL_TYPE_BLOB:
                    423:                        {
                    424:                                free(this->bind[i].buffer);
                    425:                                break;
                    426:                        }
                    427:                        default:
                    428:                                break;
                    429:                }
                    430:        }
                    431:        mysql_stmt_close(this->stmt);
                    432:        conn_release(this->db, this->conn);
                    433:        free(this->bind);
                    434:        free(this->val.p_void);
                    435:        free(this->length);
                    436:        free(this);
                    437: }
                    438: 
                    439: METHOD(enumerator_t, mysql_enumerator_enumerate, bool,
                    440:        mysql_enumerator_t *this, va_list args)
                    441: {
                    442:        int i, columns;
                    443: 
                    444:        columns = mysql_stmt_field_count(this->stmt);
                    445: 
                    446:        /* free/reset data set of previous call */
                    447:        for (i = 0; i < columns; i++)
                    448:        {
                    449:                switch (this->bind[i].buffer_type)
                    450:                {
                    451:                        case MYSQL_TYPE_STRING:
                    452:                        case MYSQL_TYPE_BLOB:
                    453:                        {
                    454:                                free(this->bind[i].buffer);
                    455:                                this->bind[i].buffer = NULL;
                    456:                                this->bind[i].buffer_length = 0;
                    457:                                this->bind[i].length = &this->length[i];
                    458:                                this->length[i] = 0;
                    459:                                break;
                    460:                        }
                    461:                        default:
                    462:                                break;
                    463:                }
                    464:        }
                    465: 
                    466:        switch (mysql_stmt_fetch(this->stmt))
                    467:        {
                    468:                case 0:
                    469:                case MYSQL_DATA_TRUNCATED:
                    470:                        break;
                    471:                case MYSQL_NO_DATA:
                    472:                        return FALSE;
                    473:                default:
                    474:                        DBG1(DBG_LIB, "fetching MySQL row failed: %s",
                    475:                                 mysql_stmt_error(this->stmt));
                    476:                        return FALSE;
                    477:        }
                    478: 
                    479:        for (i = 0; i < columns; i++)
                    480:        {
                    481:                switch (this->bind[i].buffer_type)
                    482:                {
                    483:                        case MYSQL_TYPE_LONG:
                    484:                        {
                    485:                                if (this->bind[i].is_unsigned)
                    486:                                {
                    487:                                        u_int *value = va_arg(args, u_int*);
                    488:                                        *value = this->val.p_uint[i];
                    489:                                }
                    490:                                else
                    491:                                {
                    492:                                        int *value = va_arg(args, int*);
                    493:                                        *value = this->val.p_int[i];
                    494:                                }
                    495:                                break;
                    496:                        }
                    497:                        case MYSQL_TYPE_STRING:
                    498:                        {
                    499:                                char **value = va_arg(args, char**);
                    500:                                this->bind[i].buffer = malloc(this->length[i]+1);
                    501:                                this->bind[i].buffer_length = this->length[i];
                    502:                                *value = this->bind[i].buffer;
                    503:                                mysql_stmt_fetch_column(this->stmt, &this->bind[i], i, 0);
                    504:                                ((char*)this->bind[i].buffer)[this->length[i]] = '\0';
                    505:                                break;
                    506:                        }
                    507:                        case MYSQL_TYPE_BLOB:
                    508:                        {
                    509:                                chunk_t *value = va_arg(args, chunk_t*);
                    510:                                this->bind[i].buffer = malloc(this->length[i]);
                    511:                                this->bind[i].buffer_length = this->length[i];
                    512:                                value->ptr = this->bind[i].buffer;
                    513:                                value->len = this->length[i];
                    514:                                mysql_stmt_fetch_column(this->stmt, &this->bind[i], i, 0);
                    515:                                break;
                    516:                        }
                    517:                        case MYSQL_TYPE_DOUBLE:
                    518:                        {
                    519:                                double *value = va_arg(args, double*);
                    520:                                *value = this->val.p_double[i];
                    521:                                break;
                    522:                        }
                    523:                        default:
                    524:                                break;
                    525:                }
                    526:        }
                    527:        return TRUE;
                    528: }
                    529: 
                    530: METHOD(database_t, query, enumerator_t*,
                    531:        private_mysql_database_t *this, char *sql, ...)
                    532: {
                    533:        MYSQL_STMT *stmt;
                    534:        va_list args;
                    535:        mysql_enumerator_t *enumerator = NULL;
                    536:        conn_t *conn;
                    537: 
                    538:        conn = conn_get(this, NULL);
                    539:        if (!conn)
                    540:        {
                    541:                return NULL;
                    542:        }
                    543: 
                    544:        va_start(args, sql);
                    545:        stmt = run(conn->mysql, sql, &args);
                    546:        if (stmt)
                    547:        {
                    548:                int columns, i;
                    549: 
                    550:                INIT(enumerator,
                    551:                        .public = {
                    552:                                .enumerate = enumerator_enumerate_default,
                    553:                                .venumerate = _mysql_enumerator_enumerate,
                    554:                                .destroy = _mysql_enumerator_destroy,
                    555:                        },
                    556:                        .db = this,
                    557:                        .stmt = stmt,
                    558:                        .conn = conn,
                    559:                );
                    560:                columns = mysql_stmt_field_count(stmt);
                    561:                enumerator->bind = calloc(columns, sizeof(MYSQL_BIND));
                    562:                enumerator->length = calloc(columns, sizeof(unsigned long));
                    563:                enumerator->val.p_void = calloc(columns, sizeof(enumerator->val));
                    564:                for (i = 0; i < columns; i++)
                    565:                {
                    566:                        switch (va_arg(args, db_type_t))
                    567:                        {
                    568:                                case DB_INT:
                    569:                                {
                    570:                                        enumerator->bind[i].buffer_type = MYSQL_TYPE_LONG;
                    571:                                        enumerator->bind[i].buffer = (char*)&enumerator->val.p_int[i];
                    572:                                        break;
                    573:                                }
                    574:                                case DB_UINT:
                    575:                                {
                    576:                                        enumerator->bind[i].buffer_type = MYSQL_TYPE_LONG;
                    577:                                        enumerator->bind[i].buffer = (char*)&enumerator->val.p_uint[i];
                    578:                                        enumerator->bind[i].is_unsigned = TRUE;
                    579:                                        break;
                    580:                                }
                    581:                                case DB_TEXT:
                    582:                                {
                    583:                                        enumerator->bind[i].buffer_type = MYSQL_TYPE_STRING;
                    584:                                        enumerator->bind[i].length = &enumerator->length[i];
                    585:                                        break;
                    586:                                }
                    587:                                case DB_BLOB:
                    588:                                {
                    589:                                        enumerator->bind[i].buffer_type = MYSQL_TYPE_BLOB;
                    590:                                        enumerator->bind[i].length = &enumerator->length[i];
                    591:                                        break;
                    592:                                }
                    593:                                case DB_DOUBLE:
                    594:                                {
                    595:                                        enumerator->bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
                    596:                                        enumerator->bind[i].buffer = (char*)&enumerator->val.p_double[i];
                    597:                                        break;
                    598:                                }
                    599:                                default:
                    600:                                        DBG1(DBG_LIB, "invalid result data type supplied");
                    601:                                        mysql_enumerator_destroy(enumerator);
                    602:                                        va_end(args);
                    603:                                        return NULL;
                    604:                        }
                    605:                }
                    606:                if (mysql_stmt_bind_result(stmt, enumerator->bind))
                    607:                {
                    608:                        DBG1(DBG_LIB, "binding MySQL result failed: %s",
                    609:                                 mysql_stmt_error(stmt));
                    610:                        mysql_enumerator_destroy(enumerator);
                    611:                        enumerator = NULL;
                    612:                }
                    613:        }
                    614:        else
                    615:        {
                    616:                conn_release(this, conn);
                    617:        }
                    618:        va_end(args);
                    619:        return (enumerator_t*)enumerator;
                    620: }
                    621: 
                    622: METHOD(database_t, execute, int,
                    623:        private_mysql_database_t *this, int *rowid, char *sql, ...)
                    624: {
                    625:        MYSQL_STMT *stmt;
                    626:        va_list args;
                    627:        conn_t *conn;
                    628:        int affected = -1;
                    629: 
                    630:        conn = conn_get(this, NULL);
                    631:        if (!conn)
                    632:        {
                    633:                return -1;
                    634:        }
                    635:        va_start(args, sql);
                    636:        stmt = run(conn->mysql, sql, &args);
                    637:        if (stmt)
                    638:        {
                    639:                if (rowid)
                    640:                {
                    641:                        *rowid = mysql_stmt_insert_id(stmt);
                    642:                }
                    643:                affected = mysql_stmt_affected_rows(stmt);
                    644:                mysql_stmt_close(stmt);
                    645:        }
                    646:        va_end(args);
                    647:        conn_release(this, conn);
                    648:        return affected;
                    649: }
                    650: 
                    651: METHOD(database_t, transaction, bool,
                    652:        private_mysql_database_t *this, bool serializable)
                    653: {
                    654:        transaction_t *trans = NULL;
                    655:        conn_t *conn;
                    656: 
                    657:        conn = conn_get(this, &trans);
                    658:        if (!conn)
                    659:        {
                    660:                return FALSE;
                    661:        }
                    662:        else if (trans)
                    663:        {
                    664:                ref_get(&trans->refs);
                    665:                return TRUE;
                    666:        }
                    667:        /* these statements are not supported in prepared statements that are used
                    668:         * by the execute() method */
                    669:        if (serializable)
                    670:        {
                    671:                if (mysql_query(conn->mysql,
                    672:                                                "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE") != 0)
                    673:                {
                    674:                        DBG1(DBG_LIB, "starting transaction failed: %s",
                    675:                                 mysql_error(conn->mysql));
                    676:                        conn_release(this, conn);
                    677:                        return FALSE;
                    678:                }
                    679:        }
                    680:        if (mysql_query(conn->mysql, "START TRANSACTION") != 0)
                    681:        {
                    682:                DBG1(DBG_LIB, "starting transaction failed: %s",
                    683:                         mysql_error(conn->mysql));
                    684:                conn_release(this, conn);
                    685:                return FALSE;
                    686:        }
                    687:        INIT(trans,
                    688:                .conn = conn,
                    689:                .refs = 1,
                    690:        );
                    691:        this->transaction->set(this->transaction, trans);
                    692:        return TRUE;
                    693: }
                    694: 
                    695: /**
                    696:  * Finalize a transaction depending on the reference count and if it should be
                    697:  * rolled back.
                    698:  */
                    699: static bool finalize_transaction(private_mysql_database_t *this,
                    700:                                                                 bool rollback)
                    701: {
                    702:        transaction_t *trans;
                    703:        char *command = "COMMIT";
                    704:        bool success;
                    705: 
                    706:        trans = this->transaction->get(this->transaction);
                    707:        if (!trans)
                    708:        {
                    709:                DBG1(DBG_LIB, "no database transaction found");
                    710:                return FALSE;
                    711:        }
                    712:        /* set flag, can't be unset */
                    713:        trans->rollback |= rollback;
                    714: 
                    715:        if (ref_put(&trans->refs))
                    716:        {
                    717:                if (trans->rollback)
                    718:                {
                    719:                        command = "ROLLBACK";
                    720:                }
                    721:                success = mysql_query(trans->conn->mysql, command) == 0;
                    722: 
                    723:                this->transaction->set(this->transaction, NULL);
                    724:                transaction_destroy(this, trans);
                    725:                return success;
                    726:        }
                    727:        return TRUE;
                    728: }
                    729: 
                    730: METHOD(database_t, commit_, bool,
                    731:        private_mysql_database_t *this)
                    732: {
                    733:        return finalize_transaction(this, FALSE);
                    734: }
                    735: 
                    736: METHOD(database_t, rollback, bool,
                    737:        private_mysql_database_t *this)
                    738: {
                    739:        return finalize_transaction(this, TRUE);
                    740: }
                    741: 
                    742: METHOD(database_t, get_driver,db_driver_t,
                    743:        private_mysql_database_t *this)
                    744: {
                    745:        return DB_MYSQL;
                    746: }
                    747: 
                    748: METHOD(database_t, destroy, void,
                    749:        private_mysql_database_t *this)
                    750: {
                    751:        this->transaction->destroy(this->transaction);
                    752:        this->pool->destroy_function(this->pool, (void*)conn_destroy);
                    753:        this->mutex->destroy(this->mutex);
                    754:        free(this->host);
                    755:        free(this->username);
                    756:        free(this->password);
                    757:        free(this->database);
                    758:        free(this);
                    759: }
                    760: 
                    761: static bool parse_uri(private_mysql_database_t *this, char *uri)
                    762: {
                    763:        char *username, *password, *host, *port = "0", *database, *pos;
                    764: 
                    765:        /**
                    766:         * parse mysql://username:pass@host:port/database uri
                    767:         */
                    768:        username = strdup(uri + 8);
                    769:        pos = strchr(username, ':');
                    770:        if (pos)
                    771:        {
                    772:                *pos = '\0';
                    773:                password = pos + 1;
                    774:                pos = strrchr(password, '@');
                    775:                if (pos)
                    776:                {
                    777:                        *pos = '\0';
                    778:                        host = pos + 1;
                    779:                        pos = strrchr(host, ':');
                    780:                        if (pos)
                    781:                        {
                    782:                                *pos = '\0';
                    783:                                port = pos + 1;
                    784:                                pos = strchr(port, '/');
                    785:                        }
                    786:                        else
                    787:                        {
                    788:                                pos = strchr(host, '/');
                    789:                        }
                    790:                        if (pos)
                    791:                        {
                    792:                                *pos = '\0';
                    793:                                database = pos + 1;
                    794: 
                    795:                                this->host = strdup(host);
                    796:                                this->username = strdup(username);
                    797:                                this->password = strdup(password);
                    798:                                this->database = strdup(database);
                    799:                                this->port = atoi(port);
                    800:                                free(username);
                    801:                                return TRUE;
                    802:                        }
                    803:                }
                    804:        }
                    805:        free(username);
                    806:        DBG1(DBG_LIB, "parsing MySQL database uri '%s' failed", uri);
                    807:        return FALSE;
                    808: }
                    809: 
                    810: 
                    811: /*
                    812:  * see header file
                    813:  */
                    814: mysql_database_t *mysql_database_create(char *uri)
                    815: {
                    816:        conn_t *conn;
                    817:        private_mysql_database_t *this;
                    818: 
                    819:        if (!strpfx(uri, "mysql://"))
                    820:        {
                    821:                return NULL;
                    822:        }
                    823: 
                    824:        INIT(this,
                    825:                .public = {
                    826:                        .db = {
                    827:                                .query = _query,
                    828:                                .execute = _execute,
                    829:                                .transaction = _transaction,
                    830:                                .commit = _commit_,
                    831:                                .rollback = _rollback,
                    832:                                .get_driver = _get_driver,
                    833:                                .destroy = _destroy,
                    834:                        },
                    835:                },
                    836:        );
                    837: 
                    838:        if (!parse_uri(this, uri))
                    839:        {
                    840:                free(this);
                    841:                return NULL;
                    842:        }
                    843:        this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
                    844:        this->pool = linked_list_create();
                    845:        this->transaction = thread_value_create(NULL);
                    846: 
                    847:        /* check connectivity */
                    848:        conn = conn_get(this, NULL);
                    849:        if (!conn)
                    850:        {
                    851:                destroy(this);
                    852:                return NULL;
                    853:        }
                    854:        conn_release(this, conn);
                    855:        return &this->public;
                    856: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>