7a811bb593f91dacc730ee752a15141ae3eb753e
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_firebird / rlm_sql_firebird.c
1 /*
2  * sql_firebird.c Part of Firebird rlm_sql driver
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Copyright 2006  The FreeRADIUS server project
19  * Copyright 2006  Vitaly Bodzhgua <vitaly@eastera.net>
20  */
21
22 RCSID("$Id$")
23
24 #include "sql_fbapi.h"
25 #include <freeradius-devel/rad_assert.h>
26
27
28 /* Forward declarations */
29 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
30 static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
31 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
32 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
33
34 static int _sql_socket_destructor(rlm_sql_firebird_conn_t *conn)
35 {
36         int i;
37
38         DEBUG2("rlm_sql_firebird: socket destructor called, closing socket");
39
40         fb_commit(conn);
41         if (conn->dbh) {
42                 fb_free_statement(conn);
43                 isc_detach_database(conn->status, &(conn->dbh));
44
45                 if (fb_error(conn)) {
46                         WARN("rlm_sql_firebird: Got error "
47                                "when closing socket: %s", conn->error);
48                 }
49         }
50
51 #ifdef _PTHREAD_H
52         pthread_mutex_destroy (&conn->mut);
53 #endif
54
55         for (i = 0; i < conn->row_fcount; i++) free(conn->row[i]);
56
57         free(conn->row);
58         free(conn->row_sizes);
59         fb_free_sqlda(conn->sqlda_out);
60
61         free(conn->sqlda_out);
62         free(conn->tpb);
63         free(conn->dpb);
64
65         return 0;
66 }
67
68 /** Establish connection to the db
69  *
70  */
71 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
72 {
73         rlm_sql_firebird_conn_t *conn;
74
75         long res;
76
77         MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_firebird_conn_t));
78         talloc_set_destructor(conn, _sql_socket_destructor);
79
80         res = fb_init_socket(conn);
81         if (res) return RLM_SQL_ERROR;
82
83         if (fb_connect(conn, config)) {
84                 ERROR("rlm_sql_firebird: Connection failed: %s", conn->error);
85
86                 return RLM_SQL_RECONNECT;
87         }
88
89         return 0;
90 }
91
92 /** Issue a non-SELECT query (ie: update/delete/insert) to the database.
93  *
94  */
95 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
96 {
97         rlm_sql_firebird_conn_t *conn = handle->conn;
98
99         int deadlock = 0;
100
101 #ifdef _PTHREAD_H
102         pthread_mutex_lock(&conn->mut);
103 #endif
104
105         try_again:
106         /*
107          *      Try again query when deadlock, beacuse in any case it
108          *      will be retried.
109          */
110         if (fb_sql_query(conn, query)) {
111                 /* but may be lost for short sessions */
112                 if ((conn->sql_code == DEADLOCK_SQL_CODE) &&
113                     !deadlock) {
114                         DEBUG("conn_id deadlock. Retry query %s", query);
115
116                         /*
117                          *      @todo For non READ_COMMITED transactions put
118                          *      rollback here
119                          *      fb_rollback(conn);
120                          */
121                         deadlock = 1;
122                         goto try_again;
123                 }
124
125                 ERROR("conn_id rlm_sql_firebird,sql_query error: sql_code=%li, error='%s', query=%s",
126                       (long int) conn->sql_code, conn->error, query);
127
128                 if (conn->sql_code == DOWN_SQL_CODE) {
129                 reconnect:
130 #ifdef _PTHREAD_H
131                         pthread_mutex_unlock(&conn->mut);
132 #endif
133                         return RLM_SQL_RECONNECT;
134                 }
135
136                 /* Free problem query */
137                 if (fb_rollback(conn)) {
138                         //assume the network is down if rollback had failed
139                         ERROR("Fail to rollback transaction after previous error: %s", conn->error);
140
141                         goto reconnect;
142                 }
143                 //   conn->in_use=0;
144         fail:
145 #ifdef _PTHREAD_H
146                 pthread_mutex_unlock(&conn->mut);
147 #endif
148                 return RLM_SQL_ERROR;
149         }
150
151         if (conn->statement_type != isc_info_sql_stmt_select) {
152                 if (fb_commit(conn)) goto fail;
153         }
154
155 #ifdef _PTHREAD_H
156         pthread_mutex_unlock(&conn->mut);
157 #endif
158         return 0;
159 }
160
161 /** Issue a select query to the database.
162  *
163  */
164 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
165 {
166         return sql_query(handle, config, query);
167 }
168
169 /** Returns number of columns from query.
170  *
171  */
172 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
173 {
174         return ((rlm_sql_firebird_conn_t *) handle->conn)->sqlda_out->sqld;
175 }
176
177 /** Returns number of rows in query.
178  *
179  */
180 static int sql_num_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
181 {
182         return sql_affected_rows(handle, config);
183 }
184
185 /** Returns name of fields.
186  *
187  */
188 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
189 {
190         rlm_sql_firebird_conn_t *conn = handle->conn;
191
192         int             fields, i;
193         char const      **names;
194
195         fields = conn->sqlda_out->sqld;
196         if (fields <= 0) return RLM_SQL_ERROR;
197
198         MEM(names = talloc_array(handle, char const *, fields));
199
200         for (i = 0; i < fields; i++) names[i] = conn->sqlda_out->sqlvar[i].sqlname;
201         *out = names;
202
203         return RLM_SQL_OK;
204 }
205
206 /** Returns an individual row.
207  *
208  */
209 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
210 {
211         rlm_sql_firebird_conn_t *conn = handle->conn;
212         int res;
213
214         handle->row = NULL;
215
216         if (conn->statement_type != isc_info_sql_stmt_exec_procedure) {
217                 res = fb_fetch(conn);
218                 if (res == 100) {
219                         return 0;
220                 }
221
222                 if (res) {
223                         ERROR("rlm_sql_firebird. Fetch problem: %s", conn->error);
224
225                         return RLM_SQL_ERROR;
226                 }
227         } else {
228                 conn->statement_type=0;
229         }
230
231         fb_store_row(conn);
232
233         handle->row = conn->row;
234
235         return 0;
236 }
237
238 /** End the select query, such as freeing memory or result.
239  *
240  */
241 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
242 {
243         rlm_sql_firebird_conn_t *conn = (rlm_sql_firebird_conn_t *) handle->conn;
244
245         fb_commit(conn);
246         fb_close_cursor(conn);
247
248         return 0;
249 }
250
251 /** End the query
252  *
253  */
254 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
255 {
256         sql_free_result(handle, config);
257
258         return 0;
259 }
260
261 /** Frees memory allocated for a result set.
262  *
263  */
264 static sql_rcode_t sql_free_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
265 {
266         return 0;
267 }
268
269 /** Retrieves any errors associated with the connection handle
270  *
271  * @note Caller will free any memory allocated in ctx.
272  *
273  * @param ctx to allocate temporary error buffers in.
274  * @param out Array of sql_log_entrys to fill.
275  * @param outlen Length of out array.
276  * @param handle rlm_sql connection handle.
277  * @param config rlm_sql config.
278  * @return number of errors written to the sql_log_entry array.
279  */
280 static size_t sql_error(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
281                         rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
282 {
283         rlm_sql_firebird_conn_t *conn = handle->conn;
284
285         rad_assert(conn);
286         rad_assert(outlen > 0);
287
288         if (!conn->error) return 0;
289
290         out[0].type = L_ERR;
291         out[0].msg = conn->error;
292
293         return 1;
294 }
295
296 /** Return the number of rows affected by the query (update, or insert)
297  *
298  */
299 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
300 {
301         return fb_affected_rows(handle->conn);
302 }
303
304 /* Exported to rlm_sql */
305 extern rlm_sql_module_t rlm_sql_firebird;
306 rlm_sql_module_t rlm_sql_firebird = {
307         .name                           = "rlm_sql_firebird",
308         .sql_socket_init                = sql_socket_init,
309         .sql_query                      = sql_query,
310         .sql_select_query               = sql_select_query,
311         .sql_num_fields                 = sql_num_fields,
312         .sql_num_rows                   = sql_num_rows,
313         .sql_affected_rows              = sql_affected_rows,
314         .sql_fetch_row                  = sql_fetch_row,
315         .sql_fields                     = sql_fields,
316         .sql_free_result                = sql_free_result,
317         .sql_error                      = sql_error,
318         .sql_finish_query               = sql_finish_query,
319         .sql_finish_select_query        = sql_finish_select_query
320 };