/************* Tabodbc C++ Program Source Code File (.CPP) *************/ /* PROGRAM NAME: TABODBC */ /* ------------- */ /* Version 3.2 */ /* */ /* COPYRIGHT: */ /* ---------- */ /* (C) Copyright to the author Olivier BERTRAND 2000-2018 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ /* This program are the TABODBC class DB execution routines. */ /* */ /* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ /* -------------------------------------- */ /* */ /* REQUIRED FILES: */ /* --------------- */ /* TABODBC.CPP - Source code */ /* PLGDBSEM.H - DB application declaration file */ /* TABODBC.H - TABODBC classes declaration file */ /* GLOBAL.H - Global declaration file */ /* */ /* REQUIRED LIBRARIES: */ /* ------------------- */ /* Large model C library */ /* */ /* REQUIRED PROGRAMS: */ /* ------------------ */ /* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ /* */ /***********************************************************************/ /***********************************************************************/ /* Include relevant MariaDB header file. */ /***********************************************************************/ #include "my_global.h" #include "sql_class.h" #if defined(_WIN32) #include #include #if defined(__BORLANDC__) #define __MFC_COMPAT__ // To define min/max as macro #endif //#include #include #else #if defined(UNIX) #include #define NODW #include "osutil.h" #else #include #endif #include #endif /***********************************************************************/ /* Include application header files: */ /* global.h is header containing all global declarations. */ /* plgdbsem.h is header containing the DB application declarations. */ /* kindex.h is kindex header that also includes tabdos.h. */ /* tabodbc.h is header containing the TABODBC class declarations. */ /* odbconn.h is header containing ODBC connection declarations. */ /***********************************************************************/ #include "global.h" #include "plgdbsem.h" #include "mycat.h" #include "xtable.h" #include "tabext.h" #include "odbccat.h" #include "tabodbc.h" #include "tabmul.h" //#include "reldef.h" #include "tabcol.h" #include "valblk.h" #include "ha_connect.h" #include "sql_string.h" /***********************************************************************/ /* DB static variables. */ /***********************************************************************/ // int num_read, num_there, num_eq[2], num_nf; // Statistics extern int num_read, num_there, num_eq[2]; // Statistics /***********************************************************************/ /* External function. */ /***********************************************************************/ bool ExactInfo(void); /* -------------------------- Class ODBCDEF -------------------------- */ /***********************************************************************/ /* Constructor. */ /***********************************************************************/ ODBCDEF::ODBCDEF(void) { Connect = NULL; Catver = 0; UseCnc = false; } // end of ODBCDEF constructor /***********************************************************************/ /* DefineAM: define specific AM block values from XDB file. */ /***********************************************************************/ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { Desc = Connect = GetStringCatInfo(g, "Connect", NULL); if (!Connect && !Catfunc) { sprintf(g->Message, "Missing connection for ODBC table %s", Name); return true; } // endif Connect if (EXTDEF::DefineAM(g, am, poff)) return true; Catver = GetIntCatInfo("Catver", 2); Options = ODBConn::noOdbcDialog; //Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib; Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT); Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT); UseCnc = GetBoolCatInfo("UseDSN", false); return false; } // end of DefineAM /***********************************************************************/ /* GetTable: makes a new Table Description Block. */ /***********************************************************************/ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m) { PTDB tdbp = NULL; /*********************************************************************/ /* Allocate a TDB of the proper type. */ /* Column blocks will be allocated only when needed. */ /*********************************************************************/ if (Xsrc) tdbp = new(g) TDBXDBC(this); else switch (Catfunc) { case FNC_COL: tdbp = new(g) TDBOCL(this); break; case FNC_TABLE: tdbp = new(g) TDBOTB(this); break; case FNC_DSN: tdbp = new(g) TDBSRC(this); break; case FNC_DRIVER: tdbp = new(g) TDBDRV(this); break; default: tdbp = new(g) TDBODBC(this); if (Multiple == 1) tdbp = new(g) TDBMUL(tdbp); else if (Multiple == 2) strcpy(g->Message, MSG(NO_ODBC_MUL)); } // endswitch Catfunc return tdbp; } // end of GetTable /* -------------------------- Class TDBODBC -------------------------- */ /***********************************************************************/ /* Implementation of the TDBODBC class. */ /***********************************************************************/ TDBODBC::TDBODBC(PODEF tdp) : TDBEXT(tdp) { Ocp = NULL; Cnp = NULL; if (tdp) { Connect = tdp->Connect; Ops.User = tdp->Username; Ops.Pwd = tdp->Password; Ops.Cto = tdp->Cto; Ops.Qto = tdp->Qto; Catver = tdp->Catver; Ops.UseCnc = tdp->UseCnc; } else { Connect = NULL; Ops.User = NULL; Ops.Pwd = NULL; Ops.Cto = DEFAULT_LOGIN_TIMEOUT; Ops.Qto = DEFAULT_QUERY_TIMEOUT; Catver = 0; Ops.UseCnc = false; } // endif tdp } // end of TDBODBC standard constructor TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBEXT(tdbp) { Ocp = tdbp->Ocp; // is that right ? Cnp = tdbp->Cnp; Connect = tdbp->Connect; Ops = tdbp->Ops; } // end of TDBODBC copy constructor // Method PTDB TDBODBC::Clone(PTABS t) { PTDB tp; PODBCCOL cp1, cp2; PGLOBAL g = t->G; // Is this really useful ??? tp = new(g) TDBODBC(this); for (cp1 = (PODBCCOL)Columns; cp1; cp1 = (PODBCCOL)cp1->GetNext()) { cp2 = new(g) ODBCCOL(cp1, tp); // Make a copy NewPointer(t, cp1, cp2); } // endfor cp1 return tp; } // end of CopyOne /***********************************************************************/ /* Allocate ODBC column description block. */ /***********************************************************************/ PCOL TDBODBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) { return new(g) ODBCCOL(cdp, this, cprec, n); } // end of MakeCol /***********************************************************************/ /* Extract the filename from connect string and return it. */ /* This used for Multiple(1) tables. Also prepare a connect string */ /* with a place holder to be used by SetFile. */ /***********************************************************************/ PCSZ TDBODBC::GetFile(PGLOBAL g) { if (Connect) { char *p1, *p2; int i; size_t n; if (!(p1 = strstr(Connect, "DBQ="))) { char *p, *lc = strlwr(PlugDup(g, Connect)); if ((p = strstr(lc, "database="))) p1 = Connect + (p - lc); i = 9; } else i = 4; if (p1) { p1 += i; // Beginning of file name p2 = strchr(p1, ';'); // End of file path/name // Make the File path/name from the connect string n = (p2) ? p2 - p1 : strlen(p1); DBQ = (PSZ)PlugSubAlloc(g, NULL, n + 1); memcpy(DBQ, p1, n); DBQ[n] = '\0'; // Make the Format used to re-generate Connect (3 = "%s" + 1) MulConn = (char*)PlugSubAlloc(g, NULL, strlen(Connect) - n + 3); memcpy(MulConn, Connect, p1 - Connect); MulConn[p1 - Connect] = '\0'; strcat(strcat(MulConn, "%s"), (p2) ? p2 : ";"); } // endif p1 } // endif Connect return (DBQ) ? DBQ : (PSZ)"???"; } // end of GetFile /***********************************************************************/ /* Set DBQ and get the new file name into the connect string. */ /***********************************************************************/ void TDBODBC::SetFile(PGLOBAL g, PCSZ fn) { if (MulConn) { int n = strlen(MulConn) + strlen(fn) - 1; if (n > BufSize) { // Allocate a buffer larger than needed so the chance // of having to reallocate it is reduced. BufSize = n + 6; Connect = (char*)PlugSubAlloc(g, NULL, BufSize); } // endif n // Make the complete connect string sprintf(Connect, MulConn, fn); } // endif MultConn DBQ = PlugDup(g, fn); } // end of SetFile /***********************************************************************/ /* MakeInsert: make the Insert statement used with ODBC connection. */ /***********************************************************************/ bool TDBODBC::MakeInsert(PGLOBAL g) { PCSZ schmp = NULL; char *catp = NULL, buf[NAM_LEN * 3]; int len = 0; bool oom, b = false; PCOL colp; for (colp = Columns; colp; colp = colp->GetNext()) if (colp->IsSpecial()) { strcpy(g->Message, MSG(NO_ODBC_SPECOL)); return true; } else { // Column name can be encoded in UTF-8 Decode(colp->GetName(), buf, sizeof(buf)); len += (strlen(buf) + 6); // comma + quotes + valist ((PEXTCOL)colp)->SetRank(++Nparm); } // endif colp // Below 32 is enough to contain the fixed part of the query if (Catalog && *Catalog) catp = Catalog; if (catp) len += strlen(catp) + 1; if (Schema && *Schema) schmp = Schema; if (schmp) len += strlen(schmp) + 1; // Table name can be encoded in UTF-8 Decode(TableName, buf, sizeof(buf)); len += (strlen(buf) + 32); Query = new(g) STRING(g, len, "INSERT INTO "); if (catp) { Query->Append(catp); if (schmp) { Query->Append('.'); Query->Append(schmp); } // endif schmp Query->Append('.'); } else if (schmp) { Query->Append(schmp); Query->Append('.'); } // endif schmp if (Quote) { // Put table name between identifier quotes in case in contains blanks Query->Append(Quote); Query->Append(buf); Query->Append(Quote); } else Query->Append(buf); Query->Append('('); for (colp = Columns; colp; colp = colp->GetNext()) { if (b) Query->Append(", "); else b = true; // Column name can be in UTF-8 encoding Decode(colp->GetName(), buf, sizeof(buf)); if (Quote) { // Put column name between identifier quotes in case in contains blanks Query->Append(Quote); Query->Append(buf); Query->Append(Quote); } else Query->Append(buf); } // endfor colp Query->Append(") VALUES ("); for (int i = 0; i < Nparm; i++) Query->Append("?,"); if ((oom = Query->IsTruncated())) strcpy(g->Message, "MakeInsert: Out of memory"); else Query->RepLast(')'); return oom; } // end of MakeInsert /***********************************************************************/ /* ODBC Bind Parameter function. */ /***********************************************************************/ bool TDBODBC::BindParameters(PGLOBAL g) { PODBCCOL colp; for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) { colp->AllocateBuffers(g, 0); if (Ocp->BindParam(colp)) return true; } // endfor colp return false; } // end of BindParameters #if 0 /***********************************************************************/ /* MakeUpdate: make the SQL statement to send to ODBC connection. */ /***********************************************************************/ char *TDBODBC::MakeUpdate(PGLOBAL g) { char *qc, *stmt = NULL, cmd[8], tab[96], end[1024]; stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); memset(end, 0, sizeof(end)); if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 || sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2) qc = Ocp->GetQuoteChar(); else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2) qc = (Quoted) ? Quote : ""; else { strcpy(g->Message, "Cannot use this UPDATE command"); return NULL; } // endif sscanf assert(!stricmp(cmd, "update")); strcat(strcat(strcat(strcpy(stmt, "UPDATE "), qc), TableName), qc); for (int i = 0; end[i]; i++) if (end[i] == '`') end[i] = *qc; strcat(stmt, end); return stmt; } // end of MakeUpdate /***********************************************************************/ /* MakeDelete: make the SQL statement to send to ODBC connection. */ /***********************************************************************/ char *TDBODBC::MakeDelete(PGLOBAL g) { char *qc, *stmt = NULL, cmd[8], from[8], tab[96], end[512]; stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); memset(end, 0, sizeof(end)); if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 || sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2) qc = Ocp->GetQuoteChar(); else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2) qc = (Quoted) ? Quote : ""; else { strcpy(g->Message, "Cannot use this DELETE command"); return NULL; } // endif sscanf assert(!stricmp(cmd, "delete") && !stricmp(from, "from")); strcat(strcat(strcat(strcpy(stmt, "DELETE FROM "), qc), TableName), qc); if (*end) { for (int i = 0; end[i]; i++) if (end[i] == '`') end[i] = *qc; strcat(stmt, end); } // endif end return stmt; } // end of MakeDelete #endif // 0 /***********************************************************************/ /* ResetSize: call by TDBMUL when calculating size estimate. */ /***********************************************************************/ void TDBODBC::ResetSize(void) { MaxSize = -1; if (Ocp && Ocp->IsOpen()) Ocp->Close(); } // end of ResetSize /***********************************************************************/ /* ODBC Cardinality: returns table size in number of rows. */ /***********************************************************************/ int TDBODBC::Cardinality(PGLOBAL g) { if (!g) return (Mode == MODE_ANY && !Srcdef) ? 1 : 0; if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) { // Info command, we must return the exact table row number char qry[96], tbn[64]; ODBConn *ocp = new(g) ODBConn(g, this); if (ocp->Open(Connect, &Ops, Options) < 1) return -1; // Table name can be encoded in UTF-8 Decode(TableName, tbn, sizeof(tbn)); strcpy(qry, "SELECT COUNT(*) FROM "); if (Quote) strcat(strcat(strcat(qry, Quote), tbn), Quote); else strcat(qry, tbn); // Allocate a Count(*) column (must not use the default constructor) Cnp = new(g) ODBCCOL; Cnp->InitValue(g); if ((Cardinal = ocp->GetResultSize(qry, Cnp)) < 0) return -3; ocp->Close(); } else Cardinal = 10; // To make MySQL happy return Cardinal; } // end of Cardinality /***********************************************************************/ /* ODBC Access Method opening routine. */ /* New method now that this routine is called recursively (last table */ /* first in reverse order): index blocks are immediately linked to */ /* join block of next table if it exists or else are discarted. */ /***********************************************************************/ bool TDBODBC::OpenDB(PGLOBAL g) { bool rc = true; if (trace(1)) htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n", this, Tdb_No, Use, Mode); if (Use == USE_OPEN) { /*******************************************************************/ /* Table already open, just replace it at its beginning. */ /*******************************************************************/ if (Memory == 1) { if ((Qrp = Ocp->AllocateResult(g))) Memory = 2; // Must be filled else Memory = 0; // Allocation failed, don't use it } else if (Memory == 2) Memory = 3; // Ok to use memory result if (Memory < 3) { // Method will depend on cursor type if ((Rbuf = Ocp->Rewind(Query->GetStr(), (PODBCCOL)Columns)) < 0) { if (Mode != MODE_READX) { Ocp->Close(); return true; } else { Rbuf = 0; } } } else { Rbuf = Qrp->Nblin; } CurNum = 0; Fpos = 0; Curpos = 1; return false; } // endif use /*********************************************************************/ /* Open an ODBC connection for this table. */ /* Note: this may not be the proper way to do. Perhaps it is better */ /* to test whether a connection is already open for this datasource */ /* and if so to allocate just a new result set. But this only for */ /* drivers allowing concurency in getting results ??? */ /*********************************************************************/ if (!Ocp) Ocp = new(g) ODBConn(g, this); else if (Ocp->IsOpen()) Ocp->Close(); if (Ocp->Open(Connect, &Ops, Options) < 1) return true; else if (Quoted) Quote = Ocp->GetQuoteChar(); Use = USE_OPEN; // Do it now in case we are recursively called /*********************************************************************/ /* Make the command and allocate whatever is used for getting results*/ /*********************************************************************/ if (Mode == MODE_READ || Mode == MODE_READX) { if (Memory > 1 && !Srcdef) { int n; if (!MakeSQL(g, true)) { // Allocate a Count(*) column Cnp = new(g) ODBCCOL; Cnp->InitValue(g); if ((n = Ocp->GetResultSize(Query->GetStr(), Cnp)) < 0) { char* msg = PlugDup(g, g->Message); sprintf(g->Message, "Get result size: %s (rc=%d)", msg, n); return true; } else if (n) { Ocp->m_Rows = n; if ((Qrp = Ocp->AllocateResult(g))) Memory = 2; // Must be filled else { strcpy(g->Message, "Result set memory allocation failed"); return true; } // endif n } else // Void result Memory = 0; Ocp->m_Rows = 0; } else return true; } // endif Memory if (!(rc = MakeSQL(g, false))) { for (PODBCCOL colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->GetNext()) if (!colp->IsSpecial()) colp->AllocateBuffers(g, Rows); rc = (Mode == MODE_READ) ? ((Rows = Ocp->ExecDirectSQL(Query->GetStr(), (PODBCCOL)Columns)) < 0) : false; } // endif rc } else if (Mode == MODE_INSERT) { if (!(rc = MakeInsert(g))) { if (Nparm != Ocp->PrepareSQL(Query->GetStr())) { strcpy(g->Message, MSG(PARM_CNT_MISS)); rc = true; } else rc = BindParameters(g); } // endif rc } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) { rc = false; // wait for CheckCond before calling MakeCommand(g); } else sprintf(g->Message, "Invalid mode %d", Mode); if (rc) { Ocp->Close(); return true; } // endif rc /*********************************************************************/ /* Reset statistics values. */ /*********************************************************************/ num_read = num_there = num_eq[0] = num_eq[1] = 0; return false; } // end of OpenDB #if 0 /***********************************************************************/ /* GetRecpos: return the position of last read record. */ /***********************************************************************/ int TDBODBC::GetRecpos(void) { return Fpos; } // end of GetRecpos #endif // 0 /***********************************************************************/ /* SetRecpos: set the position of next read record. */ /***********************************************************************/ bool TDBODBC::SetRecpos(PGLOBAL g, int recpos) { if (Ocp->m_Full) { Fpos = 0; CurNum = recpos - 1; } else if (Memory == 3) { Fpos = recpos; CurNum = -1; } else if (Scrollable) { // Is new position in the current row set? if (recpos >= Curpos && recpos < Curpos + Rbuf) { CurNum = recpos - Curpos; Fpos = 0; } else { Fpos = recpos; CurNum = 0; } // endif recpos } else { strcpy(g->Message, "This action requires Memory setting or a scrollable cursor"); return true; } // endif's // Indicate the table position was externally set Placed = true; return false; } // end of SetRecpos /***********************************************************************/ /* Data Base indexed read routine for ODBC access method. */ /***********************************************************************/ bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr) { char c = Quote ? *Quote : 0; int oldlen = Query->GetLength(); PHC hc = To_Def->GetHandler(); if (!(kr || hc->end_range) || op == OP_NEXT || Mode == MODE_UPDATE || Mode == MODE_DELETE) { if (!kr && Mode == MODE_READX) { // This is a false indexed read Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns); Mode = MODE_READ; return (Rows < 0); } // endif key return false; } else { if (hc->MakeKeyWhere(g, Query, op, c, kr)) return true; if (To_CondFil) { if (To_CondFil->Idx != hc->active_index) { To_CondFil->Idx = hc->active_index; To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0); *To_CondFil->Body= 0; if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond))) PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1); } // endif active_index if (To_CondFil) if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) { strcpy(g->Message, "Readkey: Out of memory"); return true; } // endif Append } // endif To_Condfil Mode = MODE_READ; } // endif's op if (trace(33)) htrc("ODBC ReadKey: Query=%s\n", Query->GetStr()); Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns); Query->Truncate(oldlen); return (Rows < 0); } // end of ReadKey /***********************************************************************/ /* VRDNDOS: Data Base read routine for odbc access method. */ /***********************************************************************/ int TDBODBC::ReadDB(PGLOBAL g) { int rc; if (trace(2)) htrc("ODBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) { if (!Query && MakeCommand(g)) return RC_FX; // Send the UPDATE/DELETE command to the remote table if (!Ocp->ExecSQLcommand(Query->GetStr())) { sprintf(g->Message, "%s: %d affected rows", TableName, AftRows); if (trace(1)) htrc("%s\n", g->Message); PushWarning(g, this, 0); // 0 means a Note return RC_EF; // Nothing else to do } else return RC_FX; // Error } // endif Mode /*********************************************************************/ /* Now start the reading process. */ /* Here is the place to fetch the line(s). */ /*********************************************************************/ if (Placed) { if (Fpos && CurNum >= 0) Rbuf = Ocp->Fetch((Curpos = Fpos)); rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX; Placed = false; } else { if (Memory != 3) { if (++CurNum >= Rbuf) { Rbuf = Ocp->Fetch(); Curpos = Fpos + 1; CurNum = 0; } // endif CurNum rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX; } else // Getting result from memory rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF; if (rc == RC_OK) { if (Memory == 2) Qrp->Nblin++; Fpos++; // Used for memory and pos } // endif rc } // endif Placed if (trace(2)) htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc); return rc; } // end of ReadDB /***********************************************************************/ /* Data Base Insert write routine for ODBC access method. */ /***********************************************************************/ int TDBODBC::WriteDB(PGLOBAL g) { int n = Ocp->ExecuteSQL(); if (n < 0) { AftRows = n; return RC_FX; } else AftRows += n; return RC_OK; } // end of WriteDB /***********************************************************************/ /* Data Base delete line routine for ODBC access method. */ /***********************************************************************/ int TDBODBC::DeleteDB(PGLOBAL g, int irc) { if (irc == RC_FX) { if (!Query && MakeCommand(g)) return RC_FX; // Send the DELETE (all) command to the remote table if (!Ocp->ExecSQLcommand(Query->GetStr())) { sprintf(g->Message, "%s: %d affected rows", TableName, AftRows); if (trace(1)) htrc("%s\n", g->Message); PushWarning(g, this, 0); // 0 means a Note return RC_OK; // This is a delete all } else return RC_FX; // Error } else return RC_OK; // Ignore } // end of DeleteDB /***********************************************************************/ /* Data Base close routine for ODBC access method. */ /***********************************************************************/ void TDBODBC::CloseDB(PGLOBAL g) { if (Ocp) Ocp->Close(); if (trace(1)) htrc("ODBC CloseDB: closing %s\n", Name); } // end of CloseDB /* --------------------------- ODBCCOL ------------------------------- */ /***********************************************************************/ /* ODBCCOL public constructor. */ /***********************************************************************/ ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am) : EXTCOL(cdp, tdbp, cprec, i, am) { // Set additional ODBC access method information for column. Slen = 0; StrLen = &Slen; Sqlbuf = NULL; } // end of ODBCCOL constructor /***********************************************************************/ /* ODBCCOL private constructor. */ /***********************************************************************/ ODBCCOL::ODBCCOL(void) : EXTCOL() { Slen = 0; StrLen = &Slen; Sqlbuf = NULL; } // end of ODBCCOL constructor /***********************************************************************/ /* ODBCCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp) { Slen = col1->Slen; StrLen = col1->StrLen; Sqlbuf = col1->Sqlbuf; } // end of ODBCCOL copy constructor /***********************************************************************/ /* ReadColumn: when SQLFetch is used there is nothing to do as the */ /* column buffer was bind to the record set. This is also the case */ /* when calculating MaxSize (Bufp is NULL even when Rows is not). */ /***********************************************************************/ void ODBCCOL::ReadColumn(PGLOBAL g) { PTDBODBC tdbp = (PTDBODBC)To_Tdb; int i = tdbp->Fpos - 1, n = tdbp->CurNum; if (tdbp->Memory == 3) { // Get the value from the stored memory if (Crp->Nulls && Crp->Nulls[i] == '*') { Value->Reset(); Value->SetNull(true); } else { Value->SetValue_pvblk(Crp->Kdata, i); Value->SetNull(false); } // endif Nulls return; } // endif Memory if (StrLen[n] == SQL_NULL_DATA) { // Null value if (Nullable) Value->SetNull(true); Value->Reset(); goto put; } else Value->SetNull(false); if (Bufp && tdbp->Rows) { if (Buf_Type == TYPE_DATE) *Sqlbuf = ((TIMESTAMP_STRUCT*)Bufp)[n]; else Value->SetValue_pvblk(Blkp, n); } // endif Bufp if (Buf_Type == TYPE_DATE) { struct tm dbtime; memset(&dbtime, 0, sizeof(tm)); dbtime.tm_sec = (int)Sqlbuf->second; dbtime.tm_min = (int)Sqlbuf->minute; dbtime.tm_hour = (int)Sqlbuf->hour; dbtime.tm_mday = (int)Sqlbuf->day; dbtime.tm_mon = (int)Sqlbuf->month - 1; dbtime.tm_year = (int)Sqlbuf->year - 1900; ((DTVAL*)Value)->MakeTime(&dbtime); } else if (Buf_Type == TYPE_DECIM && tdbp->Sep) { // Be sure to use decimal point char *p = strchr(Value->GetCharValue(), tdbp->Sep); if (p) *p = '.'; } // endif Buf_Type if (trace(2)) { char buf[64]; htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n", Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf)); } // endif trace put: if (tdbp->Memory != 2) return; /*********************************************************************/ /* Fill the allocated result structure. */ /*********************************************************************/ if (Value->IsNull()) { if (Crp->Nulls) Crp->Nulls[i] = '*'; // Null value Crp->Kdata->Reset(i); } else Crp->Kdata->SetValue(Value, i); } // end of ReadColumn /***********************************************************************/ /* AllocateBuffers: allocate the extended buffer for SQLExtendedFetch */ /* or Fetch. Note: we use Long+1 here because ODBC must have space */ /* for the ending null character. */ /***********************************************************************/ void ODBCCOL::AllocateBuffers(PGLOBAL g, int rows) { if (Buf_Type == TYPE_DATE) Sqlbuf = (TIMESTAMP_STRUCT*)PlugSubAlloc(g, NULL, sizeof(TIMESTAMP_STRUCT)); if (!rows) return; if (Buf_Type == TYPE_DATE) Bufp = PlugSubAlloc(g, NULL, rows * sizeof(TIMESTAMP_STRUCT)); else { Blkp = AllocValBlock(g, NULL, Buf_Type, rows, GetBuflen(), GetScale(), true, false, false); Bufp = Blkp->GetValPointer(); } // endelse if (rows > 1) StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(SQLLEN)); } // end of AllocateBuffers /***********************************************************************/ /* Returns the buffer to use for Fetch or Extended Fetch. */ /***********************************************************************/ void *ODBCCOL::GetBuffer(DWORD rows) { if (rows && To_Tdb) { assert(rows == (DWORD)((TDBODBC*)To_Tdb)->Rows); return Bufp; } else return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val(); } // end of GetBuffer /***********************************************************************/ /* Returns the buffer length to use for Fetch or Extended Fetch. */ /***********************************************************************/ SWORD ODBCCOL::GetBuflen(void) { SWORD flen; switch (Buf_Type) { case TYPE_DATE: flen = (SWORD)sizeof(TIMESTAMP_STRUCT); break; case TYPE_STRING: case TYPE_DECIM: flen = (SWORD)Value->GetClen() + 1; break; default: flen = (SWORD)Value->GetClen(); } // endswitch Buf_Type return flen; } // end of GetBuflen /***********************************************************************/ /* WriteColumn: make sure the bind buffer is updated. */ /***********************************************************************/ void ODBCCOL::WriteColumn(PGLOBAL g) { /*********************************************************************/ /* Do convert the column value if necessary. */ /*********************************************************************/ if (Value != To_Val) Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value if (Buf_Type == TYPE_DATE) { struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm); Sqlbuf->second = dbtime->tm_sec; Sqlbuf->minute = dbtime->tm_min; Sqlbuf->hour = dbtime->tm_hour; Sqlbuf->day = dbtime->tm_mday; Sqlbuf->month = dbtime->tm_mon + 1; Sqlbuf->year = dbtime->tm_year + 1900; Sqlbuf->fraction = 0; } else if (Buf_Type == TYPE_DECIM) { // Some data sources require local decimal separator char *p, sep = ((PTDBODBC)To_Tdb)->Sep; if (sep && (p = strchr(Value->GetCharValue(), '.'))) *p = sep; } // endif Buf_Type if (Nullable) *StrLen = (Value->IsNull()) ? SQL_NULL_DATA : (IsTypeChar(Buf_Type)) ? SQL_NTS : 0; } // end of WriteColumn /* -------------------------- Class TDBXDBC -------------------------- */ /***********************************************************************/ /* Implementation of the TDBXDBC class. */ /***********************************************************************/ TDBXDBC::TDBXDBC(PODEF tdp) : TDBODBC(tdp) { Cmdlist = NULL; Cmdcol = NULL; Mxr = tdp->Maxerr; Nerr = 0; } // end of TDBXDBC constructor TDBXDBC::TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp) { Cmdlist = tdbp->Cmdlist; Cmdcol = tdbp->Cmdcol; Mxr = tdbp->Mxr; Nerr = tdbp->Nerr; } // end of TDBXDBC copy constructor PTDB TDBXDBC::Clone(PTABS t) { PTDB tp; PXSRCCOL cp1, cp2; PGLOBAL g = t->G; // Is this really useful ??? tp = new(g) TDBXDBC(this); for (cp1 = (PXSRCCOL)Columns; cp1; cp1 = (PXSRCCOL)cp1->GetNext()) { cp2 = new(g) XSRCCOL(cp1, tp); // Make a copy NewPointer(t, cp1, cp2); } // endfor cp1 return tp; } // end of CopyOne /***********************************************************************/ /* Allocate XSRC column description block. */ /***********************************************************************/ PCOL TDBXDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) { PXSRCCOL colp = new(g) XSRCCOL(cdp, this, cprec, n); if (!colp->Flag) Cmdcol = colp->GetName(); return colp; } // end of MakeCol /***********************************************************************/ /* MakeCMD: make the SQL statement to send to ODBC connection. */ /***********************************************************************/ PCMD TDBXDBC::MakeCMD(PGLOBAL g) { PCMD xcmd = NULL; if (To_CondFil) { if (Cmdcol) { if (!stricmp(Cmdcol, To_CondFil->Body) && (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) { xcmd = To_CondFil->Cmds; } else strcpy(g->Message, "Invalid command specification filter"); } else strcpy(g->Message, "No command column in select list"); } else if (!Srcdef) strcpy(g->Message, "No Srcdef default command"); else xcmd = new(g) CMD(g, Srcdef); return xcmd; } // end of MakeCMD #if 0 /***********************************************************************/ /* ODBC Bind Parameter function. */ /***********************************************************************/ bool TDBXDBC::BindParameters(PGLOBAL g) { PODBCCOL colp; for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) { colp->AllocateBuffers(g, 0); if (Ocp->BindParam(colp)) return true; } // endfor colp return false; } // end of BindParameters #endif // 0 /***********************************************************************/ /* XDBC GetMaxSize: returns table size (not always one row). */ /***********************************************************************/ int TDBXDBC::GetMaxSize(PGLOBAL g) { if (MaxSize < 0) MaxSize = 10; // Just a guess return MaxSize; } // end of GetMaxSize /***********************************************************************/ /* ODBC Access Method opening routine. */ /* New method now that this routine is called recursively (last table */ /* first in reverse order): index blocks are immediately linked to */ /* join block of next table if it exists or else are discarted. */ /***********************************************************************/ bool TDBXDBC::OpenDB(PGLOBAL g) { if (trace(1)) htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n", this, Tdb_No, Use, Mode); if (Use == USE_OPEN) { strcpy(g->Message, "Multiple execution is not allowed"); return true; } // endif use /*********************************************************************/ /* Open an ODBC connection for this table. */ /* Note: this may not be the proper way to do. Perhaps it is better */ /* to test whether a connection is already open for this datasource */ /* and if so to allocate just a new result set. But this only for */ /* drivers allowing concurency in getting results ??? */ /*********************************************************************/ if (!Ocp) { Ocp = new(g) ODBConn(g, this); } else if (Ocp->IsOpen()) Ocp->Close(); if (Ocp->Open(Connect, &Ops, Options) < 1) return true; Use = USE_OPEN; // Do it now in case we are recursively called if (Mode != MODE_READ && Mode != MODE_READX) { strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables"); return true; } // endif Mode /*********************************************************************/ /* Get the command to execute. */ /*********************************************************************/ if (!(Cmdlist = MakeCMD(g))) { // Next lines commented out because of CHECK TABLE //Ocp->Close(); //return true; } // endif Cmdlist Rows = 1; return false; } // end of OpenDB /***********************************************************************/ /* ReadDB: Data Base read routine for xdbc access method. */ /***********************************************************************/ int TDBXDBC::ReadDB(PGLOBAL g) { if (Cmdlist) { if (!Query) Query = new(g)STRING(g, 0, Cmdlist->Cmd); else Query->Set(Cmdlist->Cmd); if (Ocp->ExecSQLcommand(Query->GetStr())) Nerr++; Fpos++; // Used for progress info Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next; return RC_OK; } else { PushWarning(g, this, 1); return RC_EF; } // endif Cmdlist } // end of ReadDB /***********************************************************************/ /* Data Base write line routine for XDBC access method. */ /***********************************************************************/ int TDBXDBC::WriteDB(PGLOBAL g) { strcpy(g->Message, "Execsrc tables are read only"); return RC_FX; } // end of DeleteDB /***********************************************************************/ /* Data Base delete line routine for XDBC access method. */ /***********************************************************************/ int TDBXDBC::DeleteDB(PGLOBAL g, int irc) { strcpy(g->Message, MSG(NO_ODBC_DELETE)); return RC_FX; } // end of DeleteDB /* --------------------------- XSRCCOL ------------------------------- */ /***********************************************************************/ /* XSRCCOL public constructor. */ /***********************************************************************/ XSRCCOL::XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am) : ODBCCOL(cdp, tdbp, cprec, i, am) { // Set additional ODBC access method information for column. Flag = cdp->GetOffset(); } // end of XSRCCOL constructor /***********************************************************************/ /* XSRCCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ XSRCCOL::XSRCCOL(XSRCCOL *col1, PTDB tdbp) : ODBCCOL(col1, tdbp) { Flag = col1->Flag; } // end of XSRCCOL copy constructor /***********************************************************************/ /* ReadColumn: set column value according to Flag. */ /***********************************************************************/ void XSRCCOL::ReadColumn(PGLOBAL g) { PTDBXDBC tdbp = (PTDBXDBC)To_Tdb; switch (Flag) { case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break; case 1: Value->SetValue(tdbp->AftRows); break; case 2: Value->SetValue_psz(g->Message); break; default: Value->SetValue_psz("Invalid Flag"); break; } // endswitch Flag } // end of ReadColumn /***********************************************************************/ /* WriteColumn: Should never be called. */ /***********************************************************************/ void XSRCCOL::WriteColumn(PGLOBAL g) { // Should never be called } // end of WriteColumn /* ---------------------------TDBDRV class --------------------------- */ /***********************************************************************/ /* GetResult: Get the list of ODBC drivers. */ /***********************************************************************/ PQRYRES TDBDRV::GetResult(PGLOBAL g) { return ODBCDrivers(g, Maxres, false); } // end of GetResult /* ---------------------------TDBSRC class --------------------------- */ /***********************************************************************/ /* GetResult: Get the list of ODBC data sources. */ /***********************************************************************/ PQRYRES TDBSRC::GetResult(PGLOBAL g) { return ODBCDataSources(g, Maxres, false); } // end of GetResult /* ---------------------------TDBOTB class --------------------------- */ /***********************************************************************/ /* TDBOTB class constructor. */ /***********************************************************************/ TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp) { Dsn = tdp->GetConnect(); Schema = tdp->GetTabschema(); Tab = tdp->GetTabname(); Tabtyp = tdp->Tabtyp; Ops.User = tdp->Username; Ops.Pwd = tdp->Password; Ops.Cto = tdp->Cto; Ops.Qto = tdp->Qto; Ops.UseCnc = tdp->UseCnc; } // end of TDBOTB constructor /***********************************************************************/ /* GetResult: Get the list of ODBC tables. */ /***********************************************************************/ PQRYRES TDBOTB::GetResult(PGLOBAL g) { return ODBCTables(g, Dsn, Schema, Tab, Tabtyp, Maxres, false, &Ops); } // end of GetResult /* ---------------------------TDBOCL class --------------------------- */ /***********************************************************************/ /* TDBOCL class constructor. */ /***********************************************************************/ TDBOCL::TDBOCL(PODEF tdp) : TDBOTB(tdp) { Colpat = tdp->Colpat; } // end of TDBOTB constructor /***********************************************************************/ /* GetResult: Get the list of ODBC table columns. */ /***********************************************************************/ PQRYRES TDBOCL::GetResult(PGLOBAL g) { return ODBCColumns(g, Dsn, Schema, Tab, Colpat, Maxres, false, &Ops); } // end of GetResult /* ------------------------ End of Tabodbc --------------------------- */