All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 
22 RCSID("$Id: bcca53162492b0266ad3ead9cc3af66865ec7c38 $")
23 
24 #include "sql_fbapi.h"
25 
26 #include <stdarg.h>
27 
28 static void fb_set_tpb(rlm_sql_firebird_conn_t *conn, int count, ...)
29 {
30  int i;
31  va_list arg;
32 
33  va_start(arg, count);
34  conn->tpb = malloc(count);
35 
36  for (i = 0; i < count; i++) {
37  conn->tpb[i] = (char) va_arg(arg, int);
38  }
39 
40  conn->tpb_len = count;
41 
42  va_end(arg);
43 }
44 
45 
46 static void fb_dpb_add_str(char **dpb, char name, char const *value)
47 {
48  int l;
49 
50  if (!value) {
51  return;
52  }
53 
54  l = strlen(value);
55 
56  *(*dpb)++= name;
57  *(*dpb)++= (char) l;
58 
59  memmove(*dpb, value, l);
60 
61  *dpb += l;
62 }
63 
64 static void fb_set_sqlda(XSQLDA *sqlda) {
65  int i;
66 
67  for (i = 0; i < sqlda->sqld; i++) {
68  if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_VARYING) {
69  sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
70  } else {
71  sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
72  }
73 
74  if (sqlda->sqlvar[i].sqltype & 1) {
75  sqlda->sqlvar[i].sqlind = (short*)calloc(sizeof(short), 1);
76  } else {
77  sqlda->sqlvar[i].sqlind = 0;
78  }
79  }
80 }
81 
82 DIAG_OFF(deprecated-declarations)
84 {
85  ISC_SCHAR error[2048]; /* Only 1024 bytes should be written to this, but were playing it extra safe */
86  ISC_STATUS *pstatus;
87 
88  conn->sql_code = 0;
89 
90  /*
91  * Free any previous errors.
92  */
93  TALLOC_FREE(conn->error);
94 
95  /*
96  * Check if the status array contains an error
97  */
98  if (IS_ISC_ERROR(conn->status)) {
99  conn->sql_code = isc_sqlcode(conn->status);
100 
101  /*
102  * pstatus is a pointer into the status array which is
103  * advanced by isc_interprete. It's initialised to the
104  * first element of the status array.
105  */
106  pstatus = &conn->status[0];
107 
108  /*
109  * It's deprecated because the size of the buffer isn't
110  * passed and this isn't safe. But as were passing a very
111  * large buffer it's unlikely this will be an issue, and
112  * allows us to maintain compatibility with the interbase
113  * API.
114  */
115  isc_interprete(&error[0], &pstatus);
116  conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]);
117 
118  while (isc_interprete(&error[0], &pstatus)) {
119  conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]);
120  }
121 
122  memset(&conn->status, 0, sizeof(conn->status));
123  }
124 
125  return conn->sql_code;
126 }
127 DIAG_ON(deprecated-declarations)
128 
129 void fb_free_sqlda(XSQLDA *sqlda)
130 {
131  int i;
132  for (i = 0; i < sqlda->sqld; i++) {
133  free(sqlda->sqlvar[i].sqldata);
134  free(sqlda->sqlvar[i].sqlind);
135  }
136  sqlda->sqld = 0;
137 }
138 
139 
140 
141 //Macro for NULLs check
142 #define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
143 
144 //Structure to manage a SQL_VARYING Firebird's data types
145 typedef struct vary_fb {
146  short vary_length;
147  char vary_string[1];
148 } VARY;
149 
150 //function fb_store_row based on fiebird's apifull example
152 {
153  int dtype;
154  struct tm times;
155  ISC_QUAD bid;
156  int i;
157  XSQLVAR *var;
158  VARY * vary;
159 
160  /* assumed: id, username, attribute, value, op */
161  if (conn->row_fcount<conn->sqlda_out->sqld) {
162  i = conn->row_fcount;
163  conn->row_fcount = conn->sqlda_out->sqld;
164  conn->row = (char **) realloc(conn->row, conn->row_fcount * sizeof(char *));
165  conn->row_sizes = (int *) realloc(conn->row_sizes, conn->row_fcount * sizeof(int));
166 
167  while( i <conn->row_fcount) {
168  conn->row[i] = 0;
169  conn->row_sizes[i++] = 0;
170  }
171  }
172 
173  for (i = 0, var = conn->sqlda_out->sqlvar; i < conn->sqlda_out->sqld; var++, i++) {
174  /*
175  * Initial buffer size to store field's data is 256 bytes
176  */
177  if (conn->row_sizes[i]<256) {
178  conn->row[i] = (char *) realloc(conn->row[i], 256);
179  conn->row_sizes[i] = 256;
180  }
181 
182  if (IS_NULL(var)) {
183  strcpy(conn->row[i], "NULL");
184  continue;
185  }
186 
187  dtype = var->sqltype & ~1;
188 
189  switch (dtype) {
190  case SQL_TEXT:
191  if (conn->row_sizes[i]<= var->sqllen) {
192  conn->row_sizes[i] = var->sqllen + 1;
193  conn->row[i] = realloc(conn->row[i],
194  conn->row_sizes[i]);
195  }
196 
197  memmove(conn->row[i], var->sqldata, var->sqllen);
198  conn->row[i][var->sqllen] = 0;
199  break;
200 
201  case SQL_VARYING:
202  vary = (VARY*) var->sqldata;
203  if (conn->row_sizes[i] <= vary->vary_length) {
204  conn->row_sizes[i] = vary->vary_length + 1;
205  conn->row[i] = realloc(conn->row[i],
206  conn->row_sizes[i]);
207  }
208  memmove(conn->row[i], vary->vary_string, vary->vary_length);
209  conn->row[i][vary->vary_length] = 0;
210  break;
211 
212  case SQL_FLOAT:
213  snprintf(conn->row[i], conn->row_sizes[i], "%15g",
214  *(float ISC_FAR *) (var->sqldata));
215  break;
216 
217  case SQL_SHORT:
218  case SQL_LONG:
219  case SQL_INT64:
220  {
221  ISC_INT64 value = 0;
222  short field_width = 0;
223  short dscale = 0;
224  char *p;
225  p = conn->row[i];
226 
227  switch (dtype) {
228  case SQL_SHORT:
229  value = (ISC_INT64) *(short *)var->sqldata;
230  field_width = 6;
231  break;
232 
233  case SQL_LONG:
234  value = (ISC_INT64) *(int *)var->sqldata;
235  field_width = 11;
236  break;
237 
238  case SQL_INT64:
239  value = (ISC_INT64) *(ISC_INT64 *)var->sqldata;
240  field_width = 21;
241  break;
242  }
243  dscale = var->sqlscale;
244 
245  if (dscale < 0) {
246  ISC_INT64 tens;
247  short j;
248 
249  tens = 1;
250  for (j = 0; j > dscale; j--) {
251  tens *= 10;
252  }
253 
254  if (value >= 0) {
255  sprintf(p, "%*lld.%0*lld",
256  field_width - 1 + dscale,
257  (ISC_INT64) value / tens,
258  -dscale,
259  (ISC_INT64) value % tens);
260  } else if ((value / tens) != 0) {
261  sprintf (p, "%*lld.%0*lld",
262  field_width - 1 + dscale,
263  (ISC_INT64) (value / tens),
264  -dscale,
265  (ISC_INT64) -(value % tens));
266  } else {
267  sprintf(p, "%*s.%0*lld", field_width - 1 + dscale,
268  "-0", -dscale, (ISC_INT64) - (value % tens));
269  }
270  } else if (dscale) {
271  sprintf(p, "%*lld%0*d", field_width,
272  (ISC_INT64) value, dscale, 0);
273  } else {
274  sprintf(p, "%*lld", field_width,
275  (ISC_INT64) value);
276  }
277  }
278  break;
279 
280  case SQL_D_FLOAT:
281  case SQL_DOUBLE:
282  snprintf(conn->row[i], conn->row_sizes[i], "%24f",
283  *(double ISC_FAR *) (var->sqldata));
284  break;
285 
286  case SQL_TIMESTAMP:
287  isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, &times);
288  snprintf(conn->row[i], conn->row_sizes[i], "%04d-%02d-%02d %02d:%02d:%02d.%04d",
289  times.tm_year + 1900,
290  times.tm_mon + 1,
291  times.tm_mday,
292  times.tm_hour,
293  times.tm_min,
294  times.tm_sec,
295  ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
296  break;
297 
298  case SQL_TYPE_DATE:
299  isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, &times);
300  snprintf(conn->row[i], conn->row_sizes[i], "%04d-%02d-%02d",
301  times.tm_year + 1900,
302  times.tm_mon + 1,
303  times.tm_mday);
304  break;
305 
306  case SQL_TYPE_TIME:
307  isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, &times);
308  snprintf(conn->row[i], conn->row_sizes[i], "%02d:%02d:%02d.%04d",
309  times.tm_hour,
310  times.tm_min,
311  times.tm_sec,
312  (*((ISC_TIME *)var->sqldata)) % 10000);
313  break;
314 
315  case SQL_BLOB:
316  case SQL_ARRAY:
317  /* Print the blob id on blobs or arrays */
318  bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
319  snprintf(conn->row[i], conn->row_sizes[i], "%08" ISC_LONG_FMT "x:%08" ISC_LONG_FMT "x",
320  bid.gds_quad_high, bid.gds_quad_low);
321  break;
322  }
323  }
324 }
325 
327 {
328  memset(conn, 0, sizeof(*conn));
329  conn->sqlda_out = (XSQLDA ISC_FAR *) calloc(XSQLDA_LENGTH (5), 1);
330  conn->sqlda_out->sqln = 5;
331  conn->sqlda_out->version = SQLDA_VERSION1;
332  conn->sql_dialect = 3;
333 #ifdef _PTHREAD_H
334  pthread_mutex_init (&conn->mut, NULL);
335  DEBUG("Init mutex %p\n", &conn->mut);
336 #endif
337 
338  /*
339  * Set tpb to read_committed/wait/no_rec_version
340  */
341  fb_set_tpb(conn, 5, isc_tpb_version3, isc_tpb_wait, isc_tpb_write,
342  isc_tpb_read_committed, isc_tpb_no_rec_version);
343  if (!conn->tpb) {
344  return -1;
345  }
346 
347  return 0;
348 }
349 
351 {
352  char *p;
353  char *database;
354 
355  conn->dpb_len = 4;
356  if (config->sql_login) {
357  conn->dpb_len+= strlen(config->sql_login) + 2;
358  }
359 
360  if (config->sql_password) {
361  conn->dpb_len += strlen(config->sql_password) + 2;
362  }
363 
364  conn->dpb = (char *) malloc(conn->dpb_len);
365  p = conn->dpb;
366 
367  *conn->dpb++= isc_dpb_version1;
368  *conn->dpb++= isc_dpb_num_buffers;
369  *conn->dpb++= 1;
370  *conn->dpb++= 90;
371 
372  fb_dpb_add_str(&conn->dpb, isc_dpb_user_name, config->sql_login);
373  fb_dpb_add_str(&conn->dpb, isc_dpb_password, config->sql_password);
374 
375  conn->dpb = p;
376 
377  /*
378  * Check if database and server in the form of server:database.
379  * If config->sql_server contains ':', then config->sql_db
380  * parameter ignored.
381  */
382  if (strchr(config->sql_server, ':')) {
383  database = strdup(config->sql_server);
384  } else {
385  /*
386  * Make database and server to be in the form
387  * of server:database
388  */
389  int ls = strlen(config->sql_server);
390  int ld = strlen(config->sql_db);
391  database = (char *) calloc(ls + ld + 2, 1);
392  strcpy(database, config->sql_server);
393  database[ls] = ':';
394  memmove(database + ls + 1, config->sql_db, ld);
395  }
396  isc_attach_database(conn->status, 0, database, &conn->dbh,
397  conn->dpb_len, conn->dpb);
398  free(database);
399 
400  return fb_error(conn);
401 }
402 
403 
405 {
406  long fetch_stat;
407  if (conn->statement_type!= isc_info_sql_stmt_select) {
408  return 100;
409  }
410 
411  fetch_stat = isc_dsql_fetch(conn->status, &conn->stmt,
412  SQL_DIALECT_V6, conn->sqlda_out);
413  if (fetch_stat) {
414  if (fetch_stat!= 100L) {
415  fb_error(conn);
416  } else {
417  conn->sql_code = 0;
418  }
419  }
420 
421  return fetch_stat;
422 }
423 
424 static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
425 {
426  static char stmt_info[] = { isc_info_sql_stmt_type };
427  char info_buffer[128];
428  short l;
429 
430  if (!conn->trh) {
431  isc_start_transaction(conn->status, &conn->trh, 1, &conn->dbh,
432  conn->tpb_len, conn->tpb);
433  if (!conn->trh) {
434  return -4;
435  }
436  }
437 
438  fb_free_statement(conn);
439  if (!conn->stmt) {
440  isc_dsql_allocate_statement(conn->status, &conn->dbh,
441  &conn->stmt);
442  if (!conn->stmt) {
443  return -1;
444  }
445  }
446 
447  fb_free_sqlda(conn->sqlda_out);
448  isc_dsql_prepare(conn->status, &conn->trh, &conn->stmt, 0, query,
449  conn->sql_dialect, conn->sqlda_out);
450  if (IS_ISC_ERROR(conn->status)) {
451  return -2;
452  }
453 
454  if (conn->sqlda_out->sqln<conn->sqlda_out->sqld) {
455  conn->sqlda_out->sqln = conn->sqlda_out->sqld;
456  conn->sqlda_out = (XSQLDA ISC_FAR *) realloc(conn->sqlda_out,
457  XSQLDA_LENGTH(conn->sqlda_out->sqld));
458  isc_dsql_describe(conn->status, &conn->stmt, SQL_DIALECT_V6,
459  conn->sqlda_out);
460 
461  if (IS_ISC_ERROR(conn->status)) {
462  return -3;
463  }
464  }
465  /*
466  * Get statement type
467  */
468  isc_dsql_sql_info(conn->status, &conn->stmt, sizeof(stmt_info),
469  stmt_info, sizeof(info_buffer), info_buffer);
470  if (IS_ISC_ERROR(conn->status)) return -4;
471 
472  l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
473  conn->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3,
474  l);
475 
476  if (conn->sqlda_out->sqld) {
477  fb_set_sqlda(conn->sqlda_out); //set out sqlda
478  }
479 
480  return 0;
481 }
482 
483 
484 int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query) {
485  if (fb_prepare(conn, query)) {
486  return fb_error(conn);
487  }
488 
489  switch (conn->statement_type) {
490  case isc_info_sql_stmt_exec_procedure:
491  isc_dsql_execute2(conn->status, &conn->trh, &conn->stmt,
492  SQL_DIALECT_V6, 0, conn->sqlda_out);
493  break;
494 
495  default:
496  isc_dsql_execute(conn->status, &conn->trh, &conn->stmt,
497  SQL_DIALECT_V6, 0);
498  break;
499  }
500  return fb_error(conn);
501 }
502 
504  static char count_info[] = {isc_info_sql_records};
505  char info_buffer[128];
506  char *p ;
507  int affected_rows = -1;
508 
509  if (!conn->stmt) return -1;
510 
511  isc_dsql_sql_info(conn->status, &conn->stmt,
512  sizeof (count_info), count_info,
513  sizeof (info_buffer), info_buffer);
514 
515  if (IS_ISC_ERROR(conn->status)) {
516  return fb_error(conn);
517  }
518 
519  p = info_buffer + 3;
520  while (*p != isc_info_end) {
521  p++;
522  short len = (short)isc_vax_integer(p, 2);
523  p += 2;
524 
525  affected_rows = isc_vax_integer(p, len);
526  if (affected_rows > 0) {
527  break;
528  }
529  p += len;
530  }
531  return affected_rows;
532 }
533 
535  isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_close);
536 
537  return fb_error(conn);
538 }
539 
541  if (conn->stmt) {
542  isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_drop);
543  conn->stmt = 0;
544  }
545 }
546 
548  conn->sql_code = 0;
549  if (conn->trh) {
550  isc_rollback_transaction(conn->status, &conn->trh);
551 // conn->in_use = 0;
552 #ifdef _PTHREAD_H
553  pthread_mutex_unlock(&conn->mut);
554 #endif
555 
556  if (IS_ISC_ERROR(conn->status)) {
557  return fb_error(conn);
558  }
559  }
560  return conn->sql_code;
561 }
562 
564  conn->sql_code = 0;
565  if (conn->trh) {
566  isc_commit_transaction (conn->status, &conn->trh);
567  if (IS_ISC_ERROR(conn->status)) {
568  fb_error(conn);
569  ERROR("Fail to commit. Error: %s. Try to rollback.", conn->error);
570  return fb_rollback(conn);
571  }
572  }
573 // conn->in_use = 0;
574 #ifdef _PTHREAD_H
575  pthread_mutex_unlock(&conn->mut);
576 #endif
577  return conn->sql_code;
578 }
#define pthread_mutex_init(_x, _y)
Definition: rlm_eap.h:75
#define DIAG_ON(_x)
Definition: build.h:103
static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:424
#define DIAG_OFF(_x)
Definition: build.h:102
static char const * name
int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:484
char vary_string[1]
Definition: sql_fbapi.c:147
int fb_close_cursor(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:534
char const * sql_server
Server to connect to.
Definition: rlm_sql.h:85
rlm_sql_row_t row
Definition: sql_fbapi.h:66
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
int fb_fetch(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:404
void fb_store_row(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:151
static void fb_set_tpb(rlm_sql_firebird_conn_t *conn, int count,...)
Definition: sql_fbapi.c:28
int fb_commit(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:563
int fb_init_socket(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:326
#define IS_ISC_ERROR(status)
Definition: sql_fbapi.h:35
int fb_affected_rows(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:503
#define pthread_mutex_unlock(_x)
Definition: rlm_eap.h:78
int fb_connect(rlm_sql_firebird_conn_t *conn, rlm_sql_config_t *config)
Definition: sql_fbapi.c:350
short vary_length
Definition: sql_fbapi.c:146
#define DEBUG(fmt,...)
Definition: log.h:175
static void fb_set_sqlda(XSQLDA *sqlda)
Definition: sql_fbapi.c:64
isc_tr_handle trh
Definition: sql_fbapi.h:51
isc_stmt_handle stmt
Definition: sql_fbapi.h:50
static void fb_dpb_add_str(char **dpb, char name, char const *value)
Definition: sql_fbapi.c:46
char * talloc_typed_asprintf(void const *t, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: missing.c:611
char const * sql_password
Login password to use.
Definition: rlm_sql.h:88
void fb_free_sqlda(XSQLDA *sqlda)
Definition: sql_fbapi.c:129
#define ISC_LONG_FMT
Definition: sql_fbapi.h:44
char const * sql_login
Login credentials to use.
Definition: rlm_sql.h:87
struct vary_fb VARY
int fb_error(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:83
int fb_rollback(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:547
void fb_free_statement(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:540
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...
char const * sql_db
Database to run queries against.
Definition: rlm_sql.h:89
#define RCSID(id)
Definition: build.h:135
isc_db_handle dbh
Definition: sql_fbapi.h:49
ISC_STATUS status[20]
Magic interbase status code array (holds multiple error codes used to construct more detailed error m...
Definition: sql_fbapi.h:53
#define ERROR(fmt,...)
Definition: log.h:145
#define IS_NULL(x)
Definition: sql_fbapi.c:142