mariadb/storage/connect/tabodbc.cpp
Olivier Bertrand 82e746ea1b Put almost all function prototypes in header files that are
included by the program using them.

Continuing implementing the "catalog" tables (ex "info").
Already existing were the ODBC data source table and the
WMI column info table.

A common way to handle them will permit to develop many
other such tables. Implemented:

The ODBC column catalog table.
The ODBC tables catalog table.
The ODBC drivers catalog table.

The INFO table option is replaced by the CATFUNC string option
whode first letter specifies the information to retrieve:
C: Columns (of a table)
T: Tables  (of a database)
S: Data sources
D: Drivers

Modified:
ha_connect.cc
odbconn.cpp
odbconn.h
tabodbc.h
tabodbc.cpp
rcmsg.c
tabfmt.h
tabmysql.cpp
tabwmi.cpp
tabwmi.h
resource.h
myconn.h
filamdbf.h
connect.cc
connect.h

Added:
myutil.h
2013-02-09 01:08:15 +01:00

1163 lines
39 KiB
C++

/************* Tabodbc C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABODBC */
/* ------------- */
/* Version 2.5 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */
/* */
/* 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"
#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 "xtable.h"
#include "tabodbc.h"
#include "tabmul.h"
#include "reldef.h"
#include "tabcol.h"
#include "valblk.h"
#include "sql_string.h"
extern "C" char *GetMsgid(int id);
/***********************************************************************/
/* DB static variables. */
/***********************************************************************/
// int num_read, num_there, num_eq[2], num_nf; // Statistics
extern int num_read, num_there, num_eq[2]; // Statistics
/* -------------------------- Class ODBCDEF -------------------------- */
/***********************************************************************/
/* Constructor. */
/***********************************************************************/
ODBCDEF::ODBCDEF(void)
{
Connect = Tabname = Tabowner = Tabqual = Qchar = NULL;
Catfunc = 0;
Catver = Options = 0;
} // end of ODBCDEF constructor
/***********************************************************************/
/* DefineAM: define specific AM block values from XDB file. */
/***********************************************************************/
bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
int dop = ODBConn::noOdbcDialog; // Default for options
Desc = Connect = Cat->GetStringCatInfo(g, Name, "Connect", "");
Catfunc = toupper(*Cat->GetStringCatInfo(g, Name, "Catfunc", ""));
Tabname = Cat->GetStringCatInfo(g, Name, "Name",
Catfunc == 'T' ? NULL : Name);
Tabname = Cat->GetStringCatInfo(g, Name, "Tabname", Tabname);
Tabowner = Cat->GetStringCatInfo(g, Name, "Owner", "");
Tabqual = Cat->GetStringCatInfo(g, Name, "Qualifier", "");
Qchar = Cat->GetStringCatInfo(g, Name, "Qchar", "");
Catver = Cat->GetIntCatInfo(Name, "Catver", 2);
Options = Cat->GetIntCatInfo(Name, "Options", dop);
Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
/***********************************************************************/
/* GetTable: makes a new Table Description Block. */
/***********************************************************************/
PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
{
PTDBASE tdbp = NULL;
/*********************************************************************/
/* Allocate a TDB of the proper type. */
/* Column blocks will be allocated only when needed. */
/*********************************************************************/
switch (Catfunc) {
case 'C':
tdbp = new(g) TDBOCL(this);
break;
case 'T':
tdbp = new(g) TDBOTB(this);
break;
case 'S':
tdbp = new(g) TDBSRC(this);
break;
case 'D':
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) : TDBASE(tdp)
{
Ocp = NULL;
Cnp = NULL;
if (tdp) {
Connect = tdp->GetConnect();
TableName = tdp->GetTabname();
Owner = tdp->GetTabowner();
Qualifier = tdp->GetTabqual();
Quote = tdp->GetQchar();
Options = tdp->GetOptions();
Rows = tdp->GetElemt();
Catver = tdp->GetCatver();
} else {
Connect = NULL;
TableName = NULL;
Owner = NULL;
Qualifier = NULL;
Quote = NULL;
Options = 0;
Rows = 0;
Catver = 0;
} // endif tdp
Query = NULL;
Count = NULL;
//Where = NULL;
MulConn = NULL;
DBQ = NULL;
Fpos = 0;
AftRows = 0;
CurNum = 0;
Rbuf = 0;
BufSize = 0;
Nparm = 0;
} // end of TDBODBC standard constructor
TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
{
Ocp = tdbp->Ocp; // is that right ?
Cnp = tdbp->Cnp;
Connect = tdbp->Connect;
TableName = tdbp->TableName;
Owner = tdbp->Owner;
Qualifier = tdbp->Qualifier;
Quote = tdbp->Quote;
Query = tdbp->Query;
Count = tdbp->Count;
//Where = tdbp->Where;
MulConn = tdbp->MulConn;
DBQ = tdbp->DBQ;
Options = tdbp->Options;
Rows = tdbp->Rows;
Fpos = tdbp->Fpos;
AftRows = tdbp->AftRows;
//Tpos = tdbp->Tpos;
//Spos = tdbp->Spos;
CurNum = tdbp->CurNum;
Rbuf = tdbp->Rbuf;
BufSize = tdbp->BufSize;
Nparm = tdbp->Nparm;
} // end of TDBODBC copy constructor
// Method
PTDB TDBODBC::CopyOne(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. */
/***********************************************************************/
PSZ TDBODBC::GetFile(PGLOBAL g)
{
if (Connect) {
char *p1, *p2;
size_t n;
if ((p1 = strstr(Connect, "DBQ="))) {
p1 += 4; // 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, PSZ 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 = fn;
} // end of SetFile
/******************************************************************/
/* Convert an UTF-8 string to latin characters. */
/******************************************************************/
int TDBODBC::Decode(char *txt, char *buf, size_t n)
{
uint dummy_errors;
uint32 len= copy_and_convert(buf, n, &my_charset_latin1,
txt, strlen(txt),
&my_charset_utf8_general_ci,
&dummy_errors);
buf[len]= '\0';
return 0;
} // end of Decode
/***********************************************************************/
/* MakeSQL: make the SQL statement use with ODBC connection. */
/* Note: when implementing EOM filtering, column only used in local */
/* filter should be removed from column list. */
/***********************************************************************/
char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
{
char *colist, *tabname, *sql, buf[64];
LPCSTR ownp = NULL, qualp = NULL;
int rc, len, ncol = 0;
bool first = true;
PTABLE tablep = To_Table;
PCOL colp;
if (!cnt) {
// Normal SQL statement to retrieve results
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial())
ncol++;
if (ncol) {
colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol);
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial()) {
// Column name can be in UTF-8 encoding
rc= Decode(colp->GetName(), buf, sizeof(buf));
if (Quote) {
if (first) {
strcat(strcat(strcpy(colist, Quote), buf), Quote);
first = false;
} else
strcat(strcat(strcat(strcat(colist, ", "),
Quote), buf), Quote);
} else {
if (first) {
strcpy(colist, buf);
first = false;
} else
strcat(strcat(colist, ", "), buf);
} // endif Quote
} // endif !Special
} else {
// ncol == 0 can occur for queries such that sql count(*) from...
// for which we will count the rows from sql * from...
colist = (char*)PlugSubAlloc(g, NULL, 2);
strcpy(colist, "*");
} // endif ncol
} else {
// SQL statement used to retrieve the size of the result
colist = (char*)PlugSubAlloc(g, NULL, 9);
strcpy(colist, "count(*)");
} // endif cnt
// Table name can be encoded in UTF-8
rc = Decode(TableName, buf, sizeof(buf));
// Put table name between identifier quotes in case in contains blanks
tabname = (char*)PlugSubAlloc(g, NULL, strlen(buf) + 3);
if (Quote)
strcat(strcat(strcpy(tabname, Quote), buf), Quote);
else
strcpy(tabname, buf);
// Below 14 is length of 'select ' + length of ' from ' + 1
len = (strlen(colist) + strlen(buf) + 14);
len += (To_Filter ? strlen(To_Filter) + 7 : 0);
// if (tablep->GetQualifier()) This is used when using a table
// qualp = tablep->GetQualifier(); from anotherPlugDB database but
// else makes no sense for ODBC.
if (Qualifier && *Qualifier)
qualp = Qualifier;
if (qualp)
len += (strlen(qualp) + 2);
if (tablep->GetCreator())
ownp = tablep->GetCreator();
else if (Owner && *Owner)
ownp = Owner;
if (ownp)
len += (strlen(ownp) + 1);
sql = (char*)PlugSubAlloc(g, NULL, len);
strcat(strcat(strcpy(sql, "SELECT "), colist), " FROM ");
if (qualp) {
strcat(sql, qualp);
if (ownp)
strcat(strcat(sql, "."), ownp);
else
strcat(sql, ".");
strcat(sql, ".");
} else if (ownp)
strcat(strcat(sql, ownp), ".");
strcat(sql, tabname);
if (To_Filter)
strcat(strcat(sql, " WHERE "), To_Filter);
return sql;
} // end of MakeSQL
/***********************************************************************/
/* ResetSize: call by TDBMUL when calculating size estimate. */
/***********************************************************************/
void TDBODBC::ResetSize(void)
{
MaxSize = -1;
if (Ocp && Ocp->IsOpen())
Ocp->Close();
} // end of ResetSize
/***********************************************************************/
/* ODBC GetMaxSize: returns table size estimate in number of lines. */
/***********************************************************************/
int TDBODBC::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
if (!Ocp)
Ocp = new(g) ODBConn(g, this);
if (!Ocp->IsOpen())
if (Ocp->Open(Connect, Options) < 1)
return -1;
if (!Count && !(Count = MakeSQL(g, true)))
return -2;
if (!Cnp) {
// Allocate a Count(*) column (must not use the default constructor)
Cnp = new(g) ODBCCOL;
Cnp->InitValue(g);
} // endif Cnp
if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0)
return -3;
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* Return 0 in mode DELETE or UPDATE to tell that it is done. */
/***********************************************************************/
int TDBODBC::GetProgMax(PGLOBAL g)
{
return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0
: GetMaxSize(g);
} // end of GetProgMax
/***********************************************************************/
/* 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 = false;
if (g->Trace)
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 (To_Kindex)
/*****************************************************************/
/* Table is to be accessed through a sorted index table. */
/*****************************************************************/
// To_Kindex->Reset();
// rewind(Stream); >>>>>>> Something to be done with Cursor <<<<<<<
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, Options) < 1)
return true;
Use = USE_OPEN; // Do it now in case we are recursively called
/*********************************************************************/
/* Allocate whatever is used for getting results. */
/*********************************************************************/
if (Mode == MODE_READ) {
/*******************************************************************/
/* The issue here is that if max result size is needed, it must be */
/* calculated before the result set for the final data retrieval is*/
/* allocated and the final statement prepared so we call GetMaxSize*/
/* here. It can be a waste of time if the max size is not needed */
/* but currently we always are asking for it (for progress info). */
/*******************************************************************/
GetMaxSize(g); // Will be set for next call
if (!Query)
if ((Query = MakeSQL(g, false))) {
for (PODBCCOL colp = (PODBCCOL)Columns;
colp; colp = (PODBCCOL)colp->GetNext())
if (!colp->IsSpecial())
colp->AllocateBuffers(g, Rows);
} else
rc = true;
if (!rc)
rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0);
} else {
strcpy(g->Message, "ODBC tables are read only in this version");
return true;
} // endelse
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
/***********************************************************************/
/* GetRecpos: return the position of last read record. */
/***********************************************************************/
int TDBODBC::GetRecpos(void)
{
return Fpos; // To be really implemented
} // end of GetRecpos
/***********************************************************************/
/* VRDNDOS: Data Base read routine for odbc access method. */
/***********************************************************************/
int TDBODBC::ReadDB(PGLOBAL g)
{
int rc;
#ifdef DEBTRACE
htrc("ODBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
#endif
if (To_Kindex) {
// Direct access of ODBC tables is not implemented yet
strcpy(g->Message, MSG(NO_ODBC_DIRECT));
longjmp(g->jumper[g->jump_level], GetAmType());
#if 0
/*******************************************************************/
/* Reading is by an index table. */
/*******************************************************************/
int recpos = To_Kindex->Fetch(g);
switch (recpos) {
case -1: // End of file reached
return RC_EF;
case -2: // No match for join
return RC_NF;
case -3: // Same record as current one
num_there++;
return RC_OK;
default:
/***************************************************************/
/* Set the cursor position according to record to read. */
/***************************************************************/
//--------------------------------- TODO --------------------------------
break;
} // endswitch recpos
#endif // 0
} // endif To_Kindex
/*********************************************************************/
/* Now start the reading process. */
/* Here is the place to fetch the line(s). */
/*********************************************************************/
if (++CurNum >= Rbuf) {
Rbuf = Ocp->Fetch();
CurNum = 0;
} // endif CurNum
rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
Fpos++; // Used for progress info
#ifdef DEBTRACE
htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
#endif
return rc;
} // end of ReadDB
/***********************************************************************/
/* Data Base Insert write routine for ODBC access method. */
/***********************************************************************/
int TDBODBC::WriteDB(PGLOBAL g)
{
strcpy(g->Message, "ODBC tables are read only");
return RC_FX;
} // end of WriteDB
/***********************************************************************/
/* Data Base delete line routine for ODBC access method. */
/***********************************************************************/
int TDBODBC::DeleteDB(PGLOBAL g, int irc)
{
strcpy(g->Message, MSG(NO_ODBC_DELETE));
return RC_FX;
} // end of DeleteDB
/***********************************************************************/
/* Data Base close routine for ODBC access method. */
/***********************************************************************/
void TDBODBC::CloseDB(PGLOBAL g)
{
//if (To_Kindex) {
// To_Kindex->Close();
// To_Kindex = NULL;
// } // endif
Ocp->Close();
#ifdef DEBTRACE
htrc("ODBC CloseDB: closing %s\n", Name);
#endif
} // end of CloseDB
/* --------------------------- ODBCCOL ------------------------------- */
/***********************************************************************/
/* ODBCCOL public constructor. */
/***********************************************************************/
ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
: COLBLK(cdp, tdbp, i)
{
if (cprec) {
Next = cprec->GetNext();
cprec->SetNext(this);
} else {
Next = tdbp->GetColumns();
tdbp->SetColumns(this);
} // endif cprec
// Set additional ODBC access method information for column.
Long = cdp->GetLong();
//strcpy(F_Date, cdp->F_Date);
To_Val = NULL;
Slen = 0;
StrLen = &Slen;
Sqlbuf = NULL;
Bufp = NULL;
Blkp = NULL;
Rank = 0; // Not known yet
#ifdef DEBTRACE
htrc(" making new %sCOL C%d %s at %p\n",
am, Index, Name, this);
#endif
} // end of ODBCCOL constructor
/***********************************************************************/
/* ODBCCOL private constructor. */
/***********************************************************************/
ODBCCOL::ODBCCOL(void) : COLBLK()
{
Buf_Type = TYPE_INT; // This is a count(*) column
// Set additional Dos access method information for column.
Long = sizeof(int);
To_Val = NULL;
Slen = 0;
StrLen = &Slen;
Sqlbuf = NULL;
Bufp = NULL;
Blkp = NULL;
Rank = 1;
} // 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) : COLBLK(col1, tdbp)
{
Long = col1->Long;
//strcpy(F_Date, col1->F_Date);
To_Val = col1->To_Val;
Slen = col1->Slen;
StrLen = col1->StrLen;
Sqlbuf = col1->Sqlbuf;
Bufp = col1->Bufp;
Blkp = col1->Blkp;
Rank = col1->Rank;
} // end of ODBCCOL copy constructor
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
/***********************************************************************/
bool ODBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
{
if (!(To_Val = value)) {
sprintf(g->Message, MSG(VALUE_ERROR), Name);
return true;
} else if (Buf_Type == value->GetType()) {
// Values are of the (good) column type
if (Buf_Type == TYPE_DATE) {
// If any of the date values is formatted
// output format must be set for the receiving table
if (GetDomain() || ((DTVAL *)value)->IsFormatted())
goto newval; // This will make a new value;
} else if (Buf_Type == TYPE_FLOAT)
// Float values must be written with the correct (column) precision
// Note: maybe this should be forced by ShowValue instead of this ?
((DFVAL *)value)->SetPrec(GetPrecision());
Value = value; // Directly access the external value
} else {
// Values are not of the (good) column type
if (check) {
sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
GetTypeName(Buf_Type), GetTypeName(value->GetType()));
return true;
} // endif check
newval:
if (InitValue(g)) // Allocate the matching value block
return true;
} // endif's Value, Buf_Type
// Because Colblk's have been made from a copy of the original TDB in
// case of Update, we must reset them to point to the original one.
if (To_Tdb->GetOrig())
To_Tdb = (PTDB)To_Tdb->GetOrig();
// Set the Column
Status = (ok) ? BUF_EMPTY : BUF_NO;
return false;
} // end of SetBuffer
/***********************************************************************/
/* 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 n = tdbp->CurNum;
if (StrLen[n] == SQL_NULL_DATA) {
// Null value
Value->Reset();
return;
} // endif StrLen
if (Bufp && tdbp->Rows)
if (Buf_Type == TYPE_DATE)
*Sqlbuf = ((TIMESTAMP_STRUCT*)Bufp)[n];
else
Value->SetValue_pvblk(Blkp, n);
if (Buf_Type == TYPE_DATE) {
struct tm dbtime = {0,0,0,0,0,0,0,0,0};
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);
} // endif Buf_Type
if (g->Trace) {
char buf[32];
htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n",
Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf));
} // endif Trace
} // 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, Long+1, 0, true, false);
Bufp = Blkp->GetValPointer();
} // endelse
if (rows > 1)
StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(int));
} // 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)
{
if (Buf_Type == TYPE_DATE)
return (SWORD)sizeof(TIMESTAMP_STRUCT);
else if (Buf_Type == TYPE_STRING)
return (SWORD)Value->GetClen() + 1;
else
return (SWORD)Value->GetClen();
} // 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 *dbtime = ((DTVAL*)Value)->GetGmTime();
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;
} // endif Buf_Type
} // end of WriteColumn
/* ---------------------------TDBOIF class --------------------------- */
/***********************************************************************/
/* Implementation of the TDBOIF class. */
/***********************************************************************/
TDBOIF::TDBOIF(PODEF tdp) : TDBASE(tdp)
{
Qrp = NULL;
ID = 0;
NC = 0;
Init = false;
N = -1;
} // end of TDBOIF constructor
/***********************************************************************/
/* Allocate OIF column description block. */
/***********************************************************************/
PCOL TDBOIF::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{
POIFCOL colp;
colp = (POIFCOL)new(g) OIFCOL(cdp, this, n);
if (cprec) {
colp->SetNext(cprec->GetNext());
cprec->SetNext(colp);
} else {
colp->SetNext(Columns);
Columns = colp;
} // endif cprec
for (int i = 1; !colp->Flag && i <= NC; i++)
if (!stricmp(colp->Name, GetMsgid(ID + i)))
colp->Flag = i;
return colp;
} // end of MakeCol
/***********************************************************************/
/* OIF: Get the number of properties. */
/***********************************************************************/
int TDBOIF::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
if (Initialize(g))
return -1;
MaxSize = Qrp->Nblin;
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* OIF Access Method opening routine. */
/***********************************************************************/
bool TDBOIF::OpenDB(PGLOBAL g)
{
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open. */
/*******************************************************************/
N = -1;
return false;
} // endif use
if (Mode != MODE_READ) {
/*******************************************************************/
/* ODBC Info tables cannot be modified. */
/*******************************************************************/
strcpy(g->Message, "OIF tables are read only");
return true;
} // endif Mode
/*********************************************************************/
/* Initialize the ODBC processing. */
/*********************************************************************/
if (Initialize(g))
return true;
return InitCol(g);
} // end of OpenDB
/***********************************************************************/
/* Initialize columns. */
/***********************************************************************/
bool TDBOIF::InitCol(PGLOBAL g)
{
POIFCOL colp;
PCOLRES crp;
for (colp = (POIFCOL)Columns; colp; colp = (POIFCOL)colp->GetNext()) {
for (crp = Qrp->Colresp; crp; crp = crp->Next)
if (colp->Flag == crp->Ncol) {
colp->Crp = crp;
break;
} // endif Flag
if (!colp->Crp) {
sprintf(g->Message, "Invalid flag %d for column %s",
colp->Flag, colp->Name);
return true;
} // endif Crp
} // endfor colp
return false;
} // end of InitCol
/***********************************************************************/
/* Data Base read routine for OIF access method. */
/***********************************************************************/
int TDBOIF::ReadDB(PGLOBAL g)
{
return (++N < Qrp->Nblin) ? RC_OK : RC_EF;
} // end of ReadDB
/***********************************************************************/
/* WriteDB: Data Base write routine for OIF access methods. */
/***********************************************************************/
int TDBOIF::WriteDB(PGLOBAL g)
{
strcpy(g->Message, "OIF tables are read only");
return RC_FX;
} // end of WriteDB
/***********************************************************************/
/* Data Base delete line routine for OIF access methods. */
/***********************************************************************/
int TDBOIF::DeleteDB(PGLOBAL g, int irc)
{
strcpy(g->Message, "Delete not enabled for OIF tables");
return RC_FX;
} // end of DeleteDB
/***********************************************************************/
/* Data Base close routine for WMI access method. */
/***********************************************************************/
void TDBOIF::CloseDB(PGLOBAL g)
{
// Nothing to do
} // end of CloseDB
// ------------------------ OIFCOL functions ----------------------------
/***********************************************************************/
/* OIFCOL public constructor. */
/***********************************************************************/
OIFCOL::OIFCOL(PCOLDEF cdp, PTDB tdbp, int n)
: COLBLK(cdp, tdbp, n)
{
Tdbp = (PTDBOIF)tdbp;
Crp = NULL;
Flag = cdp->GetOffset();
} // end of WMICOL constructor
/***********************************************************************/
/* Read the next Data Source elements. */
/***********************************************************************/
void OIFCOL::ReadColumn(PGLOBAL g)
{
// Get the value of the Name or Description property
Value->SetValue_pvblk(Crp->Kdata, Tdbp->N);
} // end of ReadColumn
/* ---------------------------TDBSRC class --------------------------- */
/***********************************************************************/
/* Initialize: Get the list of ODBC data sources. */
/***********************************************************************/
bool TDBSRC::Initialize(PGLOBAL g)
{
if (Init)
return false;
if (!(Qrp = ODBCDataSources(g, false)))
return true;
Init = true;
return false;
} // end of Initialize
/* ---------------------------TDBDRV class --------------------------- */
/***********************************************************************/
/* Initialize: Get the list of ODBC drivers. */
/***********************************************************************/
bool TDBDRV::Initialize(PGLOBAL g)
{
if (Init)
return false;
if (!(Qrp = ODBCDrivers(g, false)))
return true;
Init = true;
return false;
} // end of Initialize
/* ---------------------------TDBOCL class --------------------------- */
/***********************************************************************/
/* TDBOCL class constructor. */
/***********************************************************************/
TDBOCL::TDBOCL(PODEF tdp) : TDBOIF(tdp)
{
ID = IDS_COLUMNS + 1;
NC = 11;
Dsn = tdp->GetConnect();
Tabn = tdp->GetTabname();
} // end of TDBOCL constructor
/***********************************************************************/
/* Initialize: Get the list of ODBC table columns. */
/***********************************************************************/
bool TDBOCL::Initialize(PGLOBAL g)
{
if (Init)
return false;
if (!(Qrp = MyODBCCols(g, Dsn, Tabn, false)))
return true;
Init = true;
return false;
} // end of Initialize
/* ---------------------------TDBOTB class --------------------------- */
/***********************************************************************/
/* TDBOCL class constructor. */
/***********************************************************************/
TDBOTB::TDBOTB(PODEF tdp) : TDBOIF(tdp)
{
ID = IDS_TABLES + 1;
NC = 4;
Dsn = tdp->GetConnect();
Tabpat = tdp->GetTabname();
} // end of TDBOCL constructor
/***********************************************************************/
/* Initialize: Get the list of ODBC tables. */
/***********************************************************************/
bool TDBOTB::Initialize(PGLOBAL g)
{
if (Init)
return false;
if (!(Qrp = ODBCTables(g, Dsn, Tabpat, false)))
return true;
Init = true;
return false;
} // end of Initialize
/* ------------------------ End of Tabodbc --------------------------- */