1 /* Copyright (C) 2000-2015 Lavtech.com corp. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17 
18 #include "udm_config.h"
19 #if HAVE_IBASE
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 
28 #include "udm_common.h"
29 #include "udm_sqldbms.h"
30 #include "udm_utils.h"
31 #include "udm_vars.h"
32 
33 #include "udm_xmalloc.h"
34 #ifdef WIN32
35 #include <process.h>
36 #endif
37 
38 #include <ibase.h>
39 
40 #define SQL_VARCHAR(len) struct {short vary_length; char vary_string[(len)+1];}
41 typedef struct
42 {
43   short  len;
44   char  str[1];
45 } UDM_IBASE_VARY;
46 
47 #define UDM_IBASE_MAX_BIND_PARAM UDM_SQL_MAX_BIND_PARAM
48 
49 typedef struct
50 {
51   isc_stmt_handle query_handle; /* query handle   */
52   long query_type;          /* SELECT or UPDATE, etc */
53   XSQLDA in_sqlda;
54   XSQLVAR dummy_vars[UDM_IBASE_MAX_BIND_PARAM]; /* Must be after in_sqlda */
55   size_t long_data_lengths[UDM_IBASE_MAX_BIND_PARAM]; /* For data >64K */
56 } UDM_IB_STMT;
57 
58 
59 typedef struct
60 {
61   isc_db_handle DBH;        /* database handle    */
62   ISC_STATUS    status[20]; /* status vector      */
63   isc_tr_handle tr_handle;  /* transaction handle */
64   int autocommit;
65   UDM_IB_STMT stmt;
66 } UDM_IB;
67 
68 
69 static UDM_IB *
UdmIB(UDM_SQL * db)70 UdmIB(UDM_SQL *db)
71 {
72   return (UDM_IB*) db->specific;
73 }
74 
75 
76 static void
UdmSetErrorCode(UDM_SQL * db,int errcode)77 UdmSetErrorCode(UDM_SQL *db, int errcode)
78 {
79   db->errcode= errcode;
80 }
81 
82 static udm_rc_t
UdmIBStmtFreeInternal(UDM_SQL * db,UDM_IB_STMT * stmt)83 UdmIBStmtFreeInternal(UDM_SQL *db, UDM_IB_STMT *stmt)
84 {
85   UDM_IB *ib= UdmIB(db);
86   if (isc_dsql_free_statement(ib->status, &stmt->query_handle, DSQL_drop))
87   {
88     UdmSetErrorCode(db, 1);
89     return UDM_ERROR;
90   }
91   return UDM_OK;
92 }
93 
94 
95 #ifndef FB_API_VER
96 #pragma GCC diagnostic push
97 #pragma GCC diagnostic ignored "-Wcast-qual"
98 ISC_LONG ISC_EXPORT
fb_interpret(ISC_SCHAR * dst,unsigned int dstlen,const ISC_STATUS ** st)99 fb_interpret(ISC_SCHAR *dst,  unsigned int dstlen,  const ISC_STATUS **st)
100 {
101   return isc_interprete(dst, (ISC_STATUS **) st);
102 }
103 #pragma GCC diagnostic pop
104 #endif
105 
106 
107 static void
UdmIBaseDisplayError(UDM_SQL * db,const char * fname)108 UdmIBaseDisplayError(UDM_SQL *db, const char *fname)
109 {
110   size_t slen;
111   char *s= db->errstr;
112   UDM_IB *ib= UdmIB(db);
113   const ISC_STATUS *ibstatus= ib->status;
114   ISC_LONG len;
115 
116   s+= udm_snprintf(s, sizeof(db->errstr), "%s: ", fname);
117   slen= sizeof(db->errstr) - (s - db->errstr) - 1;
118   for( ; (len= fb_interpret(s , slen, &ibstatus)); )
119   {
120     s+= len;
121     *s++= ' ';
122     *s= '\0';
123     slen= sizeof(db->errstr) - (s - db->errstr) - 1;
124   }
125 }
126 
127 static udm_rc_t
UdmIBaseConnect(UDM_SQL * db)128 UdmIBaseConnect(UDM_SQL *db)
129 {
130   char dpb_buffer[256], *dpb, *e;
131   const char *p;
132   int dpb_length, len;
133   char connect_string[256];
134   const char* DBUser= UdmVarListFindStr(&db->Vars,"DBUser",NULL);
135   const char* DBPass= UdmVarListFindStr(&db->Vars,"DBPass",NULL);
136   const char* DBHost= UdmVarListFindStr(&db->Vars, "DBHost", "localhost");
137   const char* DBName= UdmVarListFindStr(&db->Vars, "DBName", "");
138   UDM_IB *ib= (UDM_IB*) malloc(sizeof(UDM_IB));
139   bzero(ib, sizeof(*ib));
140   db->specific= (void*) ib;
141 
142   ib->autocommit= 1;
143   dpb = dpb_buffer;
144   *dpb++ = isc_dpb_version1;
145 
146   if (DBUser != NULL && (len = strlen(DBUser)))
147   {
148     *dpb++ = isc_dpb_user_name;
149     *dpb++ = len;
150     for (p = DBUser; *p;)
151       *dpb++ = *p++;
152   }
153   if (DBPass != NULL && (len = strlen(DBPass)))
154   {
155     *dpb++ = isc_dpb_password;
156     *dpb++ = len;
157     for (p = DBPass; *p;)
158       *dpb++ = *p++;
159   }
160   /*
161   if (charset != NULL && (len = strlen(charset))) {
162     *dpb++ = isc_dpb_lc_ctype;
163     *dpb++ = strlen(charset);
164     for (p = charset; *p;) {
165       *dpb++ = *p++;
166     }
167   }
168 #ifdef isc_dpb_sql_role_name
169   if (role != NULL && (len = strlen(role))) {
170     *dpb++ = isc_dpb_sql_role_name;
171     *dpb++ = strlen(role);
172     for (p = role; *p;) {
173       *dpb++ = *p++;
174     }
175   }
176 #endif
177   */
178 
179   dpb_length = dpb - dpb_buffer;
180 
181   if(strcmp(DBHost,"localhost"))
182     udm_snprintf(connect_string,sizeof(connect_string)-1,"%s:%s",DBHost, DBName);
183   else
184     udm_snprintf(connect_string,sizeof(connect_string)-1,"%s", DBName);
185 
186   /* Remove possible trailing slash */
187   e= connect_string+strlen(connect_string);
188   if (e>connect_string && e[-1]=='/')
189     e[-1]='\0';
190 
191 #ifdef DEBUG_SQL
192   fprintf(stderr, "SQL Connect to: '%s'\n",connect_string);
193 #endif
194   if(isc_attach_database(ib->status, strlen(connect_string), connect_string,
195                          &(ib->DBH), dpb_length, dpb_buffer))
196   {
197     UdmSetErrorCode(db, 1);
198     return UDM_ERROR;
199   }
200 
201   /*
202     http://www.firebirdfaq.org/faq223/
203     To obtain the server version, use isc_info_svc_server_version() API func.
204     It does not work for Firebird Classic 1.0, so if you don't get an answer
205     you'll know it's Firebird Classic 1.0 or InterBase Classic 6.0.
206     Otherwise it returns a string like this:
207       LI-V2.0.0.12748 Firebird 2.0
208     or...
209       LI-V1.5.3.4870 Firebird 1.5
210 
211     If you use Firebird 2.1 (and higher), you can also use:
212       SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION')
213       from rdb$database;
214   */
215   return UDM_OK;
216 }
217 
218 
219 static udm_rc_t
UdmIBaseClose(UDM_SQL * db)220 UdmIBaseClose(UDM_SQL *db)
221 {
222   UDM_IB *ib= UdmIB(db);
223   if (db->connected)
224   {
225     if (isc_detach_database(ib->status, &(ib->DBH)))
226       UdmSetErrorCode(db, 1);
227     UDM_FREE(db->specific);
228   }
229   return UDM_OK;
230 }
231 
232 
233 static udm_rc_t
UdmIBPrepareInternal(UDM_SQL * db,UDM_IB_STMT * stmt,const char * query)234 UdmIBPrepareInternal(UDM_SQL *db, UDM_IB_STMT *stmt, const char *query)
235 {
236   UDM_IB *ib= UdmIB(db);
237   char    query_info[] = { isc_info_sql_stmt_type };
238   char    info_buffer[18];
239   udm_bool_t tr_started= UDM_FALSE;
240 
241   stmt->query_handle= 0;
242   stmt->in_sqlda.sqln= 0;
243   stmt->in_sqlda.sqld= 0;
244   stmt->in_sqlda.version= SQLDA_VERSION1;
245 
246   if(!ib->tr_handle)
247   {
248     if (isc_start_transaction(ib->status, &ib->tr_handle, 1,
249                               &ib->DBH, 0, NULL))
250     {
251       UdmIBaseDisplayError(db, "isc_start_transaction");
252       goto err;
253     }
254     tr_started= UDM_TRUE;
255     ib->autocommit=1;
256   }
257   else
258   {
259     ib->autocommit=0;
260   }
261 
262 
263   if (isc_dsql_allocate_statement(ib->status, &ib->DBH, &stmt->query_handle))
264   {
265     UdmIBaseDisplayError(db, "isc_dsql_allocate_statement");
266     goto err;
267   }
268   if (isc_dsql_prepare(ib->status, &ib->tr_handle, &stmt->query_handle,
269                        0, query, 1, NULL))
270   {
271     UdmIBaseDisplayError(db, "isc_dsql_prepare");
272     goto err;
273   }
274   if (!isc_dsql_sql_info(ib->status, &stmt->query_handle,
275                          sizeof(query_info), query_info,
276                          sizeof(info_buffer), info_buffer))
277   {
278     short l;
279     l= (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
280     stmt->query_type= isc_vax_integer((char ISC_FAR *) info_buffer + 3, l);
281   }
282 
283   return UDM_OK;
284 
285 err:
286   if (tr_started)
287   {
288     ISC_STATUS  status[20]; /* Not to override db->status */
289     isc_rollback_transaction(status, &ib->tr_handle);
290     ib->tr_handle= 0;
291   }
292   UdmSetErrorCode(db, 1);
293   return UDM_ERROR;
294 }
295 
296 
297 static udm_bool_t
is_duplicate_error(UDM_SQL * db)298 is_duplicate_error(UDM_SQL *db)
299 {
300   if (strstr(db->errstr,"uplicat") ||
301       strstr(db->errstr,"UNIQUE"))
302     return UDM_TRUE;
303   return UDM_FALSE;
304 }
305 
306 static udm_rc_t
UdmIBExecInternal(UDM_SQL * db,UDM_SQLRES * res,UDM_IB_STMT * stmt,int free_stmt)307 UdmIBExecInternal(UDM_SQL *db, UDM_SQLRES *res, UDM_IB_STMT *stmt, int free_stmt)
308 {
309   UDM_IB *ib= UdmIB(db);
310   ISC_STATUS  status[20]; /* To not override db->status */
311 
312   /* Find out what kind of query is to be executed */
313   if (stmt->query_type == isc_info_sql_stmt_select ||
314       stmt->query_type == isc_info_sql_stmt_select_for_upd)
315   {
316     XSQLDA *osqlda=NULL;
317     long fetch_stat;
318     int i;
319     short   sqlind_array[128];
320 
321     /* Select, need to allocate output sqlda and and prepare it for use */
322     osqlda = (XSQLDA *) UdmXmalloc(XSQLDA_LENGTH(0));
323     osqlda->sqln = 0;
324     osqlda->version = SQLDA_VERSION1;
325 
326     /* Fetch column information */
327     if (isc_dsql_describe(ib->status, &stmt->query_handle, 1, osqlda))
328     {
329       UDM_FREE(osqlda);
330       UdmSetErrorCode(db, 1);
331       isc_rollback_transaction(status, &ib->tr_handle);
332       return UDM_ERROR;
333     }
334 
335     if (osqlda->sqld)
336     {
337       osqlda = (XSQLDA *) UdmXrealloc(osqlda, XSQLDA_LENGTH(osqlda->sqld));
338       osqlda->sqln = osqlda->sqld;
339       osqlda->version = SQLDA_VERSION1;
340       if (isc_dsql_describe(ib->status, &stmt->query_handle, 1, osqlda))
341       {
342         UDM_FREE(osqlda);
343         UdmSetErrorCode(db, 1);
344         isc_rollback_transaction(status, &ib->tr_handle);
345         return UDM_ERROR;
346       }
347     }
348 
349     if (!res)
350     {
351       UdmSetErrorCode(db, 1);
352       udm_snprintf(db->errstr, sizeof(db->errstr)-1,
353                    "ibase_query with empty 'res' returned rows");
354       return UDM_ERROR;
355     }
356 
357     res->nCols = osqlda->sqld;
358     res->Fields= (UDM_SQLFIELD*)UdmXmalloc(res->nCols*sizeof(UDM_SQLFIELD));
359 
360     for (i = 0; i < osqlda->sqld; i++)
361     {
362       XSQLVAR *var= &osqlda->sqlvar[i];
363       int coltype= osqlda->sqlvar[i].sqltype & ~1;
364 
365       var->sqlind= &sqlind_array[i];
366       var->sqlind[0]= 0;
367       res->Fields[i].sqlname= (char*)UdmStrdup(var->aliasname[0] ? var->aliasname : var->sqlname);
368       res->Fields[i].sqllen= var->sqllen;
369 
370       switch(coltype)
371       {
372         case SQL_SHORT:
373           var->sqldata = (char*)UdmMalloc(sizeof(short));
374           break;
375         case SQL_LONG:
376           var->sqldata = (char*)UdmMalloc(sizeof(ISC_LONG));
377           break;
378         case SQL_FLOAT:
379           var->sqldata = (char*)UdmMalloc(sizeof(float));
380           break;
381         case SQL_DOUBLE:
382           var->sqldata = (char*)UdmMalloc(sizeof(double));
383           break;
384         case SQL_DATE:
385         case SQL_BLOB:
386         case SQL_ARRAY:
387           var->sqldata = (char*)UdmMalloc(sizeof(ISC_QUAD));
388           break;
389         case SQL_TEXT:
390           var->sqldata = (char*)UdmMalloc((size_t)(osqlda->sqlvar[i].sqllen));
391           break;
392         case SQL_VARYING:
393           osqlda->sqlvar[i].sqldata = (char*)UdmMalloc((size_t)(osqlda->sqlvar[i].sqllen+sizeof(short)));
394           break;
395       }
396     }
397 
398     if (isc_dsql_execute(ib->status, &ib->tr_handle, &stmt->query_handle, 1, NULL))
399     {
400       UDM_FREE(osqlda);
401       UdmSetErrorCode(db, 1);
402       isc_rollback_transaction(status, &ib->tr_handle);
403       return UDM_ERROR;
404     }
405 
406     while ((fetch_stat= isc_dsql_fetch(ib->status, &stmt->query_handle, 1, osqlda)) == 0)
407     {
408       res->Items= (UDM_STR *)UdmRealloc(res->Items,(res->nRows+1)*(res->nCols)*sizeof(UDM_STR));
409 
410       for(i=0;i<osqlda->sqld; i++)
411       {
412         UDM_IBASE_VARY *vary;
413         char shortdata[64];
414         XSQLVAR *var=osqlda->sqlvar+i;
415         char *p=NULL;
416         size_t len;
417 
418         if(*var->sqlind==-1) /* NULL data */
419         {
420           p= (char*)UdmStrdup("");
421           len= 0;
422         }
423         else
424         switch(var->sqltype & ~1)
425         {
426           case SQL_TEXT:
427             p=(char*)UdmMalloc((size_t)(var->sqllen+1));
428             strncpy(p,(char*)var->sqldata,(size_t)(var->sqllen));
429             p[var->sqllen]='\0';
430             len= var->sqllen;
431             break;
432           case SQL_VARYING:
433             vary=(UDM_IBASE_VARY*)var->sqldata;
434             p=(char*)UdmMalloc((size_t)(vary->len+1));
435             strncpy(p,vary->str,(size_t)(vary->len));
436             p[vary->len]='\0';
437             len= vary->len;
438             break;
439           case SQL_LONG:
440             /* SQL_LONG is in fact int4 on both x86 and x86_64 */
441             len= sprintf(shortdata,"%d", (int) (*(ISC_LONG*) (var->sqldata)));
442             p = (char*)UdmStrdup(shortdata);
443             break;
444           case SQL_SHORT:
445             len= sprintf(shortdata,"%d",*(short*)(var->sqldata));
446             p = (char*)UdmStrdup(shortdata);
447             break;
448           case SQL_FLOAT:
449             len= sprintf(shortdata,"%f",*(float*)(var->sqldata));
450             p = (char*)UdmStrdup(shortdata);
451             break;
452           case SQL_DOUBLE:
453             len= sprintf(shortdata,"%f",*(double*)(var->sqldata));
454             p = (char*)UdmStrdup(shortdata);
455             break;
456           case SQL_BLOB:
457             {
458               isc_blob_handle blob_handle= 0;
459               unsigned short blob_seg_len, blob_segment_size= 32*1024;
460               char blob_items[]= {isc_info_blob_total_length };
461               char res_buffer[20], *res_buffer_ptr;
462               size_t blob_length= 0;
463 
464               len= 0;
465               p= NULL;
466 
467               /* Open the blob with the fetched blob_id. */
468               if (isc_open_blob(status, &ib->DBH, &ib->tr_handle,
469                                 &blob_handle, (ISC_QUAD*)var->sqldata))
470               {
471                 /* isc_print_status(status); */
472                 len= sprintf(shortdata, "<isc_open_blob failed>");
473                 p= (char*) UdmStrdup(shortdata);
474                 break;
475               }
476 
477               /* Get blob info */
478               isc_blob_info(status, &blob_handle,
479                             sizeof(blob_items), blob_items,
480                             sizeof(res_buffer), res_buffer);
481               if (status[0] == 1 && status[1])
482               {
483                 /* isc_print_status(status);*/
484                 len= sprintf(shortdata, "<isc_blob_info failed>");
485                 p= (char*) UdmStrdup(shortdata);
486                 goto blob_close;
487               }
488 
489               /* Parse blob info data */
490               for (res_buffer_ptr= res_buffer; *res_buffer_ptr != isc_info_end; )
491               {
492                 char item= *res_buffer_ptr++;
493                 if (item == isc_info_blob_total_length)
494                 {
495                   int length= isc_vax_integer(res_buffer_ptr, 2);
496                   res_buffer_ptr+= 2;
497                   blob_length= isc_vax_integer(res_buffer_ptr, length);
498                   res_buffer_ptr+= length;
499                 }
500               }
501 
502               p= (char*) UdmMalloc(blob_length + 1);
503 
504               while (isc_get_segment(status, &blob_handle, &blob_seg_len,
505                                      blob_segment_size, p + len) == 0 ||
506                      status[1] == isc_segment)
507               {
508                 len+= blob_seg_len;
509               }
510 
511               p[len]= '\0';
512 
513               if (status[1] != isc_segstr_eof)
514               {
515                 len= sprintf(shortdata, "<isc_get_segment failed>");
516                 p= (char*) UdmStrdup(shortdata);
517                 break;
518               }
519 blob_close:
520               if (isc_close_blob(status, &blob_handle))
521               {
522                 len= sprintf(shortdata, "<isc_close_blob failed>");
523                 p= (char*) UdmStrdup(shortdata);
524                 break;
525               }
526             }
527             break;
528           default:
529             len= sprintf(shortdata,"Unknown SQL type %d", var->sqltype & ~1);
530             p= (char*)UdmStrdup(shortdata);
531             break;
532         }
533         /*UdmRTrim(p," ");*/
534         res->Items[res->nRows*res->nCols+i].str= p;
535         res->Items[res->nRows*res->nCols+i].length= len;
536       }
537       res->nRows++;
538       if (db->res_limit && res->nRows >= db->res_limit)
539       {
540         fetch_stat = 100L;
541         break;
542       }
543     }
544 
545     db->res_limit= 0;
546 
547     /* Free fetch buffers */
548     for (i = 0; i < osqlda->sqld; i++)
549       UDM_FREE(osqlda->sqlvar[i].sqldata);
550 
551     UDM_FREE(osqlda);
552 
553     if (fetch_stat != 100L)
554     {
555       UdmSetErrorCode(db, 1);
556       isc_rollback_transaction(status, &ib->tr_handle);
557       return UDM_ERROR;
558     }
559   }
560   else
561   {
562     /* Not select */
563     if (isc_dsql_execute(ib->status, &ib->tr_handle, &stmt->query_handle, 1,
564                          &stmt->in_sqlda))
565     {
566       UdmIBaseDisplayError(db, "isc_dsql_execute");
567       if (!is_duplicate_error(db))
568       {
569         ib->autocommit= 1;
570         UdmSetErrorCode(db, 1);
571         isc_rollback_transaction(status, &ib->tr_handle);
572         if (free_stmt)
573           UdmIBStmtFreeInternal(db, stmt);
574         return UDM_ERROR;
575       }
576     }
577   }
578 
579   if (free_stmt && UDM_OK != UdmIBStmtFreeInternal(db, stmt))
580   {
581     UdmSetErrorCode(db, 1);
582     isc_rollback_transaction(status, &ib->tr_handle);
583     return UDM_ERROR;
584   }
585 
586   if(ib->autocommit)
587   {
588     if (isc_commit_transaction(ib->status, &ib->tr_handle))
589     {
590       UdmSetErrorCode(db, 1);
591       ib->tr_handle= 0;
592       return UDM_ERROR;
593     }
594     ib->tr_handle= 0;
595   }
596 
597   return UDM_OK;
598 }
599 
600 
601 /*
602   Create a BLOB, write data to it, and return BLOB id
603 */
604 static udm_rc_t
UdmIBCreateBlob(UDM_IB * ib,const char * blob_data,size_t blob_length,ISC_QUAD * blob_id)605 UdmIBCreateBlob(UDM_IB *ib, const char *blob_data, size_t blob_length,
606                 ISC_QUAD *blob_id)
607 {
608   isc_blob_handle blob_handle= 0;
609   unsigned short seg_max_length = 32*1024;
610 
611   if (isc_create_blob(ib->status, &ib->DBH, &ib->tr_handle, &blob_handle, blob_id))
612     return UDM_ERROR;
613 
614   while (blob_length)
615   {
616     unsigned short blob_seg_length= blob_length > seg_max_length ?
617                                     seg_max_length : blob_length;
618     if (isc_put_segment(ib->status, &blob_handle, blob_seg_length, blob_data))
619       return UDM_ERROR;
620     blob_data+= blob_seg_length;
621     blob_length-= blob_seg_length;
622   }
623 
624   if (isc_close_blob(ib->status, &blob_handle))
625     return UDM_ERROR;
626 
627   return UDM_OK;
628 }
629 
630 
631 static udm_rc_t
UdmIBExec(UDM_SQL * db)632 UdmIBExec(UDM_SQL *db)
633 {
634   UDM_IB *ib= UdmIB(db);
635   UDM_IB_STMT *stmt= &ib->stmt;
636   ISC_QUAD blob_ids[UDM_IBASE_MAX_BIND_PARAM];
637   ISC_SHORT i;
638   for (i= 0; i < stmt->in_sqlda.sqln; i++)
639   {
640     XSQLVAR *var= &stmt->in_sqlda.sqlvar[i];
641     if ((var->sqltype & ~1) == SQL_BLOB)
642     {
643       size_t blob_length= stmt->long_data_lengths[i];
644       if (UDM_OK != UdmIBCreateBlob(ib, var->sqldata, blob_length, &blob_ids[i]))
645       {
646         UdmIBaseDisplayError(db, "UdmIBCreateBlob");
647         UdmSetErrorCode(db, 1);
648         return UDM_ERROR;
649       }
650       var->sqldata= (char*) &blob_ids[i];
651       var->sqllen= sizeof(ISC_QUAD);
652     }
653   }
654 
655   return UdmIBExecInternal(db, NULL, &ib->stmt, 0);
656 }
657 
658 
659 static udm_rc_t
sql_ibase_query(UDM_SQL * db,UDM_SQLRES * res,const char * query)660 sql_ibase_query(UDM_SQL *db, UDM_SQLRES *res, const char *query)
661 {
662   udm_rc_t rc= UDM_OK;
663   UDM_IB  *ib;
664   UDM_IB_STMT stmt;
665 
666   if (!db->connected)
667   {
668     UdmIBaseConnect(db);
669     if(db->errcode)
670     {
671       UdmIBaseDisplayError(db, "UdmIBaseConnect");
672       UdmSetErrorCode(db, 1);
673       return UDM_ERROR;
674     }
675     else
676     {
677       db->connected= UDM_TRUE;
678     }
679   }
680 
681   ib= UdmIB(db);
682 
683   if(!strcmp(query,"BEGIN"))
684   {
685     if(!ib->tr_handle)
686     {
687       if (isc_start_transaction(ib->status, &ib->tr_handle, 1,
688                                 &(ib->DBH), 0, NULL))
689       {
690         UdmSetErrorCode(db, 1);
691         UdmIBaseDisplayError(db, "isc_start_transaction");
692         rc= UDM_ERROR;
693       }
694       ib->autocommit= 0;
695     }
696     else
697     {
698       ib->autocommit= 1;
699       UdmSetErrorCode(db, 1);
700       udm_snprintf(db->errstr,sizeof(db->errstr),"Wrong call order: begin");
701       rc= UDM_ERROR;
702     }
703     return rc;
704   }
705 
706   if(!strcmp(query,"COMMIT"))
707   {
708     if(ib->tr_handle)
709     {
710       if (isc_commit_transaction(ib->status, &ib->tr_handle))
711       {
712         UdmSetErrorCode(db, 1);
713         UdmIBaseDisplayError(db, "isc_commit_transaction");
714         rc= UDM_ERROR;
715       }
716       ib->tr_handle= 0;
717       ib->autocommit= 1;
718     }
719     else
720     {
721       ib->autocommit= 1;
722       UdmSetErrorCode(db, 1);
723       udm_snprintf(db->errstr,sizeof(db->errstr),"Wrong call order: commit");
724       rc= UDM_ERROR;
725     }
726     return rc;
727   }
728 
729   if (UdmIBPrepareInternal(db, &stmt, query))
730     return UDM_ERROR;
731 
732   if (UdmIBExecInternal(db, res, &stmt, 1))
733     return UDM_ERROR;
734 
735   return rc;
736 }
737 
738 
739 static udm_rc_t
UdmIBaseQuery(UDM_SQL * db,UDM_SQLRES * res,const char * query)740 UdmIBaseQuery(UDM_SQL *db, UDM_SQLRES *res, const char *query)
741 {
742   udm_rc_t rc;
743   UdmSetErrorCode(db, 0);
744 
745   if (res)
746   {
747     bzero((void*) res, sizeof(UDM_SQLRES));
748     res->db= db;
749   }
750 
751   if(UDM_OK != (rc= sql_ibase_query(db,res,query)))
752   {
753     if (is_duplicate_error(db))
754     {
755       UdmSetErrorCode(db, 0);
756       rc= UDM_OK;
757     }
758     else
759     {
760       /*strcat(db->errstr," ");
761       strcat(db->errstr,query);*/
762     }
763     return rc;
764   }
765   return rc;
766 }
767 
768 
769 static udm_rc_t
UdmIBBegin(UDM_SQL * db)770 UdmIBBegin(UDM_SQL *db)
771 {
772   return UdmIBaseQuery(db,NULL,"BEGIN");
773 }
774 
775 
776 static udm_rc_t
UdmIBCommit(UDM_SQL * db)777 UdmIBCommit(UDM_SQL *db)
778 {
779   return UdmIBaseQuery(db,NULL,"COMMIT");
780 }
781 
782 
783 static int
UdmSQLType2IBType(udm_sqltype_t udm_type)784 UdmSQLType2IBType(udm_sqltype_t udm_type)
785 {
786   switch (udm_type)
787   {
788     case UDM_SQLTYPE_INT32:         return SQL_LONG;
789     case UDM_SQLTYPE_LONGVARBINARY: return SQL_BLOB;
790     case UDM_SQLTYPE_LONGVARCHAR:   return SQL_TEXT;
791     case UDM_SQLTYPE_VARCHAR:       return SQL_TEXT;
792     case UDM_SQLTYPE_UNKNOWN:       break;
793   }
794   return SQL_TEXT;
795 }
796 
797 
798 static int short flag0= 0;
799 
800 
801 static udm_rc_t
UdmIBPrepare(UDM_SQL * db,const char * query)802 UdmIBPrepare(UDM_SQL *db, const char *query)
803 {
804   UDM_IB *ib= UdmIB(db);
805   return UdmIBPrepareInternal(db, &ib->stmt, query);
806 }
807 
808 static udm_rc_t
UdmIBBind(UDM_SQL * db,int position,const void * data,int size,udm_sqltype_t type)809 UdmIBBind(UDM_SQL *db, int position, const void *data, int size, udm_sqltype_t type)
810 {
811   UDM_IB *ib= UdmIB(db);
812   UDM_IB_STMT *stmt= &ib->stmt;
813   XSQLVAR *var= &stmt->in_sqlda.sqlvar[position - 1];
814   if (stmt->in_sqlda.sqln < position)
815   {
816     stmt->in_sqlda.sqln= position;
817     stmt->in_sqlda.sqld= position;
818   }
819 #ifdef HAVE_GCC_PRAGMA_PUSH
820 #pragma GCC diagnostic push
821 #pragma GCC diagnostic ignored "-Wcast-qual"
822 #endif
823   var->sqldata= (char*) data;
824 #ifdef HAVE_GCC_PRAGMA_PUSH
825 #pragma GCC diagnostic pop
826 #endif
827   var->sqllen= size;
828   var->sqltype= UdmSQLType2IBType(type) + 1;
829   var->sqlind= &flag0;
830   stmt->long_data_lengths[position - 1]= (size_t) size;
831   return UDM_OK;
832 }
833 
834 
835 static udm_rc_t
UdmIBStmtFree(UDM_SQL * db)836 UdmIBStmtFree(UDM_SQL *db)
837 {
838   UDM_IB *ib= UdmIB(db);
839   return UdmIBStmtFreeInternal(db, &ib->stmt);
840 }
841 
842 
843 const UDM_SQLDB_HANDLER udm_sqldb_ibase_handler =
844 {
845   UdmSQLEscStrGeneric,
846   UdmIBaseQuery,
847   UdmIBaseConnect,
848   UdmIBaseClose,
849   UdmIBBegin,
850   UdmIBCommit,
851   UdmIBPrepare,
852   UdmIBBind,
853   UdmIBExec,
854   UdmIBStmtFree,
855   UdmSQLFetchRowSimple,
856   UdmSQLStoreResultSimple,
857   UdmSQLFreeResultSimple,
858   UdmIBaseQuery,
859   NULL, /* RenameTable    */
860   NULL, /* CopyStrucuture */
861   UdmSQLLockOrBeginGeneric,
862   UdmSQLUnlockOrCommitGeneric,
863 };
864 
865 #endif
866