tabmysql.cpp 56.3 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.9                                                         */
Alexander Barkov's avatar
Alexander Barkov committed
5 6 7
/*                                                                      */
/* AUTHOR:                                                              */
/* -------                                                              */
Olivier Bertrand's avatar
Olivier Bertrand committed
8
/*  Olivier BERTRAND                                      2007-2014     */
Alexander Barkov's avatar
Alexander Barkov committed
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
/*                                                                      */
/* 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"
64
#include "ha_connect.h"
Alexander Barkov's avatar
Alexander Barkov committed
65 66 67 68 69

#if defined(_CONSOLE)
void PrintResult(PGLOBAL, PSEM, PQRYRES);
#endif   // _CONSOLE

70 71 72 73
// Used to check whether a MYSQL table is created on itself
bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
                      const char *db, char *tab, const char *src, int port);

74 75 76 77 78
/***********************************************************************/
/*  External function.                                                 */
/***********************************************************************/
bool ExactInfo(void);

Alexander Barkov's avatar
Alexander Barkov committed
79 80 81 82 83 84 85 86 87 88 89
/* -------------- Implementation of the MYSQLDEF class --------------- */

/***********************************************************************/
/*  Constructor.                                                       */
/***********************************************************************/
MYSQLDEF::MYSQLDEF(void)
  {
  Pseudo = 2;                            // SERVID is Ok but not ROWID
  Hostname = NULL;
  Database = NULL;
  Tabname = NULL;
90
  Srcdef = NULL;
Alexander Barkov's avatar
Alexander Barkov committed
91 92 93
  Username = NULL;
  Password = NULL;
  Portnumber = 0;
94 95 96 97 98
  Isview = false;
  Bind = false;
  Delayed = false;
  Xsrc = false;
  Huge = false;
Alexander Barkov's avatar
Alexander Barkov committed
99 100
  } // end of MYSQLDEF constructor

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
/***********************************************************************/
/*  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
Olivier Bertrand's avatar
Olivier Bertrand committed
119
  // copies of strings in the supplied mem_root
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
  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

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

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

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

    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;

260
      if ((Tabname = strchr(Database, '/'))) {
261 262
        *Tabname++ = 0;

263 264 265 266 267 268 269
        // Make sure there's not an extra /
        if ((strchr(Tabname, '/'))) {
          strcpy(g->Message, "Syntax error in URL");
          return true;
          } // endif /

        } // endif Tabname
Olivier Bertrand's avatar
Olivier Bertrand committed
270

271 272 273 274 275
      } // endif database

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

276 277 278
    // 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) 
279
               : (b) ? GetIntCatInfo("Port", GetDefaultPort()) : 0;
280 281

    if (Username[0] == 0)
282
      Username = (b) ? GetStringCatInfo(g, "User", "*") : NULL;
283 284

    if (Hostname[0] == 0)
285
      Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL;
286 287

    if (!Database || !*Database)
288
      Database = (b) ? GetStringCatInfo(g, "Database", "*") : NULL;
289 290

    if (!Tabname || !*Tabname)
291
      Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
292

293
    if (!Password)
294
      Password = (b) ? GetStringCatInfo(g, "Password", NULL) : NULL;
295 296 297 298 299 300 301 302 303 304 305 306 307
    } // 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
308 309 310 311 312
/***********************************************************************/
/*  DefineAM: define specific AM block values from XCV file.           */
/***********************************************************************/
bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
  {
313
  char *url;
314

Alexander Barkov's avatar
Alexander Barkov committed
315
  Desc = "MySQL Table";
316

317
  if (stricmp(am, "MYPRX")) {
318
    // Normal case of specific MYSQL table
319
    url = GetStringCatInfo(g, "Connect", NULL);
320

Olivier Bertrand's avatar
Olivier Bertrand committed
321
    if (!url || !*url) {
322
      // Not using the connection URL
323 324 325 326 327 328 329
      Hostname = GetStringCatInfo(g, "Host", "localhost");
      Database = GetStringCatInfo(g, "Database", "*");
      Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated
      Tabname = GetStringCatInfo(g, "Tabname", Tabname);
      Username = GetStringCatInfo(g, "User", "*");
      Password = GetStringCatInfo(g, "Password", NULL);
      Portnumber = GetIntCatInfo("Port", GetDefaultPort());
330
      Server = Hostname;
331
    } else if (ParseURL(g, url))
332
      return true;
333

334 335
    Bind = !!GetIntCatInfo("Bind", 0);
    Delayed = !!GetIntCatInfo("Delayed", 0);
336
  } else {
337
    // MYSQL access from a PROXY table 
338
    Database = GetStringCatInfo(g, "Database", "*");
339
    Isview = GetBoolCatInfo("View", false);
340

341
    // We must get other connection parms from the calling table
342
    Remove_tshp(Cat);
343
    url = GetStringCatInfo(g, "Connect", NULL);
344 345

    if (!url || !*url) { 
346 347 348 349
      Hostname = GetStringCatInfo(g, "Host", "localhost");
      Username = GetStringCatInfo(g, "User", "*");
      Password = GetStringCatInfo(g, "Password", NULL);
      Portnumber = GetIntCatInfo("Port", GetDefaultPort());
350
      Server = Hostname;
351 352 353 354
    } else {
      char *locdb = Database;

      if (ParseURL(g, url))
355
        return true;
356 357 358 359 360

      Database = locdb;
    } // endif url

    Tabname = Name;
361
  } // endif am
362

363 364
  if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) {
    Read_Only = true;
365
    Isview = true;
366 367 368
  } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Database,
                       Tabname, Srcdef, Portnumber))
    return true;
369 370

  // Used for Update and Delete
371 372
  Qrystr = GetStringCatInfo(g, "Query_String", "?");
  Quoted = GetIntCatInfo("Quoted", 0);
373

374
  // Specific for command executing tables
375 376
  Xsrc = GetBoolCatInfo("Execsrc", false);
  Mxr = GetIntCatInfo("Maxerr", 0);
377 378
  Huge = GetBoolCatInfo("Huge", false);
  return false;
Alexander Barkov's avatar
Alexander Barkov committed
379 380 381 382 383 384 385
  } // end of DefineAM

/***********************************************************************/
/*  GetTable: makes a new TDB of the proper type.                      */
/***********************************************************************/
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
  {
386 387 388
  if (Xsrc)
    return new(g) TDBMYEXC(this);
  else if (Catfunc == FNC_COL)
389 390 391 392
    return new(g) TDBMCL(this);
  else
    return new(g) TDBMYSQL(this);

Alexander Barkov's avatar
Alexander Barkov committed
393 394 395 396 397 398 399 400 401 402
  } // end of GetTable

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

/***********************************************************************/
/*  Implementation of the TDBMYSQL class.                              */
/***********************************************************************/
TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
  {
  if (tdp) {
403 404 405 406 407 408 409
    Host = tdp->Hostname;
    Database = tdp->Database;
    Tabname = tdp->Tabname;
    Srcdef = tdp->Srcdef;
    User = tdp->Username;
    Pwd = tdp->Password;
    Server = tdp->Server;
410
    Qrystr = tdp->Qrystr;
411
    Quoted = MY_MAX(0, tdp->Quoted);
412
    Port = tdp->Portnumber;
413
    Isview = tdp->Isview;
Alexander Barkov's avatar
Alexander Barkov committed
414 415
    Prep = tdp->Bind;
    Delayed = tdp->Delayed;
416
    Myc.m_Use = tdp->Huge;
Alexander Barkov's avatar
Alexander Barkov committed
417 418 419 420
  } else {
    Host = NULL;
    Database = NULL;
    Tabname = NULL;
421
    Srcdef = NULL;
Alexander Barkov's avatar
Alexander Barkov committed
422 423
    User = NULL;
    Pwd = NULL;
424
    Server = NULL;
425 426
    Qrystr = NULL;
    Quoted = 0;
Alexander Barkov's avatar
Alexander Barkov committed
427
    Port = 0;
428 429 430
    Isview = false;
    Prep = false;
    Delayed = false;
Alexander Barkov's avatar
Alexander Barkov committed
431 432 433 434
  } // endif tdp

  Bind = NULL;
  Query = NULL;
435
  Fetched = false;
Alexander Barkov's avatar
Alexander Barkov committed
436 437 438 439 440 441 442 443 444 445 446
  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;
447
  Srcdef = tdbp->Srcdef;
Alexander Barkov's avatar
Alexander Barkov committed
448
  User = tdbp->User;
Olivier Bertrand's avatar
Olivier Bertrand committed
449
  Pwd =  tdbp->Pwd;
450 451
  Qrystr = tdbp->Qrystr;
  Quoted = tdbp->Quoted;
Alexander Barkov's avatar
Alexander Barkov committed
452
  Port = tdbp->Port;
453
  Isview = tdbp->Isview;
Alexander Barkov's avatar
Alexander Barkov committed
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 487 488 489 490 491 492 493 494 495
  Prep = tdbp->Prep;
  Delayed = tdbp->Delayed;
  Bind = NULL;
  Query = tdbp->Query;
  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.                         */
/***********************************************************************/
496
bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
Alexander Barkov's avatar
Alexander Barkov committed
497
  {
498 499
//char   *tk = "`";
  char    tk = '`';
500
  int     len = 0, rank = 0;
501
  bool    b = false, oom = false;
Alexander Barkov's avatar
Alexander Barkov committed
502
  PCOL    colp;
503
//PDBUSER dup = PlgGetUser(g);
Alexander Barkov's avatar
Alexander Barkov committed
504 505

  if (Query)
506
    return false;        // already done
Alexander Barkov's avatar
Alexander Barkov committed
507

508
  if (Srcdef) {
509
    Query = new(g)STRING(g, 0, Srcdef);
510 511
    return false;
    } // endif Srcdef
Alexander Barkov's avatar
Alexander Barkov committed
512

513 514
  // Allocate the string used to contain Query
  Query = new(g) STRING(g, 1023, "SELECT ");
Alexander Barkov's avatar
Alexander Barkov committed
515

516
  if (Columns) {
Alexander Barkov's avatar
Alexander Barkov committed
517
    for (colp = Columns; colp; colp = colp->GetNext())
518
      if (!colp->IsSpecial()) {
Alexander Barkov's avatar
Alexander Barkov committed
519
        if (b)
520
          oom |= Query->Append(", ");
Alexander Barkov's avatar
Alexander Barkov committed
521
        else
522
          b = true;
Alexander Barkov's avatar
Alexander Barkov committed
523

524 525 526
        oom |= Query->Append(tk);
        oom |= Query->Append(colp->GetName());
        oom |= Query->Append(tk);
Alexander Barkov's avatar
Alexander Barkov committed
527 528 529 530
        ((PMYCOL)colp)->Rank = rank++;
      } // endif colp

  } else {
531 532 533
    // 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
534
    // (the use of a char constant minimize the result storage)
535 536 537 538 539
    if (Isview)
      oom |= Query->Append('*');
    else
      oom |= Query->Append("'*'");

Alexander Barkov's avatar
Alexander Barkov committed
540 541
  } // endif ncol

542 543 544 545 546
  oom |= Query->Append(" FROM ");
  oom |= Query->Append(tk);
  oom |= Query->Append(Tabname);
  oom |= Query->Append(tk);
  len = Query->GetLength();
547 548 549

  if (To_CondFil) {
    if (!mx) {
550 551 552
      oom |= Query->Append(" WHERE ");
      oom |= Query->Append(To_CondFil->Body);
      len = Query->GetLength() + 1;
553 554
    } else
      len += (strlen(To_CondFil->Body) + 256);
Alexander Barkov's avatar
Alexander Barkov committed
555

556 557
  } else
    len += (mx ? 256 : 1);
Alexander Barkov's avatar
Alexander Barkov committed
558

559 560 561 562 563
  if (oom || Query->Resize(len)) {
    strcpy(g->Message, "MakeSelect: Out of memory");
    return true;
    } // endif oom

564
  if (trace)
565
    htrc("Query=%s\n", Query->GetStr());
566

567
  return false;
Alexander Barkov's avatar
Alexander Barkov committed
568 569 570 571 572 573 574 575
  } // end of MakeSelect

/***********************************************************************/
/*  MakeInsert: make the Insert statement used with MySQL connection.  */
/***********************************************************************/
bool TDBMYSQL::MakeInsert(PGLOBAL g)
  {
  char *tk = "`";
576 577
  uint  len = 0;
  bool  b = false, oom;
Alexander Barkov's avatar
Alexander Barkov committed
578 579 580
  PCOL  colp;

  if (Query)
581
    return false;        // already done
Alexander Barkov's avatar
Alexander Barkov committed
582 583

  if (Prep) {
584
#if !defined(MYSQL_PREPARED_STATEMENTS)
585 586
    strcpy(g->Message, "Prepared statements not used (not supported)");
    PushWarning(g, this);
587
    Prep = false;
588
#endif  // !MYSQL_PREPARED_STATEMENTS 
Alexander Barkov's avatar
Alexander Barkov committed
589 590
    } // endif Prep

591 592 593 594 595 596
  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);
Alexander Barkov's avatar
Alexander Barkov committed
597

598 599 600 601 602 603 604 605 606
      // Parameter marker
      if (!Prep) {
        if (colp->GetResultType() == TYPE_DATE)
          len += 20;
        else
          len += colp->GetLength();
  
      } else
        len += 2;
Alexander Barkov's avatar
Alexander Barkov committed
607

608 609
      ((PMYCOL)colp)->Rank = Nparm++;
    } // endif colp
Alexander Barkov's avatar
Alexander Barkov committed
610 611

  // Below 40 is enough to contain the fixed part of the query
612 613
  len += (strlen(Tabname) + 40);
  Query = new(g) STRING(g, len);
Alexander Barkov's avatar
Alexander Barkov committed
614 615

  if (Delayed)
616
    oom = Query->Set("INSERT DELAYED INTO ");
Alexander Barkov's avatar
Alexander Barkov committed
617
  else
618
    oom = Query->Set("INSERT INTO ");
Alexander Barkov's avatar
Alexander Barkov committed
619

620 621 622
  oom |= Query->Append(tk);
  oom |= Query->Append(Tabname);
  oom |= Query->Append("` (");
Alexander Barkov's avatar
Alexander Barkov committed
623

624 625 626 627 628 629 630 631 632 633
  for (colp = Columns; colp; colp = colp->GetNext()) {
    if (b)
      oom |= Query->Append(", ");
    else
      b = true;
  
    oom |= Query->Append(tk);
    oom |= Query->Append(colp->GetName());
    oom |= Query->Append(tk);
    } // endfor colp
Alexander Barkov's avatar
Alexander Barkov committed
634

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
  oom |= Query->Append(") VALUES (");

#if defined(MYSQL_PREPARED_STATEMENTS)
  if (Prep) {
    for (int i = 0; i < Nparm; i++)
      oom |= Query->Append("?,");

    Query->RepLast(')');
    Query->Trim();
    }  // endif Prep
#endif  // MYSQL_PREPARED_STATEMENTS 

  if (oom)
    strcpy(g->Message, "MakeInsert: Out of memory");

  return oom;
Alexander Barkov's avatar
Alexander Barkov committed
651 652
  } // end of MakeInsert

653 654 655 656 657 658
/***********************************************************************/
/*  MakeCommand: make the Update or Delete statement to send to the    */
/*  MySQL server. Limited to remote values and filtering.              */
/***********************************************************************/
int TDBMYSQL::MakeCommand(PGLOBAL g)
  {
659
  Query = new(g) STRING(g, strlen(Qrystr) + 64);
660 661 662 663 664 665 666

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


    // Make a lower case copy of the originale query
667
    qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 5);
668 669 670 671 672 673 674 675 676 677
    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))) {
678
      bool oom = Query->Set(Qrystr, p - qrystr);
679

680 681 682 683 684 685 686 687 688 689 690 691 692 693
      if (qtd && *(p-1) == ' ') {
        oom |= Query->Append('`');
        oom |= Query->Append(Tabname);
        oom |= Query->Append('`');
      } else
        oom |= Query->Append(Tabname);

      oom |= Query->Append(Qrystr + (p - qrystr) + strlen(name));

      if (oom) {
        strcpy(g->Message, "MakeCommand: Out of memory");
        return RC_FX;
      } else
        strlwr(strcpy(qrystr, Query->GetStr()));
694 695 696 697 698 699 700 701

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

  } else
702
    (void)Query->Set(Qrystr);
703 704 705 706 707

  return RC_OK;
  } // end of MakeCommand

#if 0
Alexander Barkov's avatar
Alexander Barkov committed
708 709
/***********************************************************************/
/*  MakeUpdate: make the Update statement use with MySQL connection.   */
710
/*  Limited to remote values and filtering.                            */
Alexander Barkov's avatar
Alexander Barkov committed
711
/***********************************************************************/
712
int TDBMYSQL::MakeUpdate(PGLOBAL g)
Alexander Barkov's avatar
Alexander Barkov committed
713
  {
714
  char *qc, cmd[8], tab[96], end[1024];
Alexander Barkov's avatar
Alexander Barkov committed
715

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

719 720 721
  if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
      sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
    qc = "`";
722 723
  else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
                  && !stricmp(tab, Name))
724 725 726 727 728
    qc = (Quoted) ? "`" : "";
  else {
    strcpy(g->Message, "Cannot use this UPDATE command");
    return RC_FX;
  } // endif sscanf
Alexander Barkov's avatar
Alexander Barkov committed
729

730 731 732 733
  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
734 735 736
  } // end of MakeUpdate

/***********************************************************************/
737 738
/*  MakeDelete: make the Delete statement used with MySQL connection.  */
/*  Limited to remote filtering.                                       */
Alexander Barkov's avatar
Alexander Barkov committed
739
/***********************************************************************/
740
int TDBMYSQL::MakeDelete(PGLOBAL g)
Alexander Barkov's avatar
Alexander Barkov committed
741
  {
742
  char *qc, cmd[8], from[8], tab[96], end[512];
Alexander Barkov's avatar
Alexander Barkov committed
743

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

747 748 749 750 751 752 753 754 755
  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
756

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

760 761
  if (*end)
    strcat(Query, end);
Alexander Barkov's avatar
Alexander Barkov committed
762

763
  return RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
764
  } // end of MakeDelete
765
#endif // 0
Alexander Barkov's avatar
Alexander Barkov committed
766 767

/***********************************************************************/
768
/*  MYSQL Cardinality: returns the number of rows in the table.        */
Alexander Barkov's avatar
Alexander Barkov committed
769
/***********************************************************************/
770 771 772 773
int TDBMYSQL::Cardinality(PGLOBAL g)
{
  if (!g)
    return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
Alexander Barkov's avatar
Alexander Barkov committed
774

775
  if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
776 777 778
    // Info command, we must return the exact table row number
    char   query[96];
    MYSQLC myc;
Alexander Barkov's avatar
Alexander Barkov committed
779

780
    if (myc.Open(g, Host, Database, User, Pwd, Port, csname))
781
      return -1;
Alexander Barkov's avatar
Alexander Barkov committed
782

783
    strcpy(query, "SELECT COUNT(*) FROM ");
Alexander Barkov's avatar
Alexander Barkov committed
784

785 786 787 788 789 790 791
    if (Quoted > 0)
      strcat(strcat(strcat(query, "`"), Tabname), "`");
    else
      strcat(query, Tabname);

    Cardinal = myc.GetTableSize(g, query);
    myc.Close();
792 793
  } else
    Cardinal = 10;    // To make MySQL happy
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810

  return Cardinal;
} // end of Cardinality

/***********************************************************************/
/*  MYSQL GetMaxSize: returns the maximum number of rows in the table. */
/***********************************************************************/
int TDBMYSQL::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
    if (Mode == MODE_DELETE)
      // Return 0 in mode DELETE in case of delete all.
      MaxSize = 0;
    else if (!Cardinality(NULL))
      MaxSize = 10;   // To make MySQL happy
    else if ((MaxSize = Cardinality(g)) < 0)
      MaxSize = 12;   // So we can see an error occured
Alexander Barkov's avatar
Alexander Barkov committed
811 812 813 814 815 816 817 818 819 820 821

    } // 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)
  {
822
  return N + 1;
Alexander Barkov's avatar
Alexander Barkov committed
823 824 825
  } // end of RowNumber

/***********************************************************************/
826
/*  Return 0 in mode UPDATE to tell that the update is done.           */
Alexander Barkov's avatar
Alexander Barkov committed
827 828 829
/***********************************************************************/
int TDBMYSQL::GetProgMax(PGLOBAL g)
  {
830
  return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
Alexander Barkov's avatar
Alexander Barkov committed
831 832 833 834 835 836 837
  } // end of GetProgMax

/***********************************************************************/
/*  MySQL Bind Parameter function.                                     */
/***********************************************************************/
int TDBMYSQL::BindColumns(PGLOBAL g)
  {
838
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
839 840 841 842 843 844 845
  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);
846 847
    } // endif prep
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
848

849
  return RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
850 851 852 853 854 855 856 857 858 859 860 861
  } // 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();
862
    N = -1;
863
    return false;
Alexander Barkov's avatar
Alexander Barkov committed
864 865 866 867 868 869 870 871 872 873
    } // 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()) {
874
    if (Myc.Open(g, Host, Database, User, Pwd, Port, csname))
875
      return true;
Alexander Barkov's avatar
Alexander Barkov committed
876 877 878

    } // endif Connected

879 880 881 882 883 884 885 886
  /*********************************************************************/
  /*  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
887 888 889
  /*********************************************************************/
  /*  Allocate whatever is used for getting results.                   */
  /*********************************************************************/
890 891
  if (Mode == MODE_READ || Mode == MODE_READX) {
    MakeSelect(g, Mode == MODE_READX);
892 893
    m_Rc = (Mode == MODE_READ)
         ? Myc.ExecSQL(g, Query->GetStr()) : RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
894

895 896 897 898 899 900 901 902
#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

903
    if (!m_Rc && Srcdef)
904 905 906
      if (SetColumnRanks(g))
        return true;

Alexander Barkov's avatar
Alexander Barkov committed
907
  } else if (Mode == MODE_INSERT) {
908 909
    if (Srcdef) {
      strcpy(g->Message, "No insert into anonym views");
910
      Myc.Close();
911 912 913
      return true;
      } // endif Srcdef

Alexander Barkov's avatar
Alexander Barkov committed
914
    if (!MakeInsert(g)) {
915
#if defined(MYSQL_PREPARED_STATEMENTS)
916 917
      int n = (Prep) 
            ? Myc.PrepareSQL(g, Query->GetCharValue()) : Nparm;
Alexander Barkov's avatar
Alexander Barkov committed
918 919 920 921 922 923

      if (Nparm != n) {
        if (n >= 0)          // Other errors return negative values
          strcpy(g->Message, MSG(BAD_PARM_COUNT));

      } else
924
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
925 926 927 928 929 930 931 932 933
        m_Rc = BindColumns(g);

      } // endif MakeInsert

    if (m_Rc != RC_FX) {
      char cmd[64];
      int  w;

      sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
934 935
      
      m_Rc = Myc.ExecSQL(g, cmd, &w);   // may fail for some engines
Alexander Barkov's avatar
Alexander Barkov committed
936 937
      } // endif m_Rc

938
  } else
939 940
//  m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
    m_Rc = MakeCommand(g);
Alexander Barkov's avatar
Alexander Barkov committed
941 942 943

  if (m_Rc == RC_FX) {
    Myc.Close();
944
    return true;
Alexander Barkov's avatar
Alexander Barkov committed
945 946
    } // endif rc

947 948
  Use = USE_OPEN;
  return false;
Alexander Barkov's avatar
Alexander Barkov committed
949 950
  } // end of OpenDB

951 952 953 954 955 956 957
/***********************************************************************/
/*  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))
958
      return true;
959

960
  return false;
961 962 963 964 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 997 998 999
  } // 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

1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
/***********************************************************************/
/*  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

1023 1024 1025 1026 1027 1028 1029
/***********************************************************************/
/*  Send an UPDATE or DELETE command to the remote server.             */
/***********************************************************************/
int TDBMYSQL::SendCommand(PGLOBAL g)
  {
  int w;

1030
  if (Myc.ExecSQLcmd(g, Query->GetStr(), &w) == RC_NF) {
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
    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

1055 1056 1057 1058 1059
/***********************************************************************/
/*  Data Base indexed read routine for MYSQL access method.            */
/***********************************************************************/
bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
{
1060 1061
  bool oom;
  int  oldlen = Query->GetLength();
1062

1063 1064
  if (!key || op == OP_NEXT ||
        Mode == MODE_UPDATE || Mode == MODE_DELETE)
1065 1066
    return false;
  else if (op == OP_FIRST) {
1067 1068 1069 1070 1071 1072 1073 1074 1075
    if (To_CondFil) {
      oom = Query->Append(" WHERE ");

      if ((oom |= Query->Append(To_CondFil->Body))) {
        strcpy(g->Message, "Readkey: Out of memory");
        return true;
        } // endif oom

      } // endif To_Condfil
1076 1077 1078 1079 1080

  } else {
    if (Myc.m_Res)
      Myc.FreeResult();

1081 1082
    To_Def->GetHandler()->MakeKeyWhere(g, Query->GetStr(),
                                       op, "`", key, len);
1083

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    if (To_CondFil) {
      oom = Query->Append(" AND (");
      oom |= Query->Append(To_CondFil->Body);

      if ((oom |= Query->Append(')'))) {
        strcpy(g->Message, "Readkey: Out of memory");
        return true;
        } // endif oom

      } // endif To_Condfil
1094 1095 1096

  } // endif's op

1097 1098
  m_Rc = Myc.ExecSQL(g, Query->GetStr());
  Query->Truncate(oldlen);
1099
  return (m_Rc == RC_FX) ? true : false;
1100 1101
} // end of ReadKey

Alexander Barkov's avatar
Alexander Barkov committed
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
/***********************************************************************/
/*  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);

1113 1114 1115
  if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
    return SendCommand(g);

Alexander Barkov's avatar
Alexander Barkov committed
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
  /*********************************************************************/
  /*  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)
  {
1134
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
1135 1136
  if (Prep)
    return Myc.ExecStmt(g);
1137
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
1138 1139 1140 1141

  // Statement was not prepared, we must construct and execute
  // an insert query for each line to insert
  int  rc;
1142
  uint len = Query->GetLength();
1143
  char buf[64];
1144
  bool b, oom = false;
Alexander Barkov's avatar
Alexander Barkov committed
1145 1146 1147

  // Make the Insert command value list
  for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
1148
    if (!colp->GetValue()->IsNull()) {
1149 1150 1151 1152 1153 1154 1155 1156 1157
      if ((b = colp->GetResultType() == TYPE_STRING ||
               colp->GetResultType() == TYPE_DATE))
        oom |= Query->Append('\'');
  
      oom |= Query->Append(colp->GetValue()->GetCharString(buf));
  
      if (b)
        oom |= Query->Append('\'');
  
1158
    } else
1159 1160 1161
      oom |= Query->Append("NULL");
  
    oom |= Query->Append(',');
Alexander Barkov's avatar
Alexander Barkov committed
1162 1163
    } // endfor colp

1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
  if (unlikely(oom)) {
    strcpy(g->Message, "WriteDB: Out of memory");
    rc = RC_FX;
  } else {
    Query->RepLast(')');
    Myc.m_Rows = -1;          // To execute the query
    rc = Myc.ExecSQL(g, Query->GetStr());
    Query->Truncate(len);     // Restore query
  } // endif oom

Alexander Barkov's avatar
Alexander Barkov committed
1174 1175 1176 1177
  return (rc == RC_NF) ? RC_OK : rc;      // RC_NF is Ok
  } // end of WriteDB

/***********************************************************************/
1178
/*  Data Base delete all routine for MYSQL access methods.             */
Alexander Barkov's avatar
Alexander Barkov committed
1179 1180 1181
/***********************************************************************/
int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
  {
1182 1183
  if (irc == RC_FX)
    // Send the DELETE (all) command to the remote table
Olivier Bertrand's avatar
Olivier Bertrand committed
1184
    return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
1185 1186 1187
  else
    return RC_OK;                 // Ignore

Alexander Barkov's avatar
Alexander Barkov committed
1188 1189 1190 1191 1192 1193 1194
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for MySQL access method.                   */
/***********************************************************************/
void TDBMYSQL::CloseDB(PGLOBAL g)
  {
1195 1196 1197 1198 1199 1200 1201 1202 1203
  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
1204
      m_Rc = Myc.ExecSQL(g, cmd, &w);  // May fail for some engines
1205 1206 1207 1208
      } // endif m_Rc

    Myc.Close();
    } // endif Myc
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

  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

1231
  // Set additional MySQL access method information for column.
1232
  Precision = Long = cdp->GetLong();
Alexander Barkov's avatar
Alexander Barkov committed
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
  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

1243 1244 1245 1246 1247 1248
/***********************************************************************/
/*  MYSQLCOL public constructor.                                       */
/***********************************************************************/
MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
        : COLBLK(NULL, tdbp, i)
  {
1249 1250 1251
  const char *chset = get_charset_name(fld->charsetnr);
  char  v = (!strcmp(chset, "binary")) ? 'B' : 0;

1252
  Name = fld->name;
Olivier Bertrand's avatar
Olivier Bertrand committed
1253
  Opt = 0;
1254
  Precision = Long = fld->length;
1255
  Buf_Type = MYSQLtoPLG(fld->type, &v);
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
  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
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
/***********************************************************************/
/*  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

1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
/***********************************************************************/
/*  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
1304 1305 1306 1307 1308 1309 1310
/***********************************************************************/
/*  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);
1311
    return true;
Alexander Barkov's avatar
Alexander Barkov committed
1312 1313 1314 1315 1316 1317 1318 1319
  } 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;

1320
    } else if (Buf_Type == TYPE_DOUBLE)
Alexander Barkov's avatar
Alexander Barkov committed
1321 1322
      // Float values must be written with the correct (column) precision
      // Note: maybe this should be forced by ShowValue instead of this ?
1323
      value->SetPrec(GetScale());
Alexander Barkov's avatar
Alexander Barkov committed
1324 1325 1326 1327 1328 1329 1330

    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()));
1331
      return true;
Alexander Barkov's avatar
Alexander Barkov committed
1332 1333 1334 1335
      } // endif check

 newval:
    if (InitValue(g))         // Allocate the matching value block
1336
      return true;
Alexander Barkov's avatar
Alexander Barkov committed
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346

  } // 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;
1347
  return false;
Alexander Barkov's avatar
Alexander Barkov committed
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
  } // 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) {
1363
    Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
Alexander Barkov's avatar
Alexander Barkov committed
1364 1365 1366 1367
    Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
    Bind->buffer_length = 20;
    Bind->length = &Slen;
  } else {
1368
    Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
Alexander Barkov's avatar
Alexander Barkov committed
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
    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)
  {
1381
  char  *p, *buf, tim[20];
Alexander Barkov's avatar
Alexander Barkov committed
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394
  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
1395
      tdbp->Fetched = true;
Alexander Barkov's avatar
Alexander Barkov committed
1396

1397
  if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
1398
    if (trace > 1)
1399 1400
      htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);

1401
    // TODO: have a true way to differenciate temporal values
1402
    if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
1403 1404 1405 1406 1407
      // This is a TIME value
      p = strcat(strcpy(tim, "1970-01-01 "), buf);
    else
      p = buf;

1408 1409 1410 1411 1412 1413
    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

1414
  } else {
1415 1416 1417
    if (Nullable)
      Value->SetNull(true);

Alexander Barkov's avatar
Alexander Barkov committed
1418
    Value->Reset();              // Null value
1419
  } // endif buf
Alexander Barkov's avatar
Alexander Barkov committed
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431

  } // 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)
1432
    Value->SetValue_pval(To_Val, false);   // Convert the inserted value
Alexander Barkov's avatar
Alexander Barkov committed
1433

1434
#if defined(MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
1435 1436
  if (((PTDBMY)To_Tdb)->Prep) {
    if (Buf_Type == TYPE_DATE) {
1437
      Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
Alexander Barkov's avatar
Alexander Barkov committed
1438 1439 1440 1441 1442
      Slen = strlen((char *)Bind->buffer);
    } else if (IsTypeChar(Buf_Type))
      Slen = strlen(Value->GetCharValue());

    } // endif Prep
1443
#endif   // MYSQL_PREPARED_STATEMENTS
Alexander Barkov's avatar
Alexander Barkov committed
1444 1445

  } // end of WriteColumn
1446

1447 1448 1449
/* ------------------------------------------------------------------- */

/***********************************************************************/
1450
/*  Implementation of the TDBMYEXC class.                              */
1451
/***********************************************************************/
Olivier Bertrand's avatar
Olivier Bertrand committed
1452
TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
{
  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
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

// 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.       */
/***********************************************************************/
1509
PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
1510
  {
1511
  PCMD xcmd = NULL;
1512

1513
  if (To_CondFil) {
1514
    if (Cmdcol) {
1515 1516 1517
      if (!stricmp(Cmdcol, To_CondFil->Body) &&
          (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
        xcmd = To_CondFil->Cmds;
1518 1519 1520 1521 1522 1523 1524 1525 1526
      } 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
1527
    xcmd = new(g) CMD(g, Srcdef);
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537

  return xcmd;
  } // end of MakeCMD

/***********************************************************************/
/*  EXC GetMaxSize: returns the maximum number of rows in the table.   */
/***********************************************************************/
int TDBMYEXC::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
1538
    MaxSize = 10;                 // a guess
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
    } // 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

1567
  if (Mode != MODE_READ && Mode != MODE_READX) {
1568 1569 1570 1571 1572 1573 1574
    strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
    return true;
    } // endif Mode

  /*********************************************************************/
  /*  Get the command to execute.                                      */
  /*********************************************************************/
1575
  if (!(Cmdlist = MakeCMD(g))) {
1576 1577
    Myc.Close();
    return true;
1578
    } // endif Cmdlist
1579 1580 1581 1582 1583 1584 1585 1586 1587

  return false;
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for MYSQL access method.                    */
/***********************************************************************/
int TDBMYEXC::ReadDB(PGLOBAL g)
  {
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603
  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;
Olivier Bertrand's avatar
Olivier Bertrand committed
1604

1605
    do {
1606 1607 1608 1609
      if (Query)
        Query->Set(Cmdlist->Cmd);
      else
        Query = new(g) STRING(g, 0, Cmdlist->Cmd);
1610

1611
      switch (rc = Myc.ExecSQLcmd(g, Query->GetStr(), &Warnings)) {
1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626
        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
Olivier Bertrand's avatar
Olivier Bertrand committed
1627

1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
      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;

1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688
  } // 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;

1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699
  if (tdbp->Isw) {
    char *buf = NULL;

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

  } else
    switch (Flag) {
1700 1701 1702 1703 1704
      case  0: Value->SetValue_psz(tdbp->Query->GetStr()); 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;
1705
      } // endswitch Flag
1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716

  } // end of ReadColumn

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

1717 1718 1719 1720 1721 1722 1723
/* ---------------------------TDBMCL class --------------------------- */

/***********************************************************************/
/*  TDBMCL class constructor.                                          */
/***********************************************************************/
TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
  {
Olivier Bertrand's avatar
Olivier Bertrand committed
1724 1725 1726 1727 1728
  Host = tdp->Hostname;
  Db   = tdp->Database;
  Tab  = tdp->Tabname;
  User = tdp->Username;
  Pwd  = tdp->Password;
1729 1730 1731 1732 1733 1734 1735 1736
  Port = tdp->Portnumber;
  } // end of TDBMCL constructor

/***********************************************************************/
/*  GetResult: Get the list the MYSQL table columns.                   */
/***********************************************************************/
PQRYRES TDBMCL::GetResult(PGLOBAL g)
  {
1737
  return MyColumns(g, NULL, Host, Db, User, Pwd, Tab, NULL, Port, false);
Olivier Bertrand's avatar
Olivier Bertrand committed
1738
  } // end of GetResult