1 
2 /*
3  * Copyright (C) Tildeslash Ltd. All rights reserved.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * In addition, as a special exception, the copyright holders give
17  * permission to link the code of portions of this program with the
18  * OpenSSL library under certain conditions as described in each
19  * individual source file, and distribute linked combinations
20  * including the two.
21  *
22  * You must obey the GNU General Public License in all respects
23  * for all of the code used other than OpenSSL.
24  */
25 
26 
27 #include "Config.h"
28 
29 #include <stdio.h>
30 #include <stdarg.h>
31 
32 #include "URL.h"
33 #include "Vector.h"
34 #include "system/Time.h"
35 #include "ResultSet.h"
36 #include "PreparedStatement.h"
37 #include "Connection.h"
38 #include "ConnectionPool.h"
39 #include "ConnectionDelegate.h"
40 
41 
42 /**
43  * Implementation of the Connection interface
44  *
45  * @file
46  */
47 
48 
49 /* ----------------------------------------------------------- Definitions */
50 
51 
52 #ifdef HAVE_LIBMYSQLCLIENT
53 extern const struct Cop_T mysqlcops;
54 #endif
55 #ifdef HAVE_LIBPQ
56 extern const struct Cop_T postgresqlcops;
57 #endif
58 #ifdef HAVE_LIBSQLITE3
59 extern const struct Cop_T sqlite3cops;
60 #endif
61 #ifdef HAVE_ORACLE
62 extern const struct Cop_T oraclesqlcops;
63 #endif
64 
65 static const struct Cop_T *cops[] = {
66 #ifdef HAVE_LIBMYSQLCLIENT
67         &mysqlcops,
68 #endif
69 #ifdef HAVE_LIBPQ
70         &postgresqlcops,
71 #endif
72 #ifdef HAVE_LIBSQLITE3
73         &sqlite3cops,
74 #endif
75 #ifdef HAVE_ORACLE
76         &oraclesqlcops,
77 #endif
78         NULL
79 };
80 
81 #define T Connection_T
82 struct Connection_S {
83         Cop_T op;
84         URL_T url;
85         int maxRows;
86         int fetchSize;
87         bool isAvailable;
88         int queryTimeout;
89         Vector_T prepared;
90         int isInTransaction;
91         int fetchSizeDefault;
92         time_t lastAccessedTime;
93         ResultSet_T resultSet;
94         ConnectionDelegate_T D;
95         ConnectionPool_T parent;
96 };
97 
98 
99 /* ------------------------------------------------------- Private methods */
100 
101 
_getOp(const char * protocol)102 static Cop_T _getOp(const char *protocol) {
103         for (int i = 0; cops[i]; i++)
104                 if (Str_startsWith(protocol, cops[i]->name))
105                         return (Cop_T)cops[i];
106         return NULL;
107 }
108 
109 
_setDelegate(T C,char ** error)110 static bool _setDelegate(T C, char **error) {
111         C->op = _getOp(URL_getProtocol(C->url));
112         if (! C->op) {
113                 *error = Str_cat("database protocol '%s' not supported", URL_getProtocol(C->url));
114                 return false;
115         }
116         C->D = C->op->new(C, error);
117         return (C->D != NULL);
118 }
119 
120 
_freePrepared(T C)121 static void _freePrepared(T C) {
122         while (! Vector_isEmpty(C->prepared)) {
123                 PreparedStatement_T ps = Vector_pop(C->prepared);
124                 PreparedStatement_free(&ps);
125         }
126 }
127 
128 
129 /* ----------------------------------------------------- Protected methods */
130 
131 
132 #ifdef PACKAGE_PROTECTED
133 #pragma GCC visibility push(hidden)
134 #endif
135 
Connection_new(void * pool,char ** error)136 T Connection_new(void *pool, char **error) {
137         assert(pool);
138         T C;
139         NEW(C);
140         C->parent = pool;
141         C->isAvailable = true;
142         C->isInTransaction = false;
143         C->prepared = Vector_new(4);
144         C->lastAccessedTime = Time_now();
145         C->url = ConnectionPool_getURL(pool);
146         C->fetchSize = SQL_DEFAULT_PREFETCH_ROWS;
147         if (! _setDelegate(C, error)) {
148                 Connection_free(&C);
149         } else {
150                 C->fetchSizeDefault = C->fetchSize;
151         }
152         return C;
153 }
154 
155 
Connection_free(T * C)156 void Connection_free(T *C) {
157         assert(C && *C);
158         Connection_clear((*C));
159         Vector_free(&((*C)->prepared));
160         if ((*C)->D)
161                 (*C)->op->free(&((*C)->D));
162         FREE(*C);
163 }
164 
165 
Connection_setAvailable(T C,int isAvailable)166 void Connection_setAvailable(T C, int isAvailable) {
167         assert(C);
168         C->isAvailable = isAvailable;
169         C->lastAccessedTime = Time_now();
170 }
171 
172 
Connection_isAvailable(T C)173 bool Connection_isAvailable(T C) {
174         assert(C);
175         return C->isAvailable;
176 }
177 
178 
Connection_getLastAccessedTime(T C)179 time_t Connection_getLastAccessedTime(T C) {
180         assert(C);
181         return C->lastAccessedTime;
182 }
183 
184 
Connection_isInTransaction(T C)185 bool Connection_isInTransaction(T C) {
186         assert(C);
187         return (C->isInTransaction > 0);
188 }
189 
190 #ifdef PACKAGE_PROTECTED
191 #pragma GCC visibility pop
192 #endif
193 
194 
195 /* ------------------------------------------------------------ Properties */
196 
197 
Connection_setQueryTimeout(T C,int ms)198 void Connection_setQueryTimeout(T C, int ms) {
199         assert(C);
200         assert(ms >= 0);
201         C->queryTimeout = ms;
202         if (C->op->setQueryTimeout)
203                 C->op->setQueryTimeout(C->D, ms);
204 }
205 
206 
Connection_getQueryTimeout(T C)207 int Connection_getQueryTimeout(T C) {
208         assert(C);
209         return C->queryTimeout;
210 }
211 
212 
Connection_setMaxRows(T C,int max)213 void Connection_setMaxRows(T C, int max) {
214         assert(C);
215         C->maxRows = max;
216 }
217 
218 
Connection_getMaxRows(T C)219 int Connection_getMaxRows(T C) {
220         assert(C);
221         return C->maxRows;
222 }
223 
224 
Connection_getURL(T C)225 URL_T Connection_getURL(T C) {
226         assert(C);
227         return C->url;
228 }
229 
230 
Connection_setFetchSize(T C,int rows)231 void Connection_setFetchSize(T C, int rows) {
232         assert(C);
233         assert(rows > 0);
234         C->fetchSize = rows;
235 }
236 
237 
Connection_getFetchSize(T C)238 int Connection_getFetchSize(T C) {
239         assert(C);
240         return C->fetchSize;
241 }
242 
243 
244 /* -------------------------------------------------------- Public methods */
245 
246 
Connection_ping(T C)247 bool Connection_ping(T C) {
248         assert(C);
249         return C->op->ping(C->D);
250 }
251 
252 
Connection_clear(T C)253 void Connection_clear(T C) {
254         assert(C);
255         if (C->resultSet)
256                 ResultSet_free(&C->resultSet);
257         _freePrepared(C);
258         // Set properties back to default values
259         C->maxRows = 0;
260         if (C->queryTimeout != 0)
261                 Connection_setQueryTimeout(C, 0);
262         C->fetchSize = C->fetchSizeDefault;
263 }
264 
265 
Connection_close(T C)266 void Connection_close(T C) {
267         assert(C);
268         ConnectionPool_returnConnection(C->parent, C);
269 }
270 
271 
Connection_beginTransaction(T C)272 void Connection_beginTransaction(T C) {
273         assert(C);
274         if (! C->op->beginTransaction(C->D))
275                 THROW(SQLException, "%s", Connection_getLastError(C));
276         C->isInTransaction++;
277 }
278 
279 
Connection_commit(T C)280 void Connection_commit(T C) {
281         assert(C);
282         if (C->isInTransaction)
283                 C->isInTransaction = 0;
284         // Even if we are not in a transaction, call the delegate anyway and propagate any errors
285         if (! C->op->commit(C->D))
286                 THROW(SQLException, "%s", Connection_getLastError(C));
287 }
288 
289 
Connection_rollback(T C)290 void Connection_rollback(T C) {
291         assert(C);
292         if (C->isInTransaction) {
293                 // Clear any pending resultset statements first
294                 Connection_clear(C);
295                 C->isInTransaction = 0;
296         }
297         // Even if we are not in a transaction, call the delegate anyway and propagate any errors
298         if (! C->op->rollback(C->D))
299                 THROW(SQLException, "%s", Connection_getLastError(C));
300 }
301 
302 
Connection_lastRowId(T C)303 long long Connection_lastRowId(T C) {
304         assert(C);
305         return C->op->lastRowId(C->D);
306 }
307 
308 
Connection_rowsChanged(T C)309 long long Connection_rowsChanged(T C) {
310         assert(C);
311         return C->op->rowsChanged(C->D);
312 }
313 
314 
Connection_execute(T C,const char * sql,...)315 void Connection_execute(T C, const char *sql, ...) {
316         assert(C);
317         assert(sql);
318         if (C->resultSet)
319                 ResultSet_free(&C->resultSet);
320         va_list ap;
321         va_start(ap, sql);
322         int success = C->op->execute(C->D, sql, ap);
323         va_end(ap);
324         if (! success) THROW(SQLException, "%s", Connection_getLastError(C));
325 }
326 
327 
Connection_executeQuery(T C,const char * sql,...)328 ResultSet_T Connection_executeQuery(T C, const char *sql, ...) {
329         assert(C);
330         assert(sql);
331         if (C->resultSet)
332                 ResultSet_free(&C->resultSet);
333         va_list ap;
334         va_start(ap, sql);
335         C->resultSet = C->op->executeQuery(C->D, sql, ap);
336         va_end(ap);
337         if (! C->resultSet)
338                 THROW(SQLException, "%s", Connection_getLastError(C));
339         return C->resultSet;
340 }
341 
342 
Connection_prepareStatement(T C,const char * sql,...)343 PreparedStatement_T Connection_prepareStatement(T C, const char *sql, ...) {
344         assert(C);
345         assert(sql);
346         va_list ap;
347         va_start(ap, sql);
348         PreparedStatement_T p = C->op->prepareStatement(C->D, sql, ap);
349         va_end(ap);
350         if (p)
351                 Vector_push(C->prepared, p);
352         else
353                 THROW(SQLException, "%s", Connection_getLastError(C));
354         return p;
355 }
356 
357 
Connection_getLastError(T C)358 const char *Connection_getLastError(T C) {
359         assert(C);
360         const char *s = C->op->getLastError(C->D);
361         return STR_DEF(s) ? s : "?";
362 }
363 
364 
Connection_isSupported(const char * url)365 bool Connection_isSupported(const char *url) {
366         return (url ? (_getOp(url) != NULL) : false);
367 }
368 
369