mariadb/storage/connect/tabmysql.cpp
Olivier Bertrand cd9e1938ef - Add a new table option SEPINDEX (not used yet) and remove an unused
parameter to all catalog info functions.

modified:
  storage/connect/catalog.h
  storage/connect/filamvct.cpp
  storage/connect/filamzip.cpp
  storage/connect/ha_connect.cc
  storage/connect/mycat.cc
  storage/connect/mycat.h
  storage/connect/reldef.cpp
  storage/connect/reldef.h
  storage/connect/tabdos.cpp
  storage/connect/tabfmt.cpp
  storage/connect/tabmul.cpp
  storage/connect/tabmysql.cpp
  storage/connect/tabodbc.cpp
  storage/connect/tabsys.cpp
  storage/connect/tabtbl.cpp
  storage/connect/tabvct.cpp
  storage/connect/tabwmi.cpp
  storage/connect/tabxml.cpp
  storage/connect/xindex.cpp
2013-03-25 11:07:45 +01:00

1102 lines
37 KiB
C++

/************* TabMySQL C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABMYSQL */
/* ------------- */
/* Version 1.7 */
/* */
/* AUTHOR: */
/* ------- */
/* Olivier BERTRAND 2007-2013 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
/* Implements a table type that are MySQL tables. */
/* It can optionally use the embedded MySQL library. */
/* */
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
/* -------------------------------------- */
/* */
/* REQUIRED FILES: */
/* --------------- */
/* TABMYSQL.CPP - Source code */
/* PLGDBSEM.H - DB application declaration file */
/* TABMYSQL.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 "my_global.h"
#if defined(WIN32)
//#include <windows.h>
#else // !WIN32
//#include <fnmatch.h>
//#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "osutil.h"
//#include <io.h>
//#include <fcntl.h>
#endif // !WIN32
/***********************************************************************/
/* Include application header files: */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "xtable.h"
#include "tabcol.h"
#include "colblk.h"
#include "mycat.h"
#include "reldef.h"
#include "tabmysql.h"
#include "valblk.h"
#if defined(_CONSOLE)
void PrintResult(PGLOBAL, PSEM, PQRYRES);
#endif // _CONSOLE
extern "C" int trace;
/* -------------- Implementation of the MYSQLDEF class --------------- */
/***********************************************************************/
/* Constructor. */
/***********************************************************************/
MYSQLDEF::MYSQLDEF(void)
{
Pseudo = 2; // SERVID is Ok but not ROWID
Hostname = NULL;
Database = NULL;
Tabname = NULL;
Username = NULL;
Password = NULL;
Portnumber = 0;
Bind = FALSE;
Delayed = FALSE;
} // end of MYSQLDEF constructor
/***********************************************************************/
/* Parse connection string */
/* */
/* SYNOPSIS */
/* ParseURL() */
/* url The connection string to parse */
/* */
/* DESCRIPTION */
/* Populates the table with information about the connection */
/* to the foreign database that will serve as the data source. */
/* This string must be specified (currently) in the "CONNECTION" */
/* field, listed in the CREATE TABLE statement. */
/* */
/* This string MUST be in the format of any of these: */
/* */
/* CONNECTION="scheme://user:pwd@host:port/database/table" */
/* CONNECTION="scheme://user@host/database/table" */
/* CONNECTION="scheme://user@host:port/database/table" */
/* CONNECTION="scheme://user:pwd@host/database/table" */
/* */
/* _OR_ */
/* */
/* CONNECTION="connection name" (NIY) */
/* */
/* An Example: */
/* */
/* CREATE TABLE t1 (id int(32)) */
/* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
/* CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname"; */
/* */
/* CREATE TABLE t2 ( */
/* id int(4) NOT NULL auto_increment, */
/* name varchar(32) NOT NULL, */
/* PRIMARY KEY(id) */
/* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
/* CONNECTION="my_conn"; (NIY) */
/* */
/* 'password' and 'port' are both optional. */
/* */
/* RETURN VALUE */
/* false success */
/* true error */
/* */
/***********************************************************************/
bool MYSQLDEF::ParseURL(PGLOBAL g, char *url)
{
if ((!strstr(url, "://") && (!strchr(url, '@')))) {
// No :// or @ in connection string. Must be a straight
// connection name of either "server" or "server/table"
strcpy(g->Message, "Using Federated server not implemented yet");
return true;
#if 0
/* ok, so we do a little parsing, but not completely! */
share->parsed= FALSE;
/*
If there is a single '/' in the connection string, this means the user is
specifying a table name
*/
if ((share->table_name= strchr(share->connection_string, '/')))
{
*share->table_name++= '\0';
share->table_name_length= strlen(share->table_name);
DBUG_PRINT("info",
("internal format, parsed table_name "
"share->connection_string: %s share->table_name: %s",
share->connection_string, share->table_name));
/*
there better not be any more '/'s !
*/
if (strchr(share->table_name, '/'))
goto error;
}
/*
Otherwise, straight server name, use tablename of federatedx table
as remote table name
*/
else
{
/*
Connection specifies everything but, resort to
expecting remote and foreign table names to match
*/
share->table_name= strmake_root(mem_root, table->s->table_name.str,
(share->table_name_length=
table->s->table_name.length));
DBUG_PRINT("info",
("internal format, default table_name "
"share->connection_string: %s share->table_name: %s",
share->connection_string, share->table_name));
}
if ((error_num= get_connection(mem_root, share)))
goto error;
#endif // 0
} else {
// URL, parse it
char *sport, *scheme = url;
if (!(Username = strstr(url, "://"))) {
strcpy(g->Message, "Connection is not an URL");
return true;
} // endif User
scheme[Username - scheme] = 0;
if (stricmp(scheme, "mysql")) {
strcpy(g->Message, "scheme must be mysql");
return true;
} // endif scheme
Username += 3;
if (!(Hostname = strchr(Username, '@'))) {
strcpy(g->Message, "No host specified in URL");
return true;
} else
*Hostname++ = 0; // End Username
if ((Password = strchr(Username, ':'))) {
*Password++ = 0; // End username
// Make sure there isn't an extra / or @
if ((strchr(Password, '/') || strchr(Hostname, '@'))) {
strcpy(g->Message, "Syntax error in URL");
return true;
} // endif
// Found that if the string is:
// user:@hostname:port/db/table
// Then password is a null string, so set to NULL
if ((Password[0] == 0))
Password = NULL;
} // endif password
// Make sure there isn't an extra / or @ */
if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) {
strcpy(g->Message, "Syntax error in URL");
return true;
} // endif
if ((Database = strchr(Hostname, '/'))) {
*Database++ = 0;
if ((Tabname = strchr(Database, '/')))
*Tabname++ = 0;
// Make sure there's not an extra /
if ((strchr(Tabname, '/'))) {
strcpy(g->Message, "Syntax error in URL");
return true;
} // endif /
} // endif database
if ((sport = strchr(Hostname, ':')))
*sport++ = 0;
Portnumber = (sport && sport[0]) ? atoi(sport) : MYSQL_PORT;
if (Hostname[0] == 0)
Hostname = "localhost";
if (!Database || !*Database)
Database = Cat->GetStringCatInfo(g, "Database", "*");
if (!Tabname || !*Tabname)
Tabname = Name;
} // endif URL
#if 0
if (!share->port)
if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
share->socket= (char *) MYSQL_UNIX_ADDR;
else
share->port= MYSQL_PORT;
#endif // 0
return false;
} // end of ParseURL
/***********************************************************************/
/* DefineAM: define specific AM block values from XCV file. */
/***********************************************************************/
bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
char *url = Cat->GetStringCatInfo(g, "Connect", NULL);
Desc = "MySQL Table";
if (!url || !*url) {
// Not using the connection URL
Hostname = Cat->GetStringCatInfo(g, "Host", "localhost");
Database = Cat->GetStringCatInfo(g, "Database", "*");
Tabname = Cat->GetStringCatInfo(g, "Name", Name); // Deprecated
Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname);
Username = Cat->GetStringCatInfo(g, "User", "root");
Password = Cat->GetStringCatInfo(g, "Password", NULL);
Portnumber = Cat->GetIntCatInfo("Port", MYSQL_PORT);
} else if (ParseURL(g, url))
return TRUE;
Bind = !!Cat->GetIntCatInfo("Bind", 0);
Delayed = !!Cat->GetIntCatInfo("Delayed", 0);
return FALSE;
} // end of DefineAM
/***********************************************************************/
/* GetTable: makes a new TDB of the proper type. */
/***********************************************************************/
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
{
if (Catfunc == FNC_COL)
return new(g) TDBMCL(this);
else
return new(g) TDBMYSQL(this);
} // end of GetTable
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Implementation of the TDBMYSQL class. */
/***********************************************************************/
TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
{
if (tdp) {
Host = tdp->GetHostname();
Database = tdp->GetDatabase();
Tabname = tdp->GetTabname();
User = tdp->GetUsername();
Pwd = tdp->GetPassword();
Port = tdp->GetPortnumber();
Prep = tdp->Bind;
Delayed = tdp->Delayed;
} else {
Host = NULL;
Database = NULL;
Tabname = NULL;
User = NULL;
Pwd = NULL;
Port = 0;
Prep = FALSE;
Delayed = FALSE;
} // endif tdp
Bind = NULL;
Query = NULL;
Qbuf = NULL;
Fetched = FALSE;
m_Rc = RC_FX;
AftRows = 0;
N = -1;
Nparm = 0;
} // end of TDBMYSQL constructor
TDBMYSQL::TDBMYSQL(PGLOBAL g, PTDBMY tdbp) : TDBASE(tdbp)
{
Host = tdbp->Host;
Database = tdbp->Database;
Tabname = tdbp->Tabname;
User = tdbp->User;
Pwd = tdbp->Pwd;
Port = tdbp->Port;
Prep = tdbp->Prep;
Delayed = tdbp->Delayed;
Bind = NULL;
Query = tdbp->Query;
Qbuf = NULL;
Fetched = tdbp->Fetched;
m_Rc = tdbp->m_Rc;
AftRows = tdbp->AftRows;
N = tdbp->N;
Nparm = tdbp->Nparm;
} // end of TDBMYSQL copy constructor
// Is this really useful ???
PTDB TDBMYSQL::CopyOne(PTABS t)
{
PTDB tp;
PCOL cp1, cp2;
PGLOBAL g = t->G;
tp = new(g) TDBMYSQL(g, this);
for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp);
NewPointer(t, cp1, cp2);
} // endfor cp1
return tp;
} // end of CopyOne
/***********************************************************************/
/* Allocate MYSQL column description block. */
/***********************************************************************/
PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{
return new(g) MYSQLCOL(cdp, this, cprec, n);
} // end of MakeCol
/***********************************************************************/
/* MakeSelect: make the Select statement use with MySQL connection. */
/* Note: when implementing EOM filtering, column only used in local */
/* filter should be removed from column list. */
/***********************************************************************/
bool TDBMYSQL::MakeSelect(PGLOBAL g)
{
char *colist;
char *tk = "`";
int len = 0, ncol = 0, rank = 0;
bool b = FALSE;
PCOL colp;
PDBUSER dup = PlgGetUser(g);
if (Query)
return FALSE; // already done
for (colp = Columns; colp; colp = colp->GetNext())
ncol++;
if (ncol) {
colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol);
*colist = '\0';
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->IsSpecial()) {
strcpy(g->Message, MSG(NO_SPEC_COL));
return TRUE;
} else {
if (b)
strcat(colist, ", ");
else
b = TRUE;
strcat(strcat(strcat(colist, tk), colp->GetName()), tk);
((PMYCOL)colp)->Rank = rank++;
} // endif colp
} else {
// ncol == 0 can occur for queries such as Query count(*) from...
// for which we will count the rows from Query '*' from...
// (the use of a char constant minimize the result storage)
colist = (char*)PlugSubAlloc(g, NULL, 2);
strcpy(colist, "'*'");
} // endif ncol
// Below 32 is space to contain extra stuff
len += (strlen(colist) + strlen(Tabname) + 32);
len += (To_Filter ? strlen(To_Filter) + 7 : 0);
Query = (char*)PlugSubAlloc(g, NULL, len);
strcat(strcpy(Query, "SELECT "), colist);
strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk);
if (To_Filter)
strcat(strcat(Query, " WHERE "), To_Filter);
return FALSE;
} // end of MakeSelect
/***********************************************************************/
/* MakeInsert: make the Insert statement used with MySQL connection. */
/***********************************************************************/
bool TDBMYSQL::MakeInsert(PGLOBAL g)
{
char *colist, *valist = NULL;
char *tk = "`";
int len = 0, qlen = 0;
bool b = FALSE;
PCOL colp;
if (Query)
return FALSE; // already done
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->IsSpecial()) {
strcpy(g->Message, MSG(NO_SPEC_COL));
return TRUE;
} else {
len += (strlen(colp->GetName()) + 4);
((PMYCOL)colp)->Rank = Nparm++;
} // endif colp
colist = (char*)PlugSubAlloc(g, NULL, len);
*colist = '\0';
if (Prep) {
#if defined(MYSQL_PREPARED_STATEMENTS)
valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
*valist = '\0';
#else // !MYSQL_PREPARED_STATEMENTS
strcpy(g->Message, "Prepared statements not used (not supported)");
PushWarning(g, this);
Prep = FALSE;
#endif // !MYSQL_PREPARED_STATEMENTS
} // endif Prep
for (colp = Columns; colp; colp = colp->GetNext()) {
if (b) {
strcat(colist, ", ");
if (Prep) strcat(valist, ",");
} else
b = TRUE;
strcat(strcat(strcat(colist, tk), colp->GetName()), tk);
// Parameter marker
if (!Prep) {
if (colp->GetResultType() == TYPE_DATE)
qlen += 20;
else
qlen += colp->GetLength();
} // endif Prep
if (Prep)
strcat(valist, "?");
} // endfor colp
// Below 40 is enough to contain the fixed part of the query
len = (strlen(Tabname) + strlen(colist)
+ ((Prep) ? strlen(valist) : 0) + 40);
Query = (char*)PlugSubAlloc(g, NULL, len);
if (Delayed)
strcpy(Query, "INSERT DELAYED INTO ");
else
strcpy(Query, "INSERT INTO ");
strcat(strcat(strcat(Query, tk), Tabname), tk);
strcat(strcat(strcat(Query, " ("), colist), ") VALUES (");
if (Prep)
strcat(strcat(Query, valist), ")");
else {
qlen += (strlen(Query) + Nparm);
Qbuf = (char *)PlugSubAlloc(g, NULL, qlen);
} // endelse Prep
return FALSE;
} // end of MakeInsert
#if 0
/***********************************************************************/
/* MakeUpdate: make the Update statement use with MySQL connection. */
/* Note: currently limited to local values and filtering. */
/***********************************************************************/
bool TDBMYSQL::MakeUpdate(PGLOBAL g, PSELECT selist)
{
char *setlist, *colname, *where = NULL, *tk = "`";
int len = 0, nset = 0;
bool b = FALSE;
PXOB xp;
PSELECT selp;
if (Query)
return FALSE; // already done
if (To_Filter)
if (To_Filter->CheckLocal(this)) {
where = (char*)PlugSubAlloc(g, NULL, 512); // Should be enough
*where = '\0';
if (!PlugRephraseSQL(g, where, To_Filter, TYPE_FILTER, tk))
return TRUE;
To_Filter = NULL;
len = strlen(where);
} else {
strcpy(g->Message, MSG(NO_REF_UPDATE));
return TRUE;
} // endif Local
for (selp = selist; selp; selp = selp->GetNext_Proj())
nset++;
assert(nset);
// Allocate a pretty big buffer
setlist = (char*)PlugSubAlloc(g, NULL, 256 * nset);
*setlist = '\0';
for (selp = selist; selp; selp = selp->GetNext_Proj()) {
if (selp->GetSetType() == TYPE_COLBLK) {
colname = selp->GetSetCol()->GetName();
} else if (selp->GetSetType() == TYPE_COLUMN) {
colname = (char*)((PCOLUMN)selp->GetSetCol())->GetName();
} else {
sprintf(g->Message, MSG(BAD_SET_TYPE), selp->GetSetType());
return TRUE;
} // endif Type
if (b)
strcat(setlist, ", ");
else
b = TRUE;
strcat(strcat(strcat(strcat(setlist, tk), colname), tk), " = ");
xp = selp->GetObject();
if (!xp->CheckLocal(this)) {
strcpy(g->Message, MSG(NO_REF_UPDATE));
return TRUE;
} else if (xp->GetType() == TYPE_SUBQ)
// Cannot be correlated because CheckLocal would have failed
xp = new(g) CONSTANT(xp->GetValue());
if (!PlugRephraseSQL(g, setlist + strlen(setlist),
xp, TYPE_XOBJECT, tk))
return TRUE;
} // endfor selp
// Below 16 is enough to take care of the fixed part of the query
len += (strlen(setlist) + strlen(Tabname) + 16);
Query = (char*)PlugSubAlloc(g, NULL, len);
strcat(strcat(strcat(strcpy(Query, "UPDATE "), tk), Tabname), tk);
strcat(strcat(Query, " SET "), setlist);
if (where)
strcat(Query, where);
return FALSE;
} // end of MakeUpdate
/***********************************************************************/
/* MakeDelete: make the Delete statement use with MySQL connection. */
/* If no filtering Truncate is used because it is faster than Delete. */
/* However, the number of deleted lines is not returned by MySQL. */
/* Note: currently limited to local filtering. */
/***********************************************************************/
bool TDBMYSQL::MakeDelete(PGLOBAL g)
{
char *tk = "`";
int len = 0;
if (Query)
return FALSE; // already done
if (!To_Filter)
AftRows = -1; // Means "all lines deleted"
// Below 16 is more than length of 'delete from ' + 3
len += (strlen(Tabname) + 16);
len += (To_Filter ? strlen(To_Filter) + 7 : 0);
Query = (char*)PlugSubAlloc(g, NULL, len);
strcpy(Query, (To_Filter) ? "DELETE FROM " : "TRUNCATE ");
strcat(strcat(strcat(Query, tk), Tabname), tk);
if (To_Filter)
strcat(strcat(Query, " WHERE "), To_Filter);
return FALSE;
} // end of MakeDelete
#endif // 0
/***********************************************************************/
/* XCV GetMaxSize: returns the maximum number of rows in the table. */
/***********************************************************************/
int TDBMYSQL::GetMaxSize(PGLOBAL g)
{
if (MaxSize < 0) {
#if 0
if (MakeSelect(g))
return -2;
if (!Myc.Connected()) {
if (Myc.Open(g, Host, Database, User, Pwd, Port))
return -1;
} // endif connected
if ((MaxSize = Myc.GetResultSize(g, Query)) < 0) {
Myc.Close();
return -3;
} // endif MaxSize
// FIXME: Columns should be known when Info calls GetMaxSize
if (!Columns)
Query = NULL; // Must be remade when columns are known
#endif // 0
MaxSize = 10; // To make MySQL happy
} // endif MaxSize
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* This a fake routine as ROWID does not exist in MySQL. */
/***********************************************************************/
int TDBMYSQL::RowNumber(PGLOBAL g, bool b)
{
return N;
} // end of RowNumber
/***********************************************************************/
/* Return 0 in mode DELETE to tell that the delete is done. */
/***********************************************************************/
int TDBMYSQL::GetProgMax(PGLOBAL g)
{
return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0
: GetMaxSize(g);
} // end of GetProgMax
/***********************************************************************/
/* MySQL Bind Parameter function. */
/***********************************************************************/
int TDBMYSQL::BindColumns(PGLOBAL g)
{
#if defined(MYSQL_PREPARED_STATEMENTS)
if (Prep) {
Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND));
for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
colp->InitBind(g);
return Myc.BindParams(g, Bind);
} // endif prep
#endif // MYSQL_PREPARED_STATEMENTS
for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
if (colp->Buf_Type == TYPE_DATE)
// Format must match DATETIME MySQL type
((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
return RC_OK;
} // end of BindColumns
/***********************************************************************/
/* MySQL Access Method opening routine. */
/***********************************************************************/
bool TDBMYSQL::OpenDB(PGLOBAL g)
{
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open, just replace it at its beginning. */
/*******************************************************************/
Myc.Rewind();
return FALSE;
} // endif use
/*********************************************************************/
/* Open a MySQL 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 server */
/* and if so to allocate just a new result set. But this only for */
/* servers allowing concurency in getting results ??? */
/*********************************************************************/
if (!Myc.Connected()) {
if (Myc.Open(g, Host, Database, User, Pwd, Port))
return TRUE;
} // endif Connected
/*********************************************************************/
/* Allocate whatever is used for getting results. */
/*********************************************************************/
if (Mode == MODE_READ) {
if (!MakeSelect(g))
m_Rc = Myc.ExecSQL(g, Query);
} else if (Mode == MODE_INSERT) {
if (!MakeInsert(g)) {
#if defined(MYSQL_PREPARED_STATEMENTS)
int n = (Prep) ? Myc.PrepareSQL(g, Query) : Nparm;
if (Nparm != n) {
if (n >= 0) // Other errors return negative values
strcpy(g->Message, MSG(BAD_PARM_COUNT));
} else
#endif // MYSQL_PREPARED_STATEMENTS
m_Rc = BindColumns(g);
} // endif MakeInsert
if (m_Rc != RC_FX) {
char cmd[64];
int w;
sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
m_Rc = Myc.ExecSQL(g, cmd, &w);
} // endif m_Rc
#if 0
} else if (Next) {
strcpy(g->Message, MSG(NO_JOIN_UPDEL));
} else if (Mode == MODE_DELETE) {
strcpy(g->Message, "MySQL table delete not implemented yet\n");
bool rc = MakeDelete(g);
if (!rc && Myc.ExecSQL(g, Query) == RC_NF) {
if (!AftRows)
AftRows = Myc.GetRows();
m_Rc = RC_OK;
} // endif ExecSQL
#endif // 0
} else {
// bool rc = MakeUpdate(g, sqlp->GetProj());
strcpy(g->Message, "MySQL table delete/update not implemented yet\n");
} // endelse
if (m_Rc == RC_FX) {
Myc.Close();
return TRUE;
} // endif rc
Use = USE_OPEN; // Do it now in case we are recursively called
return FALSE;
} // end of OpenDB
/***********************************************************************/
/* Data Base read routine for MYSQL access method. */
/***********************************************************************/
int TDBMYSQL::ReadDB(PGLOBAL g)
{
int rc;
if (trace > 1)
htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
/*********************************************************************/
/* Now start the reading process. */
/* Here is the place to fetch the line. */
/*********************************************************************/
N++;
Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK);
if (trace > 1)
htrc(" Read: rc=%d\n", rc);
return rc;
} // end of ReadDB
/***********************************************************************/
/* WriteDB: Data Base write routine for MYSQL access methods. */
/***********************************************************************/
int TDBMYSQL::WriteDB(PGLOBAL g)
{
#if defined(MYSQL_PREPARED_STATEMENTS)
if (Prep)
return Myc.ExecStmt(g);
#endif // MYSQL_PREPARED_STATEMENTS
// Statement was not prepared, we must construct and execute
// an insert query for each line to insert
int rc;
char buf[32];
strcpy(Qbuf, Query);
// Make the Insert command value list
for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
if (!colp->GetValue()->IsNull()) {
if (colp->GetResultType() == TYPE_STRING ||
colp->GetResultType() == TYPE_DATE)
strcat(Qbuf, "'");
strcat(Qbuf, colp->GetValue()->GetCharString(buf));
if (colp->GetResultType() == TYPE_STRING ||
colp->GetResultType() == TYPE_DATE)
strcat(Qbuf, "'");
} else
strcat(Qbuf, "NULL");
strcat(Qbuf, (colp->GetNext()) ? "," : ")");
} // endfor colp
Myc.m_Rows = -1; // To execute the query
rc = Myc.ExecSQL(g, Qbuf);
return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok
} // end of WriteDB
/***********************************************************************/
/* Data Base delete line routine for MYSQL access methods. */
/***********************************************************************/
int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
{
strcpy(g->Message, MSG(NO_MYSQL_DELETE));
return RC_FX;
} // end of DeleteDB
/***********************************************************************/
/* Data Base close routine for MySQL access method. */
/***********************************************************************/
void TDBMYSQL::CloseDB(PGLOBAL g)
{
if (Mode == MODE_INSERT) {
char cmd[64];
int w;
PDBUSER dup = PlgGetUser(g);
dup->Step = "Enabling indexes";
sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
Myc.m_Rows = -1; // To execute the query
m_Rc = Myc.ExecSQL(g, cmd, &w);
} // endif m_Rc
Myc.Close();
if (trace)
htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
} // end of CloseDB
// ------------------------ MYSQLCOL functions --------------------------
/***********************************************************************/
/* MYSQLCOL public constructor. */
/***********************************************************************/
MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
: COLBLK(cdp, tdbp, i)
{
if (cprec) {
Next = cprec->GetNext();
cprec->SetNext(this);
} else {
Next = tdbp->GetColumns();
tdbp->SetColumns(this);
} // endif cprec
// Set additional Dos access method information for column.
Long = cdp->GetLong();
Bind = NULL;
To_Val = NULL;
Slen = 0;
Rank = -1; // Not known yet
if (trace)
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
} // end of MYSQLCOL constructor
/***********************************************************************/
/* MYSQLCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
Long = col1->Long;
Bind = NULL;
To_Val = NULL;
Slen = col1->Slen;
Rank = col1->Rank;
} // end of MYSQLCOL copy constructor
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
/***********************************************************************/
bool MYSQLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
{
if (!(To_Val = value)) {
sprintf(g->Message, MSG(VALUE_ERROR), Name);
return TRUE;
} else if (Buf_Type == value->GetType()) {
// Values are of the (good) column type
if (Buf_Type == TYPE_DATE) {
// If any of the date values is formatted
// output format must be set for the receiving table
if (GetDomain() || ((DTVAL *)value)->IsFormatted())
goto newval; // This will make a new value;
} else if (Buf_Type == TYPE_FLOAT)
// Float values must be written with the correct (column) precision
// Note: maybe this should be forced by ShowValue instead of this ?
value->SetPrec(GetPrecision());
Value = value; // Directly access the external value
} else {
// Values are not of the (good) column type
if (check) {
sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
GetTypeName(Buf_Type), GetTypeName(value->GetType()));
return TRUE;
} // endif check
newval:
if (InitValue(g)) // Allocate the matching value block
return TRUE;
} // endif's Value, Buf_Type
// Because Colblk's have been made from a copy of the original TDB in
// case of Update, we must reset them to point to the original one.
if (To_Tdb->GetOrig())
To_Tdb = (PTDB)To_Tdb->GetOrig();
// Set the Column
Status = (ok) ? BUF_EMPTY : BUF_NO;
return FALSE;
} // end of SetBuffer
/***********************************************************************/
/* InitBind: Initialize the bind structure according to type. */
/***********************************************************************/
void MYSQLCOL::InitBind(PGLOBAL g)
{
PTDBMY tdbp = (PTDBMY)To_Tdb;
assert(tdbp->Bind && Rank < tdbp->Nparm);
Bind = &tdbp->Bind[Rank];
memset(Bind, 0, sizeof(MYSQL_BIND));
if (Buf_Type == TYPE_DATE) {
// Default format must match DATETIME MySQL type
// if (!((DTVAL*)Value)->IsFormatted())
((DTVAL*)Value)->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
Bind->buffer_length = 20;
Bind->length = &Slen;
} else {
Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
Bind->buffer = (char *)Value->GetTo_Val();
Bind->buffer_length = Value->GetClen();
Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL;
} // endif Buf_Type
} // end of InitBind
/***********************************************************************/
/* ReadColumn: */
/***********************************************************************/
void MYSQLCOL::ReadColumn(PGLOBAL g)
{
char *buf;
int rc;
PTDBMY tdbp = (PTDBMY)To_Tdb;
if (trace)
htrc("MySQL ReadColumn: name=%s\n", Name);
assert (Rank >= 0);
/*********************************************************************/
/* If physical fetching of the line was deferred, do it now. */
/*********************************************************************/
if (!tdbp->Fetched)
if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) {
if (rc == RC_EF)
sprintf(g->Message, MSG(INV_DEF_READ), rc);
longjmp(g->jumper[g->jump_level], 11);
} else
tdbp->Fetched = TRUE;
if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank)))
Value->SetValue_char(buf, Long);
else {
if (Nullable)
Value->SetNull(true);
Value->Reset(); // Null value
} // endelse
} // end of ReadColumn
/***********************************************************************/
/* WriteColumn: make sure the bind buffer is updated. */
/***********************************************************************/
void MYSQLCOL::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 defined(MYSQL_PREPARED_STATEMENTS)
if (((PTDBMY)To_Tdb)->Prep) {
if (Buf_Type == TYPE_DATE) {
Value->ShowValue((char *)Bind->buffer, (int)*Bind->length);
Slen = strlen((char *)Bind->buffer);
} else if (IsTypeChar(Buf_Type))
Slen = strlen(Value->GetCharValue());
} // endif Prep
#endif // MYSQL_PREPARED_STATEMENTS
} // end of WriteColumn
/* ---------------------------TDBMCL class --------------------------- */
/***********************************************************************/
/* TDBMCL class constructor. */
/***********************************************************************/
TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
{
Host = tdp->Hostname;
Db = tdp->Database;
Tab = tdp->Tabname;
User = tdp->Username;
Pwd = tdp->Password;
Port = tdp->Portnumber;
} // end of TDBMCL constructor
/***********************************************************************/
/* GetResult: Get the list the MYSQL table columns. */
/***********************************************************************/
PQRYRES TDBMCL::GetResult(PGLOBAL g)
{
return MyColumns(g, Host, Db, User, Pwd, Tab, NULL, Port, false, false);
} // end of GetResult