mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1411 lines
		
	
	
	
		
			45 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1411 lines
		
	
	
	
		
			45 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/************* 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 <io.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#if defined(__BORLANDC__)
 | 
						|
#define __MFC_COMPAT__                   // To define min/max as macro
 | 
						|
#endif
 | 
						|
//#include <windows.h>
 | 
						|
#include <sqltypes.h>
 | 
						|
#else
 | 
						|
#if defined(UNIX)
 | 
						|
#include <errno.h>
 | 
						|
#define NODW
 | 
						|
#include "osutil.h"
 | 
						|
#else
 | 
						|
#include <io.h>
 | 
						|
#endif
 | 
						|
#include <fcntl.h>
 | 
						|
#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) {
 | 
						|
    snprintf(g->Message, sizeof(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 (Query && (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);
 | 
						|
 | 
						|
					snprintf(g->Message, sizeof(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
 | 
						|
    snprintf(g->Message, sizeof(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())) {
 | 
						|
      snprintf(g->Message, sizeof(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())) {
 | 
						|
      snprintf(g->Message, sizeof(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 --------------------------- */
 |