tabmysql.cpp 52.9 KB
Newer Older
Alexander Barkov's avatar
Alexander Barkov committed
1 2 3
/************* TabMySQL C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABMYSQL                                               */
/* -------------                                                        */
4
/*  Version 1.7                                                         */
Alexander Barkov's avatar
Alexander Barkov committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*                                                                      */
/* 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            */
/*                                                                      */
/************************************************************************/
34
#define MYSQL_SERVER 1
Alexander Barkov's avatar
Alexander Barkov committed
35
#include "my_global.h"
36 37
#include "sql_class.h"
#include "sql_servers.h"
Alexander Barkov's avatar
Alexander Barkov committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
#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"
59
#include "mycat.h"
Alexander Barkov's avatar
Alexander Barkov committed
60 61 62
#include "reldef.h"
#include "tabmysql.h"
#include "valblk.h"
63
#include "tabutil.h"
Alexander Barkov's avatar
Alexander Barkov committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

#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;
82
  Srcdef = NULL;
Alexander Barkov's avatar
Alexander Barkov committed
83 84 85
  Username = NULL;
  Password = NULL;
  Portnumber = 0;
86
  Isview = FALSE;
Alexander Barkov's avatar
Alexander Barkov committed
87 88
  Bind = FALSE;
  Delayed = FALSE;
89
  Xsrc = FALSE;
Alexander Barkov's avatar
Alexander Barkov committed
90 91
  } // end of MYSQLDEF constructor

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/***********************************************************************/
/*  Get connection info from the declared server.                      */
/***********************************************************************/
bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
{
  THD      *thd= current_thd;
  MEM_ROOT *mem= thd->mem_root;
  FOREIGN_SERVER *server, server_buffer;
  DBUG_ENTER("GetServerInfo");
  DBUG_PRINT("info", ("server_name %s", server_name));

  if (!server_name || !strlen(server_name)) {
    DBUG_PRINT("info", ("server_name not defined!"));
    strcpy(g->Message, "server_name not defined!");
    DBUG_RETURN(true);
    } // endif server_name

  // get_server_by_name() clones the server if exists and allocates
	// copies of strings in the supplied mem_root
  if (!(server= get_server_by_name(mem, server_name, &server_buffer))) {
    DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!"));
    /* need to come up with error handling */
    strcpy(g->Message, "get_server_by_name returned > 0 error condition!");
    DBUG_RETURN(true);
    } // endif server

  DBUG_PRINT("info", ("get_server_by_name returned server at %lx",
                      (long unsigned int) server));

  // TODO: We need to examine which of these can really be NULL
  Hostname = PlugDup(g, server->host);
  Database = PlugDup(g, server->db);
  Username = PlugDup(g, server->username);
  Password = PlugDup(g, server->password);
  Portnumber = (server->port) ? server->port : GetDefaultPort();

  DBUG_RETURN(false);
} // end of GetServerInfo

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
/***********************************************************************/
/* 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))                                        */
158
/*   ENGINE="CONNECT" TABLE_TYPE="MYSQL"                               */
159 160 161 162 163 164
/*   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)                                                   */
165 166
/*   ) ENGINE="CONNECT" TABLE_TYPE="MYSQL"                             */
/*   CONNECTION="my_conn";    (NIY)                                    */
167 168 169 170 171 172 173 174
/*                                                                     */
/*  'password' and 'port' are both optional.                           */
/*                                                                     */
/* RETURN VALUE                                                        */
/*   false       success                                               */
/*   true        error                                                 */
/*                                                                     */
/***********************************************************************/
175
bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
176 177 178 179
  {
  if ((!strstr(url, "://") && (!strchr(url, '@')))) {
    // No :// or @ in connection string. Must be a straight
    // connection name of either "server" or "server/table"
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
    // ok, so we do a little parsing, but not completely!
    if ((Tabname= strchr(url, '/'))) {
      // If there is a single '/' in the connection string, 
      // this means the user is specifying a table name
      *Tabname++= '\0';

      // there better not be any more '/'s !
      if (strchr(Tabname, '/'))
        return true;

    } else
      // Otherwise, straight server name, 
      // use tablename of federatedx table as remote table name
      Tabname= Name;

    if (trace)
      htrc("server: %s  Tabname: %s", url, Tabname);

198
    Server = url;
199
    return GetServerInfo(g, url);
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
  } 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;
221
    } else {
222
      *Hostname++ = 0;                   // End Username
223 224
      Server = Hostname;
    } // endif Hostname
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

    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;

252
      if ((Tabname = strchr(Database, '/'))) {
253 254
        *Tabname++ = 0;

255 256 257 258 259 260 261
        // Make sure there's not an extra /
        if ((strchr(Tabname, '/'))) {
          strcpy(g->Message, "Syntax error in URL");
          return true;
          } // endif /

        } // endif Tabname
262 263 264 265 266 267
        
      } // endif database

    if ((sport = strchr(Hostname, ':')))
      *sport++ = 0;

268 269 270 271
    // For unspecified values, get the values of old style options
    // but only if called from MYSQLDEF, else set them to NULL
    Portnumber = (sport && sport[0]) ? atoi(sport) 
               : (b) ? Cat->GetIntCatInfo("Port", GetDefaultPort()) : 0;
272 273

    if (Username[0] == 0)
274
      Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL;
275 276

    if (Hostname[0] == 0)
277
      Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL;
278 279

    if (!Database || !*Database)
280
      Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL;
281 282

    if (!Tabname || !*Tabname)
283
      Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL;
284

285 286
    if (!Password)
      Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL;
287 288 289 290 291 292 293 294 295 296 297 298 299
    } // 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

Alexander Barkov's avatar
Alexander Barkov committed
300 301 302 303 304
/***********************************************************************/
/*  DefineAM: define specific AM block values from XCV file.           */
/***********************************************************************/
bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
  {
305
  char *url;
306

Alexander Barkov's avatar
Alexander Barkov committed
307
  Desc = "MySQL Table";
308

309
  if (stricmp(am, "MYPRX")) {
310
    // Normal case of specific MYSQL table
311 312
    url = Cat->GetStringCatInfo(g, "Connect", NULL);

313 314 315 316 317 318 319 320
    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", "*");
      Password = Cat->GetStringCatInfo(g, "Password", NULL);
321
      Portnumber = Cat->GetIntCatInfo("Port", GetDefaultPort());
322
      Server = Hostname;
323
    } else if (ParseURL(g, url))
324
      return true;
325

326 327 328
    Bind = !!Cat->GetIntCatInfo("Bind", 0);
    Delayed = !!Cat->GetIntCatInfo("Delayed", 0);
  } else {
329
    // MYSQL access from a PROXY table 
330
    Database = Cat->GetStringCatInfo(g, "Database", "*");
331
    Isview = Cat->GetBoolCatInfo("View", FALSE);
332

333
    // We must get other connection parms from the calling table
334
    Remove_tshp(Cat);
335 336 337 338 339 340 341
    url = Cat->GetStringCatInfo(g, "Connect", NULL);

    if (!url || !*url) { 
      Hostname = Cat->GetStringCatInfo(g, "Host", "localhost");
      Username = Cat->GetStringCatInfo(g, "User", "*");
      Password = Cat->GetStringCatInfo(g, "Password", NULL);
      Portnumber = Cat->GetIntCatInfo("Port", GetDefaultPort());
342
      Server = Hostname;
343 344 345 346
    } else {
      char *locdb = Database;

      if (ParseURL(g, url))
347
        return true;
348 349 350 351 352

      Database = locdb;
    } // endif url

    Tabname = Name;
353
  } // endif am
354

355
  if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL)))
356 357 358 359 360
    Isview = true;

  // Used for Update and Delete
  Qrystr = Cat->GetStringCatInfo(g, "Query_String", "?");
  Quoted = Cat->GetIntCatInfo("Quoted", 0);
361

362
  // Specific for command executing tables
363
  Xsrc = Cat->GetBoolCatInfo("Execsrc", false);
364
  Mxr = Cat->GetIntCatInfo("Maxerr", 0);
Alexander Barkov's avatar
Alexander Barkov committed
365 366 367 368 369 370 371 372
  return FALSE;
  } // end of DefineAM

/***********************************************************************/
/*  GetTable: makes a new TDB of the proper type.                      */
/***********************************************************************/
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
  {
373 374 375
  if (Xsrc)
    return new(g) TDBMYEXC(this);
  else if (Catfunc == FNC_COL)
376 377 378 379
    return new(g) TDBMCL(this);
  else
    return new(g) TDBMYSQL(this);

Alexander Barkov's avatar
Alexander Barkov committed
380 381 382 383 384 385 386 387 388 389
  } // end of GetTable

/* ------------------------------------------------------------------- */

/***********************************************************************/
/*  Implementation of the TDBMYSQL class.                              */
/***********************************************************************/
TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
  {
  if (tdp) {
390 391 392 393 394 395 396
    Host = tdp->Hostname;
    Database = tdp->Database;
    Tabname = tdp->Tabname;
    Srcdef = tdp->Srcdef;
    User = tdp->Username;
    Pwd = tdp->Password;
    Server = tdp->Server;
397 398
    Qrystr = tdp->Qrystr;
    Quoted = max(0, tdp->Quoted);
399
    Port = tdp->Portnumber;
400
    Isview = tdp->Isview;
Alexander Barkov's avatar
Alexander Barkov committed
401 402 403 404 405 406
    Prep = tdp->Bind;
    Delayed = tdp->Delayed;
  } else {
    Host = NULL;
    Database = NULL;
    Tabname = NULL;
407
    Srcdef = NULL;
Alexander Barkov's avatar
Alexander Barkov committed
408 409
    User = NULL;
    Pwd = NULL;
410
    Server = NULL;
411 412
    Qrystr = NULL;
    Quoted = 0;
Alexander Barkov's avatar
Alexander Barkov committed
413
    Port = 0;
414
    Isview = FALSE;
Alexander Barkov's avatar
Alexander Barkov committed
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
    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;
434
  Srcdef = tdbp->Srcdef;
Alexander Barkov's avatar
Alexander Barkov committed
435 436
  User = tdbp->User;
  Pwd =  tdbp->Pwd; 
437 438
  Qrystr = tdbp->Qrystr;
  Quoted = tdbp->Quoted;
Alexander Barkov's avatar
Alexander Barkov committed
439
  Port = tdbp->Port;
440
  Isview = tdbp->Isview;
Alexander Barkov's avatar
Alexander Barkov committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
  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   *tk = "`";
487
  int     rank = 0;
Alexander Barkov's avatar
Alexander Barkov committed
488 489
  bool    b = FALSE;
  PCOL    colp;
490
//PDBUSER dup = PlgGetUser(g);
Alexander Barkov's avatar
Alexander Barkov committed
491 492 493 494

  if (Query)
    return FALSE;        // already done

495 496 497 498
  if (Srcdef) {
    Query = Srcdef;
    return false;
    } // endif Srcdef
Alexander Barkov's avatar
Alexander Barkov committed
499

500 501 502
  //Find the address of the suballocated query
  Query = (char*)PlugSubAlloc(g, NULL, 0);
  strcpy(Query, "SELECT ");
Alexander Barkov's avatar
Alexander Barkov committed
503

504
  if (Columns) {
Alexander Barkov's avatar
Alexander Barkov committed
505
    for (colp = Columns; colp; colp = colp->GetNext())
506 507 508 509 510
      if (!colp->IsSpecial()) {
//      if (colp->IsSpecial()) {
//        strcpy(g->Message, MSG(NO_SPEC_COL));
//        return TRUE;
//      } else {
Alexander Barkov's avatar
Alexander Barkov committed
511
        if (b)
512
          strcat(Query, ", ");
Alexander Barkov's avatar
Alexander Barkov committed
513 514 515
        else
          b = TRUE;

516
        strcat(strcat(strcat(Query, tk), colp->GetName()), tk);
Alexander Barkov's avatar
Alexander Barkov committed
517 518 519 520
        ((PMYCOL)colp)->Rank = rank++;
      } // endif colp

  } else {
521 522 523
    // ncol == 0 can occur for views or queries such as
    // Query count(*) from... for which we will count the rows from
    // Query '*' from...
Alexander Barkov's avatar
Alexander Barkov committed
524
    // (the use of a char constant minimize the result storage)
525
    strcat(Query, (Isview) ? "*" : "'*'");
Alexander Barkov's avatar
Alexander Barkov committed
526 527 528 529 530
  } // endif ncol

  strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk);

  if (To_Filter)
531
    strcat(strcat(Query, " WHERE "), To_Filter->Body);
Alexander Barkov's avatar
Alexander Barkov committed
532

533 534 535
  if (trace)
    htrc("Query=%s\n", Query);

536 537
  // Now we know how much to suballocate
  PlugSubAlloc(g, NULL, strlen(Query) + 1);
Alexander Barkov's avatar
Alexander Barkov committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
  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())
556 557 558 559 560
    if (!colp->IsSpecial()) {
//    if (colp->IsSpecial()) {
//      strcpy(g->Message, MSG(NO_SPEC_COL));
//      return TRUE;
//    } else {
Alexander Barkov's avatar
Alexander Barkov committed
561 562 563 564 565 566 567 568
      len += (strlen(colp->GetName()) + 4);
      ((PMYCOL)colp)->Rank = Nparm++;
    } // endif colp

  colist = (char*)PlugSubAlloc(g, NULL, len);
  *colist = '\0';

  if (Prep) {
569
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
570 571
    valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
    *valist = '\0';
572 573 574 575 576
#else   // !MYSQL_PREPARED_STATEMENTS
    strcpy(g->Message, "Prepared statements not used (not supported)");
    PushWarning(g, this);
    Prep = FALSE;
#endif  // !MYSQL_PREPARED_STATEMENTS 
Alexander Barkov's avatar
Alexander Barkov committed
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
    } // 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

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
/***********************************************************************/
/*  MakeCommand: make the Update or Delete statement to send to the    */
/*  MySQL server. Limited to remote values and filtering.              */
/***********************************************************************/
int TDBMYSQL::MakeCommand(PGLOBAL g)
  {
  Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);

  if (Quoted > 0 || stricmp(Name, Tabname)) {
    char *p, *qrystr, name[68];
    bool  qtd = Quoted > 0;


    // Make a lower case copy of the originale query
    qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
    strlwr(strcpy(qrystr, Qrystr));

    // Check whether the table name is equal to a keyword
    // If so, it must be quoted in the original query
    strlwr(strcat(strcat(strcpy(name, "`"), Name), "`"));

    if (!strstr("`update`delete`low_priority`ignore`quick`from`", name))
      strlwr(strcpy(name, Name));     // Not a keyword

    if ((p = strstr(qrystr, name))) {
      memcpy(Query, Qrystr, p - qrystr);
      Query[p - qrystr] = 0;

      if (qtd && *(p-1) == ' ')
        strcat(strcat(strcat(Query, "`"), Tabname), "`");
      else
        strcat(Query, Tabname);

      strcat(Query, Qrystr + (p - qrystr) + strlen(name));
    } else {
      sprintf(g->Message, "Cannot use this %s command",
                   (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
      return RC_FX;
    } // endif p

  } else
    strcpy(Query, Qrystr);

  return RC_OK;
  } // end of MakeCommand

#if 0
Alexander Barkov's avatar
Alexander Barkov committed
672 673
/***********************************************************************/
/*  MakeUpdate: make the Update statement use with MySQL connection.   */
674
/*  Limited to remote values and filtering.                            */
Alexander Barkov's avatar
Alexander Barkov committed
675
/***********************************************************************/
676
int TDBMYSQL::MakeUpdate(PGLOBAL g)
Alexander Barkov's avatar
Alexander Barkov committed
677
  {
678
  char *qc, cmd[8], tab[96], end[1024];
Alexander Barkov's avatar
Alexander Barkov committed
679

680 681
  Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
  memset(end, 0, sizeof(end));
Alexander Barkov's avatar
Alexander Barkov committed
682

683 684 685
  if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
      sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
    qc = "`";
686 687
  else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
                  && !stricmp(tab, Name))
688 689 690 691 692
    qc = (Quoted) ? "`" : "";
  else {
    strcpy(g->Message, "Cannot use this UPDATE command");
    return RC_FX;
  } // endif sscanf
Alexander Barkov's avatar
Alexander Barkov committed
693

694 695 696 697
  assert(!stricmp(cmd, "update"));
  strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), Tabname), qc);
  strcat(Query, end);
  return RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
698 699 700
  } // end of MakeUpdate

/***********************************************************************/
701 702
/*  MakeDelete: make the Delete statement used with MySQL connection.  */
/*  Limited to remote filtering.                                       */
Alexander Barkov's avatar
Alexander Barkov committed
703
/***********************************************************************/
704
int TDBMYSQL::MakeDelete(PGLOBAL g)
Alexander Barkov's avatar
Alexander Barkov committed
705
  {
706
  char *qc, cmd[8], from[8], tab[96], end[512];
Alexander Barkov's avatar
Alexander Barkov committed
707

708 709
  Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
  memset(end, 0, sizeof(end));
Alexander Barkov's avatar
Alexander Barkov committed
710

711 712 713 714 715 716 717 718 719
  if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
      sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
    qc = "`";
  else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
    qc = (Quoted) ? "`" : "";
  else {
    strcpy(g->Message, "Cannot use this DELETE command");
    return RC_FX;
  } // endif sscanf
Alexander Barkov's avatar
Alexander Barkov committed
720

721 722
  assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
  strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), Tabname), qc);
Alexander Barkov's avatar
Alexander Barkov committed
723

724 725
  if (*end)
    strcat(Query, end);
Alexander Barkov's avatar
Alexander Barkov committed
726

727
  return RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
728
  } // end of MakeDelete
729
#endif // 0
Alexander Barkov's avatar
Alexander Barkov committed
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756

/***********************************************************************/
/*  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

757 758
    // Return 0 in mode DELETE in case of delete all.
    MaxSize = (Mode == MODE_DELETE) ? 0 : 10;   // To make MySQL happy
Alexander Barkov's avatar
Alexander Barkov committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772
    } // 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

/***********************************************************************/
773
/*  Return 0 in mode UPDATE to tell that the update is done.           */
Alexander Barkov's avatar
Alexander Barkov committed
774 775 776
/***********************************************************************/
int TDBMYSQL::GetProgMax(PGLOBAL g)
  {
777
  return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
Alexander Barkov's avatar
Alexander Barkov committed
778 779 780 781 782 783 784
  } // end of GetProgMax

/***********************************************************************/
/*  MySQL Bind Parameter function.                                     */
/***********************************************************************/
int TDBMYSQL::BindColumns(PGLOBAL g)
  {
785
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
786 787 788 789 790 791 792
  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);
793 794
    } // endif prep
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
795

796
  return RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
797 798 799 800 801 802 803 804 805 806 807 808
  } // 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();
809
    return false;
Alexander Barkov's avatar
Alexander Barkov committed
810 811 812 813 814 815 816 817 818 819 820
    } // 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))
821
      return true;
Alexander Barkov's avatar
Alexander Barkov committed
822 823 824

    } // endif Connected

825 826 827 828 829 830 831 832
  /*********************************************************************/
  /*  Take care of DATE columns.                                       */
  /*********************************************************************/
  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);

Alexander Barkov's avatar
Alexander Barkov committed
833 834 835 836 837 838 839
  /*********************************************************************/
  /*  Allocate whatever is used for getting results.                   */
  /*********************************************************************/
  if (Mode == MODE_READ) {
    if (!MakeSelect(g))
      m_Rc = Myc.ExecSQL(g, Query);

840 841 842 843 844 845 846 847
#if 0
    if (!Myc.m_Res || !Myc.m_Fields) {
      sprintf(g->Message, "%s result", (Myc.m_Res) ? "Void" : "No");
      Myc.Close();
      return true;
      } // endif m_Res
#endif // 0

848
    if (!m_Rc && Srcdef)
849 850 851
      if (SetColumnRanks(g))
        return true;

Alexander Barkov's avatar
Alexander Barkov committed
852
  } else if (Mode == MODE_INSERT) {
853 854 855 856 857
    if (Srcdef) {
      strcpy(g->Message, "No insert into anonym views");
      return true;
      } // endif Srcdef

Alexander Barkov's avatar
Alexander Barkov committed
858
    if (!MakeInsert(g)) {
859
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
860 861 862 863 864 865 866
      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
867
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
868 869 870 871 872 873 874 875 876 877 878 879
        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

880
  } else
881 882
//  m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
    m_Rc = MakeCommand(g);
Alexander Barkov's avatar
Alexander Barkov committed
883 884 885

  if (m_Rc == RC_FX) {
    Myc.Close();
886
    return true;
Alexander Barkov's avatar
Alexander Barkov committed
887 888
    } // endif rc

889 890
  Use = USE_OPEN;
  return false;
Alexander Barkov's avatar
Alexander Barkov committed
891 892
  } // end of OpenDB

893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
/***********************************************************************/
/*  Set the rank of columns in the result set.                         */
/***********************************************************************/
bool TDBMYSQL::SetColumnRanks(PGLOBAL g)
  {
  for (PCOL colp = Columns; colp; colp = colp->GetNext())
    if (((PMYCOL)colp)->FindRank(g))
      return TRUE;

  return FALSE;
  } // end of SetColumnRanks

/***********************************************************************/
/*  Called by Parent table to make the columns of a View.              */
/***********************************************************************/
PCOL TDBMYSQL::MakeFieldColumn(PGLOBAL g, char *name)
  {
  int          n;
  MYSQL_FIELD *fld;
  PCOL         cp, colp = NULL;

  for (n = 0; n < Myc.m_Fields; n++) {
    fld = &Myc.m_Res->fields[n];

    if (!stricmp(name, fld->name)) {
      colp = new(g) MYSQLCOL(fld, this, n);

      if (colp->InitValue(g))
        return NULL;

      if (!Columns)
        Columns = colp;
      else for (cp = Columns; cp; cp = cp->GetNext())
        if (!cp->GetNext()) {
          cp->SetNext(colp);
          break;
          } // endif Next

      break;
      } // endif name

    } // endfor n

  if (!colp)
    sprintf(g->Message, "Column %s is not in view", name);

  return colp;
  } // end of MakeFieldColumn

942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
/***********************************************************************/
/*  Called by Pivot tables to find default column names in a View      */
/*  as the name of last field not equal to the passed name.            */
/***********************************************************************/
char *TDBMYSQL::FindFieldColumn(char *name)
  {
  int          n;
  MYSQL_FIELD *fld;
  char        *cp = NULL;

  for (n = Myc.m_Fields - 1; n >= 0; n--) {
    fld = &Myc.m_Res->fields[n];

    if (!name || stricmp(name, fld->name)) {
      cp = fld->name;
      break;
      } // endif name

    } // endfor n

  return cp;
  } // end of FindFieldColumn

965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996
/***********************************************************************/
/*  Send an UPDATE or DELETE command to the remote server.             */
/***********************************************************************/
int TDBMYSQL::SendCommand(PGLOBAL g)
  {
  int w;

  if (Myc.ExecSQLcmd(g, Query, &w) == RC_NF) {
    AftRows = Myc.m_Afrw;
    sprintf(g->Message, "%s: %d affected rows", Tabname, AftRows);
    PushWarning(g, this, 0);    // 0 means a Note

    if (trace)
      htrc("%s\n", g->Message);

    if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
      // We got warnings from the remote server
      while (Myc.Fetch(g, -1) == RC_OK) {
        sprintf(g->Message, "%s: (%s) %s", Tabname,
                Myc.GetCharField(1), Myc.GetCharField(2));
        PushWarning(g, this);
        } // endwhile Fetch

      Myc.FreeResult();
      } // endif w

    return RC_EF;               // Nothing else to do
  } else
    return RC_FX;               // Error

  } // end of SendCommand

Alexander Barkov's avatar
Alexander Barkov committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
/***********************************************************************/
/*  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);

1008 1009 1010
  if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
    return SendCommand(g);

Alexander Barkov's avatar
Alexander Barkov committed
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
  /*********************************************************************/
  /*  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)
  {
1029
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
1030 1031
  if (Prep)
    return Myc.ExecStmt(g);
1032
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

  // 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()) {
1043 1044 1045 1046
    if (!colp->GetValue()->IsNull()) {
      if (colp->GetResultType() == TYPE_STRING || 
          colp->GetResultType() == TYPE_DATE)
        strcat(Qbuf, "'");
Alexander Barkov's avatar
Alexander Barkov committed
1047

1048
      strcat(Qbuf, colp->GetValue()->GetCharString(buf));
Alexander Barkov's avatar
Alexander Barkov committed
1049

1050 1051 1052 1053 1054 1055
      if (colp->GetResultType() == TYPE_STRING || 
          colp->GetResultType() == TYPE_DATE)
        strcat(Qbuf, "'");

    } else
      strcat(Qbuf, "NULL");
Alexander Barkov's avatar
Alexander Barkov committed
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065

    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

/***********************************************************************/
1066
/*  Data Base delete all routine for MYSQL access methods.             */
Alexander Barkov's avatar
Alexander Barkov committed
1067 1068 1069
/***********************************************************************/
int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
  {
1070 1071 1072 1073 1074 1075
  if (irc == RC_FX)
    // Send the DELETE (all) command to the remote table
    return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;              
  else
    return RC_OK;                 // Ignore

Alexander Barkov's avatar
Alexander Barkov committed
1076 1077 1078 1079 1080 1081 1082
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for MySQL access method.                   */
/***********************************************************************/
void TDBMYSQL::CloseDB(PGLOBAL g)
  {
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
  if (Myc.Connected()) {
    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();
    } // endif Myc
Alexander Barkov's avatar
Alexander Barkov committed
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118

  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

1119
  // Set additional MySQL access method information for column.
Alexander Barkov's avatar
Alexander Barkov committed
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
  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

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
/***********************************************************************/
/*  MYSQLCOL public constructor.                                       */
/***********************************************************************/
MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
        : COLBLK(NULL, tdbp, i)
  {
  Name = fld->name;
  Opt = 0;
  Long = fld->length;
  Buf_Type = MYSQLtoPLG(fld->type);
  strcpy(Format.Type, GetFormatType(Buf_Type));
  Format.Length = Long;
  Format.Prec = fld->decimals;
  ColUse = U_P;
  Nullable = !IS_NOT_NULL(fld->flags);

  // Set additional MySQL access method information for column.
  Bind = NULL;
  To_Val = NULL;
  Slen = 0;
  Rank = i;

  if (trace)
    htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);

  } // end of MYSQLCOL constructor

Alexander Barkov's avatar
Alexander Barkov committed
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
/***********************************************************************/
/*  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

1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
/***********************************************************************/
/*  FindRank: Find the rank of this column in the result set.          */
/***********************************************************************/
bool MYSQLCOL::FindRank(PGLOBAL g)
{
  int    n;
  MYSQLC myc = ((PTDBMY)To_Tdb)->Myc;

  for (n = 0; n < myc.m_Fields; n++)
    if (!stricmp(Name, myc.m_Res->fields[n].name)) {
      Rank = n;
      return false;
      } // endif Name

  sprintf(g->Message, "Column %s not in result set", Name);
  return true;
} // end of FindRank

Alexander Barkov's avatar
Alexander Barkov committed
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
/***********************************************************************/
/*  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 ?
1208
      value->SetPrec(GetPrecision());
Alexander Barkov's avatar
Alexander Barkov committed
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247

    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) {
1248
    Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
Alexander Barkov's avatar
Alexander Barkov committed
1249 1250 1251 1252
    Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
    Bind->buffer_length = 20;
    Bind->length = &Slen;
  } else {
1253
    Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
Alexander Barkov's avatar
Alexander Barkov committed
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
    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)
  {
1266
  char  *p, *buf, tim[20];
Alexander Barkov's avatar
Alexander Barkov committed
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
  int    rc;
  PTDBMY tdbp = (PTDBMY)To_Tdb;

  /*********************************************************************/
  /*  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;

1282 1283 1284 1285
  if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
    if (trace)
      htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);

1286
    // TODO: have a true way to differenciate temporal values
1287
    if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
1288 1289 1290 1291 1292
      // This is a TIME value
      p = strcat(strcpy(tim, "1970-01-01 "), buf);
    else
      p = buf;

1293 1294 1295 1296 1297 1298
    if (Value->SetValue_char(p, strlen(p))) {
      sprintf(g->Message, "Out of range value for column %s at row %d",
              Name, tdbp->RowNumber(g));
      PushWarning(g, tdbp);
      } // endif SetValue_char

1299
  } else {
1300 1301 1302
    if (Nullable)
      Value->SetNull(true);

Alexander Barkov's avatar
Alexander Barkov committed
1303
    Value->Reset();              // Null value
1304
  } // endif buf
Alexander Barkov's avatar
Alexander Barkov committed
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318

  } // 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

1319
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
1320 1321
  if (((PTDBMY)To_Tdb)->Prep) {
    if (Buf_Type == TYPE_DATE) {
1322
      Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
Alexander Barkov's avatar
Alexander Barkov committed
1323 1324 1325 1326 1327
      Slen = strlen((char *)Bind->buffer);
    } else if (IsTypeChar(Buf_Type))
      Slen = strlen(Value->GetCharValue());

    } // endif Prep
1328
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
1329 1330

  } // end of WriteColumn
1331

1332 1333 1334
/* ------------------------------------------------------------------- */

/***********************************************************************/
1335
/*  Implementation of the TDBMYEXC class.                              */
1336
/***********************************************************************/
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp) 
{
  Cmdlist = NULL;
  Cmdcol = NULL;
  Shw = false;
  Havew = false;
  Isw = false;
  Warnings = 0;
  Mxr = tdp->Mxr;
  Nerr = 0;
} // end of TDBMYEXC constructor

TDBMYEXC::TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp)
{
  Cmdlist = tdbp->Cmdlist;
  Cmdcol = tdbp->Cmdcol;
  Shw = tdbp->Shw;
  Havew = tdbp->Havew;
  Isw = tdbp->Isw;
  Mxr = tdbp->Mxr;
  Nerr = tdbp->Nerr;
} // end of TDBMYEXC copy constructor
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393

// Is this really useful ???
PTDB TDBMYEXC::CopyOne(PTABS t)
  {
  PTDB    tp;
  PCOL    cp1, cp2;
  PGLOBAL g = t->G;

  tp = new(g) TDBMYEXC(g, this);

  for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
    cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);

    NewPointer(t, cp1, cp2);
    } // endfor cp1

  return tp;
  } // end of CopyOne

/***********************************************************************/
/*  Allocate MYSQL column description block.                           */
/***********************************************************************/
PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  {
  PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);

  if (!colp->Flag)
    Cmdcol = colp->GetName();

  return colp;
  } // end of MakeCol

/***********************************************************************/
/*  MakeCMD: make the SQL statement to send to MYSQL connection.       */
/***********************************************************************/
1394
PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
1395
  {
1396
  PCMD xcmd = NULL;
1397 1398 1399

  if (To_Filter) {
    if (Cmdcol) {
1400 1401 1402
      if (!stricmp(Cmdcol, To_Filter->Body) &&
          (To_Filter->Op == OP_EQ || To_Filter->Op == OP_IN)) {
        xcmd = To_Filter->Cmds;
1403 1404 1405 1406 1407 1408 1409 1410 1411
      } 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
1412
    xcmd = new(g) CMD(g, Srcdef);
1413 1414 1415 1416 1417 1418 1419 1420 1421 1422

  return xcmd;
  } // end of MakeCMD

/***********************************************************************/
/*  EXC GetMaxSize: returns the maximum number of rows in the table.   */
/***********************************************************************/
int TDBMYEXC::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
1423
    MaxSize = 10;                 // a guess
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  MySQL Exec Access Method opening routine.                          */
/***********************************************************************/
bool TDBMYEXC::OpenDB(PGLOBAL g)
  {
  if (Use == USE_OPEN) {
    strcpy(g->Message, "Multiple execution is not allowed");
    return true;
    } // 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;

  Use = USE_OPEN;       // Do it now in case we are recursively called

  if (Mode != MODE_READ) {
    strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
    return true;
    } // endif Mode

  /*********************************************************************/
  /*  Get the command to execute.                                      */
  /*********************************************************************/
1460
  if (!(Cmdlist = MakeCMD(g))) {
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472
    Myc.Close();
    return true;
    } // endif Query

  return false;
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for MYSQL access method.                    */
/***********************************************************************/
int TDBMYEXC::ReadDB(PGLOBAL g)
  {
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520
  if (Havew) {
    // Process result set from SHOW WARNINGS
    if (Myc.Fetch(g, -1) != RC_OK) {
      Myc.FreeResult();
      Havew = Isw = false;
    } else {
      N++;
      Isw = true;
      return RC_OK;
    } // endif Fetch

    } // endif m_Res

  if (Cmdlist) {
    // Process query to send
    int rc;
  
    do {
      Query = Cmdlist->Cmd;

      switch (rc = Myc.ExecSQLcmd(g, Query, &Warnings)) {
        case RC_NF:
          AftRows = Myc.m_Afrw;
          strcpy(g->Message, "Affected rows");
          break;
        case RC_OK:
          AftRows = Myc.m_Fields;
          strcpy(g->Message, "Result set columns");
          break;
        case RC_FX:
          AftRows = Myc.m_Afrw;
          Nerr++;
          break;
        case RC_INFO:
          Shw = true;
        } // endswitch rc
  
      Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
      } while (rc == RC_INFO);

    if (Shw && Warnings)
      Havew = (Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK);

    ++N;
    return RC_OK;
  } else
    return RC_EF;

1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
  } // end of ReadDB

/***********************************************************************/
/*  WriteDB: Data Base write routine for Exec MYSQL access methods.    */
/***********************************************************************/
int TDBMYEXC::WriteDB(PGLOBAL g)
  {
  strcpy(g->Message, "EXEC MYSQL tables are read only");
  return RC_FX;
  } // end of WriteDB

// ------------------------- MYXCOL functions ---------------------------

/***********************************************************************/
/*  MYXCOL public constructor.                                         */
/***********************************************************************/
MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
      : MYSQLCOL(cdp, tdbp, cprec, i, am)
  {
  // Set additional EXEC MYSQL access method information for column.
  Flag = cdp->GetOffset();
  } // end of MYSQLCOL constructor

/***********************************************************************/
/*  MYSQLCOL public constructor.                                       */
/***********************************************************************/
MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
      : MYSQLCOL(fld, tdbp, i, am)
  {
  if (trace)
    htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);

  } // end of MYSQLCOL constructor

/***********************************************************************/
/*  MYXCOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
  {
  Flag = col1->Flag;
  } // end of MYXCOL copy constructor

/***********************************************************************/
/*  ReadColumn:                                                        */
/***********************************************************************/
void MYXCOL::ReadColumn(PGLOBAL g)
  {
  PTDBMYX tdbp = (PTDBMYX)To_Tdb;

1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587
  if (tdbp->Isw) {
    char *buf = NULL;

    if (Flag < 3) {
      buf = tdbp->Myc.GetCharField(Flag);
      Value->SetValue_psz(buf);
    } else
      Value->Reset();

  } else
    switch (Flag) {
      case  0: Value->SetValue_psz(tdbp->Query);    break;
      case  1: Value->SetValue(tdbp->AftRows);      break;
      case  2: Value->SetValue_psz(g->Message);     break;
      case  3: Value->SetValue(tdbp->Warnings);     break;
      default: Value->SetValue_psz("Invalid Flag"); break;
      } // endswitch Flag
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598

  } // end of ReadColumn

/***********************************************************************/
/*  WriteColumn: should never be called.                               */
/***********************************************************************/
void MYXCOL::WriteColumn(PGLOBAL g)
  {
  assert(false);
  } // end of WriteColumn

1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618
/* ---------------------------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)
  {
1619
  return MyColumns(g, Host, Db, User, Pwd, Tab, NULL, Port, false);
1620
	} // end of GetResult