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 PGLOBAL& g = m_G;
1293 PDBUSER dup = PlgGetUser(g);
1294
1295 //if (Options & noOdbcDialog || dup->Remote)
1296 wConnectOption = SQL_DRIVER_NOPROMPT;
1297 //else if (Options & forceOdbcDialog)
1298 // wConnectOption = SQL_DRIVER_PROMPT;
1299
1300 rc = SQLDriverConnect(m_hdbc, hWnd, (PUCHAR)m_Connect,
1301 SQL_NTS, ConnOut, MAX_CONNECT_LEN,
1302 &nResult, wConnectOption);
1303
1304 #if defined(_WIN32)
1305 if (hWndTop)
1306 EnableWindow(hWndTop, true);
1307 #endif // _WIN32
1308
1309 // If user hit 'Cancel'
1310 if (rc == SQL_NO_DATA_FOUND) {
1311 Close();
1312 // Free();
1313 return true;
1314 } // endif rc
1315
1316 if (!Check(rc))
1317 ThrowDBX(rc, "SQLDriverConnect");
1318
1319 // Save connect string returned from ODBC
1320 m_Connect = (PSZ)ConnOut;
1321
1322 // All done
1323 return false;
1324 } // end of DriverConnect
1325
VerifyConnect()1326 void ODBConn::VerifyConnect()
1327 {
1328 #if defined(NEWMSG) || defined(XMSG)
1329 PGLOBAL& g = m_G;
1330 #endif // NEWMSG || XMSG
1331 RETCODE rc;
1332 SWORD result;
1333 SWORD conformance;
1334
1335 rc = SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
1336 &conformance, sizeof(conformance), &result);
1337
1338 if (!Check(rc))
1339 ThrowDBX(rc, "SQLGetInfo");
1340
1341 if (conformance < SQL_OAC_LEVEL1)
1342 ThrowDBX(MSG(API_CONF_ERROR));
1343
1344 rc = SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
1345 &conformance, sizeof(conformance), &result);
1346
1347 if (!Check(rc))
1348 ThrowDBX(rc, "SQLGetInfo");
1349
1350 if (conformance < SQL_OSC_MINIMUM)
1351 ThrowDBX(MSG(SQL_CONF_ERROR));
1352
1353 } // end of VerifyConnect
1354
GetConnectInfo()1355 void ODBConn::GetConnectInfo()
1356 {
1357 RETCODE rc;
1358 SWORD nResult;
1359 #if 0 // Update not implemented yet
1360 UDWORD DrvPosOp;
1361
1362 // Reset the database update options
1363 m_UpdateOptions = 0;
1364
1365 // Check for SQLSetPos support
1366 rc = SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS,
1367 &DrvPosOp, sizeof(DrvPosOp), &nResult);
1368
1369 if (Check(rc) &&
1370 (DrvPosOp & SQL_POS_UPDATE) &&
1371 (DrvPosOp & SQL_POS_DELETE) &&
1372 (DrvPosOp & SQL_POS_ADD))
1373 m_UpdateOptions = SQL_SETPOSUPDATES;
1374
1375 // Check for positioned update SQL support
1376 UDWORD PosStatements;
1377
1378 rc = SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS,
1379 &PosStatements, sizeof(PosStatements),
1380 &nResult);
1381
1382 if (Check(rc) &&
1383 (PosStatements & SQL_PS_POSITIONED_DELETE) &&
1384 (PosStatements & SQL_PS_POSITIONED_UPDATE))
1385 m_UpdateOptions |= SQL_POSITIONEDSQL;
1386
1387 if (m_Updatable) {
1388 // Make sure data source is Updatable
1389 char ReadOnly[10];
1390
1391 rc = SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
1392 ReadOnly, sizeof(ReadOnly), &nResult);
1393
1394 if (Check(rc) && nResult == 1)
1395 m_Updatable = !!strcmp(ReadOnly, "Y");
1396 else
1397 m_Updatable = false;
1398
1399 if (trace(1))
1400 htrc("Warning: data source is readonly\n");
1401
1402 } else // Make data source is !Updatable
1403 rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
1404 SQL_MODE_READ_ONLY);
1405 #endif // 0
1406
1407 // Get the quote char to use when constructing SQL
1408 rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
1409 m_IDQuoteChar, sizeof(m_IDQuoteChar), &nResult);
1410
1411 if (trace(1))
1412 htrc("DBMS: %s, Version: %s, rc=%d\n",
1413 GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER), rc);
1414
1415 } // end of GetConnectInfo
1416
1417 /***********************************************************************/
1418 /* Allocate record set and execute an SQL query. */
1419 /***********************************************************************/
ExecDirectSQL(char * sql,ODBCCOL * tocols)1420 int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
1421 {
1422 PGLOBAL& g = m_G;
1423 void *buffer;
1424 bool b;
1425 UWORD n, k;
1426 SWORD len, tp, ncol = 0;
1427 ODBCCOL *colp;
1428 RETCODE rc;
1429 HSTMT hstmt;
1430
1431 try {
1432 b = false;
1433
1434 if (m_hstmt) {
1435 // This is a Requery
1436 rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
1437
1438 if (!Check(rc))
1439 ThrowDBX(rc, "SQLFreeStmt", m_hstmt);
1440
1441 m_hstmt = NULL;
1442 } // endif m_hstmt
1443
1444 rc = SQLAllocStmt(m_hdbc, &hstmt);
1445
1446 if (!Check(rc))
1447 ThrowDBX(rc, "SQLAllocStmt");
1448
1449 if (m_Scrollable) {
1450 rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
1451 (void*)SQL_SCROLLABLE, 0);
1452
1453 if (!Check(rc))
1454 ThrowDBX(rc, "Scrollable", hstmt);
1455
1456 } // endif m_Scrollable
1457
1458 OnSetOptions(hstmt);
1459 b = true;
1460
1461 if (trace(1))
1462 htrc("ExecDirect hstmt=%p %.256s\n", hstmt, sql);
1463
1464 if (m_Tdb->Srcdef) {
1465 // Be sure this is a query returning a result set
1466 do {
1467 rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
1468 } while (rc == SQL_STILL_EXECUTING);
1469
1470 if (!Check(rc))
1471 ThrowDBX(rc, "SQLPrepare", hstmt);
1472
1473 if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
1474 ThrowDBX(rc, "SQLNumResultCols", hstmt);
1475
1476 if (ncol == 0) {
1477 strcpy(g->Message, "This Srcdef does not return a result set");
1478 return -1;
1479 } // endif ncol
1480
1481 // Ok, now we can proceed
1482 do {
1483 rc = SQLExecute(hstmt);
1484 } while (rc == SQL_STILL_EXECUTING);
1485
1486 if (!Check(rc))
1487 ThrowDBX(rc, "SQLExecute", hstmt);
1488
1489 } else {
1490 do {
1491 rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
1492 } while (rc == SQL_STILL_EXECUTING);
1493
1494 if (!Check(rc))
1495 ThrowDBX(rc, "SQLExecDirect", hstmt);
1496
1497 do {
1498 rc = SQLNumResultCols(hstmt, &ncol);
1499 } while (rc == SQL_STILL_EXECUTING);
1500
1501 k = 0; // used for column number
1502 } // endif Srcdef
1503
1504 for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
1505 if (!colp->IsSpecial())
1506 n++;
1507
1508 // n can be 0 for query such as Select count(*) from table
1509 if (n && n > (UWORD)ncol)
1510 ThrowDBX(MSG(COL_NUM_MISM));
1511
1512 // Now bind the column buffers
1513 for (colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
1514 if (!colp->IsSpecial()) {
1515 buffer = colp->GetBuffer(m_RowsetSize);
1516 len = colp->GetBuflen();
1517 tp = GetSQLCType(colp->GetResultType());
1518
1519 if (tp == SQL_TYPE_NULL) {
1520 sprintf(m_G->Message, MSG(INV_COLUMN_TYPE),
1521 colp->GetResultType(), SVP(colp->GetName()));
1522 ThrowDBX(m_G->Message);
1523 } // endif tp
1524
1525 if (m_Tdb->Srcdef)
1526 k = colp->GetIndex();
1527 else
1528 k++;
1529
1530 if (trace(1))
1531 htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n",
1532 k, tp, buffer, len, colp->GetStrLen());
1533
1534 rc = SQLBindCol(hstmt, k, tp, buffer, len, colp->GetStrLen());
1535
1536 if (!Check(rc))
1537 ThrowDBX(rc, "SQLBindCol", hstmt);
1538
1539 } // endif colp
1540
1541 } catch(DBX *x) {
1542 if (trace(1))
1543 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1544 htrc(x->m_ErrMsg[i]);
1545
1546 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1547
1548 if (b)
1549 SQLCancel(hstmt);
1550
1551 rc = SQLFreeStmt(hstmt, SQL_DROP);
1552 m_hstmt = NULL;
1553 return -1;
1554 } // end try/catch
1555
1556 m_hstmt = hstmt;
1557 return (int)m_RowsetSize; // May have been reset in OnSetOptions
1558 } // end of ExecDirectSQL
1559
1560 /***********************************************************************/
1561 /* Get the number of lines of the result set. */
1562 /***********************************************************************/
GetResultSize(char * sql,ODBCCOL * colp)1563 int ODBConn::GetResultSize(char *sql, ODBCCOL *colp)
1564 {
1565 int n = 0;
1566 RETCODE rc;
1567
1568 if (ExecDirectSQL(sql, colp) < 0)
1569 return -1;
1570
1571 try {
1572 for (n = 0; ; n++) {
1573 do {
1574 rc = SQLFetch(m_hstmt);
1575 } while (rc == SQL_STILL_EXECUTING);
1576
1577 if (!Check(rc))
1578 ThrowDBX(rc, "SQLFetch", m_hstmt);
1579
1580 if (rc == SQL_NO_DATA_FOUND)
1581 break;
1582
1583 } // endfor n
1584
1585 } catch(DBX *x) {
1586 strcpy(m_G->Message, x->GetErrorMessage(0));
1587
1588 if (trace(1))
1589 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1590 htrc(x->m_ErrMsg[i]);
1591
1592 SQLCancel(m_hstmt);
1593 n = -2;
1594 } // end try/catch
1595
1596 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
1597 m_hstmt = NULL;
1598
1599 if (n != 1)
1600 return -3;
1601 else
1602 return colp->GetIntValue();
1603
1604 } // end of GetResultSize
1605
1606 /***********************************************************************/
1607 /* Fetch next row. */
1608 /***********************************************************************/
Fetch(int pos)1609 int ODBConn::Fetch(int pos)
1610 {
1611 ASSERT(m_hstmt);
1612 int irc;
1613 SQLULEN crow;
1614 RETCODE rc;
1615 PGLOBAL& g = m_G;
1616
1617 try {
1618 // do {
1619 if (pos) {
1620 rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_ABSOLUTE, pos, &crow, NULL);
1621 } else if (m_RowsetSize) {
1622 rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_NEXT, 1, &crow, NULL);
1623 } else {
1624 rc = SQLFetch(m_hstmt);
1625 crow = 1;
1626 } // endif m_RowsetSize
1627 // } while (rc == SQL_STILL_EXECUTING);
1628
1629 if (trace(2))
1630 htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n",
1631 m_hstmt, m_RowsetSize, rc);
1632
1633 if (!Check(rc))
1634 ThrowDBX(rc, "Fetching", m_hstmt);
1635
1636 if (rc == SQL_NO_DATA_FOUND) {
1637 m_Full = (m_Fetch == 1);
1638 irc = 0;
1639 } else
1640 irc = (int)crow;
1641
1642 m_Fetch++;
1643 m_Rows += irc;
1644 } catch(DBX *x) {
1645 if (trace(1))
1646 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1647 htrc(x->m_ErrMsg[i]);
1648
1649 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1650 irc = -1;
1651 } // end try/catch
1652
1653 return irc;
1654 } // end of Fetch
1655
1656 /***********************************************************************/
1657 /* Prepare an SQL statement for insert. */
1658 /***********************************************************************/
PrepareSQL(char * sql)1659 int ODBConn::PrepareSQL(char *sql)
1660 {
1661 PGLOBAL& g = m_G;
1662 bool b;
1663 UINT txn = 0;
1664 SWORD nparm;
1665 RETCODE rc;
1666 HSTMT hstmt;
1667
1668 if (m_Tdb->GetMode() != MODE_READ) {
1669 // Does the data source support transactions
1670 rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
1671
1672 if (Check(rc) && txn != SQL_TC_NONE) try {
1673 rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
1674 SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
1675
1676 if (!Check(rc))
1677 ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
1678
1679 m_Transact = true;
1680 } catch(DBX *x) {
1681 if (trace(1))
1682 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1683 htrc(x->m_ErrMsg[i]);
1684
1685 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1686 } // end try/catch
1687
1688 } // endif Mode
1689
1690 try {
1691 b = false;
1692
1693 if (m_hstmt) {
1694 RETCODE rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
1695
1696 hstmt = m_hstmt;
1697 m_hstmt = NULL;
1698
1699 if (m_Tdb->GetAmType() != TYPE_AM_XDBC)
1700 ThrowDBX(MSG(SEQUENCE_ERROR));
1701
1702 } // endif m_hstmt
1703
1704 rc = SQLAllocStmt(m_hdbc, &hstmt);
1705
1706 if (!Check(rc))
1707 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
1708
1709 OnSetOptions(hstmt);
1710 b = true;
1711
1712 if (trace(1))
1713 htrc("Prepare hstmt=%p %.64s\n", hstmt, sql);
1714
1715 do {
1716 rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS);
1717 } while (rc == SQL_STILL_EXECUTING);
1718
1719 if (!Check(rc))
1720 ThrowDBX(rc, "SQLPrepare", hstmt);
1721
1722 do {
1723 rc = SQLNumParams(hstmt, &nparm);
1724 } while (rc == SQL_STILL_EXECUTING);
1725
1726 } catch(DBX *x) {
1727 if (trace(1))
1728 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1729 htrc(x->m_ErrMsg[i]);
1730
1731 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1732
1733 if (b)
1734 SQLCancel(hstmt);
1735
1736 rc = SQLFreeStmt(hstmt, SQL_DROP);
1737 m_hstmt = NULL;
1738
1739 if (m_Transact) {
1740 rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
1741 m_Transact = false;
1742 } // endif m_Transact
1743
1744 return -1;
1745 } // end try/catch
1746
1747 m_hstmt = hstmt;
1748 return (int)nparm;
1749 } // end of PrepareSQL
1750
1751 /***********************************************************************/
1752 /* Execute a prepared statement. */
1753 /***********************************************************************/
ExecuteSQL(void)1754 int ODBConn::ExecuteSQL(void)
1755 {
1756 PGLOBAL& g = m_G;
1757 SWORD ncol = 0;
1758 RETCODE rc;
1759 SQLLEN afrw = -1;
1760
1761 try {
1762 do {
1763 rc = SQLExecute(m_hstmt);
1764 } while (rc == SQL_STILL_EXECUTING);
1765
1766 if (!Check(rc))
1767 ThrowDBX(rc, "SQLExecute", m_hstmt);
1768
1769 if (!Check(rc = SQLNumResultCols(m_hstmt, &ncol)))
1770 ThrowDBX(rc, "SQLNumResultCols", m_hstmt);
1771
1772 if (ncol) {
1773 // This should never happen while inserting
1774 strcpy(g->Message, "Logical error while inserting");
1775 } else {
1776 // Insert, Update or Delete statement
1777 if (!Check(rc = SQLRowCount(m_hstmt, &afrw)))
1778 ThrowDBX(rc, "SQLRowCount", m_hstmt);
1779
1780 } // endif ncol
1781
1782 } catch(DBX *x) {
1783 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1784 SQLCancel(m_hstmt);
1785 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
1786 m_hstmt = NULL;
1787
1788 if (m_Transact) {
1789 rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK);
1790 m_Transact = false;
1791 } // endif m_Transact
1792
1793 afrw = -1;
1794 } // end try/catch
1795
1796 return (int)afrw;
1797 } // end of ExecuteSQL
1798
1799 /***********************************************************************/
1800 /* Bind a parameter for inserting. */
1801 /***********************************************************************/
BindParam(ODBCCOL * colp)1802 bool ODBConn::BindParam(ODBCCOL *colp)
1803 {
1804 void *buf;
1805 int buftype = colp->GetResultType();
1806 SQLUSMALLINT n = colp->GetRank();
1807 SQLSMALLINT ct, sqlt, dec, nul __attribute__((unused));
1808 SQLULEN colsize;
1809 SQLLEN len;
1810 SQLLEN *strlen = colp->GetStrLen();
1811 SQLRETURN rc;
1812
1813 #if 0
1814 try {
1815 // This function is often not or badly implemented by data sources
1816 rc = SQLDescribeParam(m_hstmt, n, &sqlt, &colsize, &dec, &nul);
1817
1818 if (!Check(rc))
1819 ThrowDBX(rc, "SQLDescribeParam", m_hstmt);
1820
1821 } catch(DBX *x) {
1822 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
1823 #endif // 0
1824 colsize = colp->GetPrecision();
1825 sqlt = GetSQLType(buftype);
1826 dec = IsTypeNum(buftype) ? colp->GetScale() : 0;
1827 nul = colp->IsNullable() ? SQL_NULLABLE : SQL_NO_NULLS;
1828 //} // end try/catch
1829
1830 buf = colp->GetBuffer(0);
1831 len = IsTypeChar(buftype) ? colp->GetBuflen() : 0;
1832 ct = GetSQLCType(buftype);
1833 *strlen = IsTypeChar(buftype) ? SQL_NTS : 0;
1834
1835 try {
1836 rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt,
1837 colsize, dec, buf, len, strlen);
1838
1839 if (!Check(rc))
1840 ThrowDBX(rc, "SQLBindParameter", m_hstmt);
1841
1842 } catch(DBX *x) {
1843 strcpy(m_G->Message, x->GetErrorMessage(0));
1844 SQLCancel(m_hstmt);
1845 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
1846 m_hstmt = NULL;
1847 return true;
1848 } // end try/catch
1849
1850 return false;
1851 } // end of BindParam
1852
1853 /***********************************************************************/
1854 /* Execute an SQL command. */
1855 /***********************************************************************/
1856 bool ODBConn::ExecSQLcommand(char *sql)
1857 {
1858 char cmd[16];
1859 bool b, rcd = false;
1860 UINT txn = 0;
1861 PGLOBAL& g = m_G;
1862 SWORD ncol = 0;
1863 SQLLEN afrw;
1864 RETCODE rc;
1865 HSTMT hstmt;
1866
1867 try {
1868 b = FALSE;
1869
1870 // Check whether we should use transaction
1871 if (sscanf(sql, " %15s ", cmd) == 1) {
1872 if (!stricmp(cmd, "INSERT") || !stricmp(cmd, "UPDATE") ||
1873 !stricmp(cmd, "DELETE") || !stricmp(cmd, "REPLACE")) {
1874 // Does the data source support transactions
1875 rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL);
1876
1877 if (Check(rc) && txn != SQL_TC_NONE) {
1878 rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
1879 SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
1880
1881 if (!Check(rc))
1882 ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr");
1883
1884 m_Transact = TRUE;
1885 } // endif txn
1886
1887 } // endif cmd
1888
1889 } // endif sql
1890
1891 // Allocate the statement handle
1892 rc = SQLAllocStmt(m_hdbc, &hstmt);
1893
1894 if (!Check(rc))
1895 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
1896
1897 OnSetOptions(hstmt);
1898 b = true;
1899
1900 if (trace(1))
1901 htrc("ExecSQLcommand hstmt=%p %.64s\n", hstmt, sql);
1902
1903 // Proceed with command execution
1904 do {
1905 rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS);
1906 } while (rc == SQL_STILL_EXECUTING);
1907
1908 if (!Check(rc))
1909 ThrowDBX(rc, "SQLExecDirect", hstmt);
1910
1911 // Check whether this is a query returning a result set
1912 if (!Check(rc = SQLNumResultCols(hstmt, &ncol)))
1913 ThrowDBX(rc, "SQLNumResultCols", hstmt);
1914
1915 if (!ncol) {
1916 if (!Check(SQLRowCount(hstmt, &afrw)))
1917 ThrowDBX(rc, "SQLRowCount", hstmt);
1918
1919 m_Tdb->AftRows = (int)afrw;
1920 strcpy(g->Message, "Affected rows");
1921 } else {
1922 m_Tdb->AftRows = (int)ncol;
1923 strcpy(g->Message, "Result set column number");
1924 } // endif ncol
1925
1926 } catch(DBX *x) {
1927 if (trace(1))
1928 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
1929 htrc(x->m_ErrMsg[i]);
1930
1931 sprintf(g->Message, "Remote %s: %s", x->m_Msg, x->GetErrorMessage(0));
1932
1933 if (b)
1934 SQLCancel(hstmt);
1935
1936 m_Tdb->AftRows = -1;
1937 rcd = true;
1938 } // end try/catch
1939
1940 if (!Check(rc = SQLFreeStmt(hstmt, SQL_CLOSE)))
1941 sprintf(g->Message, "SQLFreeStmt: rc=%d", rc);
1942
1943 if (m_Transact) {
1944 // Terminate the transaction
1945 if (!Check(rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc,
1946 (rcd) ? SQL_ROLLBACK : SQL_COMMIT)))
1947 sprintf(g->Message, "SQLEndTran: rc=%d", rc);
1948
1949 if (!Check(rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT,
1950 (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER)))
1951 sprintf(g->Message, "SQLSetConnectAttr: rc=%d", rc);
1952
1953 m_Transact = false;
1954 } // endif m_Transact
1955
1956 return rcd;
1957 } // end of ExecSQLcommand
1958
1959 /**************************************************************************/
1960 /* GetMetaData: constructs the result blocks containing the */
1961 /* description of all the columns of an SQL command. */
1962 /**************************************************************************/
1963 PQRYRES ODBConn::GetMetaData(PGLOBAL g, PCSZ dsn, PCSZ src)
1964 {
1965 static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT,
1966 TYPE_SHORT, TYPE_SHORT};
1967 static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC,
1968 FLD_SCALE, FLD_NULL};
1969 static unsigned int length[] = {0, 6, 10, 6, 6};
1970 unsigned char cn[60];
1971 int qcol = 5;
1972 short nl, type, prec, nul, cns = (short)sizeof(cn);
1973 PQRYRES qrp = NULL;
1974 PCOLRES crp;
1975 USHORT i;
1976 SQLULEN n;
1977 SWORD ncol;
1978 RETCODE rc;
1979 HSTMT hstmt;
1980
1981 try {
1982 rc = SQLAllocStmt(m_hdbc, &hstmt);
1983
1984 if (!Check(rc))
1985 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
1986
1987 OnSetOptions(hstmt);
1988
1989 do {
1990 rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS);
1991 // rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS);
1992 } while (rc == SQL_STILL_EXECUTING);
1993
1994 if (!Check(rc))
1995 ThrowDBX(rc, "SQLExecDirect", hstmt);
1996
1997 do {
1998 rc = SQLNumResultCols(hstmt, &ncol);
1999 } while (rc == SQL_STILL_EXECUTING);
2000
2001 if (!Check(rc))
2002 ThrowDBX(rc, "SQLNumResultCols", hstmt);
2003
2004 if (ncol) for (i = 1; i <= ncol; i++) {
2005 do {
2006 rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL);
2007 } while (rc == SQL_STILL_EXECUTING);
2008
2009 if (!Check(rc))
2010 ThrowDBX(rc, "SQLDescribeCol", hstmt);
2011
2012 length[0] = MY_MAX(length[0], (UINT)nl);
2013 } // endfor i
2014
2015 } catch(DBX *x) {
2016 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2017 goto err;
2018 } // end try/catch
2019
2020 if (!ncol) {
2021 strcpy(g->Message, "Invalid Srcdef");
2022 goto err;
2023 } // endif ncol
2024
2025 /************************************************************************/
2026 /* Allocate the structures used to refer to the result set. */
2027 /************************************************************************/
2028 if (!(qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3,
2029 buftyp, fldtyp, length, false, true)))
2030 return NULL;
2031
2032 // Some columns must be renamed
2033 for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
2034 switch (++i) {
2035 case 3: crp->Name = "Precision"; break;
2036 case 4: crp->Name = "Scale"; break;
2037 case 5: crp->Name = "Nullable"; break;
2038 } // endswitch i
2039
2040 /************************************************************************/
2041 /* Now get the results into blocks. */
2042 /************************************************************************/
2043 try {
2044 for (i = 0; i < ncol; i++) {
2045 do {
2046 rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul);
2047 } while (rc == SQL_STILL_EXECUTING);
2048
2049 if (!Check(rc))
2050 ThrowDBX(rc, "SQLDescribeCol", hstmt);
2051 else
2052 qrp->Nblin++;
2053
2054 crp = qrp->Colresp; // Column_Name
2055 crp->Kdata->SetValue((char*)cn, i);
2056 crp = crp->Next; // Data_Type
2057 crp->Kdata->SetValue(type, i);
2058 crp = crp->Next; // Precision (length)
2059 crp->Kdata->SetValue((int)n, i);
2060 crp = crp->Next; // Scale
2061 crp->Kdata->SetValue(prec, i);
2062 crp = crp->Next; // Nullable
2063 crp->Kdata->SetValue(nul, i);
2064 } // endfor i
2065
2066 } catch(DBX *x) {
2067 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2068 qrp = NULL;
2069 } // end try/catch
2070
2071 /* Cleanup */
2072 err:
2073 SQLCancel(hstmt);
2074 rc = SQLFreeStmt(hstmt, SQL_DROP);
2075 Close();
2076
2077 /************************************************************************/
2078 /* Return the result pointer for use by GetData routines. */
2079 /************************************************************************/
2080 return qrp;
2081 } // end of GetMetaData
2082
2083 /***********************************************************************/
2084 /* Get the list of Data Sources and set it in qrp. */
2085 /***********************************************************************/
2086 bool ODBConn::GetDataSources(PQRYRES qrp)
2087 {
2088 bool rv = false;
2089 UCHAR *dsn, *des;
2090 UWORD dir = SQL_FETCH_FIRST;
2091 SWORD n1, n2, p1, p2;
2092 PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
2093 RETCODE rc;
2094
2095 n1 = crp1->Clen;
2096 n2 = crp2->Clen;
2097
2098 try {
2099 rc = SQLAllocEnv(&m_henv);
2100
2101 if (!Check(rc))
2102 ThrowDBX(rc, "SQLAllocEnv"); // Fatal
2103
2104 for (int i = 0; i < qrp->Maxres; i++) {
2105 dsn = (UCHAR*)crp1->Kdata->GetValPtr(i);
2106 des = (UCHAR*)crp2->Kdata->GetValPtr(i);
2107 rc = SQLDataSources(m_henv, dir, dsn, n1, &p1, des, n2, &p2);
2108
2109 if (rc == SQL_NO_DATA_FOUND)
2110 break;
2111 else if (!Check(rc))
2112 ThrowDBX(rc, "SQLDataSources");
2113
2114 qrp->Nblin++;
2115 dir = SQL_FETCH_NEXT;
2116 } // endfor i
2117
2118 } catch(DBX *x) {
2119 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2120 rv = true;
2121 } // end try/catch
2122
2123 Close();
2124 return rv;
2125 } // end of GetDataSources
2126
2127 /***********************************************************************/
2128 /* Get the list of Drivers and set it in qrp. */
2129 /***********************************************************************/
2130 bool ODBConn::GetDrivers(PQRYRES qrp)
2131 {
2132 int i, n;
2133 bool rv = false;
2134 UCHAR *des, *att;
2135 UWORD dir = SQL_FETCH_FIRST;
2136 SWORD n1, n2, p1, p2;
2137 PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next;
2138 RETCODE rc;
2139
2140 n1 = crp1->Clen;
2141 n2 = crp2->Clen;
2142
2143 try {
2144 rc = SQLAllocEnv(&m_henv);
2145
2146 if (!Check(rc))
2147 ThrowDBX(rc, "SQLAllocEnv"); // Fatal
2148
2149 for (n = 0; n < qrp->Maxres; n++) {
2150 des = (UCHAR*)crp1->Kdata->GetValPtr(n);
2151 att = (UCHAR*)crp2->Kdata->GetValPtr(n);
2152 rc = SQLDrivers(m_henv, dir, des, n1, &p1, att, n2, &p2);
2153
2154 if (rc == SQL_NO_DATA_FOUND)
2155 break;
2156 else if (!Check(rc))
2157 ThrowDBX(rc, "SQLDrivers");
2158
2159
2160 // The attributes being separated by '\0', set them to ';'
2161 for (i = 0; i < p2; i++)
2162 if (!att[i])
2163 att[i] = ';';
2164
2165 qrp->Nblin++;
2166 dir = SQL_FETCH_NEXT;
2167 } // endfor n
2168
2169 } catch(DBX *x) {
2170 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2171 rv = true;
2172 } // end try/catch
2173
2174 Close();
2175 return rv;
2176 } // end of GetDrivers
2177
2178 /***********************************************************************/
2179 /* A helper class to split an optionally qualified table name into */
2180 /* components. */
2181 /* These formats are understood: */
2182 /* "CatalogName.SchemaName.TableName" */
2183 /* "SchemaName.TableName" */
2184 /* "TableName" */
2185 /***********************************************************************/
2186 class SQLQualifiedName
2187 {
2188 static const uint max_parts= 3; // Catalog.Schema.Table
2189 MYSQL_LEX_STRING m_part[max_parts];
2190 char m_buf[512];
2191
2192 void lex_string_set(MYSQL_LEX_STRING *S, char *str, size_t length)
2193 {
2194 S->str= str;
2195 S->length= length;
2196 } // end of lex_string_set
2197
2198 void lex_string_shorten_down(MYSQL_LEX_STRING *S, size_t offs)
2199 {
2200 DBUG_ASSERT(offs <= S->length);
2201 S->str+= offs;
2202 S->length-= offs;
2203 } // end of lex_string_shorten_down
2204
2205 /*********************************************************************/
2206 /* Find the rightmost '.' delimiter and return the length */
2207 /* of the qualifier, including the rightmost '.' delimier. */
2208 /* For example, for the string {"a.b.c",5} it will return 4, */
2209 /* which is the length of the qualifier "a.b." */
2210 /*********************************************************************/
2211 size_t lex_string_find_qualifier(MYSQL_LEX_STRING *S)
2212 {
2213 size_t i;
2214 for (i= S->length; i > 0; i--)
2215 {
2216 if (S->str[i - 1] == '.')
2217 {
2218 S->str[i - 1]= '\0';
2219 return i;
2220 }
2221 }
2222 return 0;
2223 } // end of lex_string_find_qualifier
2224
2225 public:
2226 /*********************************************************************/
2227 /* Initialize to the given optionally qualified name. */
2228 /* NULL pointer in "name" is supported. */
2229 /* name qualifier has precedence over schema. */
2230 /*********************************************************************/
2231 SQLQualifiedName(CATPARM *cap)
2232 {
2233 const char *name = (const char *)cap->Tab;
2234 char *db = (char *)cap->DB;
2235 size_t len, i;
2236
2237 // Initialize the parts
2238 for (i = 0 ; i < max_parts; i++)
2239 lex_string_set(&m_part[i], NULL, 0);
2240
2241 if (name) {
2242 // Initialize the first (rightmost) part
2243 lex_string_set(&m_part[0], m_buf,
2244 strmake(m_buf, name, sizeof(m_buf) - 1) - m_buf);
2245
2246 // Initialize the other parts, if exist.
2247 for (i= 1; i < max_parts; i++) {
2248 if (!(len= lex_string_find_qualifier(&m_part[i - 1])))
2249 break;
2250
2251 lex_string_set(&m_part[i], m_part[i - 1].str, len - 1);
2252 lex_string_shorten_down(&m_part[i - 1], len);
2253 } // endfor i
2254
2255 } // endif name
2256
2257 // If it was not specified, set schema as the passed db name
2258 if (db && !m_part[1].length)
2259 lex_string_set(&m_part[1], db, strlen(db));
2260
2261 } // end of SQLQualifiedName
2262
2263 SQLCHAR *ptr(uint i)
2264 {
2265 DBUG_ASSERT(i < max_parts);
2266 return (SQLCHAR *) (m_part[i].length ? m_part[i].str : NULL);
2267 } // end of ptr
2268
2269 SQLSMALLINT length(uint i)
2270 {
2271 DBUG_ASSERT(i < max_parts);
2272 return (SQLSMALLINT)m_part[i].length;
2273 } // end of length
2274
2275 }; // end of class SQLQualifiedName
2276
2277 /***********************************************************************/
2278 /* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */
2279 /***********************************************************************/
2280 int ODBConn::GetCatInfo(CATPARM *cap)
2281 {
2282 PGLOBAL& g = m_G;
2283 void *buffer;
2284 int i, irc;
2285 bool b;
2286 PCSZ fnc = "Unknown";
2287 UWORD n = 0;
2288 SWORD ncol, len, tp;
2289 SQLULEN crow = 0;
2290 PQRYRES qrp = cap->Qrp;
2291 PCOLRES crp;
2292 RETCODE rc = 0;
2293 HSTMT hstmt = NULL;
2294 SQLLEN *vl, *vlen = NULL;
2295 PVAL *pval = NULL;
2296 char* *pbuf = NULL;
2297
2298 try {
2299 b = false;
2300
2301 if (!m_hstmt) {
2302 rc = SQLAllocStmt(m_hdbc, &hstmt);
2303
2304 if (!Check(rc))
2305 ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt");
2306
2307 } else
2308 ThrowDBX(MSG(SEQUENCE_ERROR));
2309
2310 b = true;
2311
2312 // Currently m_Catver should be always 0 here
2313 assert(!m_Catver); // This may be temporary
2314
2315 if (qrp->Maxres > 0)
2316 m_RowsetSize = 1;
2317 else
2318 ThrowDBX("0-sized result");
2319
2320 SQLQualifiedName name(cap);
2321
2322 // Now do call the proper ODBC API
2323 switch (cap->Id) {
2324 case CAT_TAB:
2325 fnc = "SQLTables";
2326 rc = SQLTables(hstmt, name.ptr(2), name.length(2),
2327 name.ptr(1), name.length(1),
2328 name.ptr(0), name.length(0),
2329 (SQLCHAR *)cap->Pat,
2330 cap->Pat ? SQL_NTS : 0);
2331 break;
2332 case CAT_COL:
2333 fnc = "SQLColumns";
2334 rc = SQLColumns(hstmt, name.ptr(2), name.length(2),
2335 name.ptr(1), name.length(1),
2336 name.ptr(0), name.length(0),
2337 (SQLCHAR *)cap->Pat,
2338 cap->Pat ? SQL_NTS : 0);
2339 break;
2340 case CAT_KEY:
2341 fnc = "SQLPrimaryKeys";
2342 rc = SQLPrimaryKeys(hstmt, name.ptr(2), name.length(2),
2343 name.ptr(1), name.length(1),
2344 name.ptr(0), name.length(0));
2345 break;
2346 case CAT_STAT:
2347 fnc = "SQLStatistics";
2348 rc = SQLStatistics(hstmt, name.ptr(2), name.length(2),
2349 name.ptr(1), name.length(1),
2350 name.ptr(0), name.length(0),
2351 cap->Unique, cap->Accuracy);
2352 break;
2353 case CAT_SPC:
2354 ThrowDBX("SQLSpecialColumns not available yet");
2355 default:
2356 ThrowDBX("Invalid SQL function id");
2357 } // endswitch infotype
2358
2359 if (!Check(rc))
2360 ThrowDBX(rc, fnc, hstmt);
2361
2362 // Some data source do not implement SQLNumResultCols
2363 if (Check(SQLNumResultCols(hstmt, &ncol)))
2364 // n because we no more ignore the first column
2365 if ((n = (UWORD)qrp->Nbcol) > (UWORD)ncol)
2366 ThrowDBX(MSG(COL_NUM_MISM));
2367
2368 // Unconditional to handle STRBLK's
2369 pval = (PVAL *)PlugSubAlloc(g, NULL, n * sizeof(PVAL));
2370 vlen = (SQLLEN *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN));
2371 pbuf = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
2372
2373 // Now bind the column buffers
2374 for (n = 0, crp = qrp->Colresp; crp; crp = crp->Next) {
2375 if ((tp = GetSQLCType(crp->Type)) == SQL_TYPE_NULL) {
2376 sprintf(g->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name);
2377 ThrowDBX(g->Message);
2378 } // endif tp
2379
2380 if (!(len = GetTypeSize(crp->Type, crp->Length))) {
2381 len = 255; // for STRBLK's
2382 ((STRBLK*)crp->Kdata)->SetSorted(true);
2383 } // endif len
2384
2385 pval[n] = AllocateValue(g, crp->Type, len);
2386 pval[n]->SetNullable(true);
2387
2388 if (crp->Type == TYPE_STRING) {
2389 pbuf[n] = (char*)PlugSubAlloc(g, NULL, len);
2390 buffer = pbuf[n];
2391 } else
2392 buffer = pval[n]->GetTo_Val();
2393
2394 vl = vlen + n;
2395
2396 // n + 1 because column numbers begin with 1
2397 rc = SQLBindCol(hstmt, n + 1, tp, buffer, len, vl);
2398
2399 if (!Check(rc))
2400 ThrowDBX(rc, "SQLBindCol", hstmt);
2401
2402 n++;
2403 } // endfor crp
2404
2405 fnc = "SQLFetch";
2406
2407 // Now fetch the result
2408 // Extended fetch cannot be used because of STRBLK's
2409 for (i = 0; i < qrp->Maxres; i++) {
2410 if ((rc = SQLFetch(hstmt)) == SQL_NO_DATA_FOUND)
2411 break;
2412 else if (rc != SQL_SUCCESS) {
2413 if (trace(2) || (trace(1) && rc != SQL_SUCCESS_WITH_INFO)) {
2414 UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1];
2415 UCHAR state[SQL_SQLSTATE_SIZE + 1];
2416 RETCODE erc;
2417 SDWORD native;
2418
2419 htrc("SQLFetch: row %d rc=%d\n", i+1, rc);
2420 erc = SQLError(m_henv, m_hdbc, hstmt, state, &native, msg,
2421 SQL_MAX_MESSAGE_LENGTH - 1, &len);
2422
2423 if (rc != SQL_INVALID_HANDLE)
2424 // Skip non-errors
2425 for (n = 0; n < MAX_NUM_OF_MSG
2426 && (erc == SQL_SUCCESS || erc == SQL_SUCCESS_WITH_INFO)
2427 && strcmp((char*)state, "00000"); n++) {
2428 htrc("%s: %s, Native=%d\n", state, msg, native);
2429 erc = SQLError(m_henv, m_hdbc, hstmt, state, &native,
2430 msg, SQL_MAX_MESSAGE_LENGTH - 1, &len);
2431 } // endfor n
2432
2433 } // endif trace
2434
2435 if (rc != SQL_SUCCESS_WITH_INFO)
2436 qrp->BadLines++;
2437
2438 } // endif rc
2439
2440 for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
2441 if (vlen[n] == SQL_NO_TOTAL)
2442 ThrowDBX("Unexpected SQL_NO_TOTAL returned from SQLFetch");
2443 else if (vlen[n] == SQL_NULL_DATA)
2444 pval[n]->SetNull(true);
2445 else if (crp->Type == TYPE_STRING/* && vlen[n] != SQL_NULL_DATA*/)
2446 pval[n]->SetValue_char(pbuf[n], (int)vlen[n]);
2447 else
2448 pval[n]->SetNull(false);
2449
2450 crp->Kdata->SetValue(pval[n], i);
2451 cap->Vlen[n][i] = vlen[n];
2452 } // endfor crp
2453
2454 } // endfor i
2455
2456 #if 0
2457 if ((crow = i) && (rc == SQL_NO_DATA || rc == SQL_SUCCESS_WITH_INFO))
2458 rc = SQL_SUCCESS;
2459
2460 if (rc == SQL_NO_DATA_FOUND) {
2461 if (cap->Pat)
2462 sprintf(g->Message, MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat);
2463 else
2464 sprintf(g->Message, MSG(NO_TAB_DATA), cap->Tab);
2465
2466 ThrowDBX(g->Message);
2467 } else if (rc == SQL_SUCCESS) {
2468 if ((rc = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND)
2469 qrp->Truncated = true;
2470
2471 } else
2472 ThrowDBX(rc, fnc, hstmt);
2473 #endif // 0
2474
2475 if (!rc || rc == SQL_NO_DATA || rc == SQL_SUCCESS_WITH_INFO) {
2476 if ((rc = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND)
2477 qrp->Truncated = true;
2478
2479 crow = i;
2480 } else
2481 ThrowDBX(rc, fnc, hstmt);
2482
2483 irc = (int)crow;
2484 } catch(DBX *x) {
2485 if (trace(1))
2486 for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
2487 htrc(x->m_ErrMsg[i]);
2488
2489 sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2490 irc = -1;
2491 } // end try/catch
2492
2493 if (b)
2494 SQLCancel(hstmt);
2495
2496 // All this (hstmt vs> m_hstmt) to be revisited
2497 if (hstmt)
2498 rc = SQLFreeStmt(hstmt, SQL_DROP);
2499
2500 return irc;
2501 } // end of GetCatInfo
2502
2503 /***********************************************************************/
2504 /* Allocate a CONNECT result structure from the ODBC result. */
2505 /***********************************************************************/
2506 PQRYRES ODBConn::AllocateResult(PGLOBAL g)
2507 {
2508 bool uns;
2509 PODBCCOL colp;
2510 PCOLRES *pcrp, crp;
2511 PQRYRES qrp;
2512
2513 if (!m_Rows) {
2514 strcpy(g->Message, "Void result");
2515 return NULL;
2516 } // endif m_Res
2517
2518 /*********************************************************************/
2519 /* Allocate the result storage for future retrieval. */
2520 /*********************************************************************/
2521 qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
2522 pcrp = &qrp->Colresp;
2523 qrp->Continued = FALSE;
2524 qrp->Truncated = FALSE;
2525 qrp->Info = FALSE;
2526 qrp->Suball = TRUE;
2527 qrp->BadLines = 0;
2528 qrp->Maxsize = m_Rows;
2529 qrp->Maxres = m_Rows;
2530 qrp->Nbcol = 0;
2531 qrp->Nblin = 0;
2532 qrp->Cursor = 0;
2533
2534 for (colp = (PODBCCOL)m_Tdb->Columns; colp;
2535 colp = (PODBCCOL)colp->GetNext())
2536 if (!colp->IsSpecial()) {
2537 *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
2538 crp = *pcrp;
2539 pcrp = &crp->Next;
2540 memset(crp, 0, sizeof(COLRES));
2541 crp->Ncol = ++qrp->Nbcol;
2542 crp->Name = colp->GetName();
2543 crp->Type = colp->GetResultType();
2544 crp->Prec = colp->GetScale();
2545 crp->Length = colp->GetLength();
2546 crp->Clen = colp->GetBuflen();
2547 uns = colp->IsUnsigned();
2548
2549 if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
2550 crp->Clen, 0, FALSE, TRUE, uns))) {
2551 sprintf(g->Message, MSG(INV_RESULT_TYPE),
2552 GetFormatType(crp->Type));
2553 return NULL;
2554 } // endif Kdata
2555
2556 if (!colp->IsNullable())
2557 crp->Nulls = NULL;
2558 else {
2559 crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
2560 memset(crp->Nulls, ' ', m_Rows);
2561 } // endelse Nullable
2562
2563 colp->SetCrp(crp);
2564 } // endif colp
2565
2566 *pcrp = NULL;
2567 //qrp->Nblin = n;
2568 return qrp;
2569 } // end of AllocateResult
2570
2571 /***********************************************************************/
2572 /* Restart from beginning of result set */
2573 /***********************************************************************/
2574 int ODBConn::Rewind(char *sql, ODBCCOL *tocols)
2575 {
2576 int rc, rbuf = -1;
2577
2578 if (!m_hstmt)
2579 rbuf = -1;
2580 else if (m_Full)
2581 rbuf = m_Rows; // No need to "rewind"
2582 else if (m_Scrollable) {
2583 SQLULEN crow;
2584
2585 try {
2586 rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_FIRST, 1, &crow, NULL);
2587
2588 if (!Check(rc))
2589 ThrowDBX(rc, "SQLExtendedFetch", m_hstmt);
2590
2591 rbuf = (int)crow;
2592 } catch(DBX *x) {
2593 sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
2594 rbuf = -1;
2595 } // end try/catch
2596
2597 } else if (ExecDirectSQL(sql, tocols) >= 0)
2598 rbuf = 0;
2599
2600 return rbuf;
2601 } // end of Rewind
2602
2603 /***********************************************************************/
2604 /* Disconnect connection */
2605 /***********************************************************************/
2606 void ODBConn::Close()
2607 {
2608 RETCODE rc;
2609
2610 if (m_hstmt) {
2611 // Is required for multiple tables
2612 rc = SQLFreeStmt(m_hstmt, SQL_DROP);
2613 m_hstmt = NULL;
2614 } // endif m_hstmt
2615
2616 if (m_hdbc != SQL_NULL_HDBC) {
2617 if (m_Transact) {
2618 rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT);
2619 m_Transact = false;
2620 } // endif m_Transact
2621
2622 rc = SQLDisconnect(m_hdbc);
2623
2624 if (trace(1) && rc != SQL_SUCCESS)
2625 htrc("Error: SQLDisconnect rc=%d\n", rc);
2626
2627 rc = SQLFreeConnect(m_hdbc);
2628
2629 if (trace(1) && rc != SQL_SUCCESS)
2630 htrc("Error: SQLFreeConnect rc=%d\n", rc);
2631
2632 m_hdbc = SQL_NULL_HDBC;
2633 } // endif m_hdbc
2634
2635 if (m_henv != SQL_NULL_HENV) {
2636 rc = SQLFreeEnv(m_henv);
2637
2638 if (trace(1) && rc != SQL_SUCCESS) // Nothing we can do
2639 htrc("Error: SQLFreeEnv failure ignored in Close\n");
2640
2641 m_henv = SQL_NULL_HENV;
2642 } // endif m_henv
2643
2644 if (m_Fp)
2645 m_Fp->Count = 0;
2646
2647 } // end of Close
2648