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)) {
38 //if error occured, free the previous error's text and create a new one
40 if (sock->lasterror) free(sock->lasterror);
42 sock->sql_code=isc_sqlcode(sock->status);
43 isc_interprete(msg,&pstatus);
45 msg[0]='.';msg[1]=' ';
46 while (isc_interprete(msg+2,&pstatus)) {
48 p=(char *) realloc(p,l+strlen(msg)+2);
53 //return empty (but not null) string if there are no error
54 if (sock->lasterror) *sock->lasterror=0;
55 else sock->lasterror=strdup("");
57 return sock->sql_code;
61 void fb_set_tpb(rlm_sql_firebird_sock * sock, int count,...) {
65 sock->tpb=(char *) malloc(count);
66 for (i=0; i<count; i++) {
67 sock->tpb[i]=(char ) va_arg(arg,int);
73 void fb_dpb_add_str(char **dpb, char name, char *value) {
79 *(*dpb)++ = (char ) l;
80 memmove(*dpb,value,l);
85 void fb_free_sqlda(XSQLDA *sqlda) {
87 for (i=0; i<sqlda->sqld; i++) {
88 free(sqlda->sqlvar[i].sqldata);
89 free(sqlda->sqlvar[i].sqlind);
94 void fb_set_sqlda(XSQLDA *sqlda) {
96 for (i=0; i<sqlda->sqld; i++) {
97 if ((sqlda->sqlvar[i].sqltype & ~1)==SQL_VARYING)
98 sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
100 sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
102 if (sqlda->sqlvar[i].sqltype & 1) sqlda->sqlvar[i].sqlind = (short*)calloc(sizeof(short),1);
103 else sqlda->sqlvar[i].sqlind = 0;
108 //Macro for NULLs check
109 #define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
111 //Structure to manage a SQL_VARYING Firebird's data types
112 typedef struct vary_fb {
117 //function fb_store_row based on fiebird's apifull example
118 void fb_store_row(rlm_sql_firebird_sock *sock) {
126 //assumed: id,username,attribute,value,op
127 if (sock->row_fcount<sock->sqlda_out->sqld) {
129 sock->row_fcount=sock->sqlda_out->sqld;
130 sock->row=(char **) realloc(sock->row,sock->row_fcount*sizeof(char *));
131 sock->row_sizes=(int *) realloc(sock->row_sizes,sock->row_fcount*sizeof(int));
132 while(i<sock->row_fcount) {
134 sock->row_sizes[i++]=0;
138 for (i=0, var=sock->sqlda_out->sqlvar; i<sock->sqlda_out->sqld; var++,i++) {
139 //Initial buffer size to store field's data is 256 bytes
140 if (sock->row_sizes[i]<256) {
141 sock->row[i]=(char *) realloc(sock->row[i],256);
142 sock->row_sizes[i]=256;
146 strcpy(sock->row[i],"NULL");
149 dtype=var->sqltype & ~1;
152 if (sock->row_sizes[i]<=var->sqllen) {
153 sock->row_sizes[i]=var->sqllen+1;
154 sock->row[i]=(char *) realloc(sock->row[i],sock->row_sizes[i]);
156 memmove(sock->row[i],var->sqldata,var->sqllen);
157 sock->row[i][var->sqllen]=0;
160 vary = (VARY*) var->sqldata;
161 if (sock->row_sizes[i]<=vary->vary_length) {
162 sock->row_sizes[i]=vary->vary_length+1;
163 sock->row[i]=(char *) realloc(sock->row[i],sock->row_sizes[i]);
165 memmove(sock->row[i],vary->vary_string,vary->vary_length);
166 sock->row[i][vary->vary_length] =0;
170 snprintf(sock->row[i],sock->row_sizes[i], "%15g", *(float ISC_FAR *) (var->sqldata));
177 short field_width = 0;
184 value = (ISC_INT64) *(short *) var->sqldata;
188 value = (ISC_INT64) *(int *) var->sqldata;
192 value = (ISC_INT64) *(ISC_INT64 *) var->sqldata;
196 dscale = var->sqlscale;
203 for (j = 0; j > dscale; j--) tens *= 10;
206 sprintf (p, "%*lld.%0*lld",
207 field_width - 1 + dscale,
208 (ISC_INT64) value / tens,
210 (ISC_INT64) value % tens);
211 else if ((value / tens) != 0)
212 sprintf (p, "%*lld.%0*lld",
213 field_width - 1 + dscale,
214 (ISC_INT64) (value / tens),
216 (ISC_INT64) -(value % tens));
218 sprintf (p, "%*s.%0*lld",
219 field_width - 1 + dscale,
222 (ISC_INT64) -(value % tens));
225 sprintf (p, "%*lld%0*d",
237 case SQL_DOUBLE: case SQL_D_FLOAT:
238 snprintf(sock->row[i],sock->row_sizes[i], "%24f", *(double ISC_FAR *) (var->sqldata));
242 isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, ×);
243 snprintf(sock->row[i],sock->row_sizes[i],"%04d-%02d-%02d %02d:%02d:%02d.%04d",
244 times.tm_year + 1900,
250 ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
254 isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, ×);
255 snprintf(sock->row[i],sock->row_sizes[i], "%04d-%02d-%02d",
256 times.tm_year + 1900,
262 isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, ×);
263 snprintf(sock->row[i],sock->row_sizes[i], "%02d:%02d:%02d.%04d",
267 (*((ISC_TIME *)var->sqldata)) % 10000);
272 /* Print the blob id on blobs or arrays */
273 bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
274 snprintf(sock->row[i],sock->row_sizes[i],"%08lx:%08lx", bid.gds_quad_high, bid.gds_quad_low);
283 int fb_init_socket(rlm_sql_firebird_sock *sock) {
284 memset(sock, 0, sizeof(*sock));
285 sock->sqlda_out = (XSQLDA ISC_FAR *) calloc(XSQLDA_LENGTH (5),1);
286 sock->sqlda_out->sqln = 5;
287 sock->sqlda_out->version = SQLDA_VERSION1;
290 pthread_mutex_init (&sock->mut, NULL);
291 radlog(L_DBG,"Init mutex %p\n",&sock->mut);
295 //set tpb to read_committed/wait/no_rec_version
300 isc_tpb_read_committed,
301 isc_tpb_no_rec_version);
302 if (!sock->tpb) return -1;
306 int fb_connect(rlm_sql_firebird_sock * sock,SQL_CONFIG *config) {
311 if (config->sql_login) sock->dpb_len+=strlen(config->sql_login)+2;
312 if (config->sql_password) sock->dpb_len+=strlen(config->sql_password)+2;
314 sock->dpb=(char *) malloc(sock->dpb_len);
317 *sock->dpb++ = isc_dpb_version1;
318 *sock->dpb++ = isc_dpb_num_buffers;
322 fb_dpb_add_str(&sock->dpb,isc_dpb_user_name,config->sql_login);
323 fb_dpb_add_str(&sock->dpb,isc_dpb_password,config->sql_password);
326 // Check if database and server in the form of server:database.
327 // If config->sql_server contains ':', then config->sql_db
329 if (strchr(config->sql_server,':')) database=strdup(config->sql_server);
331 // Make database and server to be in the form of server:database
332 int ls=strlen(config->sql_server);
333 int ld=strlen(config->sql_db);
334 database=(char *) calloc(ls+ld+2,1);
335 strcpy(database,config->sql_server);
337 memmove(database+ls+1,config->sql_db,ld);
339 isc_attach_database(sock->status, 0, database, &sock->dbh, sock->dpb_len, sock->dpb);
341 return fb_lasterror(sock);
345 int fb_fetch(rlm_sql_firebird_sock *sock) {
347 if (sock->statement_type!=isc_info_sql_stmt_select) return 100;
348 fetch_stat=isc_dsql_fetch(sock->status, &sock->stmt, SQL_DIALECT_V6, sock->sqlda_out);
350 if (fetch_stat!=100L) fb_lasterror(sock);
351 else sock->sql_code=0;
356 int fb_prepare(rlm_sql_firebird_sock *sock,char *sqlstr) {
357 static char stmt_info[] = { isc_info_sql_stmt_type };
358 char info_buffer[128];
362 isc_start_transaction(sock->status,&sock->trh,1,&sock->dbh,sock->tpb_len,sock->tpb);
363 if (!sock->trh) return -4;
366 fb_free_statement(sock);
368 isc_dsql_allocate_statement(sock->status, &sock->dbh, &sock->stmt);
369 if (!sock->stmt) return -1;
372 fb_free_sqlda(sock->sqlda_out);
373 isc_dsql_prepare(sock->status, &sock->trh, &sock->stmt, 0, sqlstr, sock->sql_dialect, sock->sqlda_out);
374 if (IS_ISC_ERROR(sock->status)) return -2;
376 if (sock->sqlda_out->sqln<sock->sqlda_out->sqld) {
377 sock->sqlda_out->sqln=sock->sqlda_out->sqld;
378 sock->sqlda_out = (XSQLDA ISC_FAR *) realloc(sock->sqlda_out, XSQLDA_LENGTH (sock->sqlda_out->sqld));
379 isc_dsql_describe(sock->status,&sock->stmt,SQL_DIALECT_V6,sock->sqlda_out);
380 if (IS_ISC_ERROR(sock->status)) return -3;
384 isc_dsql_sql_info(sock->status, &sock->stmt, sizeof (stmt_info), stmt_info,sizeof (info_buffer), info_buffer);
385 if (IS_ISC_ERROR(sock->status)) return -4;
387 l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
388 sock->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3, l);
390 if (sock->sqlda_out->sqld) fb_set_sqlda(sock->sqlda_out); //set out sqlda
396 int fb_sql_query(rlm_sql_firebird_sock *sock,char *sqlstr) {
397 if (fb_prepare(sock,sqlstr)) return fb_lasterror(sock);
398 switch (sock->statement_type) {
399 case isc_info_sql_stmt_exec_procedure:
400 isc_dsql_execute2(sock->status, &sock->trh, &sock->stmt, SQL_DIALECT_V6,0,sock->sqlda_out);
403 isc_dsql_execute(sock->status, &sock->trh, &sock->stmt, SQL_DIALECT_V6,0);
406 return fb_lasterror(sock);
409 int fb_affected_rows(rlm_sql_firebird_sock *sock) {
410 static char count_info[] = {isc_info_sql_records};
411 char info_buffer[128];
413 int affected_rows=-1;
415 if (!sock->stmt) return -1;
417 isc_dsql_sql_info(sock->status, &sock->stmt,
418 sizeof (count_info), count_info,sizeof (info_buffer), info_buffer);
419 if (IS_ISC_ERROR(sock->status)) return fb_lasterror(sock);
422 while (*p != isc_info_end) {
424 short len = (short)isc_vax_integer(p,2);
426 affected_rows = isc_vax_integer(p,len);
427 if (affected_rows>0) break;
430 return affected_rows;
433 int fb_close_cursor(rlm_sql_firebird_sock *sock) {
434 isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_close);
435 return fb_lasterror(sock);
438 void fb_free_statement(rlm_sql_firebird_sock *sock) {
440 isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_drop);
445 int fb_rollback(rlm_sql_firebird_sock *sock) {
448 isc_rollback_transaction (sock->status,&sock->trh);
451 pthread_mutex_unlock(&sock->mut);
454 if (IS_ISC_ERROR(sock->status)) {
455 return fb_lasterror(sock);
458 return sock->sql_code;
461 int fb_commit(rlm_sql_firebird_sock *sock) {
464 isc_commit_transaction (sock->status,&sock->trh);
465 if (IS_ISC_ERROR(sock->status)) {
467 radlog(L_ERR,"Fail to commit. Error: %s. Try to rollback.\n",sock->lasterror);
468 return fb_rollback(sock);
473 pthread_mutex_unlock(&sock->mut);
475 return sock->sql_code;
478 int fb_disconnect(rlm_sql_firebird_sock *sock) {
480 fb_free_statement(sock);
481 isc_detach_database(sock->status,&sock->dbh);
482 return fb_lasterror(sock);
487 void fb_destroy_socket(rlm_sql_firebird_sock *sock) {
490 if (fb_disconnect(sock)) {
491 radlog(L_ERR,"Fatal. Fail to disconnect DB. Error :%s\n",sock->lasterror);
494 pthread_mutex_destroy (&sock->mut);
496 for (i=0; i<sock->row_fcount;i++) free(sock->row[i]);
497 free(sock->row);free(sock->row_sizes);
498 fb_free_sqlda(sock->sqlda_out);
499 free(sock->sqlda_out);
502 if (sock->lasterror) free(sock->lasterror);
503 memset(sock,0,sizeof(rlm_sql_firebird_sock));