mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +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 --------------------------- */
|