From a175314c3e5827eb193872241446f2f8f5c9d33c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 20:07:14 +0200 Subject: Adding upstream version 1:10.5.12. Signed-off-by: Daniel Baumann --- storage/connect/tabodbc.cpp | 1411 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1411 insertions(+) create mode 100644 storage/connect/tabodbc.cpp (limited to 'storage/connect/tabodbc.cpp') diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp new file mode 100644 index 00000000..4e8b4417 --- /dev/null +++ b/storage/connect/tabodbc.cpp @@ -0,0 +1,1411 @@ +/************* 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 --------------------------- */ -- cgit v1.2.3