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