1 /************* Tabodbc C++ Program Source Code File (.CPP) *************/
2 /* PROGRAM NAME: TABODBC */
3 /* ------------- */
4 /* Version 3.2 */
5 /* */
6 /* COPYRIGHT: */
7 /* ---------- */
8 /* (C) Copyright to the author Olivier BERTRAND 2000-2018 */
9 /* */
10 /* WHAT THIS PROGRAM DOES: */
11 /* ----------------------- */
12 /* This program are the TABODBC class DB execution routines. */
13 /* */
14 /* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
15 /* -------------------------------------- */
16 /* */
17 /* REQUIRED FILES: */
18 /* --------------- */
19 /* TABODBC.CPP - Source code */
20 /* PLGDBSEM.H - DB application declaration file */
21 /* TABODBC.H - TABODBC classes declaration file */
22 /* GLOBAL.H - Global declaration file */
23 /* */
24 /* REQUIRED LIBRARIES: */
25 /* ------------------- */
26 /* Large model C library */
27 /* */
28 /* REQUIRED PROGRAMS: */
29 /* ------------------ */
30 /* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
31 /* */
32 /***********************************************************************/
33
34 /***********************************************************************/
35 /* Include relevant MariaDB header file. */
36 /***********************************************************************/
37 #include "my_global.h"
38 #include "sql_class.h"
39 #if defined(_WIN32)
40 #include <io.h>
41 #include <fcntl.h>
42 #if defined(__BORLANDC__)
43 #define __MFC_COMPAT__ // To define min/max as macro
44 #endif
45 //#include <windows.h>
46 #include <sqltypes.h>
47 #else
48 #if defined(UNIX)
49 #include <errno.h>
50 #define NODW
51 #include "osutil.h"
52 #else
53 #include <io.h>
54 #endif
55 #include <fcntl.h>
56 #endif
57
58 /***********************************************************************/
59 /* Include application header files: */
60 /* global.h is header containing all global declarations. */
61 /* plgdbsem.h is header containing the DB application declarations. */
62 /* kindex.h is kindex header that also includes tabdos.h. */
63 /* tabodbc.h is header containing the TABODBC class declarations. */
64 /* odbconn.h is header containing ODBC connection declarations. */
65 /***********************************************************************/
66 #include "global.h"
67 #include "plgdbsem.h"
68 #include "mycat.h"
69 #include "xtable.h"
70 #include "tabext.h"
71 #include "odbccat.h"
72 #include "tabodbc.h"
73 #include "tabmul.h"
74 //#include "reldef.h"
75 #include "tabcol.h"
76 #include "valblk.h"
77 #include "ha_connect.h"
78
79 #include "sql_string.h"
80
81 /***********************************************************************/
82 /* DB static variables. */
83 /***********************************************************************/
84 // int num_read, num_there, num_eq[2], num_nf; // Statistics
85 extern int num_read, num_there, num_eq[2]; // Statistics
86
87 /***********************************************************************/
88 /* External function. */
89 /***********************************************************************/
90 bool ExactInfo(void);
91
92 /* -------------------------- Class ODBCDEF -------------------------- */
93
94 /***********************************************************************/
95 /* Constructor. */
96 /***********************************************************************/
ODBCDEF(void)97 ODBCDEF::ODBCDEF(void)
98 {
99 Connect = NULL;
100 Catver = 0;
101 UseCnc = false;
102 } // end of ODBCDEF constructor
103
104 /***********************************************************************/
105 /* DefineAM: define specific AM block values from XDB file. */
106 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int poff)107 bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
108 {
109 Desc = Connect = GetStringCatInfo(g, "Connect", NULL);
110
111 if (!Connect && !Catfunc) {
112 sprintf(g->Message, "Missing connection for ODBC table %s", Name);
113 return true;
114 } // endif Connect
115
116 if (EXTDEF::DefineAM(g, am, poff))
117 return true;
118
119 Catver = GetIntCatInfo("Catver", 2);
120 Options = ODBConn::noOdbcDialog;
121 //Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib;
122 Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
123 Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
124 UseCnc = GetBoolCatInfo("UseDSN", false);
125 return false;
126 } // end of DefineAM
127
128 /***********************************************************************/
129 /* GetTable: makes a new Table Description Block. */
130 /***********************************************************************/
GetTable(PGLOBAL g,MODE m)131 PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
132 {
133 PTDB tdbp = NULL;
134
135 /*********************************************************************/
136 /* Allocate a TDB of the proper type. */
137 /* Column blocks will be allocated only when needed. */
138 /*********************************************************************/
139 if (Xsrc)
140 tdbp = new(g) TDBXDBC(this);
141 else switch (Catfunc) {
142 case FNC_COL:
143 tdbp = new(g) TDBOCL(this);
144 break;
145 case FNC_TABLE:
146 tdbp = new(g) TDBOTB(this);
147 break;
148 case FNC_DSN:
149 tdbp = new(g) TDBSRC(this);
150 break;
151 case FNC_DRIVER:
152 tdbp = new(g) TDBDRV(this);
153 break;
154 default:
155 tdbp = new(g) TDBODBC(this);
156
157 if (Multiple == 1)
158 tdbp = new(g) TDBMUL(tdbp);
159 else if (Multiple == 2)
160 strcpy(g->Message, MSG(NO_ODBC_MUL));
161 } // endswitch Catfunc
162
163 return tdbp;
164 } // end of GetTable
165
166 /* -------------------------- Class TDBODBC -------------------------- */
167
168 /***********************************************************************/
169 /* Implementation of the TDBODBC class. */
170 /***********************************************************************/
TDBODBC(PODEF tdp)171 TDBODBC::TDBODBC(PODEF tdp) : TDBEXT(tdp)
172 {
173 Ocp = NULL;
174 Cnp = NULL;
175
176 if (tdp) {
177 Connect = tdp->Connect;
178 Ops.User = tdp->Username;
179 Ops.Pwd = tdp->Password;
180 Ops.Cto = tdp->Cto;
181 Ops.Qto = tdp->Qto;
182 Catver = tdp->Catver;
183 Ops.UseCnc = tdp->UseCnc;
184 } else {
185 Connect = NULL;
186 Ops.User = NULL;
187 Ops.Pwd = NULL;
188 Ops.Cto = DEFAULT_LOGIN_TIMEOUT;
189 Ops.Qto = DEFAULT_QUERY_TIMEOUT;
190 Catver = 0;
191 Ops.UseCnc = false;
192 } // endif tdp
193
194 } // end of TDBODBC standard constructor
195
TDBODBC(PTDBODBC tdbp)196 TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBEXT(tdbp)
197 {
198 Ocp = tdbp->Ocp; // is that right ?
199 Cnp = tdbp->Cnp;
200 Connect = tdbp->Connect;
201 Ops = tdbp->Ops;
202 } // end of TDBODBC copy constructor
203
204 // Method
Clone(PTABS t)205 PTDB TDBODBC::Clone(PTABS t)
206 {
207 PTDB tp;
208 PODBCCOL cp1, cp2;
209 PGLOBAL g = t->G; // Is this really useful ???
210
211 tp = new(g) TDBODBC(this);
212
213 for (cp1 = (PODBCCOL)Columns; cp1; cp1 = (PODBCCOL)cp1->GetNext()) {
214 cp2 = new(g) ODBCCOL(cp1, tp); // Make a copy
215 NewPointer(t, cp1, cp2);
216 } // endfor cp1
217
218 return tp;
219 } // end of CopyOne
220
221 /***********************************************************************/
222 /* Allocate ODBC column description block. */
223 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)224 PCOL TDBODBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
225 {
226 return new(g) ODBCCOL(cdp, this, cprec, n);
227 } // end of MakeCol
228
229 /***********************************************************************/
230 /* Extract the filename from connect string and return it. */
231 /* This used for Multiple(1) tables. Also prepare a connect string */
232 /* with a place holder to be used by SetFile. */
233 /***********************************************************************/
GetFile(PGLOBAL g)234 PCSZ TDBODBC::GetFile(PGLOBAL g)
235 {
236 if (Connect) {
237 char *p1, *p2;
238 int i;
239 size_t n;
240
241 if (!(p1 = strstr(Connect, "DBQ="))) {
242 char *p, *lc = strlwr(PlugDup(g, Connect));
243
244 if ((p = strstr(lc, "database=")))
245 p1 = Connect + (p - lc);
246
247 i = 9;
248 } else
249 i = 4;
250
251 if (p1) {
252 p1 += i; // Beginning of file name
253 p2 = strchr(p1, ';'); // End of file path/name
254
255 // Make the File path/name from the connect string
256 n = (p2) ? p2 - p1 : strlen(p1);
257 DBQ = (PSZ)PlugSubAlloc(g, NULL, n + 1);
258 memcpy(DBQ, p1, n);
259 DBQ[n] = '\0';
260
261 // Make the Format used to re-generate Connect (3 = "%s" + 1)
262 MulConn = (char*)PlugSubAlloc(g, NULL, strlen(Connect) - n + 3);
263 memcpy(MulConn, Connect, p1 - Connect);
264 MulConn[p1 - Connect] = '\0';
265 strcat(strcat(MulConn, "%s"), (p2) ? p2 : ";");
266 } // endif p1
267
268 } // endif Connect
269
270 return (DBQ) ? DBQ : (PSZ)"???";
271 } // end of GetFile
272
273 /***********************************************************************/
274 /* Set DBQ and get the new file name into the connect string. */
275 /***********************************************************************/
SetFile(PGLOBAL g,PCSZ fn)276 void TDBODBC::SetFile(PGLOBAL g, PCSZ fn)
277 {
278 if (MulConn) {
279 int n = strlen(MulConn) + strlen(fn) - 1;
280
281 if (n > BufSize) {
282 // Allocate a buffer larger than needed so the chance
283 // of having to reallocate it is reduced.
284 BufSize = n + 6;
285 Connect = (char*)PlugSubAlloc(g, NULL, BufSize);
286 } // endif n
287
288 // Make the complete connect string
289 sprintf(Connect, MulConn, fn);
290 } // endif MultConn
291
292 DBQ = PlugDup(g, fn);
293 } // end of SetFile
294
295 /***********************************************************************/
296 /* MakeInsert: make the Insert statement used with ODBC connection. */
297 /***********************************************************************/
MakeInsert(PGLOBAL g)298 bool TDBODBC::MakeInsert(PGLOBAL g)
299 {
300 PCSZ schmp = NULL;
301 char *catp = NULL, buf[NAM_LEN * 3];
302 int len = 0;
303 bool oom, b = false;
304 PTABLE tablep = To_Table;
305 PCOL colp;
306
307 for (colp = Columns; colp; colp = colp->GetNext())
308 if (colp->IsSpecial()) {
309 strcpy(g->Message, MSG(NO_ODBC_SPECOL));
310 return true;
311 } else {
312 // Column name can be encoded in UTF-8
313 Decode(colp->GetName(), buf, sizeof(buf));
314 len += (strlen(buf) + 6); // comma + quotes + valist
315 ((PEXTCOL)colp)->SetRank(++Nparm);
316 } // endif colp
317
318 // Below 32 is enough to contain the fixed part of the query
319 if (Catalog && *Catalog)
320 catp = Catalog;
321
322 if (catp)
323 len += strlen(catp) + 1;
324
325 //if (tablep->GetSchema())
326 // schmp = (char*)tablep->GetSchema();
327 //else
328 if (Schema && *Schema)
329 schmp = Schema;
330
331 if (schmp)
332 len += strlen(schmp) + 1;
333
334 // Table name can be encoded in UTF-8
335 Decode(TableName, buf, sizeof(buf));
336 len += (strlen(buf) + 32);
337 Query = new(g) STRING(g, len, "INSERT INTO ");
338
339 if (catp) {
340 Query->Append(catp);
341
342 if (schmp) {
343 Query->Append('.');
344 Query->Append(schmp);
345 } // endif schmp
346
347 Query->Append('.');
348 } else if (schmp) {
349 Query->Append(schmp);
350 Query->Append('.');
351 } // endif schmp
352
353 if (Quote) {
354 // Put table name between identifier quotes in case in contains blanks
355 Query->Append(Quote);
356 Query->Append(buf);
357 Query->Append(Quote);
358 } else
359 Query->Append(buf);
360
361 Query->Append('(');
362
363 for (colp = Columns; colp; colp = colp->GetNext()) {
364 if (b)
365 Query->Append(", ");
366 else
367 b = true;
368
369 // Column name can be in UTF-8 encoding
370 Decode(colp->GetName(), buf, sizeof(buf));
371
372 if (Quote) {
373 // Put column name between identifier quotes in case in contains blanks
374 Query->Append(Quote);
375 Query->Append(buf);
376 Query->Append(Quote);
377 } else
378 Query->Append(buf);
379
380 } // endfor colp
381
382 Query->Append(") VALUES (");
383
384 for (int i = 0; i < Nparm; i++)
385 Query->Append("?,");
386
387 if ((oom = Query->IsTruncated()))
388 strcpy(g->Message, "MakeInsert: Out of memory");
389 else
390 Query->RepLast(')');
391
392 return oom;
393 } // end of MakeInsert
394
395 /***********************************************************************/
396 /* ODBC Bind Parameter function. */
397 /***********************************************************************/
BindParameters(PGLOBAL g)398 bool TDBODBC::BindParameters(PGLOBAL g)
399 {
400 PODBCCOL colp;
401
402 for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
403 colp->AllocateBuffers(g, 0);
404
405 if (Ocp->BindParam(colp))
406 return true;
407
408 } // endfor colp
409
410 return false;
411 } // end of BindParameters
412
413 #if 0
414 /***********************************************************************/
415 /* MakeUpdate: make the SQL statement to send to ODBC connection. */
416 /***********************************************************************/
417 char *TDBODBC::MakeUpdate(PGLOBAL g)
418 {
419 char *qc, *stmt = NULL, cmd[8], tab[96], end[1024];
420
421 stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
422 memset(end, 0, sizeof(end));
423
424 if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
425 sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
426 qc = Ocp->GetQuoteChar();
427 else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2)
428 qc = (Quoted) ? Quote : "";
429 else {
430 strcpy(g->Message, "Cannot use this UPDATE command");
431 return NULL;
432 } // endif sscanf
433
434 assert(!stricmp(cmd, "update"));
435 strcat(strcat(strcat(strcpy(stmt, "UPDATE "), qc), TableName), qc);
436
437 for (int i = 0; end[i]; i++)
438 if (end[i] == '`')
439 end[i] = *qc;
440
441 strcat(stmt, end);
442 return stmt;
443 } // end of MakeUpdate
444
445 /***********************************************************************/
446 /* MakeDelete: make the SQL statement to send to ODBC connection. */
447 /***********************************************************************/
448 char *TDBODBC::MakeDelete(PGLOBAL g)
449 {
450 char *qc, *stmt = NULL, cmd[8], from[8], tab[96], end[512];
451
452 stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
453 memset(end, 0, sizeof(end));
454
455 if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
456 sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
457 qc = Ocp->GetQuoteChar();
458 else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
459 qc = (Quoted) ? Quote : "";
460 else {
461 strcpy(g->Message, "Cannot use this DELETE command");
462 return NULL;
463 } // endif sscanf
464
465 assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
466 strcat(strcat(strcat(strcpy(stmt, "DELETE FROM "), qc), TableName), qc);
467
468 if (*end) {
469 for (int i = 0; end[i]; i++)
470 if (end[i] == '`')
471 end[i] = *qc;
472
473 strcat(stmt, end);
474 } // endif end
475
476 return stmt;
477 } // end of MakeDelete
478 #endif // 0
479
480 /***********************************************************************/
481 /* ResetSize: call by TDBMUL when calculating size estimate. */
482 /***********************************************************************/
ResetSize(void)483 void TDBODBC::ResetSize(void)
484 {
485 MaxSize = -1;
486
487 if (Ocp && Ocp->IsOpen())
488 Ocp->Close();
489
490 } // end of ResetSize
491
492 /***********************************************************************/
493 /* ODBC Cardinality: returns table size in number of rows. */
494 /***********************************************************************/
Cardinality(PGLOBAL g)495 int TDBODBC::Cardinality(PGLOBAL g)
496 {
497 if (!g)
498 return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
499
500 if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
501 // Info command, we must return the exact table row number
502 char qry[96], tbn[64];
503 ODBConn *ocp = new(g) ODBConn(g, this);
504
505 if (ocp->Open(Connect, &Ops, Options) < 1)
506 return -1;
507
508 // Table name can be encoded in UTF-8
509 Decode(TableName, tbn, sizeof(tbn));
510 strcpy(qry, "SELECT COUNT(*) FROM ");
511
512 if (Quote)
513 strcat(strcat(strcat(qry, Quote), tbn), Quote);
514 else
515 strcat(qry, tbn);
516
517 // Allocate a Count(*) column (must not use the default constructor)
518 Cnp = new(g) ODBCCOL;
519 Cnp->InitValue(g);
520
521 if ((Cardinal = ocp->GetResultSize(qry, Cnp)) < 0)
522 return -3;
523
524 ocp->Close();
525 } else
526 Cardinal = 10; // To make MySQL happy
527
528 return Cardinal;
529 } // end of Cardinality
530
531 /***********************************************************************/
532 /* ODBC Access Method opening routine. */
533 /* New method now that this routine is called recursively (last table */
534 /* first in reverse order): index blocks are immediately linked to */
535 /* join block of next table if it exists or else are discarted. */
536 /***********************************************************************/
OpenDB(PGLOBAL g)537 bool TDBODBC::OpenDB(PGLOBAL g)
538 {
539 bool rc = true;
540
541 if (trace(1))
542 htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n",
543 this, Tdb_No, Use, Mode);
544
545 if (Use == USE_OPEN) {
546 /*******************************************************************/
547 /* Table already open, just replace it at its beginning. */
548 /*******************************************************************/
549 if (Memory == 1) {
550 if ((Qrp = Ocp->AllocateResult(g)))
551 Memory = 2; // Must be filled
552 else
553 Memory = 0; // Allocation failed, don't use it
554
555 } else if (Memory == 2)
556 Memory = 3; // Ok to use memory result
557
558 if (Memory < 3) {
559 // Method will depend on cursor type
560 if ((Rbuf = Ocp->Rewind(Query->GetStr(), (PODBCCOL)Columns)) < 0)
561 if (Mode != MODE_READX) {
562 Ocp->Close();
563 return true;
564 } else
565 Rbuf = 0;
566
567 } else
568 Rbuf = Qrp->Nblin;
569
570 CurNum = 0;
571 Fpos = 0;
572 Curpos = 1;
573 return false;
574 } // endif use
575
576 /*********************************************************************/
577 /* Open an ODBC connection for this table. */
578 /* Note: this may not be the proper way to do. Perhaps it is better */
579 /* to test whether a connection is already open for this datasource */
580 /* and if so to allocate just a new result set. But this only for */
581 /* drivers allowing concurency in getting results ??? */
582 /*********************************************************************/
583 if (!Ocp)
584 Ocp = new(g) ODBConn(g, this);
585 else if (Ocp->IsOpen())
586 Ocp->Close();
587
588 if (Ocp->Open(Connect, &Ops, Options) < 1)
589 return true;
590 else if (Quoted)
591 Quote = Ocp->GetQuoteChar();
592
593 Use = USE_OPEN; // Do it now in case we are recursively called
594
595 /*********************************************************************/
596 /* Make the command and allocate whatever is used for getting results*/
597 /*********************************************************************/
598 if (Mode == MODE_READ || Mode == MODE_READX) {
599 if (Memory > 1 && !Srcdef) {
600 int n;
601
602 if (!MakeSQL(g, true)) {
603 // Allocate a Count(*) column
604 Cnp = new(g) ODBCCOL;
605 Cnp->InitValue(g);
606
607 if ((n = Ocp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
608 char* msg = PlugDup(g, g->Message);
609
610 sprintf(g->Message, "Get result size: %s (rc=%d)", msg, n);
611 return true;
612 } else if (n) {
613 Ocp->m_Rows = n;
614
615 if ((Qrp = Ocp->AllocateResult(g)))
616 Memory = 2; // Must be filled
617 else {
618 strcpy(g->Message, "Result set memory allocation failed");
619 return true;
620 } // endif n
621
622 } else // Void result
623 Memory = 0;
624
625 Ocp->m_Rows = 0;
626 } else
627 return true;
628
629 } // endif Memory
630
631 if (!(rc = MakeSQL(g, false))) {
632 for (PODBCCOL colp = (PODBCCOL)Columns; colp;
633 colp = (PODBCCOL)colp->GetNext())
634 if (!colp->IsSpecial())
635 colp->AllocateBuffers(g, Rows);
636
637 rc = (Mode == MODE_READ)
638 ? ((Rows = Ocp->ExecDirectSQL(Query->GetStr(), (PODBCCOL)Columns)) < 0)
639 : false;
640 } // endif rc
641
642 } else if (Mode == MODE_INSERT) {
643 if (!(rc = MakeInsert(g))) {
644 if (Nparm != Ocp->PrepareSQL(Query->GetStr())) {
645 strcpy(g->Message, MSG(PARM_CNT_MISS));
646 rc = true;
647 } else
648 rc = BindParameters(g);
649
650 } // endif rc
651
652 } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
653 rc = false; // wait for CheckCond before calling MakeCommand(g);
654 } else
655 sprintf(g->Message, "Invalid mode %d", Mode);
656
657 if (rc) {
658 Ocp->Close();
659 return true;
660 } // endif rc
661
662 /*********************************************************************/
663 /* Reset statistics values. */
664 /*********************************************************************/
665 num_read = num_there = num_eq[0] = num_eq[1] = 0;
666 return false;
667 } // end of OpenDB
668
669 #if 0
670 /***********************************************************************/
671 /* GetRecpos: return the position of last read record. */
672 /***********************************************************************/
673 int TDBODBC::GetRecpos(void)
674 {
675 return Fpos;
676 } // end of GetRecpos
677 #endif // 0
678
679 /***********************************************************************/
680 /* SetRecpos: set the position of next read record. */
681 /***********************************************************************/
SetRecpos(PGLOBAL g,int recpos)682 bool TDBODBC::SetRecpos(PGLOBAL g, int recpos)
683 {
684 if (Ocp->m_Full) {
685 Fpos = 0;
686 CurNum = recpos - 1;
687 } else if (Memory == 3) {
688 Fpos = recpos;
689 CurNum = -1;
690 } else if (Scrollable) {
691 // Is new position in the current row set?
692 if (recpos >= Curpos && recpos < Curpos + Rbuf) {
693 CurNum = recpos - Curpos;
694 Fpos = 0;
695 } else {
696 Fpos = recpos;
697 CurNum = 0;
698 } // endif recpos
699
700 } else {
701 strcpy(g->Message,
702 "This action requires Memory setting or a scrollable cursor");
703 return true;
704 } // endif's
705
706 // Indicate the table position was externally set
707 Placed = true;
708 return false;
709 } // end of SetRecpos
710
711 /***********************************************************************/
712 /* Data Base indexed read routine for ODBC access method. */
713 /***********************************************************************/
ReadKey(PGLOBAL g,OPVAL op,const key_range * kr)714 bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
715 {
716 char c = Quote ? *Quote : 0;
717 int oldlen = Query->GetLength();
718 PHC hc = To_Def->GetHandler();
719
720 if (!(kr || hc->end_range) || op == OP_NEXT ||
721 Mode == MODE_UPDATE || Mode == MODE_DELETE) {
722 if (!kr && Mode == MODE_READX) {
723 // This is a false indexed read
724 Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns);
725 Mode = MODE_READ;
726 return (Rows < 0);
727 } // endif key
728
729 return false;
730 } else {
731 if (hc->MakeKeyWhere(g, Query, op, c, kr))
732 return true;
733
734 if (To_CondFil) {
735 if (To_CondFil->Idx != hc->active_index) {
736 To_CondFil->Idx = hc->active_index;
737 To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
738 *To_CondFil->Body= 0;
739
740 if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond)))
741 PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
742
743 } // endif active_index
744
745 if (To_CondFil)
746 if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
747 strcpy(g->Message, "Readkey: Out of memory");
748 return true;
749 } // endif Append
750
751 } // endif To_Condfil
752
753 Mode = MODE_READ;
754 } // endif's op
755
756 if (trace(33))
757 htrc("ODBC ReadKey: Query=%s\n", Query->GetStr());
758
759 Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns);
760 Query->Truncate(oldlen);
761 return (Rows < 0);
762 } // end of ReadKey
763
764 /***********************************************************************/
765 /* VRDNDOS: Data Base read routine for odbc access method. */
766 /***********************************************************************/
ReadDB(PGLOBAL g)767 int TDBODBC::ReadDB(PGLOBAL g)
768 {
769 int rc;
770
771 if (trace(2))
772 htrc("ODBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
773
774 if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
775 if (!Query && MakeCommand(g))
776 return RC_FX;
777
778 // Send the UPDATE/DELETE command to the remote table
779 if (!Ocp->ExecSQLcommand(Query->GetStr())) {
780 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
781
782 if (trace(1))
783 htrc("%s\n", g->Message);
784
785 PushWarning(g, this, 0); // 0 means a Note
786 return RC_EF; // Nothing else to do
787 } else
788 return RC_FX; // Error
789
790 } // endif Mode
791
792 /*********************************************************************/
793 /* Now start the reading process. */
794 /* Here is the place to fetch the line(s). */
795 /*********************************************************************/
796 if (Placed) {
797 if (Fpos && CurNum >= 0)
798 Rbuf = Ocp->Fetch((Curpos = Fpos));
799
800 rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
801 Placed = false;
802 } else {
803 if (Memory != 3) {
804 if (++CurNum >= Rbuf) {
805 Rbuf = Ocp->Fetch();
806 Curpos = Fpos + 1;
807 CurNum = 0;
808 } // endif CurNum
809
810 rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
811 } else // Getting result from memory
812 rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
813
814 if (rc == RC_OK) {
815 if (Memory == 2)
816 Qrp->Nblin++;
817
818 Fpos++; // Used for memory and pos
819 } // endif rc
820
821 } // endif Placed
822
823 if (trace(2))
824 htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
825
826 return rc;
827 } // end of ReadDB
828
829 /***********************************************************************/
830 /* Data Base Insert write routine for ODBC access method. */
831 /***********************************************************************/
WriteDB(PGLOBAL g)832 int TDBODBC::WriteDB(PGLOBAL g)
833 {
834 int n = Ocp->ExecuteSQL();
835
836 if (n < 0) {
837 AftRows = n;
838 return RC_FX;
839 } else
840 AftRows += n;
841
842 return RC_OK;
843 } // end of WriteDB
844
845 /***********************************************************************/
846 /* Data Base delete line routine for ODBC access method. */
847 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)848 int TDBODBC::DeleteDB(PGLOBAL g, int irc)
849 {
850 if (irc == RC_FX) {
851 if (!Query && MakeCommand(g))
852 return RC_FX;
853
854 // Send the DELETE (all) command to the remote table
855 if (!Ocp->ExecSQLcommand(Query->GetStr())) {
856 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
857
858 if (trace(1))
859 htrc("%s\n", g->Message);
860
861 PushWarning(g, this, 0); // 0 means a Note
862 return RC_OK; // This is a delete all
863 } else
864 return RC_FX; // Error
865
866 } else
867 return RC_OK; // Ignore
868
869 } // end of DeleteDB
870
871 /***********************************************************************/
872 /* Data Base close routine for ODBC access method. */
873 /***********************************************************************/
CloseDB(PGLOBAL g)874 void TDBODBC::CloseDB(PGLOBAL g)
875 {
876 if (Ocp)
877
878 Ocp->Close();
879
880 if (trace(1))
881 htrc("ODBC CloseDB: closing %s\n", Name);
882
883 } // end of CloseDB
884
885 /* --------------------------- ODBCCOL ------------------------------- */
886
887 /***********************************************************************/
888 /* ODBCCOL public constructor. */
889 /***********************************************************************/
ODBCCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)890 ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
891 : EXTCOL(cdp, tdbp, cprec, i, am)
892 {
893 // Set additional ODBC access method information for column.
894 Slen = 0;
895 StrLen = &Slen;
896 Sqlbuf = NULL;
897 } // end of ODBCCOL constructor
898
899 /***********************************************************************/
900 /* ODBCCOL private constructor. */
901 /***********************************************************************/
ODBCCOL(void)902 ODBCCOL::ODBCCOL(void) : EXTCOL()
903 {
904 Slen = 0;
905 StrLen = &Slen;
906 Sqlbuf = NULL;
907 } // end of ODBCCOL constructor
908
909 /***********************************************************************/
910 /* ODBCCOL constructor used for copying columns. */
911 /* tdbp is the pointer to the new table descriptor. */
912 /***********************************************************************/
ODBCCOL(ODBCCOL * col1,PTDB tdbp)913 ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
914 {
915 Slen = col1->Slen;
916 StrLen = col1->StrLen;
917 Sqlbuf = col1->Sqlbuf;
918 } // end of ODBCCOL copy constructor
919
920 /***********************************************************************/
921 /* ReadColumn: when SQLFetch is used there is nothing to do as the */
922 /* column buffer was bind to the record set. This is also the case */
923 /* when calculating MaxSize (Bufp is NULL even when Rows is not). */
924 /***********************************************************************/
ReadColumn(PGLOBAL g)925 void ODBCCOL::ReadColumn(PGLOBAL g)
926 {
927 PTDBODBC tdbp = (PTDBODBC)To_Tdb;
928 int i = tdbp->Fpos - 1, n = tdbp->CurNum;
929
930 if (tdbp->Memory == 3) {
931 // Get the value from the stored memory
932 if (Crp->Nulls && Crp->Nulls[i] == '*') {
933 Value->Reset();
934 Value->SetNull(true);
935 } else {
936 Value->SetValue_pvblk(Crp->Kdata, i);
937 Value->SetNull(false);
938 } // endif Nulls
939
940 return;
941 } // endif Memory
942
943 if (StrLen[n] == SQL_NULL_DATA) {
944 // Null value
945 if (Nullable)
946 Value->SetNull(true);
947
948 Value->Reset();
949 goto put;
950 } else
951 Value->SetNull(false);
952
953 if (Bufp && tdbp->Rows) {
954 if (Buf_Type == TYPE_DATE)
955 *Sqlbuf = ((TIMESTAMP_STRUCT*)Bufp)[n];
956 else
957 Value->SetValue_pvblk(Blkp, n);
958
959 } // endif Bufp
960
961 if (Buf_Type == TYPE_DATE) {
962 struct tm dbtime;
963
964 memset(&dbtime, 0, sizeof(tm));
965 dbtime.tm_sec = (int)Sqlbuf->second;
966 dbtime.tm_min = (int)Sqlbuf->minute;
967 dbtime.tm_hour = (int)Sqlbuf->hour;
968 dbtime.tm_mday = (int)Sqlbuf->day;
969 dbtime.tm_mon = (int)Sqlbuf->month - 1;
970 dbtime.tm_year = (int)Sqlbuf->year - 1900;
971 ((DTVAL*)Value)->MakeTime(&dbtime);
972 } else if (Buf_Type == TYPE_DECIM && tdbp->Sep) {
973 // Be sure to use decimal point
974 char *p = strchr(Value->GetCharValue(), tdbp->Sep);
975
976 if (p)
977 *p = '.';
978
979 } // endif Buf_Type
980
981 if (trace(2)) {
982 char buf[64];
983
984 htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n",
985 Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf));
986 } // endif trace
987
988 put:
989 if (tdbp->Memory != 2)
990 return;
991
992 /*********************************************************************/
993 /* Fill the allocated result structure. */
994 /*********************************************************************/
995 if (Value->IsNull()) {
996 if (Crp->Nulls)
997 Crp->Nulls[i] = '*'; // Null value
998
999 Crp->Kdata->Reset(i);
1000 } else
1001 Crp->Kdata->SetValue(Value, i);
1002
1003 } // end of ReadColumn
1004
1005 /***********************************************************************/
1006 /* AllocateBuffers: allocate the extended buffer for SQLExtendedFetch */
1007 /* or Fetch. Note: we use Long+1 here because ODBC must have space */
1008 /* for the ending null character. */
1009 /***********************************************************************/
AllocateBuffers(PGLOBAL g,int rows)1010 void ODBCCOL::AllocateBuffers(PGLOBAL g, int rows)
1011 {
1012 if (Buf_Type == TYPE_DATE)
1013 Sqlbuf = (TIMESTAMP_STRUCT*)PlugSubAlloc(g, NULL,
1014 sizeof(TIMESTAMP_STRUCT));
1015
1016 if (!rows)
1017 return;
1018
1019 if (Buf_Type == TYPE_DATE)
1020 Bufp = PlugSubAlloc(g, NULL, rows * sizeof(TIMESTAMP_STRUCT));
1021 else {
1022 Blkp = AllocValBlock(g, NULL, Buf_Type, rows, GetBuflen(),
1023 GetScale(), true, false, false);
1024 Bufp = Blkp->GetValPointer();
1025 } // endelse
1026
1027 if (rows > 1)
1028 StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(SQLLEN));
1029
1030 } // end of AllocateBuffers
1031
1032 /***********************************************************************/
1033 /* Returns the buffer to use for Fetch or Extended Fetch. */
1034 /***********************************************************************/
GetBuffer(DWORD rows)1035 void *ODBCCOL::GetBuffer(DWORD rows)
1036 {
1037 if (rows && To_Tdb) {
1038 assert(rows == (DWORD)((TDBODBC*)To_Tdb)->Rows);
1039 return Bufp;
1040 } else
1041 return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val();
1042
1043 } // end of GetBuffer
1044
1045 /***********************************************************************/
1046 /* Returns the buffer length to use for Fetch or Extended Fetch. */
1047 /***********************************************************************/
GetBuflen(void)1048 SWORD ODBCCOL::GetBuflen(void)
1049 {
1050 SWORD flen;
1051
1052 switch (Buf_Type) {
1053 case TYPE_DATE:
1054 flen = (SWORD)sizeof(TIMESTAMP_STRUCT);
1055 break;
1056 case TYPE_STRING:
1057 case TYPE_DECIM:
1058 flen = (SWORD)Value->GetClen() + 1;
1059 break;
1060 default:
1061 flen = (SWORD)Value->GetClen();
1062 } // endswitch Buf_Type
1063
1064 return flen;
1065 } // end of GetBuflen
1066
1067 /***********************************************************************/
1068 /* WriteColumn: make sure the bind buffer is updated. */
1069 /***********************************************************************/
WriteColumn(PGLOBAL g)1070 void ODBCCOL::WriteColumn(PGLOBAL g)
1071 {
1072 /*********************************************************************/
1073 /* Do convert the column value if necessary. */
1074 /*********************************************************************/
1075 if (Value != To_Val)
1076 Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
1077
1078 if (Buf_Type == TYPE_DATE) {
1079 struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
1080
1081 Sqlbuf->second = dbtime->tm_sec;
1082 Sqlbuf->minute = dbtime->tm_min;
1083 Sqlbuf->hour = dbtime->tm_hour;
1084 Sqlbuf->day = dbtime->tm_mday;
1085 Sqlbuf->month = dbtime->tm_mon + 1;
1086 Sqlbuf->year = dbtime->tm_year + 1900;
1087 Sqlbuf->fraction = 0;
1088 } else if (Buf_Type == TYPE_DECIM) {
1089 // Some data sources require local decimal separator
1090 char *p, sep = ((PTDBODBC)To_Tdb)->Sep;
1091
1092 if (sep && (p = strchr(Value->GetCharValue(), '.')))
1093 *p = sep;
1094
1095 } // endif Buf_Type
1096
1097 if (Nullable)
1098 *StrLen = (Value->IsNull()) ? SQL_NULL_DATA :
1099 (IsTypeChar(Buf_Type)) ? SQL_NTS : 0;
1100
1101 } // end of WriteColumn
1102
1103 /* -------------------------- Class TDBXDBC -------------------------- */
1104
1105 /***********************************************************************/
1106 /* Implementation of the TDBXDBC class. */
1107 /***********************************************************************/
TDBXDBC(PODEF tdp)1108 TDBXDBC::TDBXDBC(PODEF tdp) : TDBODBC(tdp)
1109 {
1110 Cmdlist = NULL;
1111 Cmdcol = NULL;
1112 Mxr = tdp->Maxerr;
1113 Nerr = 0;
1114 } // end of TDBXDBC constructor
1115
TDBXDBC(PTDBXDBC tdbp)1116 TDBXDBC::TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp)
1117 {
1118 Cmdlist = tdbp->Cmdlist;
1119 Cmdcol = tdbp->Cmdcol;
1120 Mxr = tdbp->Mxr;
1121 Nerr = tdbp->Nerr;
1122 } // end of TDBXDBC copy constructor
1123
Clone(PTABS t)1124 PTDB TDBXDBC::Clone(PTABS t)
1125 {
1126 PTDB tp;
1127 PXSRCCOL cp1, cp2;
1128 PGLOBAL g = t->G; // Is this really useful ???
1129
1130 tp = new(g) TDBXDBC(this);
1131
1132 for (cp1 = (PXSRCCOL)Columns; cp1; cp1 = (PXSRCCOL)cp1->GetNext()) {
1133 cp2 = new(g) XSRCCOL(cp1, tp); // Make a copy
1134 NewPointer(t, cp1, cp2);
1135 } // endfor cp1
1136
1137 return tp;
1138 } // end of CopyOne
1139
1140 /***********************************************************************/
1141 /* Allocate XSRC column description block. */
1142 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)1143 PCOL TDBXDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1144 {
1145 PXSRCCOL colp = new(g) XSRCCOL(cdp, this, cprec, n);
1146
1147 if (!colp->Flag)
1148 Cmdcol = colp->GetName();
1149
1150 return colp;
1151 } // end of MakeCol
1152
1153 /***********************************************************************/
1154 /* MakeCMD: make the SQL statement to send to ODBC connection. */
1155 /***********************************************************************/
MakeCMD(PGLOBAL g)1156 PCMD TDBXDBC::MakeCMD(PGLOBAL g)
1157 {
1158 PCMD xcmd = NULL;
1159
1160 if (To_CondFil) {
1161 if (Cmdcol) {
1162 if (!stricmp(Cmdcol, To_CondFil->Body) &&
1163 (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
1164 xcmd = To_CondFil->Cmds;
1165 } else
1166 strcpy(g->Message, "Invalid command specification filter");
1167
1168 } else
1169 strcpy(g->Message, "No command column in select list");
1170
1171 } else if (!Srcdef)
1172 strcpy(g->Message, "No Srcdef default command");
1173 else
1174 xcmd = new(g) CMD(g, Srcdef);
1175
1176 return xcmd;
1177 } // end of MakeCMD
1178
1179 #if 0
1180 /***********************************************************************/
1181 /* ODBC Bind Parameter function. */
1182 /***********************************************************************/
1183 bool TDBXDBC::BindParameters(PGLOBAL g)
1184 {
1185 PODBCCOL colp;
1186
1187 for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
1188 colp->AllocateBuffers(g, 0);
1189
1190 if (Ocp->BindParam(colp))
1191 return true;
1192
1193 } // endfor colp
1194
1195 return false;
1196 } // end of BindParameters
1197 #endif // 0
1198
1199 /***********************************************************************/
1200 /* XDBC GetMaxSize: returns table size (not always one row). */
1201 /***********************************************************************/
GetMaxSize(PGLOBAL g)1202 int TDBXDBC::GetMaxSize(PGLOBAL g)
1203 {
1204 if (MaxSize < 0)
1205 MaxSize = 10; // Just a guess
1206
1207 return MaxSize;
1208 } // end of GetMaxSize
1209
1210 /***********************************************************************/
1211 /* ODBC Access Method opening routine. */
1212 /* New method now that this routine is called recursively (last table */
1213 /* first in reverse order): index blocks are immediately linked to */
1214 /* join block of next table if it exists or else are discarted. */
1215 /***********************************************************************/
OpenDB(PGLOBAL g)1216 bool TDBXDBC::OpenDB(PGLOBAL g)
1217 {
1218 bool rc = false;
1219
1220 if (trace(1))
1221 htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n",
1222 this, Tdb_No, Use, Mode);
1223
1224 if (Use == USE_OPEN) {
1225 strcpy(g->Message, "Multiple execution is not allowed");
1226 return true;
1227 } // endif use
1228
1229 /*********************************************************************/
1230 /* Open an ODBC connection for this table. */
1231 /* Note: this may not be the proper way to do. Perhaps it is better */
1232 /* to test whether a connection is already open for this datasource */
1233 /* and if so to allocate just a new result set. But this only for */
1234 /* drivers allowing concurency in getting results ??? */
1235 /*********************************************************************/
1236 if (!Ocp) {
1237 Ocp = new(g) ODBConn(g, this);
1238 } else if (Ocp->IsOpen())
1239 Ocp->Close();
1240
1241 if (Ocp->Open(Connect, &Ops, Options) < 1)
1242 return true;
1243
1244 Use = USE_OPEN; // Do it now in case we are recursively called
1245
1246 if (Mode != MODE_READ && Mode != MODE_READX) {
1247 strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables");
1248 return true;
1249 } // endif Mode
1250
1251 /*********************************************************************/
1252 /* Get the command to execute. */
1253 /*********************************************************************/
1254 if (!(Cmdlist = MakeCMD(g))) {
1255 // Next lines commented out because of CHECK TABLE
1256 //Ocp->Close();
1257 //return true;
1258 } // endif Cmdlist
1259
1260 Rows = 1;
1261 return false;
1262 } // end of OpenDB
1263
1264 /***********************************************************************/
1265 /* ReadDB: Data Base read routine for xdbc access method. */
1266 /***********************************************************************/
ReadDB(PGLOBAL g)1267 int TDBXDBC::ReadDB(PGLOBAL g)
1268 {
1269 if (Cmdlist) {
1270 if (!Query)
1271 Query = new(g)STRING(g, 0, Cmdlist->Cmd);
1272 else
1273 Query->Set(Cmdlist->Cmd);
1274
1275 if (Ocp->ExecSQLcommand(Query->GetStr()))
1276 Nerr++;
1277
1278 Fpos++; // Used for progress info
1279 Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
1280 return RC_OK;
1281 } else {
1282 PushWarning(g, this, 1);
1283 return RC_EF;
1284 } // endif Cmdlist
1285
1286 } // end of ReadDB
1287
1288 /***********************************************************************/
1289 /* Data Base write line routine for XDBC access method. */
1290 /***********************************************************************/
WriteDB(PGLOBAL g)1291 int TDBXDBC::WriteDB(PGLOBAL g)
1292 {
1293 strcpy(g->Message, "Execsrc tables are read only");
1294 return RC_FX;
1295 } // end of DeleteDB
1296
1297 /***********************************************************************/
1298 /* Data Base delete line routine for XDBC access method. */
1299 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)1300 int TDBXDBC::DeleteDB(PGLOBAL g, int irc)
1301 {
1302 strcpy(g->Message, MSG(NO_ODBC_DELETE));
1303 return RC_FX;
1304 } // end of DeleteDB
1305
1306 /* --------------------------- XSRCCOL ------------------------------- */
1307
1308 /***********************************************************************/
1309 /* XSRCCOL public constructor. */
1310 /***********************************************************************/
XSRCCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)1311 XSRCCOL::XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1312 : ODBCCOL(cdp, tdbp, cprec, i, am)
1313 {
1314 // Set additional ODBC access method information for column.
1315 Flag = cdp->GetOffset();
1316 } // end of XSRCCOL constructor
1317
1318 /***********************************************************************/
1319 /* XSRCCOL constructor used for copying columns. */
1320 /* tdbp is the pointer to the new table descriptor. */
1321 /***********************************************************************/
XSRCCOL(XSRCCOL * col1,PTDB tdbp)1322 XSRCCOL::XSRCCOL(XSRCCOL *col1, PTDB tdbp) : ODBCCOL(col1, tdbp)
1323 {
1324 Flag = col1->Flag;
1325 } // end of XSRCCOL copy constructor
1326
1327 /***********************************************************************/
1328 /* ReadColumn: set column value according to Flag. */
1329 /***********************************************************************/
ReadColumn(PGLOBAL g)1330 void XSRCCOL::ReadColumn(PGLOBAL g)
1331 {
1332 PTDBXDBC tdbp = (PTDBXDBC)To_Tdb;
1333
1334 switch (Flag) {
1335 case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
1336 case 1: Value->SetValue(tdbp->AftRows); break;
1337 case 2: Value->SetValue_psz(g->Message); break;
1338 default: Value->SetValue_psz("Invalid Flag"); break;
1339 } // endswitch Flag
1340
1341 } // end of ReadColumn
1342
1343 /***********************************************************************/
1344 /* WriteColumn: Should never be called. */
1345 /***********************************************************************/
WriteColumn(PGLOBAL g)1346 void XSRCCOL::WriteColumn(PGLOBAL g)
1347 {
1348 // Should never be called
1349 } // end of WriteColumn
1350
1351 /* ---------------------------TDBDRV class --------------------------- */
1352
1353 /***********************************************************************/
1354 /* GetResult: Get the list of ODBC drivers. */
1355 /***********************************************************************/
GetResult(PGLOBAL g)1356 PQRYRES TDBDRV::GetResult(PGLOBAL g)
1357 {
1358 return ODBCDrivers(g, Maxres, false);
1359 } // end of GetResult
1360
1361 /* ---------------------------TDBSRC class --------------------------- */
1362
1363 /***********************************************************************/
1364 /* GetResult: Get the list of ODBC data sources. */
1365 /***********************************************************************/
GetResult(PGLOBAL g)1366 PQRYRES TDBSRC::GetResult(PGLOBAL g)
1367 {
1368 return ODBCDataSources(g, Maxres, false);
1369 } // end of GetResult
1370
1371 /* ---------------------------TDBOTB class --------------------------- */
1372
1373 /***********************************************************************/
1374 /* TDBOTB class constructor. */
1375 /***********************************************************************/
TDBOTB(PODEF tdp)1376 TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp)
1377 {
1378 Dsn = tdp->GetConnect();
1379 Schema = tdp->GetTabschema();
1380 Tab = tdp->GetTabname();
1381 Tabtyp = tdp->Tabtyp;
1382 Ops.User = tdp->Username;
1383 Ops.Pwd = tdp->Password;
1384 Ops.Cto = tdp->Cto;
1385 Ops.Qto = tdp->Qto;
1386 Ops.UseCnc = tdp->UseCnc;
1387 } // end of TDBOTB constructor
1388
1389 /***********************************************************************/
1390 /* GetResult: Get the list of ODBC tables. */
1391 /***********************************************************************/
GetResult(PGLOBAL g)1392 PQRYRES TDBOTB::GetResult(PGLOBAL g)
1393 {
1394 return ODBCTables(g, Dsn, Schema, Tab, Tabtyp, Maxres, false, &Ops);
1395 } // end of GetResult
1396
1397 /* ---------------------------TDBOCL class --------------------------- */
1398
1399 /***********************************************************************/
1400 /* TDBOCL class constructor. */
1401 /***********************************************************************/
TDBOCL(PODEF tdp)1402 TDBOCL::TDBOCL(PODEF tdp) : TDBOTB(tdp)
1403 {
1404 Colpat = tdp->Colpat;
1405 } // end of TDBOTB constructor
1406
1407 /***********************************************************************/
1408 /* GetResult: Get the list of ODBC table columns. */
1409 /***********************************************************************/
GetResult(PGLOBAL g)1410 PQRYRES TDBOCL::GetResult(PGLOBAL g)
1411 {
1412 return ODBCColumns(g, Dsn, Schema, Tab, Colpat, Maxres, false, &Ops);
1413 } // end of GetResult
1414
1415 /* ------------------------ End of Tabodbc --------------------------- */
1416