myconn.cpp 34.5 KB
Newer Older
Alexander Barkov's avatar
Alexander Barkov committed
1 2 3
/************** MyConn C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: MYCONN                                                 */
/* -------------                                                        */
Olivier Bertrand's avatar
Olivier Bertrand committed
4
/*  Version 1.8                                                         */
Alexander Barkov's avatar
Alexander Barkov committed
5 6 7
/*                                                                      */
/* COPYRIGHT:                                                           */
/* ----------                                                           */
8
/*  (C) Copyright to the author Olivier BERTRAND          2007-2015     */
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 connection to MySQL.                                   */
/*  It can optionally use the embedded MySQL library.                   */
/*                                                                      */
/* WHAT YOU NEED TO COMPILE THIS PROGRAM:                               */
/* --------------------------------------                               */
/*                                                                      */
/*  REQUIRED FILES:                                                     */
/*  ---------------                                                     */
/*    MYCONN.CPP     - Source code                                      */
/*    MYCONN.H       - MYCONN class declaration file                    */
/*    GLOBAL.H       - Global declaration file                          */
/*                                                                      */
/*  REQUIRED LIBRARIES:                                                 */
/*  -------------------                                                 */
/*    Large model C library                                             */
/*                                                                      */
/*  REQUIRED PROGRAMS:                                                  */
/*  ------------------                                                  */
/*    IBM, Borland, GNU or Microsoft C++ Compiler and Linker            */
/*                                                                      */
/************************************************************************/
#include "my_global.h"
34 35 36 37
#if !defined(MYSQL_PREPARED_STATEMENTS)
#include "my_sys.h"
#include "mysqld_error.h"
#endif   // !MYSQL_PREPARED_STATEMENTS
38
#if defined(__WIN__)
Alexander Barkov's avatar
Alexander Barkov committed
39
//#include <windows.h>
40
#else   // !__WIN__
Alexander Barkov's avatar
Alexander Barkov committed
41
#include "osutil.h"
42
#endif  // !__WIN__
Alexander Barkov's avatar
Alexander Barkov committed
43 44 45

#include "global.h"
#include "plgdbsem.h"
46 47
#include "plgcnx.h"                       // For DB types
#include "resource.h"
48
//#include "value.h"
49 50
//#include "valblk.h"
#include "xobject.h"
Alexander Barkov's avatar
Alexander Barkov committed
51 52 53
#define  DLL_EXPORT            // Items are exported from this DLL
#include "myconn.h"

54 55
//extern "C" int   zconv;
int GetConvSize(void);
Olivier Bertrand's avatar
Olivier Bertrand committed
56 57
extern MYSQL_PLUGIN_IMPORT uint  mysqld_port;
extern MYSQL_PLUGIN_IMPORT char *mysqld_unix_port;
58

59 60
DllExport void PushWarning(PGLOBAL, THD*, int level = 1);

61 62 63 64 65 66
// Returns the current used port
uint GetDefaultPort(void)
{
  return mysqld_port;
} // end of GetDefaultPort

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 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
#if !defined(MYSQL_PREPARED_STATEMENTS)
/**************************************************************************
  Alloc struct for use with unbuffered reads. Data is fetched by domand
  when calling to mysql_fetch_row.
  mysql_data_seek is a noop.

  No other queries may be specified with the same MYSQL handle.
  There shouldn't be much processing per row because mysql server shouldn't
  have to wait for the client (and will not wait more than 30 sec/packet).
  NOTE: copied from client.c cli_use_result
**************************************************************************/
static MYSQL_RES *connect_use_result(MYSQL *mysql)
{
  MYSQL_RES *result;
  DBUG_ENTER("connect_use_result");

  if (!mysql->fields)
    DBUG_RETURN(NULL);

  if (mysql->status != MYSQL_STATUS_GET_RESULT) {
    my_message(ER_UNKNOWN_ERROR, "Command out of sync", MYF(0));
    DBUG_RETURN(NULL);
    } // endif status

  if (!(result = (MYSQL_RES*) my_malloc(sizeof(*result) +
				          sizeof(ulong) * mysql->field_count,
				          MYF(MY_WME | MY_ZEROFILL))))
    DBUG_RETURN(NULL);

  result->lengths = (ulong*)(result+1);
  result->methods = mysql->methods;

  /* Ptrs: to one row */
  if (!(result->row = (MYSQL_ROW)my_malloc(sizeof(result->row[0]) *
                                (mysql->field_count+1), MYF(MY_WME)))) {
    my_free(result);
    DBUG_RETURN(NULL);
    }  // endif row

  result->fields =	mysql->fields;
  result->field_alloc =	mysql->field_alloc;
  result->field_count =	mysql->field_count;
  result->current_field = 0;
  result->handle =	mysql;
  result->current_row =	0;
  mysql->fields = 0;			/* fields is now in result */
  clear_alloc_root(&mysql->field_alloc);
  mysql->status = MYSQL_STATUS_USE_RESULT;
  mysql->unbuffered_fetch_owner = &result->unbuffered_fetch_cancelled;
  DBUG_RETURN(result);			/* Data is ready to be fetched */
} // end of connect_use_result
#endif   // !MYSQL_PREPARED_STATEMENTS

120 121
/************************************************************************/
/*  MyColumns: constructs the result blocks containing all columns      */
122 123
/*  of a MySQL table or view.                                           */
/*  info = TRUE to get catalog column informations.                     */
124
/************************************************************************/
125
PQRYRES MyColumns(PGLOBAL g, THD *thd, const char *host, const char *db,
126 127
                  const char *user, const char *pwd,
                  const char *table, const char *colpat,
128
                  int port, bool info)
129
  {
130 131 132 133 134 135 136 137 138
  int  buftyp[] = {TYPE_STRING, TYPE_SHORT,  TYPE_STRING, TYPE_INT,
                   TYPE_STRING, TYPE_SHORT,  TYPE_SHORT,  TYPE_SHORT,
                   TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING,
                   TYPE_STRING};
  XFLD fldtyp[] = {FLD_NAME, FLD_TYPE,  FLD_TYPENAME, FLD_PREC,
                   FLD_KEY,  FLD_SCALE, FLD_RADIX,    FLD_NULL,
                   FLD_REM,  FLD_NO,    FLD_DEFAULT,  FLD_EXTRA,
                   FLD_CHARSET};
  unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0};
139
  char   *fld, *colname, *chset, *fmt, v, buf[128], uns[16], zero[16];
Olivier Bertrand's avatar
Olivier Bertrand committed
140
  int     i, n, nf, ncol = sizeof(buftyp) / sizeof(int);
141
  int     len, type, prec, rc, k = 0;
142 143 144 145
  PQRYRES qrp;
  PCOLRES crp;
  MYSQLC  myc;

146 147 148
  if (!port)
    port = mysqld_port;

149 150 151 152 153 154
  if (!info) {
    /********************************************************************/
    /*  Open the connection with the MySQL server.                      */
    /********************************************************************/
    if (myc.Open(g, host, db, user, pwd, port))
      return NULL;
155

156 157 158
    /********************************************************************/
    /*  Do an evaluation of the result size.                            */
    /********************************************************************/
159 160
    STRING cmd(g, 64, "SHOW FULL COLUMNS FROM ");
    bool   b = cmd.Append((PSZ)table);
161

162 163 164 165 166 167 168 169 170 171 172 173
    b |= cmd.Append(" FROM ");
    b |= cmd.Append((PSZ)(db ? db : PlgGetUser(g)->DBName));

    if (colpat) {
      b |= cmd.Append(" LIKE ");
      b |= cmd.Append((PSZ)colpat);
      } // endif colpat

    if (b) {
      strcpy(g->Message, "Out of memory");
      return NULL;
      } // endif b
174

175
    if (trace)
176
      htrc("MyColumns: cmd='%s'\n", cmd.GetStr());
177

178
    if ((n = myc.GetResultSize(g, cmd.GetStr())) < 0) {
179 180 181
      myc.Close();
      return NULL;
      } // endif n
182

183
    /********************************************************************/
184
    /*  Get the size of the name and default columns.                   */
185 186
    /********************************************************************/
    length[0] = myc.GetFieldLength(0);
187
//  length[10] = myc.GetFieldLength(5);
188 189 190 191
  } else {
    n = 0;
    length[0] = 128;
  } // endif info
192 193 194 195

  /**********************************************************************/
  /*  Allocate the structures used to refer to the result set.          */
  /**********************************************************************/
196 197 198
  if (!(qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
                             buftyp, fldtyp, length, false, true)))
    return NULL;
199 200 201 202

  // Some columns must be renamed
  for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
    switch (++i) {
203
      case  2: crp->Nulls = (char*)PlugSubAlloc(g, NULL, n); break;
204 205 206
      case  4: crp->Name = "Length";    break;
      case  5: crp->Name = "Key";       break;
      case 10: crp->Name = "Date_fmt";  break;
207 208 209
      case 11: crp->Name = "Default";   break;
      case 12: crp->Name = "Extra";     break;
      case 13: crp->Name = "Collation"; break;
210 211 212 213
      } // endswitch i

  if (info)
    return qrp;
214 215 216 217

  /**********************************************************************/
  /*  Now get the results into blocks.                                  */
  /**********************************************************************/
218 219
  for (i = 0; i < n; /*i++*/) {
    if ((rc = myc.Fetch(g, -1)) == RC_FX) {
220
      myc.Close();
221
      return NULL;
222
    } else if (rc == RC_EF)
223 224 225
      break;

    // Get column name
226
    colname = myc.GetCharField(0);
227
    crp = qrp->Colresp;                    // Column_Name
228
    crp->Kdata->SetValue(colname, i);
229

230
    // Get type, type name, precision, unsigned and zerofill
231
    chset = myc.GetCharField(2);
232 233
    fld = myc.GetCharField(1);
    prec = 0;
234
    len = 0;
235
    v = (chset && !strcmp(chset, "binary")) ? 'B' : 0;
236 237 238
    *uns = 0;
    *zero = 0;

239
    switch ((nf = sscanf(fld, "%[^(](%d,%d", buf, &len, &prec))) {
240
      case 3:
241
        nf = sscanf(fld, "%[^(](%d,%d) %s %s", buf, &len, &prec, uns, zero);
242 243
        break;
      case 2:
244
        nf = sscanf(fld, "%[^(](%d) %s %s", buf, &len, uns, zero) + 1;
245 246
        break;
      case 1:
247
        nf = sscanf(fld, "%s %s %s", buf, uns, zero) + 2;
248 249 250 251 252 253
        break;
      default:
        sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld);
        myc.Close();
        return NULL;
      } // endswitch nf
254

255
    if ((type = MYSQLtoPLG(buf, &v)) == TYPE_ERROR) {
256 257 258
      if (v == 'K') {
        // Skip this column
        sprintf(g->Message, "Column %s skipped (unsupported type %s)",
259
                colname, buf);
260 261 262 263
        PushWarning(g, thd);
        continue;
        } // endif v

264
      sprintf(g->Message, "Column %s unsupported type %s", colname, buf);
265
      myc.Close();
266
      return NULL;
267 268
    } else if (type == TYPE_STRING) {
      if (v == 'X') {
269
        len = GetConvSize();
270 271 272 273 274
        sprintf(g->Message, "Column %s converted to varchar(%d)",
                colname, len);
        PushWarning(g, thd);
        v = 'V';
      } else
275
        len = MY_MIN(len, 4096);
276 277

    } // endif type
278

279
    qrp->Nblin++;
280
    crp = crp->Next;                       // Data_Type
281
    crp->Kdata->SetValue(type, i);
282 283 284 285 286 287 288

    switch (nf) {
      case 5:  crp->Nulls[i] = 'Z'; break;
      case 4:  crp->Nulls[i] = 'U'; break;
      default: crp->Nulls[i] = v;   break;
      } // endswitch nf

289
    crp = crp->Next;                       // Type_Name
290
    crp->Kdata->SetValue(buf, i);
291

292
    if (type == TYPE_DATE) {
293
      // When creating tables we do need info about date columns
294
      fmt = MyDateFmt(buf);
295 296 297 298
      len = strlen(fmt);
    } else
      fmt = NULL;

299
    crp = crp->Next;                       // Precision
300 301
    crp->Kdata->SetValue(len, i);

302
    crp = crp->Next;                       // key (was Length)
303 304
    fld = myc.GetCharField(4);
    crp->Kdata->SetValue(fld, i);
305

306
    crp = crp->Next;                       // Scale
307 308
    crp->Kdata->SetValue(prec, i);

309 310
    crp = crp->Next;                       // Radix
    crp->Kdata->SetValue(0, i);
311

312 313 314
    crp = crp->Next;                       // Nullable
    fld = myc.GetCharField(3);
    crp->Kdata->SetValue((toupper(*fld) == 'Y') ? 1 : 0, i);
315

316 317 318
    crp = crp->Next;                       // Remark
    fld = myc.GetCharField(8);
    crp->Kdata->SetValue(fld, i);
319

320
    crp = crp->Next;                       // Date format
321 322
//  crp->Kdata->SetValue((fmt) ? fmt : (char*) "", i);
    crp->Kdata->SetValue(fmt, i);
323

324 325 326 327
    crp = crp->Next;                       // New (default)
    fld = myc.GetCharField(5);
    crp->Kdata->SetValue(fld, i);

328 329 330 331
    crp = crp->Next;                       // New (extra)
    fld = myc.GetCharField(6);
    crp->Kdata->SetValue(fld, i);

332
    crp = crp->Next;                       // New (charset)
333
    fld = chset;
334
    crp->Kdata->SetValue(fld, i);
335 336

    i++;                                   // Can be skipped
337 338
    } // endfor i

339
#if 0
340 341 342 343 344 345 346 347 348
  if (k > 1) {
    // Multicolumn primary key
    PVBLK vbp = qrp->Colresp->Next->Next->Next->Next->Kdata;

    for (i = 0; i < n; i++)
      if (vbp->GetIntValue(i))
        vbp->SetValue(k, i);

    } // endif k
349
#endif // 0
350 351 352 353 354 355 356 357 358 359 360 361

  /**********************************************************************/
  /*  Close MySQL connection.                                           */
  /**********************************************************************/
  myc.Close();

  /**********************************************************************/
  /*  Return the result pointer for use by GetData routines.            */
  /**********************************************************************/
  return qrp;
  } // end of MyColumns

362 363 364 365 366 367 368 369
/************************************************************************/
/*  SrcColumns: constructs the result blocks containing all columns     */
/*  resulting from an SQL source definition query execution.            */
/************************************************************************/
PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db,
                   const char *user, const char *pwd,
                   const char *srcdef, int port)
  {
370
  char   *query;
371 372 373 374 375 376 377
  int     w;
  MYSQLC  myc;
  PQRYRES qrp = NULL;

  if (!port)
    port = mysqld_port;

Olivier Bertrand's avatar
Olivier Bertrand committed
378
  if (!strnicmp(srcdef, "select ", 7)) {
379 380 381 382
    query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 9);
    strcat(strcpy(query, srcdef), " LIMIT 0");
  } else
    query = (char *)srcdef;
383

384 385 386 387 388
  // Open a MySQL connection for this table
  if (myc.Open(g, host, db, user, pwd, port))
    return NULL;

  // Send the source command to MySQL
389
  if (myc.ExecSQL(g, query, &w) == RC_OK)
390
    qrp = myc.GetResult(g, true);
391 392 393 394 395

  myc.Close();
  return qrp;
  } // end of SrcColumns

Alexander Barkov's avatar
Alexander Barkov committed
396 397 398 399 400 401 402 403 404 405 406 407 408 409
/* -------------------------- Class MYSQLC --------------------------- */

/***********************************************************************/
/*  Implementation of the MYSQLC class.                                */
/***********************************************************************/
MYSQLC::MYSQLC(void)
  {
  m_DB = NULL;
  m_Stmt = NULL;
  m_Res = NULL;
  m_Rows = -1;
  m_Row = NULL;
  m_Fields = -1;
  N = 0;
410
  m_Use = false;
Alexander Barkov's avatar
Alexander Barkov committed
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
  } // end of MYSQLC constructor

/***********************************************************************/
/*  Get the number of lines of the result set.                         */
/*  Currently we send the Select command and return m_Rows             */
/*  Perhaps should we use Select count(*) ... (?????)                  */
/*  No because here we execute only one query instead of two           */
/*  (the select count(*) plus the normal query)                        */
/***********************************************************************/
int MYSQLC::GetResultSize(PGLOBAL g, PSZ sql)
  {
  if (m_Rows < 0)
    if (ExecSQL(g, sql) != RC_OK)
      return -1;

  return m_Rows;
  } // end of GetResultSize

/***********************************************************************/
/*  Open a MySQL (remote) connection.                                  */
/***********************************************************************/
432 433
int MYSQLC::Open(PGLOBAL g, const char *host, const char *db,
                            const char *user, const char *pwd,
434
                            int pt, const char *csname)
Alexander Barkov's avatar
Alexander Barkov committed
435
  {
Olivier Bertrand's avatar
Olivier Bertrand committed
436
  const char *pipe = NULL;
437 438
  uint        cto = 6000, nrt = 12000;
  my_bool     my_true= 1;
439

Alexander Barkov's avatar
Alexander Barkov committed
440 441 442 443 444 445 446
  m_DB = mysql_init(NULL);

  if (!m_DB) {
    strcpy(g->Message, "mysql_init failed: no memory");
    return RC_FX;
    } // endif m_DB

447 448
  // Removed to do like FEDERATED do
//mysql_options(m_DB, MYSQL_READ_DEFAULT_GROUP, "client-mariadb");
449
  mysql_options(m_DB, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL);
Olivier Bertrand's avatar
Olivier Bertrand committed
450 451
  mysql_options(m_DB, MYSQL_OPT_CONNECT_TIMEOUT, &cto);
  mysql_options(m_DB, MYSQL_OPT_READ_TIMEOUT, &nrt);
452
//mysql_options(m_DB, MYSQL_OPT_WRITE_TIMEOUT, ...);
Alexander Barkov's avatar
Alexander Barkov committed
453

454
#if defined(__WIN__)
Olivier Bertrand's avatar
Olivier Bertrand committed
455 456 457 458
  if (!strcmp(host, ".")) {
    mysql_options(m_DB, MYSQL_OPT_NAMED_PIPE, NULL);
    pipe = mysqld_unix_port;
    } // endif host
459
#else   // !__WIN__
Olivier Bertrand's avatar
Olivier Bertrand committed
460 461
  if (!strcmp(host, "localhost"))
    pipe = mysqld_unix_port;
462
#endif  // !__WIN__
Olivier Bertrand's avatar
Olivier Bertrand committed
463

Alexander Barkov's avatar
Alexander Barkov committed
464 465 466 467 468 469 470 471 472 473 474
#if 0
  if (pwd && !strcmp(pwd, "*")) {
    if (GetPromptAnswer(g, "*Enter password:")) {
      m_DB = NULL;
      return RC_FX;
    } else
      pwd = g->Message;

    } // endif pwd
#endif // 0

475 476 477 478 479 480 481 482 483 484 485 486
/***********************************************************************/
/*	BUG# 17044 Federated Storage Engine is not UTF8 clean              */
/*	Add set names to whatever charset the table is at open of table    */
/*  this sets the csname like 'set names utf8'.                        */
/***********************************************************************/
  if (csname)
    mysql_options(m_DB, MYSQL_SET_CHARSET_NAME, csname);

  // Don't know what this one do but FEDERATED does it
  mysql_options(m_DB, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY,
                  (char*)&my_true);

Olivier Bertrand's avatar
Olivier Bertrand committed
487
  if (!mysql_real_connect(m_DB, host, user, pwd, db, pt, pipe, CLIENT_MULTI_RESULTS)) {
Alexander Barkov's avatar
Alexander Barkov committed
488 489 490 491 492 493
#if defined(_DEBUG)
    sprintf(g->Message, "mysql_real_connect failed: (%d) %s",
                        mysql_errno(m_DB), mysql_error(m_DB));
#else   // !_DEBUG
    sprintf(g->Message, "(%d) %s", mysql_errno(m_DB), mysql_error(m_DB));
#endif  // !_DEBUG
494
    mysql_close(m_DB);
Alexander Barkov's avatar
Alexander Barkov committed
495 496 497 498 499 500 501 502 503 504 505 506
    m_DB = NULL;
    return RC_FX;
    } // endif mysql_real_connect

  return RC_OK;
  } // end of Open

/***********************************************************************/
/*  Returns true if the connection is still alive.                     */
/***********************************************************************/
bool MYSQLC::Connected(void)
  {
507
//int rc;
Alexander Barkov's avatar
Alexander Barkov committed
508 509 510

  if (!m_DB)
    return FALSE;
511 512
//else if ((rc = mysql_ping(m_DB)) == CR_SERVER_GONE_ERROR)
//  return FALSE;
Alexander Barkov's avatar
Alexander Barkov committed
513 514 515 516 517
  else
    return TRUE;

  } // end of Connected

518
#if 0                         // Not used
Alexander Barkov's avatar
Alexander Barkov committed
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
/***********************************************************************/
/*  Returns the thread ID of the current MySQL connection.             */
/***********************************************************************/
ulong MYSQLC::GetThreadID(void)
  {
  return (m_DB) ? mysql_thread_id(m_DB) : 0;
  } // end of GetThreadID

/***********************************************************************/
/*  Returns a string that represents the server version number.        */
/***********************************************************************/
const char *MYSQLC::ServerInfo(void)
  {
  return (m_DB) ? mysql_get_server_info(m_DB) : NULL;
  } // end of ServerInfo

/***********************************************************************/
536 537
/*  Returns the version number of the server as a number that          */
/*  represents the MySQL server version in this format:                */
Alexander Barkov's avatar
Alexander Barkov committed
538 539 540 541 542 543
/*  major_version*10000 + minor_version *100 + sub_version             */
/***********************************************************************/
ulong MYSQLC::ServerVersion(void)
  {
  return (m_DB) ? mysql_get_server_version(m_DB) : 0;
  } // end of ServerVersion
544
#endif // 0
Alexander Barkov's avatar
Alexander Barkov committed
545 546 547 548 549 550 551 552

/**************************************************************************/
/*  KillQuery: Send MySQL a Kill Query command.                           */
/**************************************************************************/
int MYSQLC::KillQuery(ulong id)
  {
  char kill[20];

553
  sprintf(kill, "KILL QUERY %u", (unsigned int) id);
554 555
//return (m_DB) ? mysql_query(m_DB, kill) : 1;
  return (m_DB) ? mysql_real_query(m_DB, kill, strlen(kill)) : 1;
Alexander Barkov's avatar
Alexander Barkov committed
556 557
  } // end of KillQuery

558
#if defined (MYSQL_PREPARED_STATEMENTS)
Alexander Barkov's avatar
Alexander Barkov committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 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 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
/***********************************************************************/
/*  Prepare the SQL statement used to insert into a MySQL table.       */
/***********************************************************************/
int MYSQLC::PrepareSQL(PGLOBAL g, const char *stmt)
  {
  if (!m_DB) {
    strcpy(g->Message, "MySQL not connected");
    return -4;
  } else if (m_Stmt)
    return -1;              // should not append

#if defined(ALPHA)
  if (!(m_Stmt = mysql_prepare(m_DB, stmt, strlen(stmt)))) {

    sprintf(g->Message, "mysql_prepare failed: %s [%s]",
                         mysql_error(m_DB), stmt);
    return -1;
    } // endif m_Stmt

  // Return the parameter count from the statement
  return mysql_param_count(m_Stmt);
#else   // !ALPHA
  if (!(m_Stmt = mysql_stmt_init(m_DB))) {
    strcpy(g->Message, "mysql_stmt_init(), out of memory");
    return -2;
    } // endif m_Stmt

  if (mysql_stmt_prepare(m_Stmt, stmt, strlen(stmt))) {
    sprintf(g->Message, "mysql_stmt_prepare() failed: (%d) %s",
            mysql_stmt_errno(m_Stmt), mysql_stmt_error(m_Stmt));
    return -3;
    } // endif prepare

  // Return the parameter count from the statement
  return mysql_stmt_param_count(m_Stmt);
#endif   // !ALPHA
  } // end of PrepareSQL

/***********************************************************************/
/*  Bind the parameter buffers.                                        */
/***********************************************************************/
int MYSQLC::BindParams(PGLOBAL g, MYSQL_BIND *bind)
  {
  if (!m_DB) {
    strcpy(g->Message, "MySQL not connected");
    return RC_FX;
  } else
    assert(m_Stmt);

#if defined(ALPHA)
  if (mysql_bind_param(m_Stmt, bind)) {
    sprintf(g->Message, "mysql_bind_param() failed: %s",
                        mysql_stmt_error(m_Stmt));
#else   // !ALPHA
  if (mysql_stmt_bind_param(m_Stmt, bind)) {
    sprintf(g->Message, "mysql_stmt_bind_param() failed: %s",
                        mysql_stmt_error(m_Stmt));
#endif  // !ALPHA
    return RC_FX;
    } // endif bind

  return RC_OK;

/***********************************************************************/
/*  Execute a prepared statement.                                      */
/***********************************************************************/
int MYSQLC::ExecStmt(PGLOBAL g)
  {
  if (!m_DB) {
    strcpy(g->Message, "MySQL not connected");
    return RC_FX;
    } // endif m_DB

#if defined(ALPHA)
  if (mysql_execute(m_Stmt)) {
    sprintf(g->Message, "mysql_execute() failed: %s",
                        mysql_stmt_error(m_Stmt));
    return RC_FX;
    } // endif execute
#else   // !ALPHA
  if (mysql_stmt_execute(m_Stmt)) {
    sprintf(g->Message, "mysql_stmt_execute() failed: %s",
                        mysql_stmt_error(m_Stmt));
    return RC_FX;
    } // endif execute
#endif  // !ALPHA

  // Check the total number of affected rows
  if (mysql_stmt_affected_rows(m_Stmt) != 1) {
    sprintf(g->Message, "Invalid affected rows by MySQL");
    return RC_FX;
    } // endif affected_rows

  return RC_OK;
  } // end of ExecStmt
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
#endif   // MYSQL_PREPARED_STATEMENTS

/***********************************************************************/
/*  Exec the Select SQL command and get back the result size in rows.  */
/***********************************************************************/
int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w)
  {
  int rc = RC_OK;

  if (!m_DB) {
    strcpy(g->Message, "MySQL not connected");
    return RC_FX;
    } // endif m_DB

  if (w)
    *w = 0;

  if (m_Rows >= 0)
    return RC_OK;                  // Already done

//if (mysql_query(m_DB, query) != 0) {
  if (mysql_real_query(m_DB, query, strlen(query))) {
    char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));

    sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
                                 mysql_error(m_DB), query);
    strncpy(g->Message, msg, sizeof(g->Message) - 1);
    g->Message[sizeof(g->Message) - 1] = 0;
    rc = RC_FX;
//} else if (mysql_field_count(m_DB) > 0) {
  } else if (m_DB->field_count > 0) {
685 686 687 688 689 690 691 692 693 694
    if (m_Use)
#if defined(MYSQL_PREPARED_STATEMENTS)
      m_Res = mysql_use_result(m_DB);
#else   // !MYSQL_PREPARED_STATEMENTS)
      m_Res = connect_use_result(m_DB);
#endif  // !MYSQL_PREPARED_STATEMENTS
    else
      m_Res = mysql_store_result(m_DB);

    if (!m_Res) {
695 696 697 698 699 700 701 702
      char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));

      sprintf(msg, "mysql_store_result failed: %s", mysql_error(m_DB));
      strncpy(g->Message, msg, sizeof(g->Message) - 1);
      g->Message[sizeof(g->Message) - 1] = 0;
      rc = RC_FX;
    } else {
      m_Fields = mysql_num_fields(m_Res);
703
      m_Rows = (!m_Use) ? (int)mysql_num_rows(m_Res) : 0;
704 705 706 707 708 709 710 711 712
    } // endif m_Res

  } else {
//  m_Rows = (int)mysql_affected_rows(m_DB);
    m_Rows = (int)m_DB->affected_rows;
    sprintf(g->Message, "Affected rows: %d\n", m_Rows);
    rc = RC_NF;
  } // endif field count

713 714 715
  if (w)
//  *w = mysql_warning_count(m_DB);
    *w = m_DB->warning_count;
716 717 718 719

  return rc;
  } // end of ExecSQL

720 721 722
/***********************************************************************/
/*  Get table size by executing "select count(*) from table_name".     */
/***********************************************************************/
723
int MYSQLC::GetTableSize(PGLOBAL g __attribute__((unused)), PSZ query)
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
  {
  if (mysql_real_query(m_DB, query, strlen(query))) {
#if defined(_DEBUG)
    char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));

    sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
                                 mysql_error(m_DB), query);
    strncpy(g->Message, msg, sizeof(g->Message) - 1);
    g->Message[sizeof(g->Message) - 1] = 0;
#endif   // _DEBUG
    return -2;
    } // endif mysql_real_query

  if (!(m_Res = mysql_store_result(m_DB)))
    return -3;

  // Get the resulting count value
  m_Rows = (int)mysql_num_rows(m_Res);     // Should be 1

  if (m_Rows && (m_Row = mysql_fetch_row(m_Res)))
    return atoi(*m_Row);

  return -4;
  } // end of GetTableSize

749 750 751 752 753
/***********************************************************************/
/*  Move to a specific row and column                                  */
/***********************************************************************/
void MYSQLC::DataSeek(my_ulonglong row)
  {
754
  MYSQL_ROWS *tmp = 0;
755 756 757 758 759 760 761 762
//DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row));

  if (m_Res->data)
    for (tmp = m_Res->data->data; row-- && tmp; tmp = tmp->next) ;

  m_Res->current_row = 0;
  m_Res->data_cursor = tmp;
  } // end of DataSeek
Alexander Barkov's avatar
Alexander Barkov committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781

/***********************************************************************/
/*  Fetch one result line from the query result set.                   */
/***********************************************************************/
int MYSQLC::Fetch(PGLOBAL g, int pos)
  {
  if (!m_DB) {
    strcpy(g->Message, "MySQL not connected");
    return RC_FX;
    } // endif m_DB

  if (!m_Res) {
    // Result set was not initialized
    strcpy(g->Message, MSG(FETCH_NO_RES));
    return RC_FX;
  } else
    N++;

  if (pos >= 0)
782 783
//  mysql_data_seek(m_Res, (my_ulonglong)pos);
    DataSeek((my_ulonglong)pos);
Alexander Barkov's avatar
Alexander Barkov committed
784 785 786 787 788 789 790 791 792 793 794 795

  m_Row = mysql_fetch_row(m_Res);
  return (m_Row) ? RC_OK : RC_EF;
  } // end of Fetch

/***********************************************************************/
/*  Get one field of the current row.                                  */
/***********************************************************************/
char *MYSQLC::GetCharField(int i)
  {
  if (m_Res && m_Row) {
#if defined(_DEBUG)
796
//  MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i);
Alexander Barkov's avatar
Alexander Barkov committed
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
#endif   // _DEBUG
    MYSQL_ROW row = m_Row + i;

    return (row) ? (char*)*row : (char*)"<null>";
  } else
    return NULL;

  } // end of GetCharField

/***********************************************************************/
/*  Get the max length of the field.                                   */
/***********************************************************************/
int MYSQLC::GetFieldLength(int i)
  {
  if (m_Res) {
812 813 814
//  MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i);
//  return fld->max_length;
    return (m_Res)->fields[i].max_length;
Alexander Barkov's avatar
Alexander Barkov committed
815 816 817 818 819 820
  } else
    return 0;

  } // end of GetFieldLength

/***********************************************************************/
821 822 823 824 825 826 827 828 829 830
/*  Return next field of the query results.                            */
/***********************************************************************/
MYSQL_FIELD *MYSQLC::GetNextField(void)
  {
  return (m_Res->current_field >= m_Res->field_count) ? NULL
       : &m_Res->fields[m_Res->current_field++];
  } // end of GetNextField

/***********************************************************************/
/*  Make a CONNECT result structure from the MySQL result.             */
Alexander Barkov's avatar
Alexander Barkov committed
831 832 833
/***********************************************************************/
PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb)
  {
834
  char        *fmt, v;
Alexander Barkov's avatar
Alexander Barkov committed
835
  int          n;
836
  bool         uns;
Alexander Barkov's avatar
Alexander Barkov committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
  PCOLRES     *pcrp, crp;
  PQRYRES      qrp;
  MYSQL_FIELD *fld;
  MYSQL_ROW    row;

  if (!m_Res || !m_Fields) {
    sprintf(g->Message, "%s result", (m_Res) ? "Void" : "No");
    return NULL;
    } // endif m_Res

  /*********************************************************************/
  /*  Put the result in storage for future retrieval.                  */
  /*********************************************************************/
  qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
  pcrp = &qrp->Colresp;
  qrp->Continued = FALSE;
  qrp->Truncated = FALSE;
  qrp->Info = FALSE;
  qrp->Suball = TRUE;
  qrp->BadLines = 0;
  qrp->Maxsize = m_Rows;
  qrp->Maxres = m_Rows;
  qrp->Nbcol = 0;
  qrp->Nblin = 0;
  qrp->Cursor = 0;

863 864 865
//for (fld = mysql_fetch_field(m_Res); fld;
//     fld = mysql_fetch_field(m_Res)) {
  for (fld = GetNextField(); fld; fld = GetNextField()) {
Alexander Barkov's avatar
Alexander Barkov committed
866 867 868
    *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
    crp = *pcrp;
    pcrp = &crp->Next;
869
    memset(crp, 0, sizeof(COLRES));
Alexander Barkov's avatar
Alexander Barkov committed
870 871 872 873 874
    crp->Ncol = ++qrp->Nbcol;

    crp->Name = (char*)PlugSubAlloc(g, NULL, fld->name_length + 1);
    strcpy(crp->Name, fld->name);

875
    if ((crp->Type = MYSQLtoPLG(fld->type, &v)) == TYPE_ERROR) {
Alexander Barkov's avatar
Alexander Barkov committed
876 877 878 879 880 881
      sprintf(g->Message, "Type %d not supported for column %s",
                          fld->type, crp->Name);
      return NULL;
    } else if (crp->Type == TYPE_DATE && !pdb)
      // For direct MySQL connection, display the MySQL date string
      crp->Type = TYPE_STRING;
882 883
    else
      crp->Var = v;
Alexander Barkov's avatar
Alexander Barkov committed
884

885 886
    crp->Prec = (crp->Type == TYPE_DOUBLE || crp->Type == TYPE_DECIM)
              ? fld->decimals : 0;
887
    crp->Length = MY_MAX(fld->length, fld->max_length);
Alexander Barkov's avatar
Alexander Barkov committed
888
    crp->Clen = GetTypeSize(crp->Type, crp->Length);
889
    uns = (fld->flags & (UNSIGNED_FLAG | ZEROFILL_FLAG)) ? true : false;
Alexander Barkov's avatar
Alexander Barkov committed
890 891

    if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
892
                                     crp->Clen, 0, FALSE, TRUE, uns))) {
Alexander Barkov's avatar
Alexander Barkov committed
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
      sprintf(g->Message, MSG(INV_RESULT_TYPE),
                          GetFormatType(crp->Type));
      return NULL;
    } else if (crp->Type == TYPE_DATE) {
      fmt = MyDateFmt(fld->type);
      crp->Kdata->SetFormat(g, fmt, strlen(fmt));
    } // endif's

    if (fld->flags & NOT_NULL_FLAG)
      crp->Nulls = NULL;
    else {
      crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
      memset(crp->Nulls, ' ', m_Rows);
    } // endelse fld->flags

    } // endfor fld

  *pcrp = NULL;
  assert(qrp->Nbcol == m_Fields);

  /*********************************************************************/
  /*  Now fill the allocated result structure.                         */
  /*********************************************************************/
  for (n = 0; n < m_Rows; n++) {
    if (!(m_Row = mysql_fetch_row(m_Res))) {
      sprintf(g->Message, "Missing row %d from result", n + 1);
      return NULL;
      } // endif m_Row

    for (crp = qrp->Colresp; crp; crp = crp->Next) {
923
      if ((row = m_Row + (crp->Ncol - 1))) {
Alexander Barkov's avatar
Alexander Barkov committed
924 925 926 927 928
        if (*row)
          crp->Kdata->SetValue((PSZ)*row, n);
        else {
          if (!*row && crp->Nulls)
            crp->Nulls[n] = '*';           // Null value
Olivier Bertrand's avatar
Olivier Bertrand committed
929

Alexander Barkov's avatar
Alexander Barkov committed
930 931
          crp->Kdata->Reset(n);
        } // endelse *row
932
      }
Alexander Barkov's avatar
Alexander Barkov committed
933

934
    } // endfor crp
Alexander Barkov's avatar
Alexander Barkov committed
935

936
  } // endfor n
Alexander Barkov's avatar
Alexander Barkov committed
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961

  qrp->Nblin = n;
  return qrp;
  } // end of GetResult

/***********************************************************************/
/*  Free the current result.                                           */
/***********************************************************************/
void MYSQLC::FreeResult(void)
  {
  if (m_Res) {
    mysql_free_result(m_Res);
    m_Res = NULL;
    } // endif m_Res

  // Reset the connection
  m_Row = NULL;
  m_Rows = -1;
  m_Fields = -1;
  N = 0;
  } // end of FreeResult

/***********************************************************************/
/*  Place the cursor at the beginning of the result set.               */
/***********************************************************************/
962
int MYSQLC::Rewind(PGLOBAL g, PSZ sql)
Alexander Barkov's avatar
Alexander Barkov committed
963
  {
964
		int rc = RC_OK;
Alexander Barkov's avatar
Alexander Barkov committed
965

966 967 968 969 970 971
		if (m_Res)
			DataSeek(0);
		else if (sql)
			rc = ExecSQL(g, sql);

		return rc;
Alexander Barkov's avatar
Alexander Barkov committed
972 973
  } // end of Rewind

974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995
/***********************************************************************/
/*  Exec the Select SQL command and return ncol or afrws (TDBMYEXC).   */
/***********************************************************************/
int MYSQLC::ExecSQLcmd(PGLOBAL g, const char *query, int *w)
  {
  int rc = RC_OK;

  if (!m_DB) {
    strcpy(g->Message, "MySQL not connected");
    return RC_FX;
  } else
    *w = 0;

  if (!stricmp(query, "Warning") || !stricmp(query, "Note")
                                 || !stricmp(query, "Error"))
    return RC_INFO;
  else
    m_Afrw = 0;

//if (mysql_query(m_DB, query) != 0) {
  if (mysql_real_query(m_DB, query, strlen(query))) {
    m_Afrw = (int)mysql_errno(m_DB);
996
    sprintf(g->Message, "Remote: %s", mysql_error(m_DB));
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
    rc = RC_FX;
//} else if (!(m_Fields = mysql_field_count(m_DB))) {
  } else if (!(m_Fields = (int)m_DB->field_count)) {
//  m_Afrw = (int)mysql_affected_rows(m_DB);
    m_Afrw = (int)m_DB->affected_rows;
    rc = RC_NF;
  } // endif's

//*w = mysql_warning_count(m_DB);
  *w = m_DB->warning_count;
  return rc;
  } // end of ExecSQLcmd

Alexander Barkov's avatar
Alexander Barkov committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
/***********************************************************************/
/*  Close the connection.                                              */
/***********************************************************************/
void MYSQLC::Close(void)
  {
  FreeResult();
  mysql_close(m_DB);
  m_DB = NULL;
  } // end of Close

1020
#if 0                       // not used yet
Alexander Barkov's avatar
Alexander Barkov committed
1021 1022 1023 1024 1025 1026 1027
/***********************************************************************/
/*  Discard additional results from a stored procedure.                */
/***********************************************************************/
void MYSQLC::DiscardResults(void)
  {
  MYSQL_RES *res;

1028
  while (!mysql_next_result(m_DB)) {
Alexander Barkov's avatar
Alexander Barkov committed
1029 1030
    res = mysql_store_result(m_DB);
    mysql_free_result(res);
Olivier Bertrand's avatar
Olivier Bertrand committed
1031
    } // endwhile next result
Alexander Barkov's avatar
Alexander Barkov committed
1032

1033 1034
  } // end of DiscardResults
#endif // 0