1 /***********************************************************************/
2 /* Name: ODBCONN.CPP Version 2.4 */
3 /* */
4 /* (C) Copyright to the author Olivier BERTRAND 1998-2021 */
5 /* */
6 /* This file contains the ODBC connection classes functions. */
7 /***********************************************************************/
8
9 /***********************************************************************/
10 /* Include relevant MariaDB header file. */
11 /***********************************************************************/
12 #include <my_global.h>
13 #include <m_string.h>
14 #if defined(_WIN32)
15 //nclude <io.h>
16 //nclude <fcntl.h>
17 #include <direct.h> // for getcwd
18 #if defined(__BORLANDC__)
19 #define __MFC_COMPAT__ // To define min/max as macro
20 #endif
21 //#include <windows.h>
22 #else
23 #if defined(UNIX)
24 #include <errno.h>
25 #else
26 //nclude <io.h>
27 #endif
28 //nclude <fcntl.h>
29 #define NODW
30 #endif
31
32 /***********************************************************************/
33 /* Required objects includes. */
34 /***********************************************************************/
35 #include "global.h"
36 #include "plgdbsem.h"
37 #include "xobject.h"
38 #include "xtable.h"
39 #include "tabext.h"
40 #include "odbccat.h"
41 #include "tabodbc.h"
42 #include "plgcnx.h" // For DB types
43 #include "resource.h"
44 #include "valblk.h"
45 #include "osutil.h"
46
47
48 #if defined(_WIN32)
49 /***********************************************************************/
50 /* For dynamic load of ODBC32.DLL */
51 /***********************************************************************/
52 #pragma comment(lib, "odbc32.lib")
53 extern "C" HINSTANCE s_hModule; // Saved module handle
54 #endif // _WIN32
55
56 TYPCONV GetTypeConv();
57 int GetConvSize();
58 void OdbcClose(PGLOBAL g, PFBLOCK fp);
59
60 /***********************************************************************/
61 /* Some macro's (should be defined elsewhere to be more accessible) */
62 /***********************************************************************/
63 #if defined(_DEBUG)
64 #define ASSERT(f) assert(f)
65 #define DEBUG_ONLY(f) (f)
66 #else // !_DEBUG
67 #define ASSERT(f) ((void)0)
68 #define DEBUG_ONLY(f) ((void)0)
69 #endif // !_DEBUG
70
71 /***********************************************************************/
72 /* GetSQLType: returns the SQL_TYPE corresponding to a PLG type. */
73 /***********************************************************************/
GetSQLType(int type)74 static short GetSQLType(int type)
75 {
76 short tp = SQL_TYPE_NULL;
77
78 switch (type) {
79 case TYPE_STRING: tp = SQL_CHAR; break;
80 case TYPE_SHORT: tp = SQL_SMALLINT; break;
81 case TYPE_INT: tp = SQL_INTEGER; break;
82 case TYPE_DATE: tp = SQL_TIMESTAMP; break;
83 case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5)
84 case TYPE_DOUBLE: tp = SQL_DOUBLE; break;
85 case TYPE_TINY: tp = SQL_TINYINT; break;
86 case TYPE_DECIM: tp = SQL_DECIMAL; break;
87 } // endswitch type
88
89 return tp;
90 } // end of GetSQLType
91
92 /***********************************************************************/
93 /* GetSQLCType: returns the SQL_C_TYPE corresponding to a PLG type. */
94 /***********************************************************************/
GetSQLCType(int type)95 static int GetSQLCType(int type)
96 {
97 int tp = SQL_TYPE_NULL;
98
99 switch (type) {
100 case TYPE_STRING: tp = SQL_C_CHAR; break;
101 case TYPE_SHORT: tp = SQL_C_SHORT; break;
102 case TYPE_INT: tp = SQL_C_LONG; break;
103 case TYPE_DATE: tp = SQL_C_TIMESTAMP; break;
104 case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
105 case TYPE_DOUBLE: tp = SQL_C_DOUBLE; break;
106 case TYPE_TINY : tp = SQL_C_TINYINT; break;
107 //#if (ODBCVER >= 0x0300)
108 // case TYPE_DECIM: tp = SQL_C_NUMERIC; break; (CRASH!!!)
109 //#else
110 case TYPE_DECIM: tp = SQL_C_CHAR; break;
111 //#endif
112
113 } // endswitch type
114
115 return tp;
116 } // end of GetSQLCType
117
118 /***********************************************************************/
119 /* TranslateSQLType: translate a SQL Type to a PLG type. */
120 /***********************************************************************/
TranslateSQLType(int stp,int prec,int & len,char & v,bool & w)121 int TranslateSQLType(int stp, int prec, int& len, char& v, bool& w)
122 {
123 int type;
124
125 switch (stp) {
126 case SQL_WVARCHAR: // (-9)
127 w = true;
128 case SQL_VARCHAR: // 12
129 v = 'V';
130 type = TYPE_STRING;
131 break;
132 case SQL_WCHAR: // (-8)
133 w = true;
134 case SQL_CHAR: // 1
135 type = TYPE_STRING;
136 break;
137 case SQL_WLONGVARCHAR: // (-10)
138 w = true;
139 case SQL_LONGVARCHAR: // (-1)
140 if (GetTypeConv() == TPC_YES || GetTypeConv() == TPC_FORCE) {
141 v = 'V';
142 type = TYPE_STRING;
143 len = (len) ? MY_MIN(abs(len), GetConvSize()) : GetConvSize();
144 } else
145 type = TYPE_ERROR;
146
147 break;
148 case SQL_NUMERIC: // 2
149 case SQL_DECIMAL: // 3
150 // type = (prec || len > 20) ? TYPE_DOUBLE
151 // : (len > 10) ? TYPE_BIGINT : TYPE_INT;
152 type = TYPE_DECIM;
153 break;
154 case SQL_INTEGER: // 4
155 type = TYPE_INT;
156 break;
157 case SQL_SMALLINT: // 5
158 type = TYPE_SHORT;
159 break;
160 case SQL_TINYINT: // (-6)
161 case SQL_BIT: // (-7)
162 type = TYPE_TINY;
163 break;
164 case SQL_FLOAT: // 6
165 case SQL_REAL: // 7
166 case SQL_DOUBLE: // 8
167 type = TYPE_DOUBLE;
168 break;
169 case SQL_DATETIME: // 9
170 type = TYPE_DATE;
171 len = 19;
172 break;
173 case SQL_TYPE_DATE: // 91
174 type = TYPE_DATE;
175 len = 10;
176 v = 'D';
177 break;
178 case SQL_INTERVAL: // 10
179 case SQL_TYPE_TIME: // 92
180 type = TYPE_STRING;
181 len = 8 + ((prec) ? (prec+1) : 0);
182 v = 'T';
183 break;
184 case SQL_TIMESTAMP: // 11
185 case SQL_TYPE_TIMESTAMP: // 93
186 type = TYPE_DATE;
187 len = 19 + ((prec) ? (prec+1) : 0);
188 v = 'S';
189 break;
190 case SQL_BIGINT: // (-5)
191 type = TYPE_BIGINT;
192 break;
193 case SQL_BINARY: // (-2)
194 case SQL_VARBINARY: // (-3)
195 case SQL_LONGVARBINARY: // (-4)
196 if (GetTypeConv() == TPC_FORCE) {
197 v = 'V';
198 type = TYPE_STRING;
199 len = (len) ? MY_MIN(abs(len), GetConvSize()) : GetConvSize();
200 } else
201 type = TYPE_ERROR;
202
203 break;
204 case SQL_GUID: // (-11)
205 type = TYPE_STRING;
206 len = 36;
207 break;
208 case SQL_UNKNOWN_TYPE: // 0
209 default:
210 type = TYPE_ERROR;
211 len = 0;
212 } // endswitch type
213
214 return type;
215 } // end of TranslateSQLType
216
217 #if defined(PROMPT_OK)
218 /***********************************************************************/
219 /* ODBCCheckConnection: Check completeness of connection string. */
220 /***********************************************************************/
ODBCCheckConnection(PGLOBAL g,char * dsn,int cop)221 char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop)
222 {
223 char *newdsn, dir[_MAX_PATH], buf[_MAX_PATH];
224 int rc;
225 DWORD options = ODBConn::openReadOnly;
226 ODBConn *ocp = new(g) ODBConn(g, NULL);
227
228 (void) getcwd(dir, sizeof(dir) - 1);
229
230 switch (cop) {
231 case 1: options |= ODBConn::forceOdbcDialog; break;
232 case 2: options |= ODBConn::noOdbcDialog; break;
233 } // endswitch cop
234
235 if (ocp->Open(dsn, options) < 1)
236 newdsn = NULL;
237 else
238 newdsn = ocp->GetConnect();
239
240 (void) getcwd(buf, sizeof(buf) - 1);
241
242 // Some data sources change the current directory
243 if (strcmp(dir, buf))
244 rc = chdir(dir);
245
246 ocp->Close();
247 return newdsn; // Return complete connection string
248 } // end of ODBCCheckConnection
249 #endif // PROMPT_OK
250
251 /***********************************************************************/
252 /* Allocate the structure used to refer to the result set. */
253 /***********************************************************************/
AllocCatInfo(PGLOBAL g,CATINFO fid,PCSZ db,PCSZ tab,PQRYRES qrp)254 static CATPARM *AllocCatInfo(PGLOBAL g, CATINFO fid, PCSZ db,
255 PCSZ tab, PQRYRES qrp)
256 {
257 size_t i, m, n;
258 CATPARM *cap;
259
260 #if defined(_DEBUG)
261 assert(qrp);
262 #endif
263
264 try {
265 m = (size_t)qrp->Maxres;
266 n = (size_t)qrp->Nbcol;
267 cap = (CATPARM *)PlugSubAlloc(g, NULL, sizeof(CATPARM));
268 memset(cap, 0, sizeof(CATPARM));
269 cap->Id = fid;
270 cap->Qrp = qrp;
271 cap->DB = db;
272 cap->Tab = tab;
273 cap->Vlen = (SQLLEN* *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN *));
274
275 for (i = 0; i < n; i++)
276 cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SQLLEN));
277
278 cap->Status = (UWORD *)PlugSubAlloc(g, NULL, m * sizeof(UWORD));
279
280 } catch (int n) {
281 htrc("Exeption %d: %s\n", n, g->Message);
282 cap = NULL;
283 } catch (const char *msg) {
284 htrc(g->Message, msg);
285 printf("%s\n", g->Message);
286 cap = NULL;
287 } // end catch
288
289 return cap;
290 } // end of AllocCatInfo
291
292 #if 0
293 /***********************************************************************/
294 /* Check for nulls and reset them to Null (?) values. */
295 /***********************************************************************/
296 static void ResetNullValues(CATPARM *cap)
297 {
298 int i, n, ncol;
299 PCOLRES crp;
300 PQRYRES qrp = cap->Qrp;
301
302 #if defined(_DEBUG)
303 assert(qrp);
304 #endif
305
306 ncol = qrp->Nbcol;
307
308 for (i = 0, crp = qrp->Colresp; i < ncol && crp; i++, crp = crp->Next)
309 for (n = 0; n < qrp->Nblin; n++)
310 if (cap->Vlen[i][n] == SQL_NULL_DATA)
311 crp->Kdata->Reset(n);
312
313 } // end of ResetNullValues
314 #endif
315
316 /***********************************************************************/
317 /* Close an ODBC table after a thrown error (called by PlugCloseFile) */
318 /***********************************************************************/
OdbcClose(PGLOBAL g,PFBLOCK fp)319 void OdbcClose(PGLOBAL g, PFBLOCK fp) {
320 ((ODBConn*)fp->File)->Close();
321 } // end of OdbcClose
322
323 /***********************************************************************/
324 /* ODBCColumns: constructs the result blocks containing all columns */
325 /* of an ODBC table that will be retrieved by GetData commands. */
326 /***********************************************************************/
ODBCColumns(PGLOBAL g,PCSZ dsn,PCSZ db,PCSZ table,PCSZ colpat,int maxres,bool info,POPARM sop)327 PQRYRES ODBCColumns(PGLOBAL g, PCSZ dsn, PCSZ db, PCSZ table,
328 PCSZ colpat, int maxres, bool info, POPARM sop)
329 {
330 int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING,
331 TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT,
332 TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
333 XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_TABNAME, FLD_NAME,
334 FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH,
335 FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM};
336 unsigned int length[] = {0, 0, 0, 0, 6, 0, 10, 10, 6, 6, 6, 0};
337 bool b[] = {true,true,false,false,false,false,false,false,true,true,false,true};
338 int i, n, ncol = 12;
339 PCOLRES crp;
340 PQRYRES qrp;
341 CATPARM *cap;
342 ODBConn *ocp = NULL;
343
344 /************************************************************************/
345 /* Do an evaluation of the result size. */
346 /************************************************************************/
347 if (!info) {
348 ocp = new(g) ODBConn(g, NULL);
349
350 if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noODBCdialog
351 return NULL;
352
353 if (table && !strchr(table, '%')) {
354 // We fix a MySQL limit because some data sources return 32767
355 n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
356 maxres = (n) ? MY_MIN(n, 4096) : 4096;
357 } else if (!maxres)
358 maxres = 20000;
359
360 // n = ocp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
361 // length[0] = (n) ? (n + 1) : 0;
362 // n = ocp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
363 // length[1] = (n) ? (n + 1) : 0;
364 // n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
365 // length[2] = (n) ? (n + 1) : 0;
366 n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
367 length[3] = (n) ? (n + 1) : 128;
368 } else { // Info table
369 maxres = 0;
370 length[0] = 128;
371 length[1] = 128;
372 length[2] = 128;
373 length[3] = 128;
374 length[5] = 30;
375 length[11] = 255;
376 } // endif ocp
377
378 if (trace(1))
379 htrc("ODBCColumns: max=%d len=%d,%d,%d,%d\n",
380 maxres, length[0], length[1], length[2], length[3]);
381
382 /************************************************************************/
383 /* Allocate the structures used to refer to the result set. */
384 /************************************************************************/
385 qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS,
386 buftyp, fldtyp, length, false, true);
387
388 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
389 if (b[i])
390 crp->Kdata->SetNullable(true);
391
392 if (info || !qrp) // Info table
393 return qrp;
394
395 if (trace(1))
396 htrc("Getting col results ncol=%d\n", qrp->Nbcol);
397
398 if (!(cap = AllocCatInfo(g, CAT_COL, db, table, qrp)))
399 return NULL;
400
401 cap->Pat = colpat;
402
403 /************************************************************************/
404 /* Now get the results into blocks. */
405 /************************************************************************/
406 if ((n = ocp->GetCatInfo(cap)) >= 0) {
407 qrp->Nblin = n;
408 // ResetNullValues(cap);
409
410 if (trace(1))
411 htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
412
413 } else
414 qrp = NULL;
415
416 /* Cleanup */
417 ocp->Close();
418
419 /************************************************************************/
420 /* Return the result pointer for use by GetData routines. */
421 /************************************************************************/
422 return qrp;
423 } // end of ODBCColumns
424
425 /**************************************************************************/
426 /* ODBCSrcCols: constructs the result blocks containing the */
427 /* description of all the columns of a Srcdef option. */
428 /**************************************************************************/
ODBCSrcCols(PGLOBAL g,char * dsn,char * src,POPARM sop)429 PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, POPARM sop)
430 {
431 char *sqry;
432 ODBConn *ocp = new(g) ODBConn(g, NULL);
433
434 if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noOdbcDialog
435 return NULL;
436
437 if (strstr(src, "%s")) {
438 // Place holder for an eventual where clause
439 sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 3);
440 sprintf(sqry, src, "1=1", "1=1"); // dummy where clause
441 } else
442 sqry = src;
443
444 return ocp->GetMetaData(g, dsn, sqry);
445 } // end of ODBCSrcCols
446
447 #if 0
448 /**************************************************************************/
449 /* MyODBCCols: returns column info as required by ha_connect::pre_create. */
450 /**************************************************************************/
451 PQRYRES MyODBCCols(PGLOBAL g, char *dsn, char *tab, bool info)
452 {
453 // int i, type, len, prec;
454 bool w = false;
455 // PCOLRES crp, crpt, crpl, crpp;
456 PQRYRES qrp;
457 ODBConn *ocp;
458
459 /**********************************************************************/
460 /* Open the connection with the ODBC data source. */
461 /**********************************************************************/
462 if (!info) {
463 ocp = new(g) ODBConn(g, NULL);
464
465 if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
466 return NULL;
467
468 } else
469 ocp = NULL;
470
471 /**********************************************************************/
472 /* Get the information about the ODBC table columns. */
473 /**********************************************************************/
474 if ((qrp = ODBCColumns(g, ocp, dsn, NULL, tab, 0, NULL)) && ocp)
475 dsn = ocp->GetConnect(); // Complete connect string
476
477 /************************************************************************/
478 /* Close the local connection. */
479 /************************************************************************/
480 if (ocp)
481 ocp->Close();
482
483 if (!qrp)
484 return NULL; // Error in ODBCColumns
485
486 /************************************************************************/
487 /* Keep only the info used by ha_connect::pre_create. */
488 /************************************************************************/
489 qrp->Colresp = qrp->Colresp->Next->Next; // Skip Schema and Table names
490
491 crpt = qrp->Colresp->Next; // SQL type
492 crpl = crpt->Next->Next; // Length
493 crpp = crpl->Next->Next; // Decimals
494
495 for (int i = 0; i < qrp->Nblin; i++) {
496 // Types must be PLG types, not SQL types
497 type = crpt->Kdata->GetIntValue(i);
498 len = crpl->Kdata->GetIntValue(i);
499 prec = crpp->Kdata->GetIntValue(i);
500 type = TranslateSQLType(type, prec, len, w);
501 crpt->Kdata->SetValue(type, i);
502
503 // Some data sources do not count prec in length
504 if (type == TYPE_DOUBLE)
505 len += (prec + 2); // To be safe
506
507 // Could have been changed for blobs or numeric
508 crpl->Kdata->SetValue(len, i);
509 } // endfor i
510
511 crpp->Next = crpp->Next->Next->Next; // Should be Remark
512
513 // Renumber crp's for flag comparison
514 for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
515 crp->Ncol = ++i;
516
517 qrp->Nbcol = i; // Should be 7; was 11, skipped 4
518 return qrp;
519 } // end of MyODBCCols
520 #endif // 0
521
522 /*************************************************************************/
523 /* ODBCDrivers: constructs the result blocks containing all ODBC */
524 /* drivers available on the local host. */
525 /* Called with info=true to have result column names. */
526 /*************************************************************************/
ODBCDrivers(PGLOBAL g,int maxres,bool info)527 PQRYRES ODBCDrivers(PGLOBAL g, int maxres, bool info)
528 {
529 int buftyp[] = {TYPE_STRING, TYPE_STRING};
530 XFLD fldtyp[] = {FLD_NAME, FLD_REM};
531 unsigned int length[] = {128, 256};
532 bool b[] = {false, true};
533 int i, ncol = 2;
534 PCOLRES crp;
535 PQRYRES qrp;
536 ODBConn *ocp = NULL;
537
538 /************************************************************************/
539 /* Do an evaluation of the result size. */
540 /************************************************************************/
541 if (!info) {
542 ocp = new(g) ODBConn(g, NULL);
543
544 if (!maxres)
545 maxres = 256; // Estimated max number of drivers
546
547 } else
548 maxres = 0;
549
550 if (trace(1))
551 htrc("ODBCDrivers: max=%d len=%d\n", maxres, length[0]);
552
553 /************************************************************************/
554 /* Allocate the structures used to refer to the result set. */
555 /************************************************************************/
556 qrp = PlgAllocResult(g, ncol, maxres, IDS_DRIVER,
557 buftyp, fldtyp, length, false, true);
558
559 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
560 if (b[i])
561 crp->Kdata->SetNullable(true);
562
563 /************************************************************************/
564 /* Now get the results into blocks. */
565 /************************************************************************/
566 if (!info && qrp && ocp->GetDrivers(qrp))
567 qrp = NULL;
568
569 /************************************************************************/
570 /* Return the result pointer for use by GetData routines. */
571 /************************************************************************/
572 return qrp;
573 } // end of ODBCDrivers
574
575 /*************************************************************************/
576 /* ODBCDataSources: constructs the result blocks containing all ODBC */
577 /* data sources available on the local host. */
578 /* Called with info=true to have result column names. */
579 /*************************************************************************/
ODBCDataSources(PGLOBAL g,int maxres,bool info)580 PQRYRES ODBCDataSources(PGLOBAL g, int maxres, bool info)
581 {
582 int buftyp[] = {TYPE_STRING, TYPE_STRING};
583 XFLD fldtyp[] = {FLD_NAME, FLD_REM};
584 unsigned int length[] = {0, 256};
585 bool b[] = {false, true};
586 int i, n = 0, ncol = 2;
587 PCOLRES crp;
588 PQRYRES qrp;
589 ODBConn *ocp = NULL;
590
591 /************************************************************************/
592 /* Do an evaluation of the result size. */
593 /************************************************************************/
594 if (!info) {
595 ocp = new(g) ODBConn(g, NULL);
596 n = ocp->GetMaxValue(SQL_MAX_DSN_LENGTH);
597 length[0] = (n) ? (n + 1) : 256;
598
599 if (!maxres)
600 maxres = 512; // Estimated max number of data sources
601
602 } else {
603 length[0] = 256;
604 maxres = 0;
605 } // endif info
606
607 if (trace(1))
608 htrc("ODBCDataSources: max=%d len=%d\n", maxres, length[0]);
609
610 /************************************************************************/
611 /* Allocate the structures used to refer to the result set. */
612 /************************************************************************/
613 qrp = PlgAllocResult(g, ncol, maxres, IDS_DSRC,
614 buftyp, fldtyp, length, false, true);
615
616 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
617 if (b[i])
618 crp->Kdata->SetNullable(true);
619
620 /************************************************************************/
621 /* Now get the results into blocks. */
622 /************************************************************************/
623 if (!info && qrp && ocp->GetDataSources(qrp))
624 qrp = NULL;
625
626 /************************************************************************/
627 /* Return the result pointer for use by GetData routines. */
628 /************************************************************************/
629 return qrp;
630 } // end of ODBCDataSources
631
632 /**************************************************************************/
633 /* ODBCTables: constructs the result blocks containing all tables in */
634 /* an ODBC database that will be retrieved by GetData commands. */
635 /**************************************************************************/
ODBCTables(PGLOBAL g,PCSZ dsn,PCSZ db,PCSZ tabpat,PCSZ tabtyp,int maxres,bool info,POPARM sop)636 PQRYRES ODBCTables(PGLOBAL g, PCSZ dsn, PCSZ db, PCSZ tabpat, PCSZ tabtyp,
637 int maxres, bool info, POPARM sop)
638 {
639 int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
640 TYPE_STRING, TYPE_STRING};
641 XFLD fldtyp[] = {FLD_CAT, FLD_SCHEM, FLD_NAME,
642 FLD_TYPE, FLD_REM};
643 unsigned int length[] = {0, 0, 0, 16, 0};
644 bool b[] ={ true, true, false, false, true };
645 int i, n, ncol = 5;
646 PCOLRES crp;
647 PQRYRES qrp;
648 CATPARM *cap;
649 ODBConn *ocp = NULL;
650
651 /************************************************************************/
652 /* Do an evaluation of the result size. */
653 /************************************************************************/
654 if (!info) {
655 /**********************************************************************/
656 /* Open the connection with the ODBC data source. */
657 /**********************************************************************/
658 ocp = new(g) ODBConn(g, NULL);
659
660 if (ocp->Open(dsn, sop, 2) < 1) // 2 is openReadOnly
661 return NULL;
662
663 if (!maxres)
664 maxres = 10000; // This is completely arbitrary
665
666 // n = ocp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
667 // length[0] = (n) ? (n + 1) : 0;
668 // n = ocp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
669 // length[1] = (n) ? (n + 1) : 0;
670 n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
671 length[2] = (n) ? (n + 1) : 128;
672 } else {
673 maxres = 0;
674 length[0] = 128;
675 length[1] = 128;
676 length[2] = 128;
677 length[4] = 255;
678 } // endif info
679
680 if (trace(1))
681 htrc("ODBCTables: max=%d len=%d,%d\n", maxres, length[0], length[1]);
682
683 /************************************************************************/
684 /* Allocate the structures used to refer to the result set. */
685 /************************************************************************/
686 qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES, buftyp,
687 fldtyp, length, false, true);
688
689 for (i = 0, crp = qrp->Colresp; crp; i++, crp = crp->Next)
690 if (b[i])
691 crp->Kdata->SetNullable(true);
692
693 if (info || !qrp)
694 return qrp;
695
696 if (!(cap = AllocCatInfo(g, CAT_TAB, db, tabpat, qrp)))
697 return NULL;
698
699 cap->Pat = tabtyp;
700
701 if (trace(1))
702 htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol);
703
704 /************************************************************************/
705 /* Now get the results into blocks. */
706 /************************************************************************/
707 if ((n = ocp->GetCatInfo(cap)) >= 0) {
708 qrp->Nblin = n;
709 // ResetNullValues(cap);
710
711 if (trace(1))
712 htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
713
714 } else
715 qrp = NULL;
716
717 /************************************************************************/
718 /* Close any local connection. */
719 /************************************************************************/
720 ocp->Close();
721
722 /************************************************************************/
723 /* Return the result pointer for use by GetData routines. */
724 /************************************************************************/
725 return qrp;
726 } // end of ODBCTables
727
728 #if 0 // Currently not used by CONNECT
729 /**************************************************************************/
730 /* PrimaryKeys: constructs the result blocks containing all the */
731 /* ODBC catalog information concerning primary keys. */
732 /**************************************************************************/
733 PQRYRES ODBCPrimaryKeys(PGLOBAL g, ODBConn *op, char *dsn, char *table)
734 {
735 static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
736 TYPE_STRING, TYPE_SHORT, TYPE_STRING};
737 static unsigned int length[] = {0, 0, 0, 0, 6, 128};
738 int n, ncol = 5;
739 int maxres;
740 PQRYRES qrp;
741 CATPARM *cap;
742 ODBConn *ocp = op;
743
744 if (!op) {
745 /**********************************************************************/
746 /* Open the connection with the ODBC data source. */
747 /**********************************************************************/
748 ocp = new(g) ODBConn(g, NULL);
749
750 if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
751 return NULL;
752
753 } // endif op
754
755 /************************************************************************/
756 /* Do an evaluation of the result size. */
757 /************************************************************************/
758 n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE);
759 maxres = (n) ? (int)n : 250;
760 n = ocp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
761 length[0] = (n) ? (n + 1) : 128;
762 n = ocp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
763 length[1] = (n) ? (n + 1) : 128;
764 n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
765 length[2] = (n) ? (n + 1) : 128;
766 n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
767 length[3] = (n) ? (n + 1) : 128;
768
769 if (trace(1))
770 htrc("ODBCPrimaryKeys: max=%d len=%d,%d,%d\n",
771 maxres, length[0], length[1], length[2]);
772
773 /************************************************************************/
774 /* Allocate the structure used to refer to the result set. */
775 /************************************************************************/
776 qrp = PlgAllocResult(g, ncol, maxres, IDS_PKEY,
777 buftyp, NULL, length, false, true);
778
779 if (trace(1))
780 htrc("Getting pkey results ncol=%d\n", qrp->Nbcol);
781
782 cap = AllocCatInfo(g, CAT_KEY, NULL, table, qrp);
783
784 /************************************************************************/
785 /* Now get the results into blocks. */
786 /************************************************************************/
787 if ((n = ocp->GetCatInfo(cap)) >= 0) {
788 qrp->Nblin = n;
789 // ResetNullValues(cap);
790
791 if (trace(1))
792 htrc("PrimaryKeys: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
793
794 } else
795 qrp = NULL;
796
797 /************************************************************************/
798 /* Close any local connection. */
799 /************************************************************************/
800 if (!op)
801 ocp->Close();
802
803 /************************************************************************/
804 /* Return the result pointer for use by GetData routines. */
805 /************************************************************************/
806 return qrp;
807 } // end of ODBCPrimaryKeys
808
809 /**************************************************************************/
810 /* Statistics: constructs the result blocks containing statistics */
811 /* about one or several tables to be retrieved by GetData commands. */
812 /**************************************************************************/
813 PQRYRES ODBCStatistics(PGLOBAL g, ODBConn *op, char *dsn, char *pat,
814 int un, int acc)
815 {
816 static int buftyp[] = {TYPE_STRING,
817 TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_STRING,
818 TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_STRING,
819 TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_STRING};
820 static unsigned int length[] = {0, 0, 0 ,6 ,0 ,0 ,6 ,6 ,0 ,2 ,10 ,10 ,128};
821 int n, ncol = 13;
822 int maxres;
823 PQRYRES qrp;
824 CATPARM *cap;
825 ODBConn *ocp = op;
826
827 if (!op) {
828 /**********************************************************************/
829 /* Open the connection with the ODBC data source. */
830 /**********************************************************************/
831 ocp = new(g) ODBConn(g, NULL);
832
833 if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
834 return NULL;
835
836 } // endif op
837
838 /************************************************************************/
839 /* Do an evaluation of the result size. */
840 /************************************************************************/
841 n = 1 + ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_INDEX);
842 maxres = (n) ? (int)n : 32;
843 n = ocp->GetMaxValue(SQL_MAX_SCHEMA_NAME_LEN);
844 length[1] = (n) ? (n + 1) : 128;
845 n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN);
846 length[2] = length[5] = (n) ? (n + 1) : 128;
847 n = ocp->GetMaxValue(SQL_MAX_CATALOG_NAME_LEN);
848 length[0] = length[4] = (n) ? (n + 1) : length[2];
849 n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN);
850 length[7] = (n) ? (n + 1) : 128;
851
852 if (trace(1))
853 htrc("SemStatistics: max=%d pat=%s\n", maxres, SVP(pat));
854
855 /************************************************************************/
856 /* Allocate the structure used to refer to the result set. */
857 /************************************************************************/
858 qrp = PlgAllocResult(g, ncol, maxres, IDS_STAT,
859 buftyp, NULL, length, false, true);
860
861 if (trace(1))
862 htrc("Getting stat results ncol=%d\n", qrp->Nbcol);
863
864 cap = AllocCatInfo(g, CAT_STAT, NULL, pat, qrp);
865 cap->Unique = (un < 0) ? SQL_INDEX_UNIQUE : (UWORD)un;
866 cap->Accuracy = (acc < 0) ? SQL_QUICK : (UWORD)acc;
867
868 /************************************************************************/
869 /* Now get the results into blocks. */
870 /************************************************************************/
871 if ((n = ocp->GetCatInfo(cap)) >= 0) {
872 qrp->Nblin = n;
873 // ResetNullValues(cap);
874
875 if (trace(1))
876 htrc("Statistics: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin);
877
878 } else
879 qrp = NULL;
880
881 /************************************************************************/
882 /* Close any local connection. */
883 /************************************************************************/
884 if (!op)
885 ocp->Close();
886
887 /************************************************************************/
888 /* Return the result pointer for use by GetData routines. */
889 /************************************************************************/
890 return qrp;
891 } // end of Statistics
892 #endif // 0
893
894 /***********************************************************************/
895 /* Implementation of DBX class. */
896 /***********************************************************************/
DBX(RETCODE rc,PCSZ msg)897 DBX::DBX(RETCODE rc, PCSZ msg)
898 {
899 m_RC = rc;
900 m_Msg = msg;
901
902 for (int i = 0; i < MAX_NUM_OF_MSG; i++)
903 m_ErrMsg[i] = NULL;
904
905 } // end of DBX constructor
906
907 /***********************************************************************/
908 /* This function is called by ThrowDBX. */
909 /***********************************************************************/
BuildErrorMessage(ODBConn * pdb,HSTMT hstmt)910 bool DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
911 {
912 if (pdb) {
913 SWORD len;
914 RETCODE rc;
915 UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1];
916 UCHAR state[SQL_SQLSTATE_SIZE + 1];
917 SDWORD native;
918 PGLOBAL g = pdb->m_G;
919
920 rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state,
921 &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
922
923 if (rc == SQL_NO_DATA_FOUND)
924 return false;
925 else if (rc != SQL_INVALID_HANDLE) {
926 // Skip non-errors
927 for (int i = 0; i < MAX_NUM_OF_MSG
928 && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
929 && strcmp((char*)state, "00000"); i++) {
930 m_ErrMsg[i] = (PSZ)PlugDup(g, (char*)msg);
931
932 if (trace(1))
933 htrc("%s: %s, Native=%d\n", state, msg, native);
934
935 rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state,
936 &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
937
938 } // endfor i
939
940 return true;
941 } else {
942 snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg,
943 MSG(BAD_HANDLE_VAL));
944 m_ErrMsg[0] = (PSZ)PlugDup(g, (char*)msg);
945
946 if (trace(1))
947 htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC);
948
949 return true;
950 } // endif rc
951
952 } else
953 m_ErrMsg[0] = "No connexion address provided";
954
955 if (trace(1))
956 htrc("%s: rc=%hd (%s)\n", SVP(m_Msg), m_RC, SVP(m_ErrMsg[0]));
957
958 return true;
959 } // end of BuildErrorMessage
960
GetErrorMessage(int i)961 const char *DBX::GetErrorMessage(int i)
962 {
963 if (i < 0 || i >= MAX_NUM_OF_MSG)
964 return "No ODBC error";
965 else if (m_ErrMsg[i])
966 return m_ErrMsg[i];
967 else
968 return (m_Msg) ? m_Msg : "Unknown error";
969
970 } // end of GetErrorMessage
971
972 /***********************************************************************/
973 /* ODBConn construction/destruction. */
974 /***********************************************************************/
ODBConn(PGLOBAL g,TDBODBC * tdbp)975 ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
976 {
977 m_G = g;
978 m_Tdb = tdbp;
979 m_henv = SQL_NULL_HENV;
980 m_hdbc = SQL_NULL_HDBC;
981 //m_Recset = NULL
982 m_hstmt = SQL_NULL_HSTMT;
983 m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT;
984 m_QueryTimeout = DEFAULT_QUERY_TIMEOUT;
985 m_UpdateOptions = 0;
986 m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10);
987 m_Catver = (tdbp) ? tdbp->Catver : 0;
988 m_Rows = 0;
989 m_Fetch = 0;
990 m_Fp = NULL;
991 m_Connect = NULL;
992 m_User = NULL;
993 m_Pwd = NULL;
994 m_Updatable = true;
995 m_Transact = false;
996 m_Scrollable = (tdbp) ? tdbp->Scrollable : false;
997 m_Full = false;
998 m_UseCnc = false;
999 m_IDQuoteChar[0] = '"';
1000 m_IDQuoteChar[1] = 0;
1001 //*m_ErrMsg = '\0';
1002 } // end of ODBConn
1003
1004 //ODBConn::~ODBConn()
1005 // {
1006 //if (Connected())
1007 // EndCom();
1008
1009 // } // end of ~ODBConn
1010
1011 /***********************************************************************/
1012 /* Screen for errors. */
1013 /***********************************************************************/
Check(RETCODE rc)1014 bool ODBConn::Check(RETCODE rc)
1015 {
1016 switch (rc) {
1017 case SQL_SUCCESS_WITH_INFO:
1018 if (trace(1)) {
1019 DBX x(rc);
1020
1021 if (x.BuildErrorMessage(this, m_hstmt))
1022 htrc("ODBC Success With Info, hstmt=%p %s\n",
1023 m_hstmt, x.GetErrorMessage(0));
1024
1025 } // endif trace
1026
1027 // Fall through
1028 case SQL_SUCCESS:
1029 case SQL_NO_DATA_FOUND:
1030 return true;
1031 } // endswitch rc
1032
1033 return false;
1034 } // end of Check
1035
1036 /***********************************************************************/
1037 /* DB exception throw routines. */
1038 /***********************************************************************/
ThrowDBX(RETCODE rc,PCSZ msg,HSTMT hstmt)1039 void ODBConn::ThrowDBX(RETCODE rc, PCSZ msg, HSTMT hstmt)
1040 {
1041 DBX* xp = new(m_G) DBX(rc, msg);
1042
1043 // Don't throw if no error
1044 if (xp->BuildErrorMessage(this, hstmt))
1045 throw xp;
1046
1047 } // end of ThrowDBX
1048
ThrowDBX(PCSZ msg)1049 void ODBConn::ThrowDBX(PCSZ msg)
1050 {
1051 DBX* xp = new(m_G) DBX(0, "Error");
1052
1053 xp->m_ErrMsg[0] = msg;
1054 throw xp;
1055 } // end of ThrowDBX
1056
1057 /***********************************************************************/
1058 /* Utility routine. */
1059 /***********************************************************************/
GetStringInfo(ushort infotype)1060 PSZ ODBConn::GetStringInfo(ushort infotype)
1061 {
1062 //ASSERT(m_hdbc != SQL_NULL_HDBC);
1063 char *p, buffer[MAX_STRING_INFO];
1064 SWORD result;
1065 RETCODE rc;
1066
1067 rc = SQLGetInfo(m_hdbc, infotype, buffer, sizeof(buffer), &result);
1068
1069 if (!Check(rc)) {
1070 ThrowDBX(rc, "SQLGetInfo"); // Temporary
1071 // *buffer = '\0';
1072 } // endif rc
1073
1074 p = PlugDup(m_G, buffer);
1075 return p;
1076 } // end of GetStringInfo
1077
1078 /***********************************************************************/
1079 /* Utility routine. */
1080 /***********************************************************************/
GetMaxValue(ushort infotype)1081 int ODBConn::GetMaxValue(ushort infotype)
1082 {
1083 //ASSERT(m_hdbc != SQL_NULL_HDBC);
1084 ushort maxval;
1085 RETCODE rc;
1086
1087 rc = SQLGetInfo(m_hdbc, infotype, &maxval, 0, NULL);
1088
1089 if (!Check(rc))
1090 maxval = 0;
1091
1092 return (int)maxval;
1093 } // end of GetMaxValue
1094
1095 /***********************************************************************/
1096 /* Utility routines. */
1097 /***********************************************************************/
OnSetOptions(HSTMT hstmt)1098 void ODBConn::OnSetOptions(HSTMT hstmt)
1099 {
1100 RETCODE rc;
1101 ASSERT(m_hdbc != SQL_NULL_HDBC);
1102
1103 if ((signed)m_QueryTimeout != -1) {
1104 // Attempt to set query timeout. Ignore failure
1105 rc = SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT, m_QueryTimeout);
1106
1107 if (!Check(rc))
1108 // don't attempt it again
1109 m_QueryTimeout = (DWORD)-1;
1110
1111 } // endif m_QueryTimeout
1112
1113 if (m_RowsetSize > 0) {
1114 // Attempt to set rowset size.
1115 // In case of failure reset it to 0 to use Fetch.
1116 rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize);
1117
1118 if (!Check(rc))
1119 // don't attempt it again
1120 m_RowsetSize = 0;
1121
1122 } // endif m_RowsetSize
1123
1124 } // end of OnSetOptions
1125
1126 /***********************************************************************/
1127 /* Open: connect to a data source. */
1128 /***********************************************************************/
Open(PCSZ ConnectString,POPARM sop,DWORD options)1129 int ODBConn::Open(PCSZ ConnectString, POPARM sop, DWORD options)
1130 {
1131 PGLOBAL& g = m_G;
1132 //ASSERT_VALID(this);
1133 //ASSERT(ConnectString == NULL || AfxIsValidString(ConnectString));
1134 ASSERT(!(options & noOdbcDialog && options & forceOdbcDialog));
1135
1136 m_Updatable = !(options & openReadOnly);
1137 m_Connect = ConnectString;
1138 m_User = sop->User;
1139 m_Pwd = sop->Pwd;
1140 m_LoginTimeout = sop->Cto;
1141 m_QueryTimeout = sop->Qto;
1142 m_UseCnc = sop->UseCnc;
1143
1144 // Allocate the HDBC and make connection
1145 try {
1146 /*PSZ ver;*/
1147
1148 AllocConnect(options);
1149 /*ver = GetStringInfo(SQL_ODBC_VER);*/
1150
1151 if (!m_UseCnc) {
1152 if (DriverConnect(options)) {
1153 strcpy(g->Message, MSG(CONNECT_CANCEL));
1154 return 0;
1155 } // endif
1156
1157 } else // Connect using SQLConnect
1158 Connect();
1159
1160 /*********************************************************************/
1161 /* Link a Fblock. This make possible to automatically close it */
1162 /* in case of error (throw). */
1163 /*********************************************************************/
1164 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1165
1166 m_Fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1167 m_Fp->Type = TYPE_FB_ODBC;
1168 m_Fp->Fname = NULL;
1169 m_Fp->Next = dbuserp->Openlist;
1170 dbuserp->Openlist = m_Fp;
1171 m_Fp->Count = 1;
1172 m_Fp->Length = 0;
1173 m_Fp->Memory = NULL;
1174 m_Fp->Mode = MODE_ANY;
1175 m_Fp->File = this;
1176 m_Fp->Handle = 0;
1177
1178 /*ver = GetStringInfo(SQL_DRIVER_ODBC_VER);*/
1179 // Verify support for required functionality and cache info
1180 // VerifyConnect(); Deprecated
1181 GetConnectInfo();
1182 } catch(DBX *xp) {
1183 sprintf(g->Message, "%s: %s", xp->m_Msg, xp->GetErrorMessage(0));
1184 Close();
1185 // Free();
1186 return -1;
1187 } // end try-catch
1188
1189 return 1;
1190 } // end of Open
1191
1192 /***********************************************************************/
1193 /* Allocate an henv (first time called) and hdbc. */
1194 /***********************************************************************/
AllocConnect(DWORD Options)1195 void ODBConn::AllocConnect(DWORD Options)
1196 {
1197 if (m_hdbc != SQL_NULL_HDBC)
1198 return;
1199
1200 RETCODE rc;
1201 //AfxLockGlobals(CRIT_ODBC);
1202
1203 // Need to allocate an environment for first connection
1204 if (m_henv == SQL_NULL_HENV) {
1205 // ASSERT(m_nAlloc == 0);
1206
1207 rc = SQLAllocEnv(&m_henv);
1208
1209 if (!Check(rc)) {
1210 // AfxUnlockGlobals(CRIT_ODBC);
1211 ThrowDBX(rc, "SQLAllocEnv"); // Fatal
1212 } // endif rc
1213
1214 } // endif m_henv
1215
1216 // Do the real thing, allocating connection data
1217 rc = SQLAllocConnect(m_henv, &m_hdbc);
1218
1219 if (!Check(rc)) {
1220 // AfxUnlockGlobals(CRIT_ODBC);
1221 ThrowDBX(rc, "SQLAllocConnect"); // Fatal
1222 } // endif rc
1223
1224 //m_nAlloc++; // allocated at last
1225 //AfxUnlockGlobals(CRIT_ODBC);
1226
1227 #if defined(_DEBUG)
1228 if (Options & traceSQL) {
1229 SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE, (SQLULEN)"xodbc.out");
1230 SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1);
1231 } // endif
1232 #endif // _DEBUG
1233
1234 if ((signed)m_LoginTimeout >= 0) {
1235 rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout);
1236
1237 if (trace(1) && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
1238 htrc("Warning: Failure setting login timeout\n");
1239
1240 } // endif Timeout
1241
1242 if (!m_Updatable) {
1243 rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY);
1244
1245 if (trace(1) && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
1246 htrc("Warning: Failure setting read only access mode\n");
1247
1248 } // endif
1249
1250 // Turn on cursor lib support
1251 if (Options & useCursorLib)
1252 rc = SQLSetConnectOption(m_hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_DRIVER);
1253
1254 return;
1255 } // end of AllocConnect
1256
1257 /***********************************************************************/
1258 /* Connect to data source using SQLConnect. */
1259 /***********************************************************************/
Connect(void)1260 void ODBConn::Connect(void)
1261 {
1262 SQLRETURN rc;
1263 SQLSMALLINT ul = (m_User ? SQL_NTS : 0);
1264 SQLSMALLINT pl = (m_Pwd ? SQL_NTS : 0);
1265
1266 rc = SQLConnect(m_hdbc, (SQLCHAR*)m_Connect, SQL_NTS,
1267 (SQLCHAR*)m_User, ul, (SQLCHAR*)m_Pwd, pl);
1268
1269 if (!Check(rc))
1270 ThrowDBX(rc, "SQLConnect");
1271
1272 } // end of Connect
1273
1274 /***********************************************************************/
1275 /* Connect to data source using SQLDriverConnect. */
1276 /***********************************************************************/
DriverConnect(DWORD Options)1277 bool ODBConn::DriverConnect(DWORD Options)
1278 {
1279 RETCODE rc;
1280 SWORD nResult;
1281 PUCHAR ConnOut = (PUCHAR)PlugSubAlloc(m_G, NULL, MAX_CONNECT_LEN);
1282 UWORD wConnectOption = SQL_DRIVER_COMPLETE;
1283 #if defined(_WIN32)
1284 HWND hWndTop = GetForegroundWindow();
1285 HWND hWnd = GetParent(hWndTop);
1286
1287 if (hWnd == NULL)
1288 hWnd = GetDesktopWindow();
1289 #else // !_WIN32
1290 HWND hWnd = (HWND)1;
1291 #endif // !_WIN32
1292
1293 wConnectOption = SQL_DRIVER_NOPROMPT;
1294 //else if (Options & forceOdbcDialog)
1295 // wConnectOption = SQL_DRIVER_PROMPT;
1296
1297 rc = SQLDriverConnect(m_hdbc, hWnd, (PUCHAR)m_Connect,
1298 SQL_NTS, ConnOut, MAX_CONNECT_LEN,
1299 &nResult, wConnectOption);
1300
1301 #if defined(_WIN32)
1302 if (hWndTop)
1303 EnableWindow(hWndTop, true);
1304 #endif // _WIN32
1305
1306 // If user hit 'Cancel'
1307 if (rc == SQL_NO_DATA_FOUND) {
1308 Close();
1309 // Free();
1310 return true;
1311 } // endif rc
1312
1313 if (!Check(rc))
1314 ThrowDBX(rc, "SQLDriverConnect");
1315
1316 // Save connect string returned from ODBC
1317 m_Connect = (PSZ)ConnOut;
1318
1319 // All done
1320 return false;
1321 } // end of DriverConnect
1322
VerifyConnect()1323 void ODBConn::VerifyConnect()
1324 {
1325 #if defined(NEWMSG) || defined(XMSG)
1326 PGLOBAL& g = m_G;
1327 #endif // NEWMSG || XMSG
1328 RETCODE rc;
1329 SWORD result;
1330 SWORD conformance;
1331
1332 rc = SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
1333 &conformance, sizeof(conformance), &result);
1334
1335 if (!Check(rc))
1336 ThrowDBX(rc, "SQLGetInfo");
1337
1338 if (conformance < SQL_OAC_LEVEL1)
1339 ThrowDBX(MSG(API_CONF_ERROR));
1340
1341 rc = SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
1342 &conformance, sizeof(conformance), &result);
1343
1344 if (!Check(rc))
1345 ThrowDBX(rc, "SQLGetInfo");
1346
1347 if (conformance < SQL_OSC_MINIMUM)
1348 ThrowDBX(MSG(SQL_CONF_ERROR));
1349
1350 } // end of VerifyConnect
1351
GetConnectInfo()1352 void ODBConn::GetConnectInfo()
1353 {
1354 RETCODE rc;
1355 SWORD nResult;
1356 #if 0 // Update not implemented yet
1357 UDWORD DrvPosOp;
1358
1359 // Reset the database update options
1360 m_UpdateOptions = 0;
1361
1362 // Check for SQLSetPos support
1363 rc = SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS,
1364 &DrvPosOp, sizeof(DrvPosOp), &nResult);
1365
1366 if (Check(rc) &&
1367 (DrvPosOp & SQL_POS_UPDATE) &&
1368 (DrvPosOp & SQL_POS_DELETE) &&
1369 (DrvPosOp & SQL_POS_ADD))
1370 m_UpdateOptions = SQL_SETPOSUPDATES;
1371
1372 // Check for positioned update SQL support
1373 UDWORD PosStatements;
1374
1375 rc = SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS,
1376 &PosStatements, sizeof(PosStatements),
1377 &nResult);
1378
1379 if (Check(rc) &&
1380 (PosStatements & SQL_PS_POSITIONED_DELETE) &&
1381 (PosStatements & SQL_PS_POSITIONED_UPDATE))
1382 m_UpdateOptions |= SQL_POSITIONEDSQL;
1383
1384 if (m_Updatable) {
1385 // Make sure data source is Updatable
1386 char ReadOnly[10];
1387
1388 rc = SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
1389 ReadOnly, sizeof(ReadOnly), &nResult);
1390
1391 if (Check(rc) && nResult == 1)
1392 m_Updatable = !!strcmp(ReadOnly, "Y");
1393 else
1394 m_Updatable = false;
1395
1396 if (trace(1))
1397 htrc("Warning: data source is readonly\n");
1398
1399 } else // Make data source is !Updatable
1400 rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
1401 SQL_MODE_READ_ONLY);
1402 #endif // 0
1403
1404 // Get the quote char to use when constructing SQL
1405 rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
1406 m_IDQuoteChar, sizeof(m_IDQuoteChar), &nResult);
1407
1408 if (trace(1))
1409 htrc("DBMS: %s, Version: %s, rc=%d\n",
1410 GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER), rc);
1411
1412 } // end of GetConnectInfo
1413
1414 /***********************************************************************/
1415 /* Allocate record set and execute an SQL query. */
1416 /***********************************************************************/
ExecDirectSQL(char * sql,ODBCCOL * tocols)1417 int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
1418 {
1419 PGLOBAL& g = m_G;
1420 void *buffer;
1421 bool b;
1422 UWORD n, k;
1423 SWORD len, tp, ncol = 0;
1424 ODBCCOL *colp;
1425 RETCODE rc;
1426 HSTMT hstmt;
1427
1428 try {
1429 b = false;
1430
1431 if (m_hstmt) {
1432 // This is a Requery
1433 rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
1434
1435 if (!Check(rc))
1436 ThrowDBX(rc, "SQLFreeStmt", m_hstmt);
1437
1438 m_hstmt = NULL;
1439 } // endif m_hstmt
1440
1441 rc = SQLAllocStmt(m_hdbc, &hstmt);
1442
1443 if (!Check(rc))
1444 ThrowDBX(rc, "SQLAllocStmt");
1445
1446 if (m_Scrollable) {
1447 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1448 (void*)SQL_SCROLLABLE, 0);
1449
1450 if (!Check(rc))
1451 ThrowDBX(rc, "Scrollable", hstmt);
1452
1453 } // endif m_Scrollable
1454
1455 OnSetOptions(hstmt);
1456 b = true;
1457
1458 if (trace(1))
1459 htrc("ExecDirect hstmt=%p %.256s\n", hstmt, sql);
1460
1461 if (m_Tdb->Srcdef) {
1462 // Be sure this is a query returning a result set
1463 do {
1464 rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
1465 } while (rc == SQL_STILL_EXECUTING);
1466
1467 if (!Check(rc))
1468 ThrowDBX(rc, "SQLPrepare", hstmt);
1469
1470 if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
1471 ThrowDBX(rc, "SQLNumResultCols", hstmt);
1472
1473 if (ncol == 0) {
1474 strcpy(g->Message, "This Srcdef does not return a result set");
1475 return -1;
1476 } // endif ncol
1477
1478 // Ok, now we can proceed
1479 do {
1480 rc = SQLExecute(hstmt);
1481 } while (rc == SQL_STILL_EXECUTING);
1482
1483 if (!Check(rc))
1484 ThrowDBX(rc, "SQLExecute", hstmt);
1485
1486 } else {
1487 do {
1488 rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
1489 } while (rc == SQL_STILL_EXECUTING);
1490
1491 if (!Check(rc))
1492 ThrowDBX(rc, "SQLExecDirect", hstmt);
1493
1494 do {
1495 rc = SQLNumResultCols(hstmt, &ncol);
1496 } while (rc == SQL_STILL_EXECUTING);
1497
1498 k = 0; // used for column number
1499 } // endif Srcdef
1500
1501 for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
1502 if (!colp->IsSpecial())
1503 n++;
1504
1505 // n can be 0 for query such as Select count(*) from table
1506 if (n && n > (UWORD)ncol)
1507 ThrowDBX(MSG(COL_NUM_MISM));
1508
1509 // Now bind the column buffers
1510 for (colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
1511 if (!colp->IsSpecial()) {
1512 buffer = colp->GetBuffer(m_RowsetSize);
1513 len = colp->GetBuflen();
1514 tp = GetSQLCType(colp->GetResultType());
1515
1516 if (tp == SQL_TYPE_NULL) {
1517 sprintf(m_G->Message, MSG(INV_COLUMN_TYPE),
1518 colp->GetResultType(), SVP(colp->GetName()));
1519 ThrowDBX(m_G->Message);
1520 } // endif tp
1521
1522 if (m_Tdb->Srcdef)
1523 k = colp->GetIndex();
1524 else
1525 k++;
1526
1527 if (trace(1))
1528 htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n",
1529 k, tp, buffer, len, colp->GetStrLen());
1530
1531 rc = SQLBindCol(hstmt, k, tp, buffer, len, colp->GetStrLen());
1532
1533 if (!Check(rc))
1534 ThrowDBX(rc, "SQLBindCol", hstmt);
1535
1536 } // endif colp
1537
1538 } catch(DBX *x) {
1539 if (trace(1))
1540 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1541 htrc(x->m_ErrMsg[i]);
1542
1543 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1544
1545 if (b)
1546 SQLCancel(hstmt);
1547
1548 rc = SQLFreeStmt(hstmt, SQL_DROP);
1549 m_hstmt = NULL;
1550 return -1;
1551 } // end try/catch
1552
1553 m_hstmt = hstmt;
1554 return (int)m_RowsetSize; // May have been reset in OnSetOptions
1555 } // end of ExecDirectSQL
1556
1557 /***********************************************************************/
1558 /* Get the number of lines of the result set. */
1559 /***********************************************************************/
GetResultSize(char * sql,ODBCCOL * colp)1560 int ODBConn::GetResultSize(char *sql, ODBCCOL *colp)
1561 {
1562 int n = 0;
1563 RETCODE rc;
1564
1565 if (ExecDirectSQL(sql, colp) < 0)
1566 return -1;
1567
1568 try {
1569 for (n = 0; ; n++) {
1570 do {
1571 rc = SQLFetch(m_hstmt);
1572 } while (rc == SQL_STILL_EXECUTING);
1573
1574 if (!Check(rc))
1575 ThrowDBX(rc, "SQLFetch", m_hstmt);
1576
1577 if (rc == SQL_NO_DATA_FOUND)
1578 break;
1579
1580 } // endfor n
1581
1582 } catch(DBX *x) {
1583 strcpy(m_G->Message, x->GetErrorMessage(0));
1584
1585 if (trace(1))
1586 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1587 htrc(x->m_ErrMsg[i]);
1588
1589 SQLCancel(m_hstmt);
1590 n = -2;
1591 } // end try/catch
1592
1593 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
1594 m_hstmt = NULL;
1595
1596 if (n != 1)
1597 return -3;
1598 else
1599 return colp->GetIntValue();
1600
1601 } // end of GetResultSize
1602
1603 /***********************************************************************/
1604 /* Fetch next row. */
1605 /***********************************************************************/
Fetch(int pos)1606 int ODBConn::Fetch(int pos)
1607 {
1608 ASSERT(m_hstmt);
1609 int irc;
1610 SQLULEN crow;
1611 RETCODE rc;
1612 PGLOBAL& g = m_G;
1613
1614 try {
1615 // do {
1616 if (pos) {
1617 rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_ABSOLUTE, pos, &crow, NULL);
1618 } else if (m_RowsetSize) {
1619 rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_NEXT, 1, &crow, NULL);
1620 } else {
1621 rc = SQLFetch(m_hstmt);
1622 crow = 1;
1623 } // endif m_RowsetSize
1624 // } while (rc == SQL_STILL_EXECUTING);
1625
1626 if (trace(2))
1627 htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n",
1628 m_hstmt, m_RowsetSize, rc);
1629
1630 if (!Check(rc))
1631 ThrowDBX(rc, "Fetching", m_hstmt);
1632
1633 if (rc == SQL_NO_DATA_FOUND) {
1634 m_Full = (m_Fetch == 1);
1635 irc = 0;
1636 } else
1637 irc = (int)crow;
1638
1639 m_Fetch++;
1640 m_Rows += irc;
1641 } catch(DBX *x) {
1642 if (trace(1))
1643 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1644 htrc(x->m_ErrMsg[i]);
1645
1646 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1647 irc = -1;
1648 } // end try/catch
1649
1650 return irc;
1651 } // end of Fetch
1652
1653 /***********************************************************************/
1654 /* Prepare an SQL statement for insert. */
1655 /***********************************************************************/
PrepareSQL(char * sql)1656 int ODBConn::PrepareSQL(char *sql)
1657 {
1658 PGLOBAL& g = m_G;
1659 bool b;
1660 UINT txn = 0;
1661 SWORD nparm;
1662 RETCODE rc;
1663 HSTMT hstmt;
1664
1665 if (m_Tdb->GetMode() != MODE_READ) {
1666 // Does the data source support transactions
1667 rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
1668
1669 if (Check(rc) && txn != SQL_TC_NONE) try {
1670 rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
1671 SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
1672
1673 if (!Check(rc))
1674 ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
1675
1676 m_Transact = true;
1677 } catch(DBX *x) {
1678 if (trace(1))
1679 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1680 htrc(x->m_ErrMsg[i]);
1681
1682 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1683 } // end try/catch
1684
1685 } // endif Mode
1686
1687 try {
1688 b = false;
1689
1690 if (m_hstmt) {
1691 SQLFreeStmt(m_hstmt, SQL_CLOSE);
1692
1693 hstmt = m_hstmt;
1694 m_hstmt = NULL;
1695
1696 if (m_Tdb->GetAmType() != TYPE_AM_XDBC)
1697 ThrowDBX(MSG(SEQUENCE_ERROR));
1698
1699 } // endif m_hstmt
1700
1701 rc = SQLAllocStmt(m_hdbc, &hstmt);
1702
1703 if (!Check(rc))
1704 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
1705
1706 OnSetOptions(hstmt);
1707 b = true;
1708
1709 if (trace(1))
1710 htrc("Prepare hstmt=%p %.64s\n", hstmt, sql);
1711
1712 do {
1713 rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
1714 } while (rc == SQL_STILL_EXECUTING);
1715
1716 if (!Check(rc))
1717 ThrowDBX(rc, "SQLPrepare", hstmt);
1718
1719 do {
1720 rc = SQLNumParams(hstmt, &nparm);
1721 } while (rc == SQL_STILL_EXECUTING);
1722
1723 } catch(DBX *x) {
1724 if (trace(1))
1725 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1726 htrc(x->m_ErrMsg[i]);
1727
1728 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1729
1730 if (b)
1731 SQLCancel(hstmt);
1732
1733 rc = SQLFreeStmt(hstmt, SQL_DROP);
1734 m_hstmt = NULL;
1735
1736 if (m_Transact) {
1737 rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
1738 m_Transact = false;
1739 } // endif m_Transact
1740
1741 return -1;
1742 } // end try/catch
1743
1744 m_hstmt = hstmt;
1745 return (int)nparm;
1746 } // end of PrepareSQL
1747
1748 /***********************************************************************/
1749 /* Execute a prepared statement. */
1750 /***********************************************************************/
ExecuteSQL(void)1751 int ODBConn::ExecuteSQL(void)
1752 {
1753 PGLOBAL& g = m_G;
1754 SWORD ncol = 0;
1755 RETCODE rc;
1756 SQLLEN afrw = -1;
1757
1758 try {
1759 do {
1760 rc = SQLExecute(m_hstmt);
1761 } while (rc == SQL_STILL_EXECUTING);
1762
1763 if (!Check(rc))
1764 ThrowDBX(rc, "SQLExecute", m_hstmt);
1765
1766 if (!Check(rc = SQLNumResultCols(m_hstmt, &ncol)))
1767 ThrowDBX(rc, "SQLNumResultCols", m_hstmt);
1768
1769 if (ncol) {
1770 // This should never happen while inserting
1771 strcpy(g->Message, "Logical error while inserting");
1772 } else {
1773 // Insert, Update or Delete statement
1774 if (!Check(rc = SQLRowCount(m_hstmt, &afrw)))
1775 ThrowDBX(rc, "SQLRowCount", m_hstmt);
1776
1777 } // endif ncol
1778
1779 } catch(DBX *x) {
1780 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1781 SQLCancel(m_hstmt);
1782 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
1783 m_hstmt = NULL;
1784
1785 if (m_Transact) {
1786 rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
1787 m_Transact = false;
1788 } // endif m_Transact
1789
1790 afrw = -1;
1791 } // end try/catch
1792
1793 return (int)afrw;
1794 } // end of ExecuteSQL
1795
1796 /***********************************************************************/
1797 /* Bind a parameter for inserting. */
1798 /***********************************************************************/
BindParam(ODBCCOL * colp)1799 bool ODBConn::BindParam(ODBCCOL *colp)
1800 {
1801 void *buf;
1802 int buftype = colp->GetResultType();
1803 SQLUSMALLINT n = colp->GetRank();
1804 SQLSMALLINT ct, sqlt, dec, nul __attribute__((unused));
1805 SQLULEN colsize;
1806 SQLLEN len;
1807 SQLLEN *strlen = colp->GetStrLen();
1808 SQLRETURN rc;
1809
1810 #if 0
1811 try {
1812 // This function is often not or badly implemented by data sources
1813 rc = SQLDescribeParam(m_hstmt, n, &sqlt, &colsize, &dec, &nul);
1814
1815 if (!Check(rc))
1816 ThrowDBX(rc, "SQLDescribeParam", m_hstmt);
1817
1818 } catch(DBX *x) {
1819 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1820 #endif // 0
1821 colsize = colp->GetPrecision();
1822 sqlt = GetSQLType(buftype);
1823 dec = IsTypeNum(buftype) ? colp->GetScale() : 0;
1824 nul = colp->IsNullable() ? SQL_NULLABLE : SQL_NO_NULLS;
1825 //} // end try/catch
1826
1827 buf = colp->GetBuffer(0);
1828 len = IsTypeChar(buftype) ? colp->GetBuflen() : 0;
1829 ct = GetSQLCType(buftype);
1830 *strlen = IsTypeChar(buftype) ? SQL_NTS : 0;
1831
1832 try {
1833 rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt,
1834 colsize, dec, buf, len, strlen);
1835
1836 if (!Check(rc))
1837 ThrowDBX(rc, "SQLBindParameter", m_hstmt);
1838
1839 } catch(DBX *x) {
1840 strcpy(m_G->Message, x->GetErrorMessage(0));
1841 SQLCancel(m_hstmt);
1842 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
1843 m_hstmt = NULL;
1844 return true;
1845 } // end try/catch
1846
1847 return false;
1848 } // end of BindParam
1849
1850 /***********************************************************************/
1851 /* Execute an SQL command. */
1852 /***********************************************************************/
1853 bool ODBConn::ExecSQLcommand(char *sql)
1854 {
1855 char cmd[16];
1856 bool b, rcd = false;
1857 UINT txn = 0;
1858 PGLOBAL& g = m_G;
1859 SWORD ncol = 0;
1860 SQLLEN afrw;
1861 RETCODE rc;
1862 HSTMT hstmt;
1863
1864 try {
1865 b = FALSE;
1866
1867 // Check whether we should use transaction
1868 if (sscanf(sql, " %15s ", cmd) == 1) {
1869 if (!stricmp(cmd, "INSERT") || !stricmp(cmd, "UPDATE") ||
1870 !stricmp(cmd, "DELETE") || !stricmp(cmd, "REPLACE")) {
1871 // Does the data source support transactions
1872 rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
1873
1874 if (Check(rc) && txn != SQL_TC_NONE) {
1875 rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
1876 SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
1877
1878 if (!Check(rc))
1879 ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
1880
1881 m_Transact = TRUE;
1882 } // endif txn
1883
1884 } // endif cmd
1885
1886 } // endif sql
1887
1888 // Allocate the statement handle
1889 rc = SQLAllocStmt(m_hdbc, &hstmt);
1890
1891 if (!Check(rc))
1892 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
1893
1894 OnSetOptions(hstmt);
1895 b = true;
1896
1897 if (trace(1))
1898 htrc("ExecSQLcommand hstmt=%p %.64s\n", hstmt, sql);
1899
1900 // Proceed with command execution
1901 do {
1902 rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
1903 } while (rc == SQL_STILL_EXECUTING);
1904
1905 if (!Check(rc))
1906 ThrowDBX(rc, "SQLExecDirect", hstmt);
1907
1908 // Check whether this is a query returning a result set
1909 if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
1910 ThrowDBX(rc, "SQLNumResultCols", hstmt);
1911
1912 if (!ncol) {
1913 if (!Check(SQLRowCount(hstmt, &afrw)))
1914 ThrowDBX(rc, "SQLRowCount", hstmt);
1915
1916 m_Tdb->AftRows = (int)afrw;
1917 strcpy(g->Message, "Affected rows");
1918 } else {
1919 m_Tdb->AftRows = (int)ncol;
1920 strcpy(g->Message, "Result set column number");
1921 } // endif ncol
1922
1923 } catch(DBX *x) {
1924 if (trace(1))
1925 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1926 htrc(x->m_ErrMsg[i]);
1927
1928 sprintf(g->Message, "Remote %s: %s", x->m_Msg, x->GetErrorMessage(0));
1929
1930 if (b)
1931 SQLCancel(hstmt);
1932
1933 m_Tdb->AftRows = -1;
1934 rcd = true;
1935 } // end try/catch
1936
1937 if (!Check(rc = SQLFreeStmt(hstmt, SQL_CLOSE)))
1938 sprintf(g->Message, "SQLFreeStmt: rc=%d", rc);
1939
1940 if (m_Transact) {
1941 // Terminate the transaction
1942 if (!Check(rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc,
1943 (rcd) ? SQL_ROLLBACK : SQL_COMMIT)))
1944 sprintf(g->Message, "SQLEndTran: rc=%d", rc);
1945
1946 if (!Check(rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
1947 (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)))
1948 sprintf(g->Message, "SQLSetConnectAttr: rc=%d", rc);
1949
1950 m_Transact = false;
1951 } // endif m_Transact
1952
1953 return rcd;
1954 } // end of ExecSQLcommand
1955
1956 /**************************************************************************/
1957 /* GetMetaData: constructs the result blocks containing the */
1958 /* description of all the columns of an SQL command. */
1959 /**************************************************************************/
1960 PQRYRES ODBConn::GetMetaData(PGLOBAL g, PCSZ dsn, PCSZ src)
1961 {
1962 static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT,
1963 TYPE_SHORT, TYPE_SHORT};
1964 static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
1965 FLD_SCALE, FLD_NULL};
1966 static unsigned int length[] = {0, 6, 10, 6, 6};
1967 unsigned char cn[60];
1968 int qcol = 5;
1969 short nl, type, prec, nul, cns = (short)sizeof(cn);
1970 PQRYRES qrp = NULL;
1971 PCOLRES crp;
1972 USHORT i;
1973 SQLULEN n;
1974 SWORD ncol;
1975 RETCODE rc;
1976 HSTMT hstmt;
1977
1978 try {
1979 rc = SQLAllocStmt(m_hdbc, &hstmt);
1980
1981 if (!Check(rc))
1982 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
1983
1984 OnSetOptions(hstmt);
1985
1986 do {
1987 rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS);
1988 // rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS);
1989 } while (rc == SQL_STILL_EXECUTING);
1990
1991 if (!Check(rc))
1992 ThrowDBX(rc, "SQLExecDirect", hstmt);
1993
1994 do {
1995 rc = SQLNumResultCols(hstmt, &ncol);
1996 } while (rc == SQL_STILL_EXECUTING);
1997
1998 if (!Check(rc))
1999 ThrowDBX(rc, "SQLNumResultCols", hstmt);
2000
2001 if (ncol) for (i = 1; i <= ncol; i++) {
2002 do {
2003 rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL);
2004 } while (rc == SQL_STILL_EXECUTING);
2005
2006 if (!Check(rc))
2007 ThrowDBX(rc, "SQLDescribeCol", hstmt);
2008
2009 length[0] = MY_MAX(length[0], (UINT)nl);
2010 } // endfor i
2011
2012 } catch(DBX *x) {
2013 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2014 goto err;
2015 } // end try/catch
2016
2017 if (!ncol) {
2018 strcpy(g->Message, "Invalid Srcdef");
2019 goto err;
2020 } // endif ncol
2021
2022 /************************************************************************/
2023 /* Allocate the structures used to refer to the result set. */
2024 /************************************************************************/
2025 if (!(qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
2026 buftyp, fldtyp, length, false, true)))
2027 return NULL;
2028
2029 // Some columns must be renamed
2030 for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
2031 switch (++i) {
2032 case 3: crp->Name = "Precision"; break;
2033 case 4: crp->Name = "Scale"; break;
2034 case 5: crp->Name = "Nullable"; break;
2035 } // endswitch i
2036
2037 /************************************************************************/
2038 /* Now get the results into blocks. */
2039 /************************************************************************/
2040 try {
2041 for (i = 0; i < ncol; i++) {
2042 do {
2043 rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul);
2044 } while (rc == SQL_STILL_EXECUTING);
2045
2046 if (!Check(rc))
2047 ThrowDBX(rc, "SQLDescribeCol", hstmt);
2048 else
2049 qrp->Nblin++;
2050
2051 crp = qrp->Colresp; // Column_Name
2052 crp->Kdata->SetValue((char*)cn, i);
2053 crp = crp->Next; // Data_Type
2054 crp->Kdata->SetValue(type, i);
2055 crp = crp->Next; // Precision (length)
2056 crp->Kdata->SetValue((int)n, i);
2057 crp = crp->Next; // Scale
2058 crp->Kdata->SetValue(prec, i);
2059 crp = crp->Next; // Nullable
2060 crp->Kdata->SetValue(nul, i);
2061 } // endfor i
2062
2063 } catch(DBX *x) {
2064 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2065 qrp = NULL;
2066 } // end try/catch
2067
2068 /* Cleanup */
2069 err:
2070 SQLCancel(hstmt);
2071 rc = SQLFreeStmt(hstmt, SQL_DROP);
2072 Close();
2073
2074 /************************************************************************/
2075 /* Return the result pointer for use by GetData routines. */
2076 /************************************************************************/
2077 return qrp;
2078 } // end of GetMetaData
2079
2080 /***********************************************************************/
2081 /* Get the list of Data Sources and set it in qrp. */
2082 /***********************************************************************/
2083 bool ODBConn::GetDataSources(PQRYRES qrp)
2084 {
2085 bool rv = false;
2086 UCHAR *dsn, *des;
2087 UWORD dir = SQL_FETCH_FIRST;
2088 SWORD n1, n2, p1, p2;
2089 PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
2090 RETCODE rc;
2091
2092 n1 = crp1->Clen;
2093 n2 = crp2->Clen;
2094
2095 try {
2096 rc = SQLAllocEnv(&m_henv);
2097
2098 if (!Check(rc))
2099 ThrowDBX(rc, "SQLAllocEnv"); // Fatal
2100
2101 for (int i = 0; i < qrp->Maxres; i++) {
2102 dsn = (UCHAR*)crp1->Kdata->GetValPtr(i);
2103 des = (UCHAR*)crp2->Kdata->GetValPtr(i);
2104 rc = SQLDataSources(m_henv, dir, dsn, n1, &p1, des, n2, &p2);
2105
2106 if (rc == SQL_NO_DATA_FOUND)
2107 break;
2108 else if (!Check(rc))
2109 ThrowDBX(rc, "SQLDataSources");
2110
2111 qrp->Nblin++;
2112 dir = SQL_FETCH_NEXT;
2113 } // endfor i
2114
2115 } catch(DBX *x) {
2116 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2117 rv = true;
2118 } // end try/catch
2119
2120 Close();
2121 return rv;
2122 } // end of GetDataSources
2123
2124 /***********************************************************************/
2125 /* Get the list of Drivers and set it in qrp. */
2126 /***********************************************************************/
2127 bool ODBConn::GetDrivers(PQRYRES qrp)
2128 {
2129 int i, n;
2130 bool rv = false;
2131 UCHAR *des, *att;
2132 UWORD dir = SQL_FETCH_FIRST;
2133 SWORD n1, n2, p1, p2;
2134 PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
2135 RETCODE rc;
2136
2137 n1 = crp1->Clen;
2138 n2 = crp2->Clen;
2139
2140 try {
2141 rc = SQLAllocEnv(&m_henv);
2142
2143 if (!Check(rc))
2144 ThrowDBX(rc, "SQLAllocEnv"); // Fatal
2145
2146 for (n = 0; n < qrp->Maxres; n++) {
2147 des = (UCHAR*)crp1->Kdata->GetValPtr(n);
2148 att = (UCHAR*)crp2->Kdata->GetValPtr(n);
2149 rc = SQLDrivers(m_henv, dir, des, n1, &p1, att, n2, &p2);
2150
2151 if (rc == SQL_NO_DATA_FOUND)
2152 break;
2153 else if (!Check(rc))
2154 ThrowDBX(rc, "SQLDrivers");
2155
2156
2157 // The attributes being separated by '\0', set them to ';'
2158 for (i = 0; i < p2; i++)
2159 if (!att[i])
2160 att[i] = ';';
2161
2162 qrp->Nblin++;
2163 dir = SQL_FETCH_NEXT;
2164 } // endfor n
2165
2166 } catch(DBX *x) {
2167 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2168 rv = true;
2169 } // end try/catch
2170
2171 Close();
2172 return rv;
2173 } // end of GetDrivers
2174
2175 /***********************************************************************/
2176 /* A helper class to split an optionally qualified table name into */
2177 /* components. */
2178 /* These formats are understood: */
2179 /* "CatalogName.SchemaName.TableName" */
2180 /* "SchemaName.TableName" */
2181 /* "TableName" */
2182 /***********************************************************************/
2183 class SQLQualifiedName
2184 {
2185 static const uint max_parts= 3; // Catalog.Schema.Table
2186 MYSQL_LEX_STRING m_part[max_parts];
2187 char m_buf[512];
2188
2189 void lex_string_set(MYSQL_LEX_STRING *S, char *str, size_t length)
2190 {
2191 S->str= str;
2192 S->length= length;
2193 } // end of lex_string_set
2194
2195 void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs)
2196 {
2197 DBUG_ASSERT(offs <= S->length);
2198 S->str+= offs;
2199 S->length-= offs;
2200 } // end of lex_string_shorten_down
2201
2202 /*********************************************************************/
2203 /* Find the rightmost '.' delimiter and return the length */
2204 /* of the qualifier, including the rightmost '.' delimier. */
2205 /* For example, for the string {"a.b.c",5} it will return 4, */
2206 /* which is the length of the qualifier "a.b." */
2207 /*********************************************************************/
2208 size_t lex_string_find_qualifier(MYSQL_LEX_STRING *S)
2209 {
2210 size_t i;
2211 for (i= S->length; i > 0; i--)
2212 {
2213 if (S->str[i - 1] == '.')
2214 {
2215 S->str[i - 1]= '\0';
2216 return i;
2217 }
2218 }
2219 return 0;
2220 } // end of lex_string_find_qualifier
2221
2222 public:
2223 /*********************************************************************/
2224 /* Initialize to the given optionally qualified name. */
2225 /* NULL pointer in "name" is supported. */
2226 /* name qualifier has precedence over schema. */
2227 /*********************************************************************/
2228 SQLQualifiedName(CATPARM *cap)
2229 {
2230 const char *name = (const char *)cap->Tab;
2231 char *db = (char *)cap->DB;
2232 size_t len, i;
2233
2234 // Initialize the parts
2235 for (i = 0 ; i < max_parts; i++)
2236 lex_string_set(&m_part[i], NULL, 0);
2237
2238 if (name) {
2239 // Initialize the first (rightmost) part
2240 lex_string_set(&m_part[0], m_buf,
2241 strmake(m_buf, name, sizeof(m_buf) - 1) - m_buf);
2242
2243 // Initialize the other parts, if exist.
2244 for (i= 1; i < max_parts; i++) {
2245 if (!(len= lex_string_find_qualifier(&m_part[i - 1])))
2246 break;
2247
2248 lex_string_set(&m_part[i], m_part[i - 1].str, len - 1);
2249 lex_string_shorten_down(&m_part[i - 1], len);
2250 } // endfor i
2251
2252 } // endif name
2253
2254 // If it was not specified, set schema as the passed db name
2255 if (db && !m_part[1].length)
2256 lex_string_set(&m_part[1], db, strlen(db));
2257
2258 } // end of SQLQualifiedName
2259
2260 SQLCHAR *ptr(uint i)
2261 {
2262 DBUG_ASSERT(i < max_parts);
2263 return (SQLCHAR *) (m_part[i].length ? m_part[i].str : NULL);
2264 } // end of ptr
2265
2266 SQLSMALLINT length(uint i)
2267 {
2268 DBUG_ASSERT(i < max_parts);
2269 return (SQLSMALLINT)m_part[i].length;
2270 } // end of length
2271
2272 }; // end of class SQLQualifiedName
2273
2274 /***********************************************************************/
2275 /* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */
2276 /***********************************************************************/
2277 int ODBConn::GetCatInfo(CATPARM *cap)
2278 {
2279 PGLOBAL& g = m_G;
2280 void *buffer;
2281 int i, irc;
2282 bool b;
2283 PCSZ fnc = "Unknown";
2284 UWORD n = 0;
2285 SWORD ncol, len, tp;
2286 SQLULEN crow = 0;
2287 PQRYRES qrp = cap->Qrp;
2288 PCOLRES crp;
2289 RETCODE rc = 0;
2290 HSTMT hstmt = NULL;
2291 SQLLEN *vl, *vlen = NULL;
2292 PVAL *pval = NULL;
2293 char* *pbuf = NULL;
2294
2295 try {
2296 b = false;
2297
2298 if (!m_hstmt) {
2299 rc = SQLAllocStmt(m_hdbc, &hstmt);
2300
2301 if (!Check(rc))
2302 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
2303
2304 } else
2305 ThrowDBX(MSG(SEQUENCE_ERROR));
2306
2307 b = true;
2308
2309 // Currently m_Catver should be always 0 here
2310 assert(!m_Catver); // This may be temporary
2311
2312 if (qrp->Maxres > 0)
2313 m_RowsetSize = 1;
2314 else
2315 ThrowDBX("0-sized result");
2316
2317 SQLQualifiedName name(cap);
2318
2319 // Now do call the proper ODBC API
2320 switch (cap->Id) {
2321 case CAT_TAB:
2322 fnc = "SQLTables";
2323 rc = SQLTables(hstmt, name.ptr(2), name.length(2),
2324 name.ptr(1), name.length(1),
2325 name.ptr(0), name.length(0),
2326 (SQLCHAR *)cap->Pat,
2327 cap->Pat ? SQL_NTS : 0);
2328 break;
2329 case CAT_COL:
2330 fnc = "SQLColumns";
2331 rc = SQLColumns(hstmt, name.ptr(2), name.length(2),
2332 name.ptr(1), name.length(1),
2333 name.ptr(0), name.length(0),
2334 (SQLCHAR *)cap->Pat,
2335 cap->Pat ? SQL_NTS : 0);
2336 break;
2337 case CAT_KEY:
2338 fnc = "SQLPrimaryKeys";
2339 rc = SQLPrimaryKeys(hstmt, name.ptr(2), name.length(2),
2340 name.ptr(1), name.length(1),
2341 name.ptr(0), name.length(0));
2342 break;
2343 case CAT_STAT:
2344 fnc = "SQLStatistics";
2345 rc = SQLStatistics(hstmt, name.ptr(2), name.length(2),
2346 name.ptr(1), name.length(1),
2347 name.ptr(0), name.length(0),
2348 cap->Unique, cap->Accuracy);
2349 break;
2350 case CAT_SPC:
2351 ThrowDBX("SQLSpecialColumns not available yet");
2352 default:
2353 ThrowDBX("Invalid SQL function id");
2354 } // endswitch infotype
2355
2356 if (!Check(rc))
2357 ThrowDBX(rc, fnc, hstmt);
2358
2359 // Some data source do not implement SQLNumResultCols
2360 if (Check(SQLNumResultCols(hstmt, &ncol)))
2361 // n because we no more ignore the first column
2362 if ((n = (UWORD)qrp->Nbcol) > (UWORD)ncol)
2363 ThrowDBX(MSG(COL_NUM_MISM));
2364
2365 // Unconditional to handle STRBLK's
2366 pval = (PVAL *)PlugSubAlloc(g, NULL, n * sizeof(PVAL));
2367 vlen = (SQLLEN *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN));
2368 pbuf = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
2369
2370 // Now bind the column buffers
2371 for (n = 0, crp = qrp->Colresp; crp; crp = crp->Next) {
2372 if ((tp = GetSQLCType(crp->Type)) == SQL_TYPE_NULL) {
2373 sprintf(g->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name);
2374 ThrowDBX(g->Message);
2375 } // endif tp
2376
2377 if (!(len = GetTypeSize(crp->Type, crp->Length))) {
2378 len = 255; // for STRBLK's
2379 ((STRBLK*)crp->Kdata)->SetSorted(true);
2380 } // endif len
2381
2382 pval[n] = AllocateValue(g, crp->Type, len);
2383 pval[n]->SetNullable(true);
2384
2385 if (crp->Type == TYPE_STRING) {
2386 pbuf[n] = (char*)PlugSubAlloc(g, NULL, len);
2387 buffer = pbuf[n];
2388 } else
2389 buffer = pval[n]->GetTo_Val();
2390
2391 vl = vlen + n;
2392
2393 // n + 1 because column numbers begin with 1
2394 rc = SQLBindCol(hstmt, n + 1, tp, buffer, len, vl);
2395
2396 if (!Check(rc))
2397 ThrowDBX(rc, "SQLBindCol", hstmt);
2398
2399 n++;
2400 } // endfor crp
2401
2402 fnc = "SQLFetch";
2403
2404 // Now fetch the result
2405 // Extended fetch cannot be used because of STRBLK's
2406 for (i = 0; i < qrp->Maxres; i++) {
2407 if ((rc = SQLFetch(hstmt)) == SQL_NO_DATA_FOUND)
2408 break;
2409 else if (rc != SQL_SUCCESS) {
2410 if (trace(2) || (trace(1) && rc != SQL_SUCCESS_WITH_INFO)) {
2411 UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1];
2412 UCHAR state[SQL_SQLSTATE_SIZE + 1];
2413 RETCODE erc;
2414 SDWORD native;
2415
2416 htrc("SQLFetch: row %d rc=%d\n", i+1, rc);
2417 erc = SQLError(m_henv, m_hdbc, hstmt, state, &native, msg,
2418 SQL_MAX_MESSAGE_LENGTH - 1, &len);
2419
2420 if (rc != SQL_INVALID_HANDLE)
2421 // Skip non-errors
2422 for (n = 0; n < MAX_NUM_OF_MSG
2423 && (erc == SQL_SUCCESS || erc == SQL_SUCCESS_WITH_INFO)
2424 && strcmp((char*)state, "00000"); n++) {
2425 htrc("%s: %s, Native=%d\n", state, msg, native);
2426 erc = SQLError(m_henv, m_hdbc, hstmt, state, &native,
2427 msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
2428 } // endfor n
2429
2430 } // endif trace
2431
2432 if (rc != SQL_SUCCESS_WITH_INFO)
2433 qrp->BadLines++;
2434
2435 } // endif rc
2436
2437 for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
2438 if (vlen[n] == SQL_NO_TOTAL)
2439 ThrowDBX("Unexpected SQL_NO_TOTAL returned from SQLFetch");
2440 else if (vlen[n] == SQL_NULL_DATA)
2441 pval[n]->SetNull(true);
2442 else if (crp->Type == TYPE_STRING/* && vlen[n] != SQL_NULL_DATA*/)
2443 pval[n]->SetValue_char(pbuf[n], (int)vlen[n]);
2444 else
2445 pval[n]->SetNull(false);
2446
2447 crp->Kdata->SetValue(pval[n], i);
2448 cap->Vlen[n][i] = vlen[n];
2449 } // endfor crp
2450
2451 } // endfor i
2452
2453 #if 0
2454 if ((crow = i) && (rc == SQL_NO_DATA || rc == SQL_SUCCESS_WITH_INFO))
2455 rc = SQL_SUCCESS;
2456
2457 if (rc == SQL_NO_DATA_FOUND) {
2458 if (cap->Pat)
2459 sprintf(g->Message, MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat);
2460 else
2461 sprintf(g->Message, MSG(NO_TAB_DATA), cap->Tab);
2462
2463 ThrowDBX(g->Message);
2464 } else if (rc == SQL_SUCCESS) {
2465 if ((rc = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND)
2466 qrp->Truncated = true;
2467
2468 } else
2469 ThrowDBX(rc, fnc, hstmt);
2470 #endif // 0
2471
2472 if (!rc || rc == SQL_NO_DATA || rc == SQL_SUCCESS_WITH_INFO) {
2473 if ((rc = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND)
2474 qrp->Truncated = true;
2475
2476 crow = i;
2477 } else
2478 ThrowDBX(rc, fnc, hstmt);
2479
2480 irc = (int)crow;
2481 } catch(DBX *x) {
2482 if (trace(1))
2483 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
2484 htrc(x->m_ErrMsg[i]);
2485
2486 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2487 irc = -1;
2488 } // end try/catch
2489
2490 if (b)
2491 SQLCancel(hstmt);
2492
2493 // All this (hstmt vs> m_hstmt) to be revisited
2494 if (hstmt)
2495 rc = SQLFreeStmt(hstmt, SQL_DROP);
2496
2497 return irc;
2498 } // end of GetCatInfo
2499
2500 /***********************************************************************/
2501 /* Allocate a CONNECT result structure from the ODBC result. */
2502 /***********************************************************************/
2503 PQRYRES ODBConn::AllocateResult(PGLOBAL g)
2504 {
2505 bool uns;
2506 PODBCCOL colp;
2507 PCOLRES *pcrp, crp;
2508 PQRYRES qrp;
2509
2510 if (!m_Rows) {
2511 strcpy(g->Message, "Void result");
2512 return NULL;
2513 } // endif m_Res
2514
2515 /*********************************************************************/
2516 /* Allocate the result storage for future retrieval. */
2517 /*********************************************************************/
2518 qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
2519 pcrp = &qrp->Colresp;
2520 qrp->Continued = FALSE;
2521 qrp->Truncated = FALSE;
2522 qrp->Info = FALSE;
2523 qrp->Suball = TRUE;
2524 qrp->BadLines = 0;
2525 qrp->Maxsize = m_Rows;
2526 qrp->Maxres = m_Rows;
2527 qrp->Nbcol = 0;
2528 qrp->Nblin = 0;
2529 qrp->Cursor = 0;
2530
2531 for (colp = (PODBCCOL)m_Tdb->Columns; colp;
2532 colp = (PODBCCOL)colp->GetNext())
2533 if (!colp->IsSpecial()) {
2534 *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
2535 crp = *pcrp;
2536 pcrp = &crp->Next;
2537 memset(crp, 0, sizeof(COLRES));
2538 crp->Ncol = ++qrp->Nbcol;
2539 crp->Name = colp->GetName();
2540 crp->Type = colp->GetResultType();
2541 crp->Prec = colp->GetScale();
2542 crp->Length = colp->GetLength();
2543 crp->Clen = colp->GetBuflen();
2544 uns = colp->IsUnsigned();
2545
2546 if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
2547 crp->Clen, 0, FALSE, TRUE, uns))) {
2548 sprintf(g->Message, MSG(INV_RESULT_TYPE),
2549 GetFormatType(crp->Type));
2550 return NULL;
2551 } // endif Kdata
2552
2553 if (!colp->IsNullable())
2554 crp->Nulls = NULL;
2555 else {
2556 crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
2557 memset(crp->Nulls, ' ', m_Rows);
2558 } // endelse Nullable
2559
2560 colp->SetCrp(crp);
2561 } // endif colp
2562
2563 *pcrp = NULL;
2564 //qrp->Nblin = n;
2565 return qrp;
2566 } // end of AllocateResult
2567
2568 /***********************************************************************/
2569 /* Restart from beginning of result set */
2570 /***********************************************************************/
2571 int ODBConn::Rewind(char *sql, ODBCCOL *tocols)
2572 {
2573 int rc, rbuf = -1;
2574
2575 if (!m_hstmt)
2576 rbuf = -1;
2577 else if (m_Full)
2578 rbuf = m_Rows; // No need to "rewind"
2579 else if (m_Scrollable) {
2580 SQLULEN crow;
2581
2582 try {
2583 rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_FIRST, 1, &crow, NULL);
2584
2585 if (!Check(rc))
2586 ThrowDBX(rc, "SQLExtendedFetch", m_hstmt);
2587
2588 rbuf = (int)crow;
2589 } catch(DBX *x) {
2590 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2591 rbuf = -1;
2592 } // end try/catch
2593
2594 } else if (ExecDirectSQL(sql, tocols) >= 0)
2595 rbuf = 0;
2596
2597 return rbuf;
2598 } // end of Rewind
2599
2600 /***********************************************************************/
2601 /* Disconnect connection */
2602 /***********************************************************************/
2603 void ODBConn::Close()
2604 {
2605 RETCODE rc;
2606
2607 if (m_hstmt) {
2608 // Is required for multiple tables
2609 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
2610 m_hstmt = NULL;
2611 } // endif m_hstmt
2612
2613 if (m_hdbc != SQL_NULL_HDBC) {
2614 if (m_Transact) {
2615 rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT);
2616 m_Transact = false;
2617 } // endif m_Transact
2618
2619 rc = SQLDisconnect(m_hdbc);
2620
2621 if (trace(1) && rc != SQL_SUCCESS)
2622 htrc("Error: SQLDisconnect rc=%d\n", rc);
2623
2624 rc = SQLFreeConnect(m_hdbc);
2625
2626 if (trace(1) && rc != SQL_SUCCESS)
2627 htrc("Error: SQLFreeConnect rc=%d\n", rc);
2628
2629 m_hdbc = SQL_NULL_HDBC;
2630 } // endif m_hdbc
2631
2632 if (m_henv != SQL_NULL_HENV) {
2633 rc = SQLFreeEnv(m_henv);
2634
2635 if (trace(1) && rc != SQL_SUCCESS) // Nothing we can do
2636 htrc("Error: SQLFreeEnv failure ignored in Close\n");
2637
2638 m_henv = SQL_NULL_HENV;
2639 } // endif m_henv
2640
2641 if (m_Fp)
2642 m_Fp->Count = 0;
2643
2644 } // end of Close
2645