/* * Copyright (C) 2007 Tildeslash Ltd. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * There are special exceptions to the terms and conditions of the GPL * as it is applied to this software. View the full text of the exception * in the file EXCEPTIONS accompanying this software distribution. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Config.h" #include #include #include "URL.h" #include "Util.h" #include "Vector.h" #include "ResultSet.h" #include "PreparedStatement.h" #include "Connection.h" #include "ConnectionPool.h" #include "ConnectionStrategy.h" /** * Implementation of the Connection interface * * @version \$Id: Connection.c,v 1.38 2007/02/11 12:08:19 hauk Exp $ * @file */ /* ----------------------------------------------------------- Definitions */ #ifdef HAVE_LIBMYSQLCLIENT extern const struct conop mysqlconops; #endif #ifdef HAVE_LIBPQ extern const struct conop postgresqlconops; #endif #ifdef HAVE_LIBSQLITE3 extern const struct conop sqlite3conops; #endif const struct conop *conops[]= { #ifdef HAVE_LIBMYSQLCLIENT &mysqlconops, #endif #ifdef HAVE_LIBPQ &postgresqlconops, #endif #ifdef HAVE_LIBSQLITE3 &sqlite3conops, #endif NULL }; #define T Connection_T struct T { Cop_T op; URL_T url; int maxRows; int timeout; int isAvailable; IConnection_T db; Vector_T prepared; int isInTransaction; long lastAccessedTime; ResultSet_T resultSet; ConnectionPool_T parent; }; /* ------------------------------------------------------------ Prototypes */ static void freePrepared(T C); static void clearResultSet(T P); static Cop_T getOp(const char *protocol); static int setStrategy(T C, char **error); /* ----------------------------------------------------- Protected methods */ #ifdef PACKAGE_PROTECTED #pragma GCC visibility push(hidden) #endif T Connection_new(void *pool, char **error) { T C; assert(pool); NEW(C); C->parent= pool; C->isAvailable= TRUE; C->isInTransaction= FALSE; C->prepared= Vector_new(1); C->timeout= SQL_DEFAULT_TIMEOUT; C->url= ConnectionPool_getURL(pool); if(setStrategy(C, error)==FALSE) { Connection_free(&C); return NULL; } C->lastAccessedTime= Util_seconds(); return C; } void Connection_free(T *C) { assert(C && *C); Connection_clear((*C)); Vector_free(&(*C)->prepared); if((*C)->db) (*C)->op->free(&(*C)->db); FREE(*C); } void Connection_clear(T C) { assert(C); if(C->resultSet) { ResultSet_free(&C->resultSet); } freePrepared(C); C->isInTransaction= FALSE; } void Connection_setAvailable(T C, int isAvailable) { assert(C); C->isAvailable= isAvailable; C->lastAccessedTime= Util_seconds(); } int Connection_isAvailable(T C) { assert(C); return C->isAvailable; } int Connection_ping(T C) { assert(C); return C->op->ping(C->db); } long Connection_getLastAccessedTime(T C) { assert(C); return C->lastAccessedTime; } int Connection_isInTransaction(T C) { assert(C); return (C->isInTransaction > 0); } #ifdef PACKAGE_PROTECTED #pragma GCC visibility pop #endif /* -------------------------------------------------------- Public methods */ URL_T Connection_getURL(T C) { assert(C); return C->url; } void Connection_close(T C) { assert(C); ConnectionPool_returnConnection(C->parent, C); } void Connection_setQueryTimeout(T C, int ms) { assert(C); C->timeout= ms; C->op->setQueryTimeout(C->db, ms); } int Connection_getQueryTimeout(T C) { assert(C); return C->timeout; } void Connection_setMaxRows(T C, int max) { assert(C); C->maxRows= max; C->op->setMaxRows(C->db, max); } int Connection_getMaxRows(T C) { assert(C); return C->maxRows; } int Connection_beginTransaction(T C) { assert(C); if(! C->op->beginTransaction(C->db)) { THROW(SQLException); return FALSE; } C->isInTransaction++; return TRUE; } int Connection_commit(T C) { assert(C); if(C->isInTransaction) C->isInTransaction--; if(! C->op->commit(C->db)) { THROW(SQLException); return FALSE; } return TRUE; } int Connection_rollback(T C) { assert(C); if(C->isInTransaction) C->isInTransaction--; if(! C->op->rollback(C->db)) { THROW(SQLException); return FALSE; } return TRUE; } long long int Connection_lastRowId(T C) { assert(C); return C->op->lastRowId(C->db); } long long int Connection_rowsChanged(T C) { assert(C); return C->op->rowsChanged(C->db); } int Connection_execute(T C, const char *sql, ...) { int rv= FALSE; va_list ap; assert(C); clearResultSet(C); va_start(ap, sql); rv= C->op->execute(C->db, sql, ap); va_end(ap); if(rv == FALSE) THROW(SQLException); return rv; } ResultSet_T Connection_executeQuery(T C, const char *sql, ...) { va_list ap; assert(C); clearResultSet(C); va_start(ap, sql); C->resultSet= C->op->executeQuery(C->db, sql, ap); va_end(ap); if(C->resultSet == NULL) THROW(SQLException); return C->resultSet; } PreparedStatement_T Connection_prepareStatement(T C, const char *sql) { PreparedStatement_T p; assert(C); p= C->op->prepareStatement(C->db, sql); if(p) Vector_push(C->prepared, p); else THROW(SQLException); return p; } const char *Connection_getLastError(T C) { const char *s; assert(C); s= C->op->getLastError(C->db); return (s ? s : "?"); } int Connection_isSupported(const char *url) { if(url) return (getOp(url) != NULL); return FALSE; } /* ------------------------------------------------------- Private methods */ static Cop_T getOp(const char *protocol) { int i; for(i= 0; conops[i]; i++) if(Util_startsWith(conops[i]->name, protocol)) return (Cop_T)conops[i]; return NULL; } static int setStrategy(T C, char **error) { const char *protocol= URL_getProtocol(C->url); C->op= getOp(protocol); if(C->op == NULL) { *error= Util_getString("database protocol '%s' not supported", protocol); return FALSE; } C->db= C->op->new(C->url, error); return (C->db != NULL); } static void freePrepared(T C) { PreparedStatement_T ps; while(! Vector_isEmpty(C->prepared)) { ps= Vector_pop(C->prepared); PreparedStatement_free(&ps); } assert(Vector_isEmpty(C->prepared)); } static void clearResultSet(T C) { if(C->resultSet) ResultSet_free(&C->resultSet); }