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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 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 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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 /***********************************************************************/ 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