#include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+#include <dlfcn.h>
+#include <pthread.h>
 
 #include <mysql.h>
 #include <mysqld_error.h>
 #define GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH 256
 #endif
 
-
 #define CLR_ERR(CTX) lbu_clrerr((CTX))
-#define ERR(CTX, CODE, DESC) lbu_err((CTX), (CODE), (DESC), __FUNCTION__, __LINE__)
+#define ERR(CTX, CODE, ARGS...) lbu_err((CTX), (CODE), __FUNCTION__, __LINE__, ##ARGS)
 #define STATUS(CTX) ((CTX)->err.code)
 #define MY_ERR(CTX) myerr((CTX), __FUNCTION__, __LINE__)
 #define MY_ERRSTMT(STMT) myerrstmt((STMT), __FUNCTION__, __LINE__)
 #define MY_ISOKSTMT(STMT, RETRY) myisokstmt((STMT), __FUNCTION__, __LINE__, (RETRY))
 
 #define USE_TRANS(CTX) ((CTX->caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0)
+#define LOAD(SYM, SYM2) if ((*(void **)(&db_handle.SYM) = dlsym(db_handle.lib, SYM2)) == NULL) { \
+       err = ERR(ctx, ENOENT, "can't load symbol %s from mysql library (%s)", SYM2, dlerror()); \
+       break; \
+}
 
 #define dprintf(CTX, FMT...) if (CTX->caps & GLITE_LBU_DB_CAP_ERRORS) fprintf(stderr, ##FMT)
 
 };
 
 
+typedef struct {
+       void *lib;
+       pthread_mutex_t lock;
+
+       void *(*mysql_init)(void *);
+       int (*mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg);
+       unsigned int (*mysql_errno)(MYSQL *mysql);
+       const char *(*mysql_error)(MYSQL *mysql);
+       unsigned int (*mysql_stmt_errno)(MYSQL_STMT *stmt);
+       const char *(*mysql_stmt_error)(MYSQL_STMT *stmt);
+       MYSQL *(*mysql_real_connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag);
+       void (*mysql_close)(MYSQL *mysql);
+       int (*mysql_query)(MYSQL *mysql, const char *stmt_str);
+       MYSQL_RES *(*mysql_store_result)(MYSQL *mysql);
+       void (*mysql_free_result)(MYSQL_RES *result);
+       my_ulonglong (*mysql_affected_rows)(MYSQL *mysql);
+       my_bool (*mysql_stmt_close)(MYSQL_STMT *);
+       unsigned int (*mysql_num_fields)(MYSQL_RES *result);
+       unsigned long *(*mysql_fetch_lengths)(MYSQL_RES *result);
+       my_bool (*mysql_stmt_bind_result)(MYSQL_STMT *stmt, MYSQL_BIND *bind);
+       int (*mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *stmt_str, unsigned long length);
+       int (*mysql_stmt_store_result)(MYSQL_STMT *stmt);
+       MYSQL_ROW (*mysql_fetch_row)(MYSQL_RES *result);
+       MYSQL_FIELD *(*mysql_fetch_field)(MYSQL_RES *result);
+       const char *(*mysql_get_server_info)(MYSQL *mysql);
+       MYSQL_STMT *(*mysql_stmt_init)(MYSQL *mysql);
+       my_bool (*mysql_stmt_bind_param)(MYSQL_STMT *stmt, MYSQL_BIND *bind);
+       int (*mysql_stmt_execute)(MYSQL_STMT *stmt);
+       int (*mysql_stmt_fetch)(MYSQL_STMT *stmt);
+       my_ulonglong (*mysql_stmt_insert_id)(MYSQL_STMT *stmt);
+       my_ulonglong (*mysql_stmt_affected_rows)(MYSQL_STMT *stmt);
+       MYSQL_RES *(*mysql_stmt_result_metadata)(MYSQL_STMT *stmt);
+       int (*mysql_stmt_fetch_column)(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset);
+} mysql_lib_handle_t;
+
+
+static mysql_lib_handle_t db_handle = {
+       lib: NULL,
+       lock: PTHREAD_MUTEX_INITIALIZER
+};
+
 
 static int lbu_clrerr(glite_lbu_DBContext ctx);
-static int lbu_err(glite_lbu_DBContext ctx, int code, const char *desc, const char *func, int line);
+static int lbu_err(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...);
 static int myerr(glite_lbu_DBContext ctx, const char *source, int line);
 static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line);
 static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, int *retry);
 }
 
 
+static void glite_lbu_DBCleanup(void) {
+       pthread_mutex_lock(&db_handle.lock);
+       if (db_handle.lib) {
+               dlclose(db_handle.lib);
+               db_handle.lib = NULL;
+       }
+       pthread_mutex_unlock(&db_handle.lock);
+}
+
+
 int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
+       int err = 0;
+
+       /* dynamic load the mysql library */
+       pthread_mutex_lock(&db_handle.lock);
+       if (!db_handle.lib) {
+               if ((!MYSQL_LIBPATH[0] || (db_handle.lib = dlopen(MYSQL_LIBPATH, RTLD_LAZY | RTLD_LOCAL)) == NULL) &&
+                   (db_handle.lib = dlopen("libmysqlclient.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
+                       return ERR(ctx, ENOENT, "can't load '%s' or 'libmysqlclient.so' (%s)", MYSQL_LIBPATH, dlerror());
+               do {
+                       LOAD(mysql_init, "mysql_init");
+                       LOAD(mysql_options, "mysql_options");
+                       LOAD(mysql_errno, "mysql_errno");
+                       LOAD(mysql_error, "mysql_error");
+                       LOAD(mysql_stmt_errno, "mysql_stmt_errno");
+                       LOAD(mysql_stmt_error, "mysql_stmt_error");
+                       LOAD(mysql_real_connect, "mysql_real_connect");
+                       LOAD(mysql_close, "mysql_close");
+                       LOAD(mysql_query, "mysql_query");
+                       LOAD(mysql_store_result, "mysql_store_result");
+                       LOAD(mysql_free_result, "mysql_free_result");
+                       LOAD(mysql_affected_rows, "mysql_affected_rows");
+                       LOAD(mysql_stmt_close, "mysql_stmt_close");
+                       LOAD(mysql_num_fields, "mysql_num_fields");
+                       LOAD(mysql_fetch_lengths, "mysql_fetch_lengths");
+                       LOAD(mysql_stmt_bind_result, "mysql_stmt_bind_result");
+                       LOAD(mysql_stmt_prepare, "mysql_stmt_prepare");
+                       LOAD(mysql_stmt_store_result, "mysql_stmt_store_result");
+                       LOAD(mysql_fetch_row, "mysql_fetch_row");
+                       LOAD(mysql_fetch_field, "mysql_fetch_field");
+                       LOAD(mysql_get_server_info, "mysql_get_server_info");
+                       LOAD(mysql_stmt_init, "mysql_stmt_init");
+                       LOAD(mysql_stmt_bind_param, "mysql_stmt_bind_param");
+                       LOAD(mysql_stmt_execute, "mysql_stmt_execute");
+                       LOAD(mysql_stmt_fetch, "mysql_stmt_fetch");
+                       LOAD(mysql_stmt_insert_id, "mysql_stmt_insert_id");
+                       LOAD(mysql_stmt_affected_rows, "mysql_stmt_affected_rows");
+                       LOAD(mysql_stmt_result_metadata, "mysql_stmt_result_metadata");
+                       LOAD(mysql_stmt_fetch_column, "mysql_stmt_fetch_column");
+                       pthread_mutex_unlock(&db_handle.lock);
+                       atexit(glite_lbu_DBCleanup);
+               } while(0);
+               if (err) {
+                       dlclose(db_handle.lib);
+                       db_handle.lib = NULL;
+                       pthread_mutex_unlock(&db_handle.lock);
+                       return err;
+               }
+       } else pthread_mutex_unlock(&db_handle.lock);
+
        if (db_connect(ctx, cs, &ctx->mysql) != 0) return STATUS(ctx);
        return 0;
 }
        origcaps = ctx->caps;
        caps = 0;
 
-       ver_s = mysql_get_server_info(m);
+       ver_s = db_handle.mysql_get_server_info(m);
        if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub))
                return ERR(ctx, EINVAL, "problem retreiving MySQL version");
        version = 10000*major + 100*minor + sub;
 
 void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) {
        if (*stmt) {
-               if ((*stmt)->result) mysql_free_result((*stmt)->result);
-               if ((*stmt)->stmt) mysql_stmt_close((*stmt)->stmt);
+               if ((*stmt)->result) db_handle.mysql_free_result((*stmt)->result);
+               if ((*stmt)->stmt) db_handle.mysql_stmt_close((*stmt)->stmt);
                free(*stmt);
                *stmt = NULL;
        }
 
        while (retry_nr == 0 || do_reconnect) {
                do_reconnect = 0;
-               if (mysql_query(ctx->mysql, cmd)) {
+               if (db_handle.mysql_query(ctx->mysql, cmd)) {
                        /* error occured */
-                       switch (merr = mysql_errno(ctx->mysql)) {
+                       switch (merr = db_handle.mysql_errno(ctx->mysql)) {
                                case 0:
                                        break;
                                case ER_DUP_ENTRY: 
-                                       ERR(ctx, EEXIST, mysql_error(ctx->mysql));
+                                       ERR(ctx, EEXIST, db_handle.mysql_error(ctx->mysql));
                                        return -1;
                                        break;
                                case CR_SERVER_LOST:
                        return -1;
                }
                (**stmt).ctx = ctx;
-               (**stmt).result = mysql_store_result(ctx->mysql);
+               (**stmt).result = db_handle.mysql_store_result(ctx->mysql);
                if (!(**stmt).result) {
-                       if (mysql_errno(ctx->mysql)) {
+                       if (db_handle.mysql_errno(ctx->mysql)) {
                                MY_ERR(ctx);
                                *stmt = NULL;
                                return -1;
                        }
                }
        } else {
-               MYSQL_RES       *r = mysql_store_result(ctx->mysql);
-               mysql_free_result(r);
+               MYSQL_RES       *r = db_handle.mysql_store_result(ctx->mysql);
+               db_handle.mysql_free_result(r);
        }
 #ifdef LBS_DB_PROFILE
        pid = getpid();
        fprintf(stderr,"[%d] %s\n[%d] %3ld.%06ld (sum: %3ld.%06ld)\n",pid,txt,pid,end.tv_sec,end.tv_usec,sum.tv_sec,sum.tv_usec);
 #endif
 
-       return mysql_affected_rows(ctx->mysql);
+       return db_handle.mysql_affected_rows(ctx->mysql);
 }
 
 
 
        CLR_ERR(stmt->ctx);
        if (!stmt->result) return ERR(stmt->ctx, EINVAL, "QueryColumns implemented only for simple API");
-       while ((f = mysql_fetch_field(stmt->result))) cols[i++] = f->name;
+       while ((f = db_handle.mysql_fetch_field(stmt->result))) cols[i++] = f->name;
        return i == 0;
 }
 
        (*stmt)->ctx = ctx;
 
        // create the SQL command
-       if (((*stmt)->stmt = mysql_stmt_init(ctx->mysql)) == NULL)
+       if (((*stmt)->stmt = db_handle.mysql_stmt_init(ctx->mysql)) == NULL)
                return MY_ERRSTMT(*stmt);
 
        // prepare the SQL command
        retry = 1;
        do {
-               mysql_stmt_prepare((*stmt)->stmt, sql, strlen(sql));
+               db_handle.mysql_stmt_prepare((*stmt)->stmt, sql, strlen(sql));
                ret = MY_ISOKSTMT(*stmt, &retry);
        } while (ret == 0);
        if (ret == -1) goto failed;
 
        // number of fields (0 for no results)
-       if ((meta = mysql_stmt_result_metadata((*stmt)->stmt)) != NULL) {
-               (*stmt)->nrfields = mysql_num_fields(meta);
-               mysql_free_result(meta);
+       if ((meta = db_handle.mysql_stmt_result_metadata((*stmt)->stmt)) != NULL) {
+               (*stmt)->nrfields = db_handle.mysql_num_fields(meta);
+               db_handle.mysql_free_result(meta);
        } else
                (*stmt)->nrfields = 0;
 
        va_end(ap);
 
        // bind parameters
-       if (mysql_stmt_bind_param(stmt->stmt, binds) != 0) {
+       if (db_handle.mysql_stmt_bind_param(stmt->stmt, binds) != 0) {
                MY_ERRSTMT(stmt);
                goto failed;
        }
        ctx = stmt->ctx;
        retry = 1;
        do {
-               mysql_stmt_execute(stmt->stmt);
+               db_handle.mysql_stmt_execute(stmt->stmt);
                ret = MY_ISOKSTMT(stmt, &retry);
        } while (ret == 0);
        if (ret == -1) goto failed;
        // result
        retry = 1;
        do {
-               mysql_stmt_store_result(stmt->stmt);
+               db_handle.mysql_stmt_store_result(stmt->stmt);
                ret = MY_ISOKSTMT(stmt, &retry);
        } while (ret == 0);
        if (ret == -1) goto failed;
        free(binds);
        free(lens);
        CLR_ERR(ctx);
-       return mysql_stmt_affected_rows(stmt->stmt);
+       return db_handle.mysql_stmt_affected_rows(stmt->stmt);
 
 failed:
        for (i = 0; i < n; i++) free(data[i]);
        my_ulonglong i;
 
        CLR_ERR(stmt->ctx);
-       i = mysql_stmt_insert_id(stmt->stmt);
+       i = db_handle.mysql_stmt_insert_id(stmt->stmt);
        assert(i < ((unsigned long int)-1) >> 1);
        return (long int)i;
 }
 /*
  * helping compatibility function: sets error on the context
  */
-static int lbu_err(glite_lbu_DBContext ctx, int code, const char *desc, const char *func, int line) {
+static int lbu_err(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...) {
+       va_list ap;
+
        if (code) {
                ctx->err.code = code;
                free(ctx->err.desc);
-               ctx->err.desc = desc ? strdup(desc) : NULL;
+               if (desc) {
+                       va_start(ap, desc);
+                       vasprintf(&ctx->err.desc, desc, ap);
+                       va_end(ap);
+               } else
+                       ctx->err.desc = NULL;
                dprintf(ctx, "[db %d] %s:%d %s\n", getpid(), func, line, desc);
                return code;
        } else
  * helping function: find oud mysql error and sets on the context
  */
 static int myerr(glite_lbu_DBContext ctx, const char *source, int line) {
-       return lbu_err(ctx, EIO, mysql_error(ctx->mysql), source, line);
+       return lbu_err(ctx, EIO, source, line, db_handle.mysql_error(ctx->mysql));
 }
 
 
  * helping function: find oud mysql stmt error and sets on the context
  */
 static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line) { 
-       return lbu_err(stmt->ctx, EIO, mysql_stmt_error(stmt->stmt), source, line);
+       return lbu_err(stmt->ctx, EIO, source, line, db_handle.mysql_stmt_error(stmt->stmt));
 }
 
 
  * \return  1 OK
  */
 static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, int *retry) {
-       switch (mysql_stmt_errno(stmt->stmt)) {
+       switch (db_handle.mysql_stmt_errno(stmt->stmt)) {
                case 0:
                        return 1;
                        break;
                case ER_DUP_ENTRY:
-                       lbu_err(stmt->ctx, EEXIST, mysql_stmt_error(stmt->stmt), source, line);
+                       lbu_err(stmt->ctx, EEXIST, source, line, db_handle.mysql_stmt_error(stmt->stmt));
                        return -1;
                        break;
                case CR_SERVER_LOST:
 
        if (!cs) return ERR(ctx, EINVAL, "connect string not specified");
        
-       if (!(*mysql = mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL);
+       if (!(*mysql = db_handle.mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL);
 
-       mysql_options(*mysql, MYSQL_READ_DEFAULT_FILE, "my");
+       db_handle.mysql_options(*mysql, MYSQL_READ_DEFAULT_FILE, "my");
 #ifdef MYSQL_OPT_RECONNECT
        /* XXX: may result in weird behaviour in the middle of transaction */
-       mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect);
+       db_handle.mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect);
 #endif
 
        host = user = pw = db = NULL;
        /* ljocha: CLIENT_FOUND_ROWS added to make authorization check
         * working in update_notif(). 
         * Hope it does not break anything else */ 
-       if (!mysql_real_connect(*mysql,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) {
+       if (!db_handle.mysql_real_connect(*mysql,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) {
                free(buf);
                ret = MY_ERR(ctx);
                glite_lbu_DBClose(ctx);
  * mysql close
  */
 static void db_close(MYSQL *mysql) {
-       if (mysql) mysql_close(mysql);
+       if (mysql) db_handle.mysql_close(mysql);
 }
 
 
 
        CLR_ERR(ctx);
 
-       if (!(row = mysql_fetch_row(result))) {
-               if (mysql_errno((MYSQL *) ctx->mysql)) {
+       if (!(row = db_handle.mysql_fetch_row(result))) {
+               if (db_handle.mysql_errno((MYSQL *) ctx->mysql)) {
                        MY_ERR(ctx);
                        return -1;
                } else return 0;
        }
 
-       nr = mysql_num_fields(result);
-       len = mysql_fetch_lengths(result);
+       nr = db_handle.mysql_num_fields(result);
+       len = db_handle.mysql_fetch_lengths(result);
        for (i=0; i<nr; i++) {
                if (lengths) lengths[i] = len[i];
                if (len[i]) {
                binds[i].length = &lengths[i];
                binds[i].buffer = results[i] = calloc(1, GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH);
        }
-       if (mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt;
+       if (db_handle.mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt;
 
        // fetch data, all can be truncated
        retry = 1;
        do {
-               switch(mysql_stmt_fetch(stmt->stmt)) {
+               switch(db_handle.mysql_stmt_fetch(stmt->stmt)) {
 #ifdef MYSQL_DATA_TRUNCATED
                        case MYSQL_DATA_TRUNCATED:
 #endif
 
                        retry = 1;
                        do {
-                               switch (mysql_stmt_fetch_column(stmt->stmt, binds + i, i, fetched)) {
+                               switch (db_handle.mysql_stmt_fetch_column(stmt->stmt, binds + i, i, fetched)) {
                                        case 0: ret = 1; break;
                                        case 1: ret = MY_ISOKSTMT(stmt, &retry); break;
                                        case MYSQL_NO_DATA: ret = 0; goto quit; /* it's OK */