The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
sql_fbapi.c
Go to the documentation of this file.
1/*
2 * sql_fbapi.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
22RCSID("$Id: 57be163cd0603de51a9c609da01210fb5ec2c30a $")
23
24#include "sql_fbapi.h"
25
26#include <stdarg.h>
27
28static void fb_dpb_add_str(char **dpb, char name, char const *value)
29{
30 int l;
31
32 if (!value) return;
33
34 l = strlen(value);
35
36 *(*dpb)++= name;
37 *(*dpb)++= (char) l;
38
39 memmove(*dpb, value, l);
40
41 *dpb += l;
42}
43
44static void fb_set_sqlda(XSQLDA *sqlda) {
45 int i;
46
47 for (i = 0; i < sqlda->sqld; i++) {
48 if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_VARYING) {
49 MEM(sqlda->sqlvar[i].sqldata = talloc_array(sqlda, char, sqlda->sqlvar[i].sqllen + sizeof(short)));
50 } else {
51 MEM(sqlda->sqlvar[i].sqldata = talloc_array(sqlda, char, sqlda->sqlvar[i].sqllen));
52 }
53
54 if (sqlda->sqlvar[i].sqltype & 1) {
55 MEM(sqlda->sqlvar[i].sqlind = talloc(sqlda, short));
56 } else {
57 sqlda->sqlvar[i].sqlind = 0;
58 }
59 }
60}
61
62DIAG_OFF(deprecated-declarations)
64{
65 ISC_SCHAR error[2048]; /* Only 1024 bytes should be written to this, but were playing it extra safe */
66 ISC_STATUS *pstatus;
67
68 conn->sql_code = 0;
69
70 /*
71 * Free any previous errors.
72 */
73 TALLOC_FREE(conn->error);
74
75 /*
76 * Check if the status array contains an error
77 */
78 if (IS_ISC_ERROR(conn->status)) {
79 conn->sql_code = isc_sqlcode(conn->status);
80
81 if (conn->sql_code == DUPLICATE_KEY_SQL_CODE) return conn->sql_code;
82
83 /*
84 * pstatus is a pointer into the status array which is
85 * advanced by isc_interprete. It's initialised to the
86 * first element of the status array.
87 */
88 pstatus = &conn->status[0];
89
90 /*
91 * It's deprecated because the size of the buffer isn't
92 * passed and this isn't safe. But as were passing a very
93 * large buffer it's unlikely this will be an issue, and
94 * allows us to maintain compatibility with the interbase
95 * API.
96 */
97 isc_interprete(&error[0], &pstatus);
98 conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]);
99
100 while (isc_interprete(&error[0], &pstatus)) {
101 conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]);
102 }
103
104 memset(&conn->status, 0, sizeof(conn->status));
105 }
106
107 return conn->sql_code;
108}
109DIAG_ON(deprecated-declarations)
110
111void fb_free_sqlda(XSQLDA *sqlda)
112{
113 int i;
114 for (i = 0; i < sqlda->sqld; i++) {
115 free(sqlda->sqlvar[i].sqldata);
116 free(sqlda->sqlvar[i].sqlind);
117 }
118 sqlda->sqld = 0;
119}
120
121
122
123//Macro for NULLs check
124#define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
125
126//Structure to manage a SQL_VARYING Firebird's data types
127typedef struct {
129 char vary_string[1];
130} VARY;
131
132//function fb_store_row based on fiebird's apifull example
134{
135 int dtype, i, nulls = 0;
136 struct tm times;
137 ISC_QUAD bid;
138 XSQLVAR *var;
139 VARY *vary;
140
141 conn->row = talloc_zero_array(conn, char *, conn->sqlda_out->sqld + 1);
142
143 for (i = 0, var = conn->sqlda_out->sqlvar; i < conn->sqlda_out->sqld; var++, i++) {
144 if (IS_NULL(var)) {
145 nulls++;
146 continue;
147 }
148
149 dtype = var->sqltype & ~1;
150
151 switch (dtype) {
152 case SQL_TEXT:
153 conn->row[i] = talloc_bstrndup(conn->row, var->sqldata, var->sqllen);
154 break;
155
156 case SQL_VARYING:
157 vary = (VARY *)var->sqldata;
158 conn->row[i] = talloc_bstrndup(conn->row, vary->vary_string, vary->vary_length);
159 break;
160
161 case SQL_FLOAT:
162 conn->row[i] = talloc_typed_asprintf(conn->row, "%g", *(double ISC_FAR *) (var->sqldata));
163 break;
164
165 case SQL_SHORT:
166 case SQL_LONG:
167 case SQL_INT64:
168 {
169 ISC_INT64 value = 0;
170 short dscale = 0;
171
172 switch (dtype) {
173 case SQL_SHORT:
174 value = (ISC_INT64) *(short *)var->sqldata;
175 break;
176
177 case SQL_LONG:
178 value = (ISC_INT64) *(int *)var->sqldata;
179 break;
180
181 case SQL_INT64:
182 value = (ISC_INT64) *(ISC_INT64 *)var->sqldata;
183 break;
184 }
185 dscale = var->sqlscale;
186
187 if (dscale < 0) {
188 ISC_INT64 tens;
189 short j;
190
191 tens = 1;
192 for (j = 0; j > dscale; j--) {
193 tens *= 10;
194 }
195
196 if (value >= 0) {
197 conn->row[i] = talloc_typed_asprintf(conn->row, "%lld.%0*lld",
198 (ISC_INT64) value / tens,
199 -dscale,
200 (ISC_INT64) value % tens);
201 } else if ((value / tens) != 0) {
202 conn->row[i] = talloc_typed_asprintf(conn->row, "%lld.%0*lld",
203 (ISC_INT64) (value / tens),
204 -dscale,
205 (ISC_INT64) -(value % tens));
206 } else {
207 conn->row[i] = talloc_typed_asprintf(conn->row, "%s.%0*lld",
208 "-0", -dscale, (ISC_INT64) - (value % tens));
209 }
210 } else if (dscale) {
211 conn->row[i] = talloc_typed_asprintf(conn->row, "%lld%0*d", (ISC_INT64) value, dscale, 0);
212 } else {
213 conn->row[i] = talloc_typed_asprintf(conn->row, "%lld", (ISC_INT64) value);
214 }
215 }
216 break;
217
218 case SQL_D_FLOAT:
219 case SQL_DOUBLE:
220 conn->row[i] = talloc_typed_asprintf(conn->row, "%f", *(double ISC_FAR *) (var->sqldata));
221 break;
222
223 case SQL_TIMESTAMP:
224 isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, &times);
225 conn->row[i] = talloc_typed_asprintf(conn->row, "%04d-%02d-%02d %02d:%02d:%02d.%04d",
226 times.tm_year + 1900,
227 times.tm_mon + 1,
228 times.tm_mday,
229 times.tm_hour,
230 times.tm_min,
231 times.tm_sec,
232 ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
233 break;
234
235 case SQL_TYPE_DATE:
236 isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, &times);
237 conn->row[i] = talloc_typed_asprintf(conn->row, "%04d-%02d-%02d",
238 times.tm_year + 1900,
239 times.tm_mon + 1,
240 times.tm_mday);
241 break;
242
243 case SQL_TYPE_TIME:
244 isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, &times);
245 conn->row[i] = talloc_typed_asprintf(conn->row, "%02d:%02d:%02d.%04d",
246 times.tm_hour,
247 times.tm_min,
248 times.tm_sec,
249 (*((ISC_TIME *)var->sqldata)) % 10000);
250 break;
251
252 case SQL_BLOB:
253 case SQL_ARRAY:
254 /* Print the blob id on blobs or arrays */
255 bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
256 conn->row[i] = talloc_typed_asprintf(conn->row, "%08" ISC_LONG_FMT "x:%08" ISC_LONG_FMT "x",
257 bid.gds_quad_high, bid.gds_quad_low);
258 break;
259 }
260 }
261
262 /*
263 * An "UPDATE ... RETURNING" which updated nothing actually returns a row
264 * with all fields set to NULL. This is effectively no rows.
265 */
266 if (nulls == i) return RLM_SQL_NO_MORE_ROWS;
267
268 return RLM_SQL_OK;
269}
270
272{
273 char *p, *buff = NULL;
274 char const *database;
275 uint8_t timeout;
276
277 conn->dpb_len = 7;
278 if (config->sql_login) conn->dpb_len+= strlen(config->sql_login) + 2;
279
280 if (config->sql_password) conn->dpb_len += strlen(config->sql_password) + 2;
281
282 MEM(conn->dpb = talloc_array(conn, char, conn->dpb_len));
283 p = conn->dpb;
284
285 *conn->dpb++= isc_dpb_version1;
286
287 /*
288 * Except for the version above, all Database Parameter Buffer options
289 * are LTV format, built from:
290 * - 1 byte option code
291 * - 1 byte length of value
292 * - 1 or more bytes of value. Integers are lsb first.
293 */
294 *conn->dpb++= isc_dpb_num_buffers;
295 *conn->dpb++= 1;
296 *conn->dpb++= 90;
297
298 timeout = fr_time_delta_to_sec(config->trunk_conf.conn_conf->connection_timeout);
299 *conn->dpb++= isc_dpb_connect_timeout;
300 *conn->dpb++= 1;
301 *conn->dpb++= timeout;
302
303 fb_dpb_add_str(&conn->dpb, isc_dpb_user_name, config->sql_login);
304 fb_dpb_add_str(&conn->dpb, isc_dpb_password, config->sql_password);
305
306 conn->dpb = p;
307
308 /*
309 * Check if database and server in the form of server:database.
310 * If config->sql_server contains ':', then config->sql_db
311 * parameter ignored.
312 */
313 if (strchr(config->sql_server, ':')) {
314 database = config->sql_server;
315 } else {
316 /*
317 * Make database and server to be in the form
318 * of server:database
319 */
320 database = buff = talloc_asprintf(NULL, "%s:%s", config->sql_server, config->sql_db);
321 }
322 DEBUG2("rlm_sql_firebird: Connecting to %s", database);
323 isc_attach_database(conn->status, 0, database, &conn->dbh,
324 conn->dpb_len, conn->dpb);
326
327 return fb_error(conn);
328}
329
330
332{
333 long fetch_stat;
334 if (conn->statement_type!= isc_info_sql_stmt_select) return 100;
335
336 fetch_stat = isc_dsql_fetch(conn->status, &conn->stmt,
337 SQL_DIALECT_V6, conn->sqlda_out);
338 if (fetch_stat) {
339 if (fetch_stat!= 100L) {
340 fb_error(conn);
341 } else {
342 conn->sql_code = 0;
343 }
344 }
345
346 return fetch_stat;
347}
348
349static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
350{
351 static char stmt_info[] = { isc_info_sql_stmt_type };
352 char info_buffer[128];
353 short l;
354
355 if (!conn->trh) {
356 isc_start_transaction(conn->status, &conn->trh, 1, &conn->dbh,
357 conn->tpb_len, conn->tpb);
358 if (!conn->trh) return -4;
359 }
360
361 if (!conn->stmt) {
362 isc_dsql_allocate_statement(conn->status, &conn->dbh,
363 &conn->stmt);
364 if (!conn->stmt) return -1;
365 }
366
367 isc_dsql_prepare(conn->status, &conn->trh, &conn->stmt, 0, query,
368 conn->sql_dialect, conn->sqlda_out);
369 if (IS_ISC_ERROR(conn->status)) return -2;
370
371 if (conn->sqlda_out->sqln < conn->sqlda_out->sqld) {
372 conn->sqlda_out = (XSQLDA ISC_FAR *) _talloc_realloc_array(conn, conn->sqlda_out, 1,
373 XSQLDA_LENGTH(conn->sqlda_out->sqld), "XSQLDA");
374 conn->sqlda_out->sqln = conn->sqlda_out->sqld;
375 isc_dsql_describe(conn->status, &conn->stmt, SQL_DIALECT_V6,
376 conn->sqlda_out);
377
378 if (IS_ISC_ERROR(conn->status)) return -3;
379 }
380 /*
381 * Get statement type
382 */
383 isc_dsql_sql_info(conn->status, &conn->stmt, sizeof(stmt_info),
384 stmt_info, sizeof(info_buffer), info_buffer);
385 if (IS_ISC_ERROR(conn->status)) return -4;
386
387 l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
388 conn->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3, l);
389
390 if (conn->sqlda_out->sqld) fb_set_sqlda(conn->sqlda_out); //set out sqlda
391
392 return 0;
393}
394
395
396int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query) {
397 if (fb_prepare(conn, query)) return fb_error(conn);
398
399 switch (conn->statement_type) {
400 case isc_info_sql_stmt_exec_procedure:
401 isc_dsql_execute2(conn->status, &conn->trh, &conn->stmt,
402 SQL_DIALECT_V6, 0, conn->sqlda_out);
403 break;
404
405 default:
406 isc_dsql_execute(conn->status, &conn->trh, &conn->stmt,
407 SQL_DIALECT_V6, 0);
408 break;
409 }
410 return fb_error(conn);
411}
412
414 static char count_info[] = {isc_info_sql_records};
415 char info_buffer[128];
416 char *p ;
417 int affected_rows = -1;
418
419 if (!conn->stmt) return -1;
420
421 isc_dsql_sql_info(conn->status, &conn->stmt,
422 sizeof (count_info), count_info,
423 sizeof (info_buffer), info_buffer);
424
425 if (IS_ISC_ERROR(conn->status)) return fb_error(conn);
426
427 p = info_buffer + 3;
428 while (*p != isc_info_end) {
429 short len;
430 len = (short)isc_vax_integer(++p, 2);
431 p += 2;
432
433 affected_rows = isc_vax_integer(p, len);
434 if (affected_rows > 0) break;
435 p += len;
436 }
437 return affected_rows;
438}
439
441 if (conn->stmt) {
442 isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_drop);
443 conn->stmt = 0;
444 }
445}
446
448 conn->sql_code = 0;
449 if (conn->trh) {
450 isc_rollback_transaction(conn->status, &conn->trh);
451 if (IS_ISC_ERROR(conn->status)) return fb_error(conn);
452 }
453 return conn->sql_code;
454}
455
457 conn->sql_code = 0;
458 if (conn->trh) {
459 isc_commit_transaction(conn->status, &conn->trh);
460 if (IS_ISC_ERROR(conn->status)) {
461 fb_error(conn);
462 ERROR("Fail to commit. Error: %s. Try to rollback.", conn->error);
463 return fb_rollback(conn);
464 }
465 }
466 return conn->sql_code;
467}
#define RCSID(id)
Definition build.h:483
#define DIAG_ON(_x)
Definition build.h:458
#define DIAG_OFF(_x)
Definition build.h:457
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
Test enumeration values.
Definition dict_test.h:92
free(array)
talloc_free(reap)
unsigned char uint8_t
static const conf_parser_t config[]
Definition base.c:183
#define DEBUG2(fmt,...)
Definition radclient.h:43
static char const * name
sql_rcode_t
Action to take at end of an SQL query.
Definition rlm_sql.h:44
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition rlm_sql.h:50
static int affected_rows(PGresult *result)
Return the number of affected rows of the result as an int instead of the string that postgresql prov...
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
short vary_length
Definition sql_fbapi.c:128
static void fb_dpb_add_str(char **dpb, char name, char const *value)
Definition sql_fbapi.c:28
int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query)
Definition sql_fbapi.c:396
int fb_connect(rlm_sql_firebird_conn_t *conn, rlm_sql_config_t const *config)
Definition sql_fbapi.c:271
static void fb_set_sqlda(XSQLDA *sqlda)
Definition sql_fbapi.c:44
char vary_string[1]
Definition sql_fbapi.c:129
int fb_rollback(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:447
int fb_error(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:63
int fb_fetch(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:331
void fb_free_sqlda(XSQLDA *sqlda)
Definition sql_fbapi.c:111
static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
Definition sql_fbapi.c:349
#define IS_NULL(x)
Definition sql_fbapi.c:124
sql_rcode_t fb_store_row(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:133
int fb_commit(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:456
int fb_affected_rows(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:413
void fb_free_statement(rlm_sql_firebird_conn_t *conn)
Definition sql_fbapi.c:440
#define DUPLICATE_KEY_SQL_CODE
Definition sql_fbapi.h:35
isc_tr_handle trh
Definition sql_fbapi.h:48
#define IS_ISC_ERROR(status)
Definition sql_fbapi.h:31
isc_stmt_handle stmt
Definition sql_fbapi.h:47
isc_db_handle dbh
Definition sql_fbapi.h:46
ISC_STATUS status[20]
Magic interbase status code array (holds multiple error codes used to construct more detailed error m...
Definition sql_fbapi.h:50
rlm_sql_row_t row
Definition sql_fbapi.h:63
#define ISC_LONG_FMT
Definition sql_fbapi.h:41
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:492
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647