1/*
2 *  hdbc.c
3 *
4 *  $Id: hdbc.c 2613 1999-06-01 15:32:12Z VZ $
5 *
6 *  Data source connect object management functions
7 *
8 *  The iODBC driver manager.
9 *
10 *  Copyright (C) 1995 by Ke Jin <kejin@empress.com>
11 *
12 *  This library is free software; you can redistribute it and/or
13 *  modify it under the terms of the GNU Library General Public
14 *  License as published by the Free Software Foundation; either
15 *  version 2 of the License, or (at your option) any later version.
16 *
17 *  This library is distributed in the hope that it will be useful,
18 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 *  Library General Public License for more details.
21 *
22 *  You should have received a copy of the GNU Library General Public
23 *  License along with this library; if not, write to the Free
24 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27#include	"config.h"
28
29#include	"isql.h"
30#include	"isqlext.h"
31
32#include        "dlproc.h"
33
34#include	"herr.h"
35#include	"henv.h"
36#include	"hdbc.h"
37#include	"hstmt.h"
38
39#include	"itrace.h"
40#include	"stdio.h"
41
42extern	RETCODE	_iodbcdm_driverunload();
43
44
45RETCODE SQL_API
46SQLAllocConnect (
47    HENV henv,
48    HDBC FAR * phdbc)
49{
50  GENV_t FAR *genv = (GENV_t FAR *) henv;
51  DBC_t FAR *pdbc;
52
53#if (ODBCVER >= 0x0300)
54  if (henv == SQL_NULL_HENV || genv->type != SQL_HANDLE_ENV)
55#else
56  if (henv == SQL_NULL_HENV)
57#endif
58
59    {
60      return SQL_INVALID_HANDLE;
61    }
62
63  if (phdbc == NULL)
64    {
65      PUSHSQLERR (genv->herr, en_S1009);
66
67      return SQL_ERROR;
68    }
69
70  pdbc = (DBC_t FAR *) MEM_ALLOC (sizeof (DBC_t));
71
72  if (pdbc == NULL)
73    {
74      *phdbc = SQL_NULL_HDBC;
75
76      PUSHSQLERR (genv->herr, en_S1001);
77
78      return SQL_ERROR;
79    }
80
81#if (ODBCVER >= 0x0300)
82  pdbc->type = SQL_HANDLE_DBC;
83#endif
84
85  /* insert this dbc entry into the link list */
86  pdbc->next = genv->hdbc;
87  genv->hdbc = pdbc;
88  pdbc->genv = henv;
89
90  pdbc->henv = SQL_NULL_HENV;
91  pdbc->hstmt = SQL_NULL_HSTMT;
92  pdbc->herr = SQL_NULL_HERR;
93  pdbc->dhdbc = SQL_NULL_HDBC;
94  pdbc->state = en_dbc_allocated;
95  pdbc->trace = 0;
96  pdbc->tstm = NULL;
97  pdbc->tfile = NULL;
98
99  /* set connect options to default values */
100  pdbc->access_mode = SQL_MODE_DEFAULT;
101  pdbc->autocommit = SQL_AUTOCOMMIT_DEFAULT;
102  pdbc->current_qualifier = NULL;
103  pdbc->login_timeout = 0UL;
104  pdbc->odbc_cursors = SQL_CUR_DEFAULT;
105  pdbc->packet_size = 0UL;
106  pdbc->quiet_mode = (UDWORD) NULL;
107  pdbc->txn_isolation = SQL_TXN_READ_UNCOMMITTED;
108  pdbc->cb_commit = (SWORD) SQL_CB_DELETE;
109  pdbc->cb_rollback = (SWORD) SQL_CB_DELETE;
110
111  *phdbc = (HDBC) pdbc;
112
113  return SQL_SUCCESS;
114}
115
116
117RETCODE SQL_API
118SQLFreeConnect (HDBC hdbc)
119{
120  GENV_t FAR *genv;
121  DBC_t FAR *pdbc = (DBC_t FAR *) hdbc;
122  DBC_t FAR *tpdbc;
123
124  if (hdbc == SQL_NULL_HDBC)
125    {
126      return SQL_INVALID_HANDLE;
127    }
128
129  /* check state */
130  if (pdbc->state != en_dbc_allocated)
131    {
132      PUSHSQLERR (pdbc->herr, en_S1010);
133
134      return SQL_ERROR;
135    }
136
137  genv = (GENV_t FAR *) pdbc->genv;
138
139  for (tpdbc = (DBC_t FAR *) genv->hdbc;
140      tpdbc != NULL;
141      tpdbc = tpdbc->next)
142    {
143      if (pdbc == tpdbc)
144	{
145	  genv->hdbc = pdbc->next;
146	  break;
147	}
148
149      if (pdbc == tpdbc->next)
150	{
151	  tpdbc->next = pdbc->next;
152	  break;
153	}
154    }
155
156  /* free this dbc */
157  _iodbcdm_driverunload (pdbc);
158  _iodbcdm_freesqlerrlist (pdbc->herr);
159
160  if (pdbc->tfile)
161    {
162      MEM_FREE (pdbc->tfile);
163    }
164
165  SQLSetConnectOption (pdbc, SQL_OPT_TRACE, SQL_OPT_TRACE_OFF);
166
167  MEM_FREE (pdbc);
168
169  return SQL_SUCCESS;
170}
171
172
173RETCODE SQL_API
174SQLSetConnectOption (
175    HDBC hdbc,
176    UWORD fOption,
177    UDWORD vParam)
178{
179  DBC_t FAR *pdbc = (DBC_t FAR *) hdbc;
180  STMT_t FAR *pstmt;
181  HPROC hproc = SQL_NULL_HPROC;
182  int sqlstat = en_00000;
183  RETCODE retcode = SQL_SUCCESS;
184
185  if (hdbc == SQL_NULL_HDBC)
186    {
187      return SQL_INVALID_HANDLE;
188    }
189
190  /* check option */
191  if (fOption < SQL_CONN_OPT_MIN ||
192	(fOption > SQL_CONN_OPT_MAX && fOption < SQL_CONNECT_OPT_DRVR_START))
193    {
194      PUSHSQLERR (pdbc->herr, en_S1092);
195
196      return SQL_ERROR;
197    }
198
199  /* check state of connection handle */
200  switch (pdbc->state)
201     {
202     case en_dbc_allocated:
203       if (fOption == SQL_TRANSLATE_DLL || fOption == SQL_TRANSLATE_OPTION)
204	 {
205	   /* This two options are only meaningful
206	    * for specified driver. So, has to be
207	    * set after a dirver has been loaded.
208	    */
209	   sqlstat = en_08003;
210	   break;
211	 }
212
213       if (fOption >= SQL_CONNECT_OPT_DRVR_START && pdbc->henv == SQL_NULL_HENV)
214	 /* An option only meaningful for drivers
215	  * is passed before loading a driver.
216	  * We classify this as an invalid option error.
217	  * This is not documented by MS SDK guide.
218	  */
219	 {
220	   sqlstat = en_S1092;
221	   break;
222	 }
223       break;
224
225     case en_dbc_needdata:
226       sqlstat = en_S1010;
227       break;
228
229     case en_dbc_connected:
230     case en_dbc_hstmt:
231       if (fOption == SQL_ODBC_CURSORS)
232	 {
233	   sqlstat = en_08002;
234	 }
235       break;
236
237     default:
238       break;
239     }
240
241  /* check state of statement handle(s) */
242  for (pstmt = (STMT_t FAR *) pdbc->hstmt;
243      pstmt != NULL && sqlstat == en_00000;
244      pstmt = (STMT_t FAR *) pstmt->next)
245    {
246      if (pstmt->state >= en_stmt_needdata || pstmt->asyn_on != en_NullProc)
247	{
248	  sqlstat = en_S1010;
249	}
250    }
251
252  if (sqlstat != en_00000)
253    {
254      PUSHSQLERR (pdbc->herr, sqlstat);
255
256      return SQL_ERROR;
257    }
258
259  if (fOption == SQL_OPT_TRACE)
260    /* tracing flag can be set before and after connect
261     * and only meaningful for driver manager(actually
262     * there is only one tracing file under one global
263     * environment).
264     */
265    {
266      switch (vParam)
267	 {
268	 case SQL_OPT_TRACE_ON:
269	   if (pdbc->tfile == NULL)
270	     {
271	       pdbc->tfile = (char FAR *) MEM_ALLOC (1 +
272		   STRLEN (SQL_OPT_TRACE_FILE_DEFAULT));
273
274	       if (pdbc->tfile == NULL)
275		 {
276		   PUSHSQLERR (pdbc->herr, en_S1001);
277
278		   return SQL_ERROR;
279		 }
280
281	       STRCPY (pdbc->tfile, SQL_OPT_TRACE_FILE_DEFAULT);
282	     }
283
284	   if (pdbc->tstm == NULL)
285	     {
286
287#if	defined(stderr) && defined(stdout)
288	       if (STREQ (pdbc->tfile, "stderr"))
289		 {
290		   pdbc->tstm = stderr;
291		 }
292	       else if (STREQ (pdbc->tfile, "stdout"))
293		 {
294		   pdbc->tstm = stdout;
295		 }
296	       else
297#endif
298
299		 {
300		   pdbc->tstm
301		       = fopen (pdbc->tfile, "a+");
302		 }
303
304	       if (pdbc->tstm)
305		 {
306		   pdbc->trace = 1;
307		 }
308	       else
309		 {
310		   pdbc->trace = 0;
311
312		   sqlstat = en_IM013;
313		   retcode = SQL_ERROR;
314		 }
315	     }
316	   break;
317
318	 case SQL_OPT_TRACE_OFF:
319	   if (pdbc->trace && pdbc->tstm)
320	     {
321
322#if	defined(stderr) && defined(stdout)
323	       if (stderr != (FILE FAR *) (pdbc->tstm)
324		   && stdout != (FILE FAR *) (pdbc->tstm))
325#endif
326
327		 {
328		   fclose (pdbc->tstm);
329		 }
330	     }
331	   pdbc->tstm = NULL;
332	   pdbc->trace = 0;
333	   break;
334
335	 default:
336	   PUSHSQLERR (pdbc->herr, en_S1009);
337	   retcode = SQL_ERROR;
338	 }
339
340      if (sqlstat != en_00000)
341	{
342	  PUSHSQLERR (pdbc->herr, sqlstat);
343	}
344
345      return retcode;
346    }
347
348  if (fOption == SQL_OPT_TRACEFILE)
349    /* Tracing file can be set before and after connect
350     * and only meaningful for driver manager.
351     */
352    {
353      if (vParam == 0UL || ((char FAR *) vParam)[0] == 0)
354	{
355	  PUSHSQLERR (pdbc->herr, en_S1009);
356
357	  return SQL_ERROR;
358	}
359
360      if (pdbc->tfile && STREQ (pdbc->tfile, vParam))
361	{
362	  return SQL_SUCCESS;
363	}
364
365      if (pdbc->trace)
366	{
367	  PUSHSQLERR (pdbc->herr, en_IM014);
368
369	  return SQL_ERROR;
370	}
371
372      if (pdbc->tfile)
373	{
374	  MEM_FREE (pdbc->tfile);
375	}
376
377      pdbc->tfile = (char FAR *) MEM_ALLOC (1 + STRLEN (vParam));
378
379      if (pdbc->tfile == NULL)
380	{
381	  PUSHSQLERR (pdbc->herr, en_S1001);
382
383	  return SQL_ERROR;
384	}
385
386      STRCPY (pdbc->tfile, vParam);
387
388      return SQL_SUCCESS;
389    }
390
391  if (pdbc->state != en_dbc_allocated)
392    {
393      /* If already connected, then, driver's odbc call
394       * will be invoked. Otherwise, we only save the options
395       * and delay the setting process until the connection
396       * been established.
397       */
398      hproc = _iodbcdm_getproc (hdbc, en_SetConnectOption);
399
400      if (hproc == SQL_NULL_HPROC)
401	{
402	  PUSHSQLERR (pdbc->herr, en_IM001);
403
404	  return SQL_ERROR;
405	}
406
407      CALL_DRIVER (hdbc, retcode, hproc, en_SetConnectOption,
408        (pdbc->dhdbc, fOption, vParam))
409
410      if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
411	{
412	  return retcode;
413	}
414    }
415
416  /*
417   * Now, either driver's odbc call was successed or
418   * driver has not been loaded yet. In the first case, we
419   * need flip flag for(such as access_mode, autocommit, ...)
420   * for our finit state machine. While in the second case,
421   * we need save option values(such as current_qualifier, ...)
422   * for delaied setting. So, ...
423   */
424
425  /* No matter what state we are(i.e. allocated or connected, ..)
426   * we need to flip the flag.
427   */
428  switch (fOption)
429     {
430     case SQL_ACCESS_MODE:
431       pdbc->access_mode = vParam;
432       break;
433
434     case SQL_AUTOCOMMIT:
435       pdbc->autocommit = vParam;
436       break;
437     }
438
439  /* state transition */
440  if (pdbc->state != en_dbc_allocated)
441    {
442      return retcode;
443    }
444
445  /* Only 'allocated' state is possible here, and we need to
446   * save the options for delaied setting.
447   */
448  switch (fOption)
449     {
450     case SQL_CURRENT_QUALIFIER:
451       if (pdbc->current_qualifier != NULL)
452	 {
453	   MEM_FREE (pdbc->current_qualifier);
454	 }
455
456       if (vParam == 0UL)
457	 {
458	   pdbc->current_qualifier = NULL;
459
460	   break;
461	 }
462
463       pdbc->current_qualifier
464	   = (char FAR *) MEM_ALLOC (
465	   STRLEN (vParam) + 1);
466
467       if (pdbc->current_qualifier == NULL)
468	 {
469	   PUSHSQLERR (pdbc->herr, en_S1001);
470	   return SQL_ERROR;
471	 }
472
473       STRCPY (pdbc->current_qualifier, vParam);
474       break;
475
476     case SQL_LOGIN_TIMEOUT:
477       pdbc->login_timeout = vParam;
478       break;
479
480     case SQL_ODBC_CURSORS:
481       pdbc->odbc_cursors = vParam;
482       break;
483
484     case SQL_PACKET_SIZE:
485       pdbc->packet_size = vParam;
486       break;
487
488     case SQL_QUIET_MODE:
489       pdbc->quiet_mode = vParam;
490       break;
491
492     case SQL_TXN_ISOLATION:
493       pdbc->txn_isolation = vParam;
494       break;
495
496     default:
497       /* Since we didn't save the option value for delaied
498        * setting, we should raise an error here.
499        */
500       break;
501     }
502
503  return retcode;
504}
505
506
507RETCODE SQL_API
508SQLGetConnectOption (
509    HDBC hdbc,
510    UWORD fOption,
511    PTR pvParam)
512{
513  DBC_t FAR *pdbc = (DBC_t FAR *) hdbc;
514  int sqlstat = en_00000;
515  HPROC hproc = SQL_NULL_HPROC;
516  RETCODE retcode;
517
518  if (hdbc == SQL_NULL_HDBC)
519    {
520      return SQL_INVALID_HANDLE;
521    }
522
523  /* check option */
524  if (fOption < SQL_CONN_OPT_MIN ||
525	(fOption > SQL_CONN_OPT_MAX && fOption < SQL_CONNECT_OPT_DRVR_START))
526    {
527      PUSHSQLERR (pdbc->herr, en_S1092);
528
529      return SQL_ERROR;
530    }
531
532  /* check state */
533  switch (pdbc->state)
534     {
535     case en_dbc_allocated:
536       if (fOption != SQL_ACCESS_MODE
537	   && fOption != SQL_AUTOCOMMIT
538	   && fOption != SQL_LOGIN_TIMEOUT
539	   && fOption != SQL_OPT_TRACE
540	   && fOption != SQL_OPT_TRACEFILE)
541	 {
542	   sqlstat = en_08003;
543	 }
544       /* MS ODBC SDK document only
545        * allows SQL_ACCESS_MODE
546        * and SQL_AUTOCOMMIT in this
547        * dbc state. We allow another
548        * two options, because they
549        * are only meaningful for driver
550        * manager.
551        */
552       break;
553
554     case en_dbc_needdata:
555       sqlstat = en_S1010;
556       break;
557
558     default:
559       break;
560     }
561
562  if (sqlstat != en_00000)
563    {
564      PUSHSQLERR (pdbc->herr, sqlstat);
565
566      return SQL_ERROR;
567    }
568
569  /* Tracing and tracing file options are only
570   * meaningful for driver manager
571   */
572  if (fOption == SQL_OPT_TRACE)
573    {
574      if (pdbc->trace)
575	*((UDWORD *) pvParam) = (UDWORD) SQL_OPT_TRACE_ON;
576      else
577	*((UDWORD *) pvParam) = (UDWORD) SQL_OPT_TRACE_OFF;
578
579      return SQL_SUCCESS;
580    }
581
582  if (fOption == SQL_OPT_TRACEFILE)
583    {
584      STRCPY (pvParam, pdbc->tfile);
585
586      return SQL_ERROR;
587    }
588
589  if (pdbc->state != en_dbc_allocated)
590    /* if already connected, we will invoke driver's function */
591    {
592      hproc = _iodbcdm_getproc (hdbc, en_GetConnectOption);
593
594      if (hproc == SQL_NULL_HPROC)
595	{
596	  PUSHSQLERR (pdbc->herr, en_IM001);
597
598	  return SQL_ERROR;
599	}
600
601      CALL_DRIVER (hdbc, retcode, hproc, en_GetConnectOption,
602	(pdbc->dhdbc, fOption, pvParam))
603
604      return retcode;
605    }
606
607  /* We needn't to handle options which are not allowed
608   * to be *get* at a allocated dbc state(and two tracing
609   * options which has been handled and returned). Thus,
610   * there are only two possible cases.
611   */
612  switch (fOption)
613     {
614     case SQL_ACCESS_MODE:
615       *((UDWORD *) pvParam) = pdbc->access_mode;
616       break;
617
618     case SQL_AUTOCOMMIT:
619       *((UDWORD *) pvParam) = pdbc->autocommit;
620       break;
621
622     case SQL_LOGIN_TIMEOUT:
623       *((UDWORD *) pvParam) = pdbc->login_timeout;
624       break;
625
626     default:
627       break;
628     }
629
630  return SQL_SUCCESS;
631}
632
633
634static RETCODE
635_iodbcdm_transact (
636    HDBC hdbc,
637    UWORD fType)
638{
639  DBC_t FAR *pdbc = (DBC_t FAR *) hdbc;
640  STMT_t FAR *pstmt;
641  HPROC hproc;
642  RETCODE retcode;
643
644  /* check state */
645  switch (pdbc->state)
646     {
647     case en_dbc_allocated:
648     case en_dbc_needdata:
649       PUSHSQLERR (pdbc->herr, en_08003);
650       return SQL_ERROR;
651
652     case en_dbc_connected:
653       return SQL_SUCCESS;
654
655     case en_dbc_hstmt:
656     default:
657       break;
658     }
659
660  for (pstmt = (STMT_t FAR *) (pdbc->hstmt);
661      pstmt != NULL;
662      pstmt = pstmt->next)
663    {
664      if (pstmt->state >= en_stmt_needdata
665	  || pstmt->asyn_on != en_NullProc)
666	{
667	  PUSHSQLERR (pdbc->herr, en_S1010);
668
669	  return SQL_ERROR;
670	}
671    }
672
673  hproc = _iodbcdm_getproc (hdbc, en_Transact);
674
675  if (hproc == SQL_NULL_HPROC)
676    {
677      PUSHSQLERR (pdbc->herr, en_IM001);
678
679      return SQL_ERROR;
680    }
681
682  CALL_DRIVER (hdbc, retcode, hproc, en_Transact,
683    (SQL_NULL_HENV, pdbc->dhdbc, fType))
684
685  /* state transition */
686  if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
687    {
688      return retcode;
689    }
690
691  pdbc->state = en_dbc_hstmt;
692
693  for (pstmt = (STMT_t FAR *) (pdbc->hstmt);
694      pstmt != NULL;
695      pstmt = pstmt->next)
696    {
697      switch (pstmt->state)
698	 {
699	 case en_stmt_prepared:
700	   if (pdbc->cb_commit == SQL_CB_DELETE
701	       || pdbc->cb_rollback == SQL_CB_DELETE)
702	     {
703	       pstmt->state = en_stmt_allocated;
704	       pstmt->prep_state = 0;
705	       break;
706	     }
707	   break;
708
709	 case en_stmt_executed:
710	 case en_stmt_cursoropen:
711	 case en_stmt_fetched:
712	 case en_stmt_xfetched:
713	   if (!pstmt->prep_state
714	       && pdbc->cb_commit != SQL_CB_PRESERVE
715	       && pdbc->cb_rollback != SQL_CB_PRESERVE)
716	     {
717	       pstmt->state = en_stmt_allocated;
718	       pstmt->prep_state = 0;
719	       pstmt->cursor_state = en_stmt_cursor_no;
720	       break;
721	     }
722
723	   if (pstmt->prep_state)
724	     {
725	       if (pdbc->cb_commit == SQL_CB_DELETE
726		   || pdbc->cb_rollback == SQL_CB_DELETE)
727		 {
728		   pstmt->state = en_stmt_allocated;
729		   pstmt->prep_state = 0;
730		   pstmt->cursor_state = en_stmt_cursor_no;
731		   break;
732		 }
733
734	       if (pdbc->cb_commit == SQL_CB_CLOSE
735		   || pdbc->cb_rollback == SQL_CB_CLOSE)
736		 {
737		   pstmt->state
738		       = en_stmt_prepared;
739		   pstmt->cursor_state
740		       = en_stmt_cursor_no;
741		   break;
742		 }
743	       break;
744	     }
745	   break;
746
747	 default:
748	   break;
749	 }
750    }
751
752  return retcode;
753}
754
755
756RETCODE SQL_API
757SQLTransact (
758    HENV henv,
759    HDBC hdbc,
760    UWORD fType)
761{
762  GENV_t FAR *genv = (GENV_t FAR *) henv;
763  DBC_t FAR *pdbc = (DBC_t FAR *) hdbc;
764  HERR herr;
765  RETCODE retcode = 0;
766
767  if (hdbc != SQL_NULL_HDBC)
768    {
769      herr = pdbc->herr;
770    }
771  else if (henv != SQL_NULL_HENV)
772    {
773      herr = genv->herr;
774    }
775  else
776    {
777      return SQL_INVALID_HANDLE;
778    }
779
780  /* check argument */
781  if (fType != SQL_COMMIT
782      && fType != SQL_ROLLBACK)
783    {
784      PUSHSQLERR (herr, en_S1012);
785
786      return SQL_ERROR;
787    }
788
789  if (hdbc != SQL_NULL_HDBC)
790    {
791      retcode = _iodbcdm_transact (hdbc, fType);
792    }
793  else
794    {
795      for (pdbc = (DBC_t FAR *) (genv->hdbc);
796	  pdbc != NULL;
797	  pdbc = pdbc->next)
798	{
799	  retcode |= _iodbcdm_transact (hdbc, fType);
800	}
801    }
802
803  if (retcode != SQL_SUCCESS
804      && retcode != SQL_SUCCESS_WITH_INFO)
805    {
806      /* fail on one of the connection */
807      return SQL_ERROR;
808    }
809
810  return retcode;
811}
812