2 * sql_fbapi.c Part of Firebird rlm_sql driver
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.
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.
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
18 * Copyright 2006 The FreeRADIUS server project
19 * Copyright 2006 Vitaly Bodzhgua <vitaly@eastera.net>
22 #include <freeradius-devel/ident.h>
25 #include "sql_fbapi.h"
29 int fb_lasterror(rlm_sql_firebird_sock *sock) {
37 if (IS_ISC_ERROR(sock->status)) {
39 * If error occured, free the previous error's text
40 * and create a new one.
42 pstatus = sock->status;
43 if (sock->lasterror) {
44 free(sock->lasterror);
48 sock->sql_code = isc_sqlcode(sock->status);
50 isc_interprete(msg,&pstatus);
56 while (isc_interprete(msg + 2, &pstatus)) {
58 p = realloc(p, l + strlen(msg) + 2);
65 //return empty (but not null) string if there are no error
66 if (sock->lasterror) {
67 *(sock->lasterror) = '\0';
69 sock->lasterror = strdup("");
73 return sock->sql_code;
77 void fb_set_tpb(rlm_sql_firebird_sock * sock, int count, ...) {
82 sock->tpb = malloc(count);
84 for (i = 0; i < count; i++) {
85 sock->tpb[i] = (char) va_arg(arg, int);
92 void fb_dpb_add_str(char **dpb, char name, char *value) {
102 *(*dpb)++ = (char) l;
104 memmove(*dpb, value, l);
109 void fb_free_sqlda(XSQLDA *sqlda) {
111 for (i = 0; i < sqlda->sqld; i++) {
112 free(sqlda->sqlvar[i].sqldata);
113 free(sqlda->sqlvar[i].sqlind);
118 void fb_set_sqlda(XSQLDA *sqlda) {
121 for (i = 0; i < sqlda->sqld; i++) {
122 if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_VARYING) {
123 sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
125 sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
128 if (sqlda->sqlvar[i].sqltype & 1) {
129 sqlda->sqlvar[i].sqlind = (short*)calloc(sizeof(short),1);
131 sqlda->sqlvar[i].sqlind = 0;
137 //Macro for NULLs check
138 #define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
140 //Structure to manage a SQL_VARYING Firebird's data types
141 typedef struct vary_fb {
146 //function fb_store_row based on fiebird's apifull example
147 void fb_store_row(rlm_sql_firebird_sock *sock) {
155 /* assumed: id,username,attribute,value,op */
156 if (sock->row_fcount<sock->sqlda_out->sqld) {
158 sock->row_fcount=sock->sqlda_out->sqld;
159 sock->row = (char **) realloc(sock->row, sock->row_fcount * sizeof(char *));
160 sock->row_sizes = (int *) realloc(sock->row_sizes, sock->row_fcount * sizeof(int));
162 while( i <sock->row_fcount) {
164 sock->row_sizes[i++] = 0;
168 for (i=0, var=sock->sqlda_out->sqlvar; i<sock->sqlda_out->sqld; var++,i++) {
170 * Initial buffer size to store field's data is 256 bytes
172 if (sock->row_sizes[i]<256) {
173 sock->row[i]=(char *) realloc(sock->row[i],256);
174 sock->row_sizes[i]=256;
178 strcpy(sock->row[i],"NULL");
182 dtype = var->sqltype & ~1;
186 if (sock->row_sizes[i]<=var->sqllen) {
187 sock->row_sizes[i] = var->sqllen + 1;
188 sock->row[i] = realloc(sock->row[i],
192 memmove(sock->row[i], var->sqldata, var->sqllen);
193 sock->row[i][var->sqllen] = 0;
197 vary = (VARY*) var->sqldata;
198 if (sock->row_sizes[i] <= vary->vary_length) {
199 sock->row_sizes[i] = vary->vary_length+1;
200 sock->row[i] = realloc(sock->row[i],
203 memmove(sock->row[i],vary->vary_string,vary->vary_length);
204 sock->row[i][vary->vary_length] = 0;
209 snprintf(sock->row[i], sock->row_sizes[i], "%15g",
210 *(float ISC_FAR *) (var->sqldata));
217 short field_width = 0;
225 value = (ISC_INT64) *(short *)var->sqldata;
229 value = (ISC_INT64) *(int *)var->sqldata;
233 value = (ISC_INT64) *(ISC_INT64 *)var->sqldata;
237 dscale = var->sqlscale;
244 for (j = 0; j > dscale; j--) {
249 sprintf(p, "%*lld.%0*lld",
250 field_width - 1 + dscale,
251 (ISC_INT64) value / tens,
253 (ISC_INT64) value % tens);
254 } else if ((value / tens) != 0) {
255 sprintf (p, "%*lld.%0*lld",
256 field_width - 1 + dscale,
257 (ISC_INT64) (value / tens),
259 (ISC_INT64) -(value % tens));
261 sprintf(p, "%*s.%0*lld", field_width - 1 + dscale,
262 "-0", -dscale, (ISC_INT64) - (value % tens));
265 sprintf(p, "%*lld%0*d", field_width,
266 (ISC_INT64) value, dscale, 0);
268 sprintf(p, "%*lld", field_width,
276 snprintf(sock->row[i],sock->row_sizes[i], "%24f",
277 *(double ISC_FAR *) (var->sqldata));
281 isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, ×);
282 snprintf(sock->row[i],sock->row_sizes[i],"%04d-%02d-%02d %02d:%02d:%02d.%04d",
283 times.tm_year + 1900,
289 ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
293 isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, ×);
294 snprintf(sock->row[i],sock->row_sizes[i], "%04d-%02d-%02d",
295 times.tm_year + 1900,
301 isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, ×);
302 snprintf(sock->row[i],sock->row_sizes[i], "%02d:%02d:%02d.%04d",
306 (*((ISC_TIME *)var->sqldata)) % 10000);
311 /* Print the blob id on blobs or arrays */
312 bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
313 snprintf(sock->row[i],sock->row_sizes[i],"%08lx:%08lx", bid.gds_quad_high, bid.gds_quad_low);
320 int fb_init_socket(rlm_sql_firebird_sock *sock) {
321 memset(sock, 0, sizeof(*sock));
322 sock->sqlda_out = (XSQLDA ISC_FAR *) calloc(XSQLDA_LENGTH (5),1);
323 sock->sqlda_out->sqln = 5;
324 sock->sqlda_out->version = SQLDA_VERSION1;
325 sock->sql_dialect = 3;
327 pthread_mutex_init (&sock->mut, NULL);
328 radlog(L_DBG,"Init mutex %p\n",&sock->mut);
332 * Set tpb to read_committed/wait/no_rec_version
334 fb_set_tpb(sock, 5, isc_tpb_version3, isc_tpb_wait, isc_tpb_write,
335 isc_tpb_read_committed, isc_tpb_no_rec_version);
343 int fb_connect(rlm_sql_firebird_sock * sock,rlm_sql_config_t *config) {
348 if (config->sql_login) {
349 sock->dpb_len+=strlen(config->sql_login) + 2;
352 if (config->sql_password) {
353 sock->dpb_len += strlen(config->sql_password) + 2;
356 sock->dpb = (char *) malloc(sock->dpb_len);
359 *sock->dpb++ = isc_dpb_version1;
360 *sock->dpb++ = isc_dpb_num_buffers;
364 fb_dpb_add_str(&sock->dpb, isc_dpb_user_name, config->sql_login);
365 fb_dpb_add_str(&sock->dpb, isc_dpb_password, config->sql_password);
370 * Check if database and server in the form of server:database.
371 * If config->sql_server contains ':', then config->sql_db
374 if (strchr(config->sql_server,':')) {
375 database=strdup(config->sql_server);
378 * Make database and server to be in the form
381 int ls = strlen(config->sql_server);
382 int ld = strlen(config->sql_db);
383 database=(char *) calloc(ls+ld+2,1);
384 strcpy(database,config->sql_server);
386 memmove(database+ls+1,config->sql_db,ld);
388 isc_attach_database(sock->status, 0, database, &sock->dbh,
389 sock->dpb_len, sock->dpb);
392 return fb_lasterror(sock);
396 int fb_fetch(rlm_sql_firebird_sock *sock) {
398 if (sock->statement_type!=isc_info_sql_stmt_select) {
402 fetch_stat = isc_dsql_fetch(sock->status, &sock->stmt,
403 SQL_DIALECT_V6, sock->sqlda_out);
405 if (fetch_stat!=100L) {
415 int fb_prepare(rlm_sql_firebird_sock *sock,char *sqlstr) {
416 static char stmt_info[] = { isc_info_sql_stmt_type };
417 char info_buffer[128];
421 isc_start_transaction(sock->status, &sock->trh, 1, &sock->dbh,
422 sock->tpb_len,sock->tpb);
428 fb_free_statement(sock);
430 isc_dsql_allocate_statement(sock->status, &sock->dbh,
437 fb_free_sqlda(sock->sqlda_out);
438 isc_dsql_prepare(sock->status, &sock->trh, &sock->stmt, 0, sqlstr,
439 sock->sql_dialect, sock->sqlda_out);
440 if (IS_ISC_ERROR(sock->status)) {
444 if (sock->sqlda_out->sqln<sock->sqlda_out->sqld) {
445 sock->sqlda_out->sqln=sock->sqlda_out->sqld;
446 sock->sqlda_out = (XSQLDA ISC_FAR *) realloc(sock->sqlda_out,
447 XSQLDA_LENGTH(sock->sqlda_out->sqld));
448 isc_dsql_describe(sock->status, &sock->stmt, SQL_DIALECT_V6,
451 if (IS_ISC_ERROR(sock->status)) {
458 isc_dsql_sql_info(sock->status, &sock->stmt, sizeof(stmt_info),
459 stmt_info,sizeof(info_buffer), info_buffer);
460 if (IS_ISC_ERROR(sock->status)) return -4;
462 l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
463 sock->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3,
466 if (sock->sqlda_out->sqld) {
467 fb_set_sqlda(sock->sqlda_out); //set out sqlda
474 int fb_sql_query(rlm_sql_firebird_sock *sock,char *sqlstr) {
475 if (fb_prepare(sock,sqlstr)) {
476 return fb_lasterror(sock);
479 switch (sock->statement_type) {
480 case isc_info_sql_stmt_exec_procedure:
481 isc_dsql_execute2(sock->status, &sock->trh, &sock->stmt,
482 SQL_DIALECT_V6, 0, sock->sqlda_out);
485 isc_dsql_execute(sock->status, &sock->trh, &sock->stmt,
489 return fb_lasterror(sock);
492 int fb_affected_rows(rlm_sql_firebird_sock *sock) {
493 static char count_info[] = {isc_info_sql_records};
494 char info_buffer[128];
496 int affected_rows = -1;
498 if (!sock->stmt) return -1;
500 isc_dsql_sql_info(sock->status, &sock->stmt,
501 sizeof (count_info), count_info,
502 sizeof (info_buffer), info_buffer);
504 if (IS_ISC_ERROR(sock->status)) {
505 return fb_lasterror(sock);
509 while (*p != isc_info_end) {
511 short len = (short)isc_vax_integer(p, 2);
514 affected_rows = isc_vax_integer(p, len);
515 if (affected_rows > 0) {
520 return affected_rows;
523 int fb_close_cursor(rlm_sql_firebird_sock *sock) {
524 isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_close);
526 return fb_lasterror(sock);
529 void fb_free_statement(rlm_sql_firebird_sock *sock) {
531 isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_drop);
536 int fb_rollback(rlm_sql_firebird_sock *sock) {
539 isc_rollback_transaction(sock->status, &sock->trh);
542 pthread_mutex_unlock(&sock->mut);
545 if (IS_ISC_ERROR(sock->status)) {
546 return fb_lasterror(sock);
549 return sock->sql_code;
552 int fb_commit(rlm_sql_firebird_sock *sock) {
555 isc_commit_transaction (sock->status,&sock->trh);
556 if (IS_ISC_ERROR(sock->status)) {
558 radlog(L_ERR, "Fail to commit. Error: %s. Try to rollback.",
560 return fb_rollback(sock);
565 pthread_mutex_unlock(&sock->mut);
567 return sock->sql_code;
570 int fb_disconnect(rlm_sql_firebird_sock *sock) {
572 fb_free_statement(sock);
573 isc_detach_database(sock->status,&sock->dbh);
574 return fb_lasterror(sock);
579 void fb_destroy_socket(rlm_sql_firebird_sock *sock) {
583 if (fb_disconnect(sock)) {
584 radlog(L_ERR, "Fatal. Fail to disconnect DB. Error :%s\n",
589 pthread_mutex_destroy (&sock->mut);
591 for (i=0; i<sock->row_fcount;i++) {
595 free(sock->row);free(sock->row_sizes);
596 fb_free_sqlda(sock->sqlda_out);
598 free(sock->sqlda_out);
602 if (sock->lasterror) {
603 free(sock->lasterror);
606 memset(sock,0,sizeof(rlm_sql_firebird_sock));