import from HEAD
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_firebird / sql_fbapi.c
1 /*
2  * sql_fbapi.c  Part of Firebird rlm_sql driver
3  *
4  * Copyright 2006  Vitaly Bodzhgua <vitaly@eastera.net>
5  */
6
7 #include "sql_fbapi.h"
8
9 #include <stdarg.h>
10
11 int fb_lasterror(rlm_sql_firebird_sock *sock) {
12  char msg[512];
13  int l;
14  ISC_LONG *pstatus;
15  char *p=0;
16  
17  sock->sql_code=0;
18  
19  if (IS_ISC_ERROR(sock->status)) {
20    pstatus=sock->status;   
21    if (sock->lasterror) free(sock->lasterror);
22    sock->lasterror=0;
23    sock->sql_code=isc_sqlcode(sock->status);
24    isc_interprete(msg,&pstatus);
25    p=strdup(msg);
26    msg[0]='.';msg[1]=' '; 
27    while (isc_interprete(msg+2,&pstatus)) {
28      l=strlen(p);
29      p=(char *) realloc(p,l+strlen(msg)+2);
30      strcat(p,msg);
31    }    
32    sock->lasterror=p; 
33  } else {
34   if (sock->lasterror) *sock->lasterror=0;
35   else sock->lasterror=strdup("");
36  }
37  return sock->sql_code;
38 }
39
40
41 void fb_set_tpb(rlm_sql_firebird_sock * sock, int count,...) {
42  int i;
43  va_list arg;
44  va_start(arg,count);
45  sock->tpb=(char *) malloc(count);
46  for (i=0; i<count; i++) {
47     sock->tpb[i]=(char ) va_arg(arg,int);
48  }
49  sock->tpb_len=count;
50 }
51
52
53 void fb_dpb_add_str(char **dpb, char name, char *value) {
54  int l;
55  if (!value) return;
56  l=strlen(value);
57  
58  *(*dpb)++ = name;
59  *(*dpb)++ = (char ) l;
60  memmove(*dpb,value,l);
61  *dpb+=l;
62  
63 }
64
65 void fb_free_sqlda(XSQLDA *sqlda) {
66  int i; 
67  for (i=0; i<sqlda->sqld; i++) {
68   free(sqlda->sqlvar[i].sqldata);
69   free(sqlda->sqlvar[i].sqlind);
70  }
71  sqlda->sqld=0;
72 }
73
74 void fb_set_sqlda(XSQLDA *sqlda) {
75  int i;
76  for (i=0; i<sqlda->sqld; i++) {
77   if ((sqlda->sqlvar[i].sqltype & ~1)==SQL_VARYING)
78     sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
79   else
80    sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
81    
82   if (sqlda->sqlvar[i].sqltype & 1) sqlda->sqlvar[i].sqlind = (short*)calloc(sizeof(short),1);
83   else sqlda->sqlvar[i].sqlind = 0;
84  }
85 }
86
87
88 int fb_prepare(rlm_sql_firebird_sock *sock,char *sqlstr) {
89  static char     stmt_info[] = { isc_info_sql_stmt_type };
90  char            info_buffer[20];
91  short l;
92  
93  //isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_close);
94  
95  fb_free_sqlda(sock->sqlda_out);
96  if (!sock->trh) 
97     isc_start_transaction(sock->status,&sock->trh,1,&sock->dbh,sock->tpb_len,sock->tpb);
98   
99  if (!sock->stmt) {
100    isc_dsql_allocate_statement(sock->status, &sock->dbh, &sock->stmt);
101    if (!sock->stmt) return -1;
102  }
103
104  isc_dsql_prepare(sock->status, &sock->trh, &sock->stmt, 0, sqlstr, sock->sql_dialect, sock->sqlda_out);
105  if (IS_ISC_ERROR(sock->status)) return -2;
106   
107  if (sock->sqlda_out->sqln<sock->sqlda_out->sqld) {
108    sock->sqlda_out->sqln=sock->sqlda_out->sqld;
109    sock->sqlda_out = (XSQLDA ISC_FAR *) realloc(sock->sqlda_out, XSQLDA_LENGTH (sock->sqlda_out->sqld));
110    isc_dsql_describe(sock->status,&sock->stmt,SQL_DIALECT_V6,sock->sqlda_out);    
111    if (IS_ISC_ERROR(sock->status)) return -3;
112  }
113
114 //get sql type  
115  isc_dsql_sql_info(sock->status, &sock->stmt, sizeof (stmt_info), stmt_info,sizeof (info_buffer), info_buffer);
116  if (IS_ISC_ERROR(sock->status)) return -4; 
117   
118  l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
119  sock->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3, l);
120   
121  if (sock->sqlda_out->sqld) fb_set_sqlda(sock->sqlda_out); //set out sqlda
122   
123  return 0;
124 }
125
126 #define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
127
128 typedef struct vary_fb {
129   short vary_length;                                                 
130   char vary_string[1];
131 } VARY;
132
133 //function fb_store_row based on fiebird's apifull example
134 void fb_store_row(rlm_sql_firebird_sock *sock) {
135  int dtype;
136  struct tm times;
137  ISC_QUAD bid;
138  int i;
139  XSQLVAR *var;
140  VARY * vary;
141  
142
143 //assumed: id,username,attribute,value,op
144  if (sock->row_fcount<sock->sqlda_out->sqld)  {
145    i=sock->row_fcount;
146    sock->row_fcount=sock->sqlda_out->sqld;
147    sock->row=(char **) realloc(sock->row,sock->row_fcount*sizeof(char *));
148    sock->row_sizes=(int *) realloc(sock->row_sizes,sock->row_fcount*sizeof(int));
149    while(i<sock->row_fcount) {
150      sock->row[i]=0;
151      sock->row_sizes[i++]=0;
152    }     
153  }
154  
155 for (i=0, var=sock->sqlda_out->sqlvar; i<sock->sqlda_out->sqld; var++,i++) {
156   if (sock->row_sizes[i]<256) {
157    sock->row[i]=(char *) realloc(sock->row[i],256);
158    sock->row_sizes[i]=256;
159   }  
160   
161  if (IS_NULL(var)) {
162   strcpy(sock->row[i],"NULL");
163   continue;
164  }
165  dtype=var->sqltype & ~1;
166  switch (dtype) {
167    case SQL_TEXT:
168            if (sock->row_sizes[i]<=var->sqllen) {
169             sock->row_sizes[i]=var->sqllen+1;
170             sock->row[i]=(char *) realloc(sock->row[i],sock->row_sizes[i]);
171            }
172            memmove(sock->row[i],var->sqldata,var->sqllen);
173            sock->row[i][var->sqllen]=0;
174            break;
175    case SQL_VARYING:
176            vary = (VARY*) var->sqldata;
177            if (sock->row_sizes[i]<=vary->vary_length) {
178             sock->row_sizes[i]=vary->vary_length+1;
179             sock->row[i]=(char *) realloc(sock->row[i],sock->row_sizes[i]);
180            }
181            memmove(sock->row[i],vary->vary_string,vary->vary_length);
182            sock->row[i][vary->vary_length] =0;
183            break;
184            
185     case SQL_FLOAT:
186             snprintf(sock->row[i],sock->row_sizes[i], "%15g", *(float ISC_FAR *) (var->sqldata));
187             break;
188     case SQL_SHORT:
189     case SQL_LONG:
190     case SQL_INT64:
191                 {
192                 ISC_INT64       value;
193                 short           field_width;
194                 short           dscale;
195                 char *p;
196                 p=sock->row[i];
197                 switch (dtype)
198                     {
199                     case SQL_SHORT:
200                         value = (ISC_INT64) *(short *) var->sqldata;
201                         field_width = 6;
202                         break;
203                     case SQL_LONG:
204                         value = (ISC_INT64) *(int *) var->sqldata;
205                         field_width = 11;
206                         break;
207                     case SQL_INT64:
208                         value = (ISC_INT64) *(ISC_INT64 *) var->sqldata;
209                         field_width = 21;
210                         break;
211                     }
212                 dscale = var->sqlscale;
213                 if (dscale < 0)
214                     {
215                     ISC_INT64   tens;
216                     short       j;
217
218                     tens = 1;
219                     for (j = 0; j > dscale; j--) tens *= 10;
220
221                     if (value >= 0)
222                         sprintf (p, "%*lld.%0*lld",
223                                 field_width - 1 + dscale, 
224                                 (ISC_INT64) value / tens,
225                                 -dscale, 
226                                 (ISC_INT64) value % tens);
227                     else if ((value / tens) != 0)
228                         sprintf (p, "%*lld.%0*lld",
229                                 field_width - 1 + dscale, 
230                                 (ISC_INT64) (value / tens),
231                                 -dscale, 
232                                 (ISC_INT64) -(value % tens));
233                     else
234                         sprintf (p, "%*s.%0*lld",
235                                 field_width - 1 + dscale, 
236                                 "-0",
237                                 -dscale, 
238                                 (ISC_INT64) -(value % tens));
239                     }
240                 else if (dscale)
241                     sprintf (p, "%*lld%0*d", 
242                             field_width, 
243                             (ISC_INT64) value,
244                             dscale, 0);
245                 else
246                     sprintf (p, "%*lld",
247                             field_width, 
248                             (ISC_INT64) value);
249                 }
250                 break;
251
252
253     case SQL_DOUBLE: case SQL_D_FLOAT:
254             snprintf(sock->row[i],sock->row_sizes[i], "%24f", *(double ISC_FAR *) (var->sqldata));
255             break;
256
257     case SQL_TIMESTAMP:
258                 isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, &times);
259                 snprintf(sock->row[i],sock->row_sizes[i],"%04d-%02d-%02d %02d:%02d:%02d.%04d",
260                                 times.tm_year + 1900,
261                                 times.tm_mon+1,
262                                 times.tm_mday,
263                                 times.tm_hour,
264                                 times.tm_min,
265                                 times.tm_sec,
266                                 ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
267                 break;
268
269     case SQL_TYPE_DATE:
270                 isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, &times);
271                 snprintf(sock->row[i],sock->row_sizes[i], "%04d-%02d-%02d",
272                                 times.tm_year + 1900,
273                                 times.tm_mon+1,
274                                 times.tm_mday);
275                 break;
276
277     case SQL_TYPE_TIME:
278                 isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, &times);
279                 snprintf(sock->row[i],sock->row_sizes[i], "%02d:%02d:%02d.%04d",
280                                 times.tm_hour,
281                                 times.tm_min,
282                                 times.tm_sec,
283                                 (*((ISC_TIME *)var->sqldata)) % 10000);
284                 break;
285
286     case SQL_BLOB:
287     case SQL_ARRAY:
288                 /* Print the blob id on blobs or arrays */
289                 bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
290                 snprintf(sock->row[i],sock->row_sizes[i],"%08x:%08x", bid.gds_quad_high, bid.gds_quad_low);
291                 break;
292                 
293  } //END SWITCH
294 } //END FOR
295 }
296
297
298 //=================
299 int fb_init_socket(rlm_sql_firebird_sock *sock) {
300     memset(sock, 0, sizeof(*sock));
301     sock->sqlda_out = (XSQLDA ISC_FAR *) calloc(XSQLDA_LENGTH (5),1);
302     sock->sqlda_out->sqln = 5;
303     sock->sqlda_out->version =  SQLDA_VERSION1;         
304     sock->sql_dialect=3;
305 //set tpb to read_committed/wait    
306     fb_set_tpb(sock,5,isc_tpb_version3, isc_tpb_write, isc_tpb_read_committed,
307               isc_tpb_no_rec_version,isc_tpb_wait);
308     if (!sock->tpb) return -1;        
309     return 0;
310 }
311
312 int fb_connect(rlm_sql_firebird_sock * sock,SQL_CONFIG *config) {
313  char *p;
314  char * database;
315  
316  sock->dpb_len=4;
317  if (config->sql_login) sock->dpb_len+=strlen(config->sql_login)+2;
318  if (config->sql_password) sock->dpb_len+=strlen(config->sql_password)+2;
319
320  sock->dpb=(char *) malloc(sock->dpb_len);
321  p=sock->dpb;
322    
323  *sock->dpb++ = isc_dpb_version1;
324  *sock->dpb++ = isc_dpb_num_buffers;
325  *sock->dpb++ = 1;
326  *sock->dpb++ = 90;
327   
328  fb_dpb_add_str(&sock->dpb,isc_dpb_user_name,config->sql_login);
329  fb_dpb_add_str(&sock->dpb,isc_dpb_password,config->sql_password);
330  
331  sock->dpb=p;
332  if (strchr(config->sql_server,':'))  database=strdup(config->sql_server);
333  else {
334   int ls=strlen(config->sql_server);
335   int ld=strlen(config->sql_db);
336   database=(char *) calloc(ls+ld+2,1);
337   strcpy(database,config->sql_server);
338   database[ls]=':';
339   memmove(database+ls+1,config->sql_db,ld);
340  }
341  isc_attach_database(sock->status, 0, database, &sock->dbh, sock->dpb_len, sock->dpb);
342  free(database);
343  return fb_lasterror(sock);
344 }
345
346
347 int fb_fetch(rlm_sql_firebird_sock *sock) {
348  long fetch_stat;
349  if (sock->statement_type!=isc_info_sql_stmt_select) return 100;
350  fetch_stat=isc_dsql_fetch(sock->status, &sock->stmt, SQL_DIALECT_V6, sock->sqlda_out);
351  if (fetch_stat) {
352    if (fetch_stat!=100L) fb_lasterror(sock);
353    else  sock->sql_code=0;
354 //   isc_commit_transaction(sock->status,&sock->trh);
355 //   isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_close);
356  }
357  return fetch_stat;
358  
359 }
360
361 int fb_sql_query(rlm_sql_firebird_sock *sock,char *sqlstr) {
362  if (fb_prepare(sock,sqlstr)) return fb_lasterror(sock);
363  switch (sock->statement_type) {
364     case isc_info_sql_stmt_exec_procedure: 
365          isc_dsql_execute2(sock->status, &sock->trh, &sock->stmt, SQL_DIALECT_V6,0,sock->sqlda_out);
366          break;
367     default: 
368          isc_dsql_execute(sock->status, &sock->trh, &sock->stmt, SQL_DIALECT_V6,0);
369          break;
370  }
371  fb_lasterror(sock);
372  if (sock->sql_code) fb_free_result(sock);
373       
374  return sock->sql_code;
375 }
376
377 int fb_disconnect(rlm_sql_firebird_sock *sock) {
378  if (sock->dbh) {
379    isc_detach_database(sock->status,&sock->dbh);
380    return fb_lasterror(sock);
381  }  
382  return 0;
383 }
384
385
386 void fb_destroy_socket(rlm_sql_firebird_sock *sock) {
387  int i;
388  fb_free_result(sock);
389  isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_drop);
390  fb_disconnect(sock);
391  for (i=0; i<sock->row_fcount;i++) free(sock->row[i]);
392  free(sock->row);free(sock->row_sizes);
393  free(sock->sqlda_out); free(sock->tpb); free(sock->dpb);
394  if (sock->lasterror) free(sock->lasterror);
395  memset(sock,0,sizeof(rlm_sql_firebird_sock));
396 }
397
398 int fb_free_result(rlm_sql_firebird_sock *sock) {
399  isc_commit_transaction(sock->status,&sock->trh);
400  isc_dsql_free_statement(sock->status, &sock->stmt, DSQL_close);
401  fb_free_sqlda(sock->sqlda_out);
402  sock->statement_type=0;
403  return 0;
404 }