2013-02-07 13:34:27 +04:00
|
|
|
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
|
|
|
|
/* PROGRAM NAME: TABODBC */
|
|
|
|
/* ------------- */
|
2017-05-23 19:35:50 +02:00
|
|
|
/* Version 3.2 */
|
2013-02-07 13:34:27 +04:00
|
|
|
/* */
|
|
|
|
/* COPYRIGHT: */
|
|
|
|
/* ---------- */
|
2018-12-01 16:56:55 +01:00
|
|
|
/* (C) Copyright to the author Olivier BERTRAND 2000-2018 */
|
2013-02-07 13:34:27 +04:00
|
|
|
/* */
|
|
|
|
/* 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"
|
2015-12-04 22:38:16 +01:00
|
|
|
#include "sql_class.h"
|
2021-06-08 17:44:43 +02:00
|
|
|
#if defined(_WIN32)
|
2013-02-07 13:34:27 +04:00
|
|
|
#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"
|
2013-02-12 12:34:14 +01:00
|
|
|
#include "mycat.h"
|
2013-02-07 13:34:27 +04:00
|
|
|
#include "xtable.h"
|
2017-02-16 18:01:48 +01:00
|
|
|
#include "tabext.h"
|
Fixing compilation problems on Unix:
1. Conflicting declarations:
In file included from /usr/include/sql.h:19:0,
from <path>/storage/connect/odbconn.h:15,
from <path>/storage/connect/ha_connect.cc:117:
/usr/include/sqltypes.h:98:23: error: conflicting declaration
‘typedef unsigned int DWORD’
os.h and unixODBC's sqltypes.h (included from sql.h) have conflicting
declarations, because unixODBC for some reasons incorrectly defines
DWORD as "unsigned int", while we define DWORD as "unsigned long"
(which is the Microsoft way).
We should never include os.h and odbconn.h from the same file.
Inside tabodbc.cpp DWORD must be seen as sql.h defines it.
In all other files DWORD must be seen as os.h defines it.
Fix:
Moving ODBC catalog function prototypes into a separate file odbccat.h.
Fixing ha_connect.cc to include odbccat.h instead of odbcon.h
2. Use of ambiguous overloaded function in myconn.cpp:
There's no a method SetValue(const char *fmt, int i);
There's only a method SetValue(char *fmt, int i);
Fixing the call accordingly:
- crp->Kdata->SetValue((fmt) ? fmt : "", i);
+ crp->Kdata->SetValue((fmt) ? fmt : (char*) "", i);
Note, this is a quick hack. The correct fix would be to change
the method prototype to have the "fmt" argument as "const char *".
However, it is tightly related to about 300 other places where
"char*" is used instead of "const char *". We'll need to fix
all of them gradually (in separate changes).
added:
storage/connect/odbccat.h
modified:
storage/connect/ha_connect.cc
storage/connect/myconn.cpp
storage/connect/odbconn.h
storage/connect/tabodbc.cpp
2013-02-11 10:16:52 +04:00
|
|
|
#include "odbccat.h"
|
2015-01-31 15:05:43 +01:00
|
|
|
#include "tabodbc.h"
|
2013-02-07 13:34:27 +04:00
|
|
|
#include "tabmul.h"
|
2017-02-16 18:01:48 +01:00
|
|
|
//#include "reldef.h"
|
2013-02-07 13:34:27 +04:00
|
|
|
#include "tabcol.h"
|
|
|
|
#include "valblk.h"
|
2015-12-04 22:38:16 +01:00
|
|
|
#include "ha_connect.h"
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
#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
|
|
|
|
|
2014-10-21 17:29:51 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* External function. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool ExactInfo(void);
|
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
/* -------------------------- Class ODBCDEF -------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
ODBCDEF::ODBCDEF(void)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2017-02-16 18:01:48 +01:00
|
|
|
Connect = NULL;
|
|
|
|
Catver = 0;
|
|
|
|
UseCnc = false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ODBCDEF constructor
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* DefineAM: define specific AM block values from XDB file. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2014-07-17 18:13:51 +02:00
|
|
|
Desc = Connect = GetStringCatInfo(g, "Connect", NULL);
|
|
|
|
|
|
|
|
if (!Connect && !Catfunc) {
|
2022-07-19 19:06:55 +00:00
|
|
|
snprintf(g->Message, sizeof(g->Message), "Missing connection for ODBC table %s", Name);
|
2014-07-17 18:13:51 +02:00
|
|
|
return true;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif Connect
|
2014-07-17 18:13:51 +02:00
|
|
|
|
2017-02-16 18:01:48 +01:00
|
|
|
if (EXTDEF::DefineAM(g, am, poff))
|
|
|
|
return true;
|
|
|
|
|
2014-04-14 14:26:48 +02:00
|
|
|
Catver = GetIntCatInfo("Catver", 2);
|
2013-10-26 00:43:03 +02:00
|
|
|
Options = ODBConn::noOdbcDialog;
|
2014-11-15 18:28:24 +01:00
|
|
|
//Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib;
|
2015-01-13 17:24:31 +01:00
|
|
|
Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
|
|
|
|
Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
|
2016-07-14 20:12:22 +02:00
|
|
|
UseCnc = GetBoolCatInfo("UseDSN", false);
|
2013-02-07 13:34:27 +04:00
|
|
|
return false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of DefineAM
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* GetTable: makes a new Table Description Block. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2017-02-16 18:01:48 +01:00
|
|
|
PTDB tdbp = NULL;
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Allocate a TDB of the proper type. */
|
|
|
|
/* Column blocks will be allocated only when needed. */
|
|
|
|
/*********************************************************************/
|
2013-10-11 13:57:56 +02:00
|
|
|
if (Xsrc)
|
|
|
|
tdbp = new(g) TDBXDBC(this);
|
|
|
|
else switch (Catfunc) {
|
2013-02-12 12:34:14 +01:00
|
|
|
case FNC_COL:
|
2013-02-09 01:08:15 +01:00
|
|
|
tdbp = new(g) TDBOCL(this);
|
|
|
|
break;
|
2013-02-12 12:34:14 +01:00
|
|
|
case FNC_TABLE:
|
2013-02-09 01:08:15 +01:00
|
|
|
tdbp = new(g) TDBOTB(this);
|
|
|
|
break;
|
2013-02-12 12:34:14 +01:00
|
|
|
case FNC_DSN:
|
2013-02-09 01:08:15 +01:00
|
|
|
tdbp = new(g) TDBSRC(this);
|
|
|
|
break;
|
2013-02-12 12:34:14 +01:00
|
|
|
case FNC_DRIVER:
|
2013-02-09 01:08:15 +01:00
|
|
|
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));
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endswitch Catfunc
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
return tdbp;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetTable
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/* -------------------------- Class TDBODBC -------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Implementation of the TDBODBC class. */
|
|
|
|
/***********************************************************************/
|
2017-02-16 18:01:48 +01:00
|
|
|
TDBODBC::TDBODBC(PODEF tdp) : TDBEXT(tdp)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
Ocp = NULL;
|
|
|
|
Cnp = NULL;
|
|
|
|
|
|
|
|
if (tdp) {
|
2013-10-11 13:57:56 +02:00
|
|
|
Connect = tdp->Connect;
|
2015-01-31 15:05:43 +01:00
|
|
|
Ops.User = tdp->Username;
|
|
|
|
Ops.Pwd = tdp->Password;
|
|
|
|
Ops.Cto = tdp->Cto;
|
|
|
|
Ops.Qto = tdp->Qto;
|
2013-10-11 13:57:56 +02:00
|
|
|
Catver = tdp->Catver;
|
2015-01-31 15:05:43 +01:00
|
|
|
Ops.UseCnc = tdp->UseCnc;
|
2013-02-07 13:34:27 +04:00
|
|
|
} else {
|
|
|
|
Connect = NULL;
|
2015-01-31 15:05:43 +01:00
|
|
|
Ops.User = NULL;
|
|
|
|
Ops.Pwd = NULL;
|
|
|
|
Ops.Cto = DEFAULT_LOGIN_TIMEOUT;
|
|
|
|
Ops.Qto = DEFAULT_QUERY_TIMEOUT;
|
2013-02-07 13:34:27 +04:00
|
|
|
Catver = 0;
|
2015-01-31 15:05:43 +01:00
|
|
|
Ops.UseCnc = false;
|
2013-02-07 13:34:27 +04:00
|
|
|
} // endif tdp
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of TDBODBC standard constructor
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2017-02-16 18:01:48 +01:00
|
|
|
TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBEXT(tdbp)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
Ocp = tdbp->Ocp; // is that right ?
|
|
|
|
Cnp = tdbp->Cnp;
|
|
|
|
Connect = tdbp->Connect;
|
2015-01-31 15:05:43 +01:00
|
|
|
Ops = tdbp->Ops;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of TDBODBC copy constructor
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
// Method
|
2017-02-16 18:01:48 +01:00
|
|
|
PTDB TDBODBC::Clone(PTABS t)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
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);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endfor cp1
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
return tp;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of CopyOne
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Allocate ODBC column description block. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PCOL TDBODBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
return new(g) ODBCCOL(cdp, this, cprec, n);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of MakeCol
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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. */
|
|
|
|
/***********************************************************************/
|
2017-05-23 19:35:50 +02:00
|
|
|
PCSZ TDBODBC::GetFile(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
if (Connect) {
|
|
|
|
char *p1, *p2;
|
2015-10-21 19:24:01 +02:00
|
|
|
int i;
|
2015-12-04 22:38:16 +01:00
|
|
|
size_t n;
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2015-10-21 19:24:01 +02:00
|
|
|
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;
|
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (p1) {
|
|
|
|
p1 += i; // Beginning of file name
|
2013-02-07 13:34:27 +04:00
|
|
|
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 : ";");
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif p1
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif Connect
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
return (DBQ) ? DBQ : (PSZ)"???";
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetFile
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Set DBQ and get the new file name into the connect string. */
|
|
|
|
/***********************************************************************/
|
2017-05-23 19:35:50 +02:00
|
|
|
void TDBODBC::SetFile(PGLOBAL g, PCSZ fn)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
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);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif n
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
// Make the complete connect string
|
|
|
|
sprintf(Connect, MulConn, fn);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif MultConn
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-02-25 14:31:28 +01:00
|
|
|
DBQ = PlugDup(g, fn);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of SetFile
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-10-11 13:57:56 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeInsert: make the Insert statement used with ODBC connection. */
|
|
|
|
/***********************************************************************/
|
2015-12-04 22:38:16 +01:00
|
|
|
bool TDBODBC::MakeInsert(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2017-05-23 19:35:50 +02:00
|
|
|
PCSZ schmp = NULL;
|
|
|
|
char *catp = NULL, buf[NAM_LEN * 3];
|
2015-12-04 22:38:16 +01:00
|
|
|
int len = 0;
|
2017-05-23 19:35:50 +02:00
|
|
|
bool oom, b = false;
|
2015-12-04 22:38:16 +01:00
|
|
|
PCOL colp;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
for (colp = Columns; colp; colp = colp->GetNext())
|
|
|
|
if (colp->IsSpecial()) {
|
|
|
|
strcpy(g->Message, MSG(NO_ODBC_SPECOL));
|
2015-12-04 22:38:16 +01:00
|
|
|
return true;
|
2013-10-11 13:57:56 +02:00
|
|
|
} else {
|
2015-12-04 22:38:16 +01:00
|
|
|
// Column name can be encoded in UTF-8
|
|
|
|
Decode(colp->GetName(), buf, sizeof(buf));
|
|
|
|
len += (strlen(buf) + 6); // comma + quotes + valist
|
2017-02-16 18:01:48 +01:00
|
|
|
((PEXTCOL)colp)->SetRank(++Nparm);
|
2013-10-11 13:57:56 +02:00
|
|
|
} // endif colp
|
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
// Below 32 is enough to contain the fixed part of the query
|
|
|
|
if (Catalog && *Catalog)
|
|
|
|
catp = Catalog;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (catp)
|
|
|
|
len += strlen(catp) + 1;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2016-08-10 18:27:31 +02:00
|
|
|
if (Schema && *Schema)
|
2015-12-04 22:38:16 +01:00
|
|
|
schmp = Schema;
|
2015-02-02 15:35:58 +01:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (schmp)
|
|
|
|
len += strlen(schmp) + 1;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2017-02-16 18:01:48 +01:00
|
|
|
// Table name can be encoded in UTF-8
|
2015-12-04 22:38:16 +01:00
|
|
|
Decode(TableName, buf, sizeof(buf));
|
|
|
|
len += (strlen(buf) + 32);
|
|
|
|
Query = new(g) STRING(g, len, "INSERT INTO ");
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (catp) {
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(catp);
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (schmp) {
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append('.');
|
|
|
|
Query->Append(schmp);
|
2015-12-04 22:38:16 +01:00
|
|
|
} // endif schmp
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append('.');
|
2015-12-04 22:38:16 +01:00
|
|
|
} else if (schmp) {
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(schmp);
|
|
|
|
Query->Append('.');
|
2015-12-04 22:38:16 +01:00
|
|
|
} // endif schmp
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (Quote) {
|
|
|
|
// Put table name between identifier quotes in case in contains blanks
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(Quote);
|
|
|
|
Query->Append(buf);
|
|
|
|
Query->Append(Quote);
|
2015-12-04 22:38:16 +01:00
|
|
|
} else
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(buf);
|
2015-12-04 22:38:16 +01:00
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append('(');
|
2015-12-04 22:38:16 +01:00
|
|
|
|
|
|
|
for (colp = Columns; colp; colp = colp->GetNext()) {
|
|
|
|
if (b)
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(", ");
|
2015-12-04 22:38:16 +01:00
|
|
|
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
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(Quote);
|
|
|
|
Query->Append(buf);
|
|
|
|
Query->Append(Quote);
|
2015-12-04 22:38:16 +01:00
|
|
|
} else
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(buf);
|
2015-12-04 22:38:16 +01:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endfor colp
|
2015-12-04 22:38:16 +01:00
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append(") VALUES (");
|
2015-12-04 22:38:16 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < Nparm; i++)
|
2017-05-23 19:35:50 +02:00
|
|
|
Query->Append("?,");
|
2015-12-04 22:38:16 +01:00
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
if ((oom = Query->IsTruncated()))
|
2015-12-04 22:38:16 +01:00
|
|
|
strcpy(g->Message, "MakeInsert: Out of memory");
|
|
|
|
else
|
|
|
|
Query->RepLast(')');
|
|
|
|
|
|
|
|
return oom;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of MakeInsert
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ODBC Bind Parameter function. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBODBC::BindParameters(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
|
|
|
PODBCCOL colp;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) {
|
|
|
|
colp->AllocateBuffers(g, 0);
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
if (Ocp->BindParam(colp))
|
|
|
|
return true;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endfor colp
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
return false;
|
|
|
|
} // end of BindParameters
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2017-02-16 18:01:48 +01:00
|
|
|
#if 0
|
2013-11-09 17:32:57 +01:00
|
|
|
/***********************************************************************/
|
2013-11-11 13:00:39 +01:00
|
|
|
/* MakeUpdate: make the SQL statement to send to ODBC connection. */
|
2013-11-09 17:32:57 +01:00
|
|
|
/***********************************************************************/
|
2013-11-11 13:00:39 +01:00
|
|
|
char *TDBODBC::MakeUpdate(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-11-11 13:00:39 +01:00
|
|
|
char *qc, *stmt = NULL, cmd[8], tab[96], end[1024];
|
2013-11-09 17:32:57 +01:00
|
|
|
|
|
|
|
stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
|
2013-11-11 13:00:39 +01:00
|
|
|
memset(end, 0, sizeof(end));
|
2013-11-09 17:32:57 +01:00
|
|
|
|
2013-11-11 13:00:39 +01:00
|
|
|
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 : "";
|
2013-11-09 17:32:57 +01:00
|
|
|
else {
|
2013-11-11 13:00:39 +01:00
|
|
|
strcpy(g->Message, "Cannot use this UPDATE command");
|
2013-11-09 17:32:57 +01:00
|
|
|
return NULL;
|
|
|
|
} // endif sscanf
|
|
|
|
|
2013-11-11 13:00:39 +01:00
|
|
|
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;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of MakeUpdate
|
2013-11-11 13:00:39 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeDelete: make the SQL statement to send to ODBC connection. */
|
|
|
|
/***********************************************************************/
|
|
|
|
char *TDBODBC::MakeDelete(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
|
|
|
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
|
2013-11-13 00:15:38 +01:00
|
|
|
#endif // 0
|
2013-11-09 17:32:57 +01:00
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
/***********************************************************************/
|
|
|
|
/* ResetSize: call by TDBMUL when calculating size estimate. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void TDBODBC::ResetSize(void)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
MaxSize = -1;
|
|
|
|
|
|
|
|
if (Ocp && Ocp->IsOpen())
|
|
|
|
Ocp->Close();
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ResetSize
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2014-07-17 18:13:51 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* ODBC Cardinality: returns table size in number of rows. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBODBC::Cardinality(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2014-07-17 18:13:51 +02:00
|
|
|
if (!g)
|
|
|
|
return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
|
|
|
|
|
2014-10-21 17:29:51 +02:00
|
|
|
if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
|
2014-07-17 18:13:51 +02:00
|
|
|
// Info command, we must return the exact table row number
|
|
|
|
char qry[96], tbn[64];
|
|
|
|
ODBConn *ocp = new(g) ODBConn(g, this);
|
|
|
|
|
2015-01-31 15:05:43 +01:00
|
|
|
if (ocp->Open(Connect, &Ops, Options) < 1)
|
2014-07-17 18:13:51 +02:00
|
|
|
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();
|
2014-08-07 17:59:21 +02:00
|
|
|
} else
|
|
|
|
Cardinal = 10; // To make MySQL happy
|
2014-07-17 18:13:51 +02:00
|
|
|
|
|
|
|
return Cardinal;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of Cardinality
|
2014-07-17 18:13:51 +02:00
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
/***********************************************************************/
|
|
|
|
/* 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)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2015-02-08 18:17:29 +01:00
|
|
|
bool rc = true;
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(1))
|
2013-02-07 13:34:27 +04:00
|
|
|
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. */
|
|
|
|
/*******************************************************************/
|
2014-11-20 11:00:02 +01:00
|
|
|
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
|
2022-09-23 14:02:41 +01:00
|
|
|
if (Query && (Rbuf = Ocp->Rewind(Query->GetStr(), (PODBCCOL)Columns)) < 0) {
|
2019-05-31 04:18:24 +02:00
|
|
|
if (Mode != MODE_READX) {
|
|
|
|
Ocp->Close();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
Rbuf = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2015-02-28 23:01:55 +01:00
|
|
|
Rbuf = Qrp->Nblin;
|
2019-05-31 04:18:24 +02:00
|
|
|
}
|
2014-11-15 18:28:24 +01:00
|
|
|
|
2014-11-24 18:26:44 +01:00
|
|
|
CurNum = 0;
|
2014-11-15 18:28:24 +01:00
|
|
|
Fpos = 0;
|
2015-02-28 23:01:55 +01:00
|
|
|
Curpos = 1;
|
2013-02-07 13:34:27 +04:00
|
|
|
return false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif use
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* 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 ??? */
|
|
|
|
/*********************************************************************/
|
2015-01-31 15:05:43 +01:00
|
|
|
if (!Ocp)
|
2013-02-07 13:34:27 +04:00
|
|
|
Ocp = new(g) ODBConn(g, this);
|
2015-01-31 15:05:43 +01:00
|
|
|
else if (Ocp->IsOpen())
|
2013-02-07 13:34:27 +04:00
|
|
|
Ocp->Close();
|
|
|
|
|
2015-01-31 15:05:43 +01:00
|
|
|
if (Ocp->Open(Connect, &Ops, Options) < 1)
|
2013-02-07 13:34:27 +04:00
|
|
|
return true;
|
2013-11-11 13:00:39 +01:00
|
|
|
else if (Quoted)
|
|
|
|
Quote = Ocp->GetQuoteChar();
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
Use = USE_OPEN; // Do it now in case we are recursively called
|
|
|
|
|
|
|
|
/*********************************************************************/
|
2018-12-01 16:56:55 +01:00
|
|
|
/* Make the command and allocate whatever is used for getting results*/
|
2013-02-07 13:34:27 +04:00
|
|
|
/*********************************************************************/
|
2014-04-19 17:02:53 +02:00
|
|
|
if (Mode == MODE_READ || Mode == MODE_READX) {
|
2015-02-28 23:01:55 +01:00
|
|
|
if (Memory > 1 && !Srcdef) {
|
2015-12-04 22:38:16 +01:00
|
|
|
int n;
|
2015-02-28 23:01:55 +01:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!MakeSQL(g, true)) {
|
2015-02-28 23:01:55 +01:00
|
|
|
// Allocate a Count(*) column
|
|
|
|
Cnp = new(g) ODBCCOL;
|
|
|
|
Cnp->InitValue(g);
|
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if ((n = Ocp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
|
2019-10-16 17:40:49 +02:00
|
|
|
char* msg = PlugDup(g, g->Message);
|
|
|
|
|
2022-07-19 19:06:55 +00:00
|
|
|
snprintf(g->Message, sizeof(g->Message), "Get result size: %s (rc=%d)", msg, n);
|
2019-10-16 17:40:49 +02:00
|
|
|
return true;
|
2016-02-20 01:02:48 +01:00
|
|
|
} else if (n) {
|
|
|
|
Ocp->m_Rows = n;
|
2015-02-28 23:01:55 +01:00
|
|
|
|
2016-02-20 01:02:48 +01:00
|
|
|
if ((Qrp = Ocp->AllocateResult(g)))
|
|
|
|
Memory = 2; // Must be filled
|
|
|
|
else {
|
|
|
|
strcpy(g->Message, "Result set memory allocation failed");
|
|
|
|
return true;
|
|
|
|
} // endif n
|
2015-02-28 23:01:55 +01:00
|
|
|
|
2016-02-20 01:02:48 +01:00
|
|
|
} else // Void result
|
|
|
|
Memory = 0;
|
2015-02-28 23:01:55 +01:00
|
|
|
|
2016-02-20 01:02:48 +01:00
|
|
|
Ocp->m_Rows = 0;
|
|
|
|
} else
|
2015-02-28 23:01:55 +01:00
|
|
|
return true;
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif Memory
|
2015-02-28 23:01:55 +01:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!(rc = MakeSQL(g, false))) {
|
2013-11-11 13:00:39 +01:00
|
|
|
for (PODBCCOL colp = (PODBCCOL)Columns; colp;
|
|
|
|
colp = (PODBCCOL)colp->GetNext())
|
|
|
|
if (!colp->IsSpecial())
|
|
|
|
colp->AllocateBuffers(g, Rows);
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
rc = (Mode == MODE_READ)
|
|
|
|
? ((Rows = Ocp->ExecDirectSQL(Query->GetStr(), (PODBCCOL)Columns)) < 0)
|
|
|
|
: false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif rc
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-10-11 13:57:56 +02:00
|
|
|
} else if (Mode == MODE_INSERT) {
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!(rc = MakeInsert(g))) {
|
|
|
|
if (Nparm != Ocp->PrepareSQL(Query->GetStr())) {
|
2013-10-11 13:57:56 +02:00
|
|
|
strcpy(g->Message, MSG(PARM_CNT_MISS));
|
|
|
|
rc = true;
|
|
|
|
} else
|
|
|
|
rc = BindParameters(g);
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif rc
|
2013-11-11 13:00:39 +01:00
|
|
|
|
2015-02-08 18:17:29 +01:00
|
|
|
} else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
|
|
|
|
rc = false; // wait for CheckCond before calling MakeCommand(g);
|
|
|
|
} else
|
2022-07-19 19:06:55 +00:00
|
|
|
snprintf(g->Message, sizeof(g->Message), "Invalid mode %d", Mode);
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2015-02-08 18:17:29 +01:00
|
|
|
if (rc) {
|
2013-02-07 13:34:27 +04:00
|
|
|
Ocp->Close();
|
|
|
|
return true;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif rc
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Reset statistics values. */
|
|
|
|
/*********************************************************************/
|
|
|
|
num_read = num_there = num_eq[0] = num_eq[1] = 0;
|
|
|
|
return false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of OpenDB
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2017-02-16 18:01:48 +01:00
|
|
|
#if 0
|
2013-02-07 13:34:27 +04:00
|
|
|
/***********************************************************************/
|
|
|
|
/* GetRecpos: return the position of last read record. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBODBC::GetRecpos(void)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2015-02-28 23:01:55 +01:00
|
|
|
return Fpos;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetRecpos
|
2017-02-16 18:01:48 +01:00
|
|
|
#endif // 0
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2015-02-28 23:01:55 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* SetRecpos: set the position of next read record. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBODBC::SetRecpos(PGLOBAL g, int recpos)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2015-02-28 23:01:55 +01:00
|
|
|
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 {
|
2018-12-01 16:56:55 +01:00
|
|
|
strcpy(g->Message,
|
|
|
|
"This action requires Memory setting or a scrollable cursor");
|
2015-02-28 23:01:55 +01:00
|
|
|
return true;
|
|
|
|
} // endif's
|
|
|
|
|
|
|
|
// Indicate the table position was externally set
|
|
|
|
Placed = true;
|
|
|
|
return false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of SetRecpos
|
2015-02-28 23:01:55 +01:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
/***********************************************************************/
|
2016-05-12 23:26:40 +02:00
|
|
|
/* Data Base indexed read routine for ODBC access method. */
|
2015-12-04 22:38:16 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
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);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif key
|
2015-12-04 22:38:16 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
} else {
|
2016-05-12 23:26:40 +02:00
|
|
|
if (hc->MakeKeyWhere(g, Query, op, c, kr))
|
2015-12-04 22:38:16 +01:00
|
|
|
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;
|
|
|
|
|
2017-05-23 19:35:50 +02:00
|
|
|
if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond)))
|
2015-12-04 22:38:16 +01:00
|
|
|
PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif active_index
|
2015-12-04 22:38:16 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(33))
|
2015-12-04 22:38:16 +01:00
|
|
|
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
|
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
/***********************************************************************/
|
|
|
|
/* VRDNDOS: Data Base read routine for odbc access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBODBC::ReadDB(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
int rc;
|
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(2))
|
2017-02-16 18:01:48 +01:00
|
|
|
htrc("ODBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-11-11 13:00:39 +01:00
|
|
|
if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!Query && MakeCommand(g))
|
2015-02-08 18:17:29 +01:00
|
|
|
return RC_FX;
|
|
|
|
|
2013-11-11 13:00:39 +01:00
|
|
|
// Send the UPDATE/DELETE command to the remote table
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!Ocp->ExecSQLcommand(Query->GetStr())) {
|
2022-07-19 19:06:55 +00:00
|
|
|
snprintf(g->Message, sizeof(g->Message), "%s: %d affected rows", TableName, AftRows);
|
2013-11-11 13:00:39 +01:00
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(1))
|
2013-11-11 13:00:39 +01:00
|
|
|
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
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif Mode
|
2013-11-11 13:00:39 +01:00
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
/*********************************************************************/
|
|
|
|
/* Now start the reading process. */
|
|
|
|
/* Here is the place to fetch the line(s). */
|
|
|
|
/*********************************************************************/
|
2015-02-28 23:01:55 +01:00
|
|
|
if (Placed) {
|
|
|
|
if (Fpos && CurNum >= 0)
|
|
|
|
Rbuf = Ocp->Fetch((Curpos = Fpos));
|
2014-11-20 11:00:02 +01:00
|
|
|
|
|
|
|
rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
|
2015-02-28 23:01:55 +01:00
|
|
|
Placed = false;
|
|
|
|
} else {
|
|
|
|
if (Memory != 3) {
|
|
|
|
if (++CurNum >= Rbuf) {
|
|
|
|
Rbuf = Ocp->Fetch();
|
|
|
|
Curpos = Fpos + 1;
|
|
|
|
CurNum = 0;
|
|
|
|
} // endif CurNum
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2015-02-28 23:01:55 +01:00
|
|
|
rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
|
|
|
|
} else // Getting result from memory
|
|
|
|
rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
|
2014-11-20 11:00:02 +01:00
|
|
|
|
2015-02-28 23:01:55 +01:00
|
|
|
if (rc == RC_OK) {
|
|
|
|
if (Memory == 2)
|
|
|
|
Qrp->Nblin++;
|
|
|
|
|
|
|
|
Fpos++; // Used for memory and pos
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif rc
|
2015-02-28 23:01:55 +01:00
|
|
|
|
|
|
|
} // endif Placed
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(2))
|
2013-02-15 22:31:13 +01:00
|
|
|
htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
|
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
return rc;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ReadDB
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base Insert write routine for ODBC access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBODBC::WriteDB(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-11-06 18:22:09 +01:00
|
|
|
int n = Ocp->ExecuteSQL();
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
AftRows = n;
|
|
|
|
return RC_FX;
|
|
|
|
} else
|
|
|
|
AftRows += n;
|
|
|
|
|
|
|
|
return RC_OK;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of WriteDB
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base delete line routine for ODBC access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBODBC::DeleteDB(PGLOBAL g, int irc)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-11-11 13:00:39 +01:00
|
|
|
if (irc == RC_FX) {
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!Query && MakeCommand(g))
|
2015-02-08 18:17:29 +01:00
|
|
|
return RC_FX;
|
|
|
|
|
2013-11-11 13:00:39 +01:00
|
|
|
// Send the DELETE (all) command to the remote table
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!Ocp->ExecSQLcommand(Query->GetStr())) {
|
2022-07-19 19:06:55 +00:00
|
|
|
snprintf(g->Message, sizeof(g->Message), "%s: %d affected rows", TableName, AftRows);
|
2013-11-11 13:00:39 +01:00
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(1))
|
2013-11-11 13:00:39 +01:00
|
|
|
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
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of DeleteDB
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base close routine for ODBC access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void TDBODBC::CloseDB(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
if (Ocp)
|
2013-11-11 13:00:39 +01:00
|
|
|
|
2013-10-11 13:57:56 +02:00
|
|
|
Ocp->Close();
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(1))
|
2013-02-15 22:31:13 +01:00
|
|
|
htrc("ODBC CloseDB: closing %s\n", Name);
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of CloseDB
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/* --------------------------- ODBCCOL ------------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ODBCCOL public constructor. */
|
|
|
|
/***********************************************************************/
|
2017-05-23 19:35:50 +02:00
|
|
|
ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
|
2017-02-16 18:01:48 +01:00
|
|
|
: EXTCOL(cdp, tdbp, cprec, i, am)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
// Set additional ODBC access method information for column.
|
|
|
|
Slen = 0;
|
|
|
|
StrLen = &Slen;
|
|
|
|
Sqlbuf = NULL;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ODBCCOL constructor
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ODBCCOL private constructor. */
|
|
|
|
/***********************************************************************/
|
2017-02-16 18:01:48 +01:00
|
|
|
ODBCCOL::ODBCCOL(void) : EXTCOL()
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
Slen = 0;
|
|
|
|
StrLen = &Slen;
|
|
|
|
Sqlbuf = NULL;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ODBCCOL constructor
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ODBCCOL constructor used for copying columns. */
|
|
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
|
|
/***********************************************************************/
|
2017-02-16 18:01:48 +01:00
|
|
|
ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
Slen = col1->Slen;
|
|
|
|
StrLen = col1->StrLen;
|
|
|
|
Sqlbuf = col1->Sqlbuf;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ODBCCOL copy constructor
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
PTDBODBC tdbp = (PTDBODBC)To_Tdb;
|
2014-11-20 11:00:02 +01:00
|
|
|
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
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
if (StrLen[n] == SQL_NULL_DATA) {
|
|
|
|
// Null value
|
2013-02-24 01:23:18 +01:00
|
|
|
if (Nullable)
|
|
|
|
Value->SetNull(true);
|
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
Value->Reset();
|
2014-11-20 11:00:02 +01:00
|
|
|
goto put;
|
2013-02-24 01:23:18 +01:00
|
|
|
} else
|
|
|
|
Value->SetNull(false);
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-12-28 15:46:49 +01:00
|
|
|
if (Bufp && tdbp->Rows) {
|
2013-02-07 13:34:27 +04:00
|
|
|
if (Buf_Type == TYPE_DATE)
|
|
|
|
*Sqlbuf = ((TIMESTAMP_STRUCT*)Bufp)[n];
|
|
|
|
else
|
|
|
|
Value->SetValue_pvblk(Blkp, n);
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif Bufp
|
2013-12-28 15:46:49 +01:00
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
if (Buf_Type == TYPE_DATE) {
|
2014-10-12 15:46:31 +02:00
|
|
|
struct tm dbtime;
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2014-10-12 15:46:31 +02:00
|
|
|
memset(&dbtime, 0, sizeof(tm));
|
2013-02-07 13:34:27 +04:00
|
|
|
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);
|
2013-12-28 15:46:49 +01:00
|
|
|
} 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
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(2)) {
|
2014-03-18 19:25:50 +01:00
|
|
|
char buf[64];
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n",
|
|
|
|
Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf));
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif trace
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2014-12-14 22:47:12 +01:00
|
|
|
put:
|
2014-11-20 11:00:02 +01:00
|
|
|
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);
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ReadColumn
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
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 {
|
2013-12-28 15:46:49 +01:00
|
|
|
Blkp = AllocValBlock(g, NULL, Buf_Type, rows, GetBuflen(),
|
|
|
|
GetScale(), true, false, false);
|
2013-02-07 13:34:27 +04:00
|
|
|
Bufp = Blkp->GetValPointer();
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endelse
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
if (rows > 1)
|
2013-12-06 01:37:56 +01:00
|
|
|
StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(SQLLEN));
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of AllocateBuffers
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Returns the buffer to use for Fetch or Extended Fetch. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void *ODBCCOL::GetBuffer(DWORD rows)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
if (rows && To_Tdb) {
|
|
|
|
assert(rows == (DWORD)((TDBODBC*)To_Tdb)->Rows);
|
|
|
|
return Bufp;
|
|
|
|
} else
|
|
|
|
return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val();
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetBuffer
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Returns the buffer length to use for Fetch or Extended Fetch. */
|
|
|
|
/***********************************************************************/
|
|
|
|
SWORD ODBCCOL::GetBuflen(void)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-12-28 15:46:49 +01:00
|
|
|
SWORD flen;
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-12-28 15:46:49 +01:00
|
|
|
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;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetBuflen
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteColumn: make sure the bind buffer is updated. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void ODBCCOL::WriteColumn(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-02-07 13:34:27 +04:00
|
|
|
/*********************************************************************/
|
|
|
|
/* Do convert the column value if necessary. */
|
|
|
|
/*********************************************************************/
|
|
|
|
if (Value != To_Val)
|
2013-10-11 13:57:56 +02:00
|
|
|
Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
if (Buf_Type == TYPE_DATE) {
|
2013-05-23 12:04:52 +04:00
|
|
|
struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
|
2013-02-07 13:34:27 +04:00
|
|
|
|
|
|
|
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;
|
2013-10-11 13:57:56 +02:00
|
|
|
Sqlbuf->fraction = 0;
|
2013-12-28 15:46:49 +01:00
|
|
|
} 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
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-10-11 13:57:56 +02:00
|
|
|
if (Nullable)
|
|
|
|
*StrLen = (Value->IsNull()) ? SQL_NULL_DATA :
|
2013-12-28 15:46:49 +01:00
|
|
|
(IsTypeChar(Buf_Type)) ? SQL_NTS : 0;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of WriteColumn
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/* -------------------------- Class TDBXDBC -------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2015-06-02 12:28:42 +02:00
|
|
|
/* Implementation of the TDBXDBC class. */
|
2013-10-11 13:57:56 +02:00
|
|
|
/***********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
TDBXDBC::TDBXDBC(PODEF tdp) : TDBODBC(tdp)
|
|
|
|
{
|
|
|
|
Cmdlist = NULL;
|
|
|
|
Cmdcol = NULL;
|
2013-12-16 01:32:47 +01:00
|
|
|
Mxr = tdp->Maxerr;
|
2013-11-06 18:22:09 +01:00
|
|
|
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
|
|
|
|
|
2017-02-16 18:01:48 +01:00
|
|
|
PTDB TDBXDBC::Clone(PTABS t)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
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);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endfor cp1
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
return tp;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of CopyOne
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Allocate XSRC column description block. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PCOL TDBXDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
PXSRCCOL colp = new(g) XSRCCOL(cdp, this, cprec, n);
|
|
|
|
|
|
|
|
if (!colp->Flag)
|
|
|
|
Cmdcol = colp->GetName();
|
|
|
|
|
|
|
|
return colp;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of MakeCol
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeCMD: make the SQL statement to send to ODBC connection. */
|
|
|
|
/***********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
PCMD TDBXDBC::MakeCMD(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-11-06 18:22:09 +01:00
|
|
|
PCMD xcmd = NULL;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2014-03-10 18:29:04 +01:00
|
|
|
if (To_CondFil) {
|
2013-10-11 13:57:56 +02:00
|
|
|
if (Cmdcol) {
|
2014-03-10 18:29:04 +01:00
|
|
|
if (!stricmp(Cmdcol, To_CondFil->Body) &&
|
|
|
|
(To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
|
|
|
|
xcmd = To_CondFil->Cmds;
|
2013-10-11 13:57:56 +02:00
|
|
|
} 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
|
2013-11-06 18:22:09 +01:00
|
|
|
xcmd = new(g) CMD(g, Srcdef);
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
return xcmd;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of MakeCMD
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ODBC Bind Parameter function. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBXDBC::BindParameters(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
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;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of BindParameters
|
2013-10-11 13:57:56 +02:00
|
|
|
#endif // 0
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
/* XDBC GetMaxSize: returns table size (not always one row). */
|
2013-10-11 13:57:56 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBXDBC::GetMaxSize(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
if (MaxSize < 0)
|
2013-11-06 18:22:09 +01:00
|
|
|
MaxSize = 10; // Just a guess
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
return MaxSize;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetMaxSize
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* 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)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2018-01-30 15:43:20 +01:00
|
|
|
if (trace(1))
|
2013-10-11 13:57:56 +02:00
|
|
|
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;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif use
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* 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 ??? */
|
|
|
|
/*********************************************************************/
|
2015-01-13 17:24:31 +01:00
|
|
|
if (!Ocp) {
|
2013-10-11 13:57:56 +02:00
|
|
|
Ocp = new(g) ODBConn(g, this);
|
2015-01-13 17:24:31 +01:00
|
|
|
} else if (Ocp->IsOpen())
|
2013-10-11 13:57:56 +02:00
|
|
|
Ocp->Close();
|
|
|
|
|
2015-01-31 15:05:43 +01:00
|
|
|
if (Ocp->Open(Connect, &Ops, Options) < 1)
|
2013-10-11 13:57:56 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
Use = USE_OPEN; // Do it now in case we are recursively called
|
|
|
|
|
2014-04-19 17:02:53 +02:00
|
|
|
if (Mode != MODE_READ && Mode != MODE_READX) {
|
2013-10-11 13:57:56 +02:00
|
|
|
strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables");
|
|
|
|
return true;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // endif Mode
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Get the command to execute. */
|
|
|
|
/*********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
if (!(Cmdlist = MakeCMD(g))) {
|
2018-10-14 12:28:46 +02:00
|
|
|
// Next lines commented out because of CHECK TABLE
|
|
|
|
//Ocp->Close();
|
|
|
|
//return true;
|
|
|
|
} // endif Cmdlist
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
Rows = 1;
|
|
|
|
return false;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of OpenDB
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ReadDB: Data Base read routine for xdbc access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBXDBC::ReadDB(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-11-06 18:22:09 +01:00
|
|
|
if (Cmdlist) {
|
2015-12-04 22:38:16 +01:00
|
|
|
if (!Query)
|
|
|
|
Query = new(g)STRING(g, 0, Cmdlist->Cmd);
|
|
|
|
else
|
|
|
|
Query->Set(Cmdlist->Cmd);
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2015-12-04 22:38:16 +01:00
|
|
|
if (Ocp->ExecSQLcommand(Query->GetStr()))
|
2013-11-06 18:22:09 +01:00
|
|
|
Nerr++;
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2013-11-06 18:22:09 +01:00
|
|
|
Fpos++; // Used for progress info
|
|
|
|
Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
|
|
|
|
return RC_OK;
|
2018-10-14 12:28:46 +02:00
|
|
|
} else {
|
|
|
|
PushWarning(g, this, 1);
|
|
|
|
return RC_EF;
|
|
|
|
} // endif Cmdlist
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ReadDB
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2018-12-01 16:56:55 +01:00
|
|
|
/* Data Base write line routine for XDBC access method. */
|
2013-10-11 13:57:56 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBXDBC::WriteDB(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
strcpy(g->Message, "Execsrc tables are read only");
|
|
|
|
return RC_FX;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of DeleteDB
|
2013-10-11 13:57:56 +02:00
|
|
|
|
2013-11-11 13:00:39 +01:00
|
|
|
/***********************************************************************/
|
2018-12-01 16:56:55 +01:00
|
|
|
/* Data Base delete line routine for XDBC access method. */
|
2013-11-11 13:00:39 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBXDBC::DeleteDB(PGLOBAL g, int irc)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-11-11 13:00:39 +01:00
|
|
|
strcpy(g->Message, MSG(NO_ODBC_DELETE));
|
|
|
|
return RC_FX;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of DeleteDB
|
2013-11-11 13:00:39 +01:00
|
|
|
|
2013-10-11 13:57:56 +02:00
|
|
|
/* --------------------------- XSRCCOL ------------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* XSRCCOL public constructor. */
|
|
|
|
/***********************************************************************/
|
2017-05-23 19:35:50 +02:00
|
|
|
XSRCCOL::XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
|
2013-10-11 13:57:56 +02:00
|
|
|
: ODBCCOL(cdp, tdbp, cprec, i, am)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
// Set additional ODBC access method information for column.
|
|
|
|
Flag = cdp->GetOffset();
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of XSRCCOL constructor
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* XSRCCOL constructor used for copying columns. */
|
|
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
XSRCCOL::XSRCCOL(XSRCCOL *col1, PTDB tdbp) : ODBCCOL(col1, tdbp)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
Flag = col1->Flag;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of XSRCCOL copy constructor
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ReadColumn: set column value according to Flag. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void XSRCCOL::ReadColumn(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
PTDBXDBC tdbp = (PTDBXDBC)To_Tdb;
|
|
|
|
|
|
|
|
switch (Flag) {
|
2015-12-04 22:38:16 +01:00
|
|
|
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;
|
2013-10-11 13:57:56 +02:00
|
|
|
} // endswitch Flag
|
|
|
|
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of ReadColumn
|
2013-10-11 13:57:56 +02:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteColumn: Should never be called. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void XSRCCOL::WriteColumn(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
// Should never be called
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of WriteColumn
|
2013-02-07 13:34:27 +04:00
|
|
|
|
2013-12-16 01:32:47 +01:00
|
|
|
/* ---------------------------TDBDRV class --------------------------- */
|
2013-02-08 03:27:12 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-12-16 01:32:47 +01:00
|
|
|
/* GetResult: Get the list of ODBC drivers. */
|
2013-02-08 03:27:12 +01:00
|
|
|
/***********************************************************************/
|
2013-12-16 01:32:47 +01:00
|
|
|
PQRYRES TDBDRV::GetResult(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-12-16 01:32:47 +01:00
|
|
|
return ODBCDrivers(g, Maxres, false);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetResult
|
2013-02-09 01:08:15 +01:00
|
|
|
|
2013-12-16 01:32:47 +01:00
|
|
|
/* ---------------------------TDBSRC class --------------------------- */
|
2013-02-09 01:08:15 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-12-16 01:32:47 +01:00
|
|
|
/* GetResult: Get the list of ODBC data sources. */
|
2013-02-09 01:08:15 +01:00
|
|
|
/***********************************************************************/
|
2013-12-16 01:32:47 +01:00
|
|
|
PQRYRES TDBSRC::GetResult(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-12-16 01:32:47 +01:00
|
|
|
return ODBCDataSources(g, Maxres, false);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetResult
|
2013-02-08 03:27:12 +01:00
|
|
|
|
2013-02-11 00:31:03 +01:00
|
|
|
/* ---------------------------TDBOTB class --------------------------- */
|
2013-02-08 03:27:12 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-02-11 00:31:03 +01:00
|
|
|
/* TDBOTB class constructor. */
|
2013-02-08 03:27:12 +01:00
|
|
|
/***********************************************************************/
|
2013-12-16 01:32:47 +01:00
|
|
|
TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2013-12-16 01:32:47 +01:00
|
|
|
Dsn = tdp->GetConnect();
|
2013-12-28 15:46:49 +01:00
|
|
|
Schema = tdp->GetTabschema();
|
2013-02-11 00:31:03 +01:00
|
|
|
Tab = tdp->GetTabname();
|
2016-07-14 20:12:22 +02:00
|
|
|
Tabtyp = tdp->Tabtyp;
|
2015-01-31 15:05:43 +01:00
|
|
|
Ops.User = tdp->Username;
|
|
|
|
Ops.Pwd = tdp->Password;
|
|
|
|
Ops.Cto = tdp->Cto;
|
|
|
|
Ops.Qto = tdp->Qto;
|
2015-02-01 12:16:30 +01:00
|
|
|
Ops.UseCnc = tdp->UseCnc;
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of TDBOTB constructor
|
2013-02-08 03:27:12 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-02-11 00:31:03 +01:00
|
|
|
/* GetResult: Get the list of ODBC tables. */
|
2013-02-08 03:27:12 +01:00
|
|
|
/***********************************************************************/
|
2013-02-11 00:31:03 +01:00
|
|
|
PQRYRES TDBOTB::GetResult(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2016-07-14 20:12:22 +02:00
|
|
|
return ODBCTables(g, Dsn, Schema, Tab, Tabtyp, Maxres, false, &Ops);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetResult
|
2013-02-08 03:27:12 +01:00
|
|
|
|
2013-02-11 00:31:03 +01:00
|
|
|
/* ---------------------------TDBOCL class --------------------------- */
|
2013-02-09 01:08:15 +01:00
|
|
|
|
2016-07-14 20:12:22 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* TDBOCL class constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
TDBOCL::TDBOCL(PODEF tdp) : TDBOTB(tdp)
|
|
|
|
{
|
|
|
|
Colpat = tdp->Colpat;
|
|
|
|
} // end of TDBOTB constructor
|
|
|
|
|
2013-02-09 01:08:15 +01:00
|
|
|
/***********************************************************************/
|
2013-02-11 00:31:03 +01:00
|
|
|
/* GetResult: Get the list of ODBC table columns. */
|
2013-02-09 01:08:15 +01:00
|
|
|
/***********************************************************************/
|
2013-02-11 00:31:03 +01:00
|
|
|
PQRYRES TDBOCL::GetResult(PGLOBAL g)
|
2018-12-01 16:56:55 +01:00
|
|
|
{
|
2016-07-14 20:12:22 +02:00
|
|
|
return ODBCColumns(g, Dsn, Schema, Tab, Colpat, Maxres, false, &Ops);
|
2018-12-01 16:56:55 +01:00
|
|
|
} // end of GetResult
|
2013-02-08 03:27:12 +01:00
|
|
|
|
2013-02-07 13:34:27 +04:00
|
|
|
/* ------------------------ End of Tabodbc --------------------------- */
|