1/************** MyConn C++ Program Source Code File (.CPP) **************/
2/* PROGRAM NAME: MYCONN */
3/* ------------- */
4/* Version 1.9 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 2007-2017 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* Implements a connection to MySQL. */
13/* It can optionally use the embedded MySQL library. */
14/* */
15/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
16/* -------------------------------------- */
17/* */
18/* REQUIRED FILES: */
19/* --------------- */
20/* MYCONN.CPP - Source code */
21/* MYCONN.H - MYCONN class declaration file */
22/* GLOBAL.H - Global declaration file */
23/* */
24/* REQUIRED LIBRARIES: */
25/* ------------------- */
26/* Large model C library */
27/* */
28/* REQUIRED PROGRAMS: */
29/* ------------------ */
30/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
31/* */
32/************************************************************************/
33#include "my_global.h"
34#if !defined(MYSQL_PREPARED_STATEMENTS)
35#include "my_sys.h"
36#include "mysqld_error.h"
37#endif // !MYSQL_PREPARED_STATEMENTS
38#if defined(__WIN__)
39//#include <windows.h>
40#else // !__WIN__
41#include "osutil.h"
42#endif // !__WIN__
43
44#include "global.h"
45#include "plgdbsem.h"
46#include "plgcnx.h" // For DB types
47#include "resource.h"
48//#include "value.h"
49//#include "valblk.h"
50#include "xobject.h"
51#define DLL_EXPORT // Items are exported from this DLL
52#include "myconn.h"
53
54//extern "C" int zconv;
55int GetConvSize(void);
56extern MYSQL_PLUGIN_IMPORT uint mysqld_port;
57extern MYSQL_PLUGIN_IMPORT char *mysqld_unix_port;
58
59DllExport void PushWarning(PGLOBAL, THD*, int level = 1);
60
61// Returns the current used port
62uint GetDefaultPort(void)
63{
64 return mysqld_port;
65} // end of GetDefaultPort
66
67#if !defined(MYSQL_PREPARED_STATEMENTS)
68/**************************************************************************
69 Alloc struct for use with unbuffered reads. Data is fetched by domand
70 when calling to mysql_fetch_row.
71 mysql_data_seek is a noop.
72
73 No other queries may be specified with the same MYSQL handle.
74 There shouldn't be much processing per row because mysql server shouldn't
75 have to wait for the client (and will not wait more than 30 sec/packet).
76 NOTE: copied from client.c cli_use_result
77**************************************************************************/
78static MYSQL_RES *connect_use_result(MYSQL *mysql)
79{
80 MYSQL_RES *result;
81 DBUG_ENTER("connect_use_result");
82
83 if (!mysql->fields)
84 DBUG_RETURN(NULL);
85
86 if (mysql->status != MYSQL_STATUS_GET_RESULT) {
87 my_message(ER_UNKNOWN_ERROR, "Command out of sync", MYF(0));
88 DBUG_RETURN(NULL);
89 } // endif status
90
91 if (!(result = (MYSQL_RES*) my_malloc(sizeof(*result) +
92 sizeof(ulong) * mysql->field_count,
93 MYF(MY_WME | MY_ZEROFILL))))
94 DBUG_RETURN(NULL);
95
96 result->lengths = (ulong*)(result+1);
97 result->methods = mysql->methods;
98
99 /* Ptrs: to one row */
100 if (!(result->row = (MYSQL_ROW)my_malloc(sizeof(result->row[0]) *
101 (mysql->field_count+1), MYF(MY_WME)))) {
102 my_free(result);
103 DBUG_RETURN(NULL);
104 } // endif row
105
106 result->fields = mysql->fields;
107 result->field_alloc = mysql->field_alloc;
108 result->field_count = mysql->field_count;
109 result->current_field = 0;
110 result->handle = mysql;
111 result->current_row = 0;
112 mysql->fields = 0; /* fields is now in result */
113 clear_alloc_root(&mysql->field_alloc);
114 mysql->status = MYSQL_STATUS_USE_RESULT;
115 mysql->unbuffered_fetch_owner = &result->unbuffered_fetch_cancelled;
116 DBUG_RETURN(result); /* Data is ready to be fetched */
117} // end of connect_use_result
118#endif // !MYSQL_PREPARED_STATEMENTS
119
120/************************************************************************/
121/* MyColumns: constructs the result blocks containing all columns */
122/* of a MySQL table or view. */
123/* info = TRUE to get catalog column informations. */
124/************************************************************************/
125PQRYRES MyColumns(PGLOBAL g, THD *thd, const char *host, const char *db,
126 const char *user, const char *pwd,
127 const char *table, const char *colpat,
128 int port, bool info)
129 {
130 int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
131 TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT,
132 TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING,
133 TYPE_STRING};
134 XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
135 FLD_KEY, FLD_SCALE, FLD_RADIX, FLD_NULL,
136 FLD_REM, FLD_NO, FLD_DEFAULT, FLD_EXTRA,
137 FLD_CHARSET};
138 //unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0};
139 unsigned int length[] = {0, 4, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0};
140 PCSZ fmt;
141 char *fld, *colname, *chset, v, buf[128], uns[16], zero[16];
142 int i, n, nf = 0, ncol = sizeof(buftyp) / sizeof(int);
143 int len, type, prec, rc, k = 0;
144 bool b;
145 PQRYRES qrp;
146 PCOLRES crp;
147 MYSQLC myc;
148
149 if (!port)
150 port = mysqld_port;
151
152 if (!info) {
153 /********************************************************************/
154 /* Open the connection with the MySQL server. */
155 /********************************************************************/
156 if (myc.Open(g, host, db, user, pwd, port))
157 return NULL;
158
159 /********************************************************************/
160 /* Do an evaluation of the result size. */
161 /********************************************************************/
162 STRING cmd(g, 64, "SHOW FULL COLUMNS FROM ");
163 b = cmd.Append('`');
164 b |= cmd.Append((PSZ)table);
165 b |= cmd.Append('`');
166
167 b |= cmd.Append(" FROM ");
168 b |= cmd.Append((PSZ)(db ? db : PlgGetUser(g)->DBName));
169
170 if (colpat) {
171 b |= cmd.Append(" LIKE ");
172 b |= cmd.Append((PSZ)colpat);
173 } // endif colpat
174
175 if (b) {
176 strcpy(g->Message, "Out of memory");
177 return NULL;
178 } // endif b
179
180 if (trace(1))
181 htrc("MyColumns: cmd='%s'\n", cmd.GetStr());
182
183 if ((n = myc.GetResultSize(g, cmd.GetStr())) < 0) {
184 myc.Close();
185 return NULL;
186 } // endif n
187
188 /********************************************************************/
189 /* Get the size of the name and default columns. */
190 /********************************************************************/
191 length[0] = myc.GetFieldLength(0);
192// length[10] = myc.GetFieldLength(5);
193 } else {
194 n = 0;
195 length[0] = 128;
196 } // endif info
197
198 /**********************************************************************/
199 /* Allocate the structures used to refer to the result set. */
200 /**********************************************************************/
201 if (!(qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
202 buftyp, fldtyp, length, false, true)))
203 return NULL;
204
205 // Some columns must be renamed
206 for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next)
207 switch (++i) {
208 case 2: crp->Nulls = (char*)PlugSubAlloc(g, NULL, n); break;
209 case 4: crp->Name = "Length"; break;
210 case 5: crp->Name = "Key"; break;
211 case 10: crp->Name = "Date_fmt"; break;
212 case 11: crp->Name = "Default"; break;
213 case 12: crp->Name = "Extra"; break;
214 case 13: crp->Name = "Collation"; break;
215 } // endswitch i
216
217 if (info)
218 return qrp;
219
220 /**********************************************************************/
221 /* Now get the results into blocks. */
222 /**********************************************************************/
223 for (i = 0; i < n; /*i++*/) {
224 if ((rc = myc.Fetch(g, -1)) == RC_FX) {
225 myc.Close();
226 return NULL;
227 } else if (rc == RC_EF)
228 break;
229
230 // Get column name
231 colname = myc.GetCharField(0);
232 crp = qrp->Colresp; // Column_Name
233 crp->Kdata->SetValue(colname, i);
234
235 // Get type, type name, precision, unsigned and zerofill
236 chset = myc.GetCharField(2);
237 fld = myc.GetCharField(1);
238 prec = 0;
239 len = 0;
240// v = (chset && !strcmp(chset, "binary")) ? 'B' : 0;
241 v = 0;
242 *uns = 0;
243 *zero = 0;
244 b = false;
245
246 if (!strnicmp(fld, "enum", 4)) {
247 char *p2, *p1 = fld + 6; // to skip enum('
248
249 while (true) {
250 p2 = strchr(p1, '\'');
251 len = MY_MAX(len, (int)(p2 - p1));
252 if (*++p2 != ',') break;
253 p1 = p2 + 2;
254 } // endwhile
255
256 v = (len > 255) ? 'V' : 0;
257 strcpy(buf, "enum");
258 b = true;
259 } else if (!strnicmp(fld, "set", 3)) {
260 len = (int)strlen(fld) - 2;
261 v = 'V';
262 strcpy(buf, "set");
263 b = true;
264 } else switch ((nf = sscanf(fld, "%[^(](%d,%d", buf, &len, &prec))) {
265 case 3:
266 nf = sscanf(fld, "%[^(](%d,%d) %s %s", buf, &len, &prec, uns, zero);
267 break;
268 case 2:
269 nf = sscanf(fld, "%[^(](%d) %s %s", buf, &len, uns, zero) + 1;
270 break;
271 case 1:
272 nf = sscanf(fld, "%s %s %s", buf, uns, zero) + 2;
273 break;
274 default:
275 sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld);
276 myc.Close();
277 return NULL;
278 } // endswitch nf
279
280 if ((type = MYSQLtoPLG(buf, &v)) == TYPE_ERROR) {
281 if (v == 'K') {
282 // Skip this column
283 sprintf(g->Message, "Column %s skipped (unsupported type %s)",
284 colname, buf);
285 PushWarning(g, thd);
286 continue;
287 } // endif v
288
289 sprintf(g->Message, "Column %s unsupported type %s", colname, buf);
290 myc.Close();
291 return NULL;
292 } else if (type == TYPE_STRING) {
293 if (v == 'X') {
294 len = GetConvSize();
295 sprintf(g->Message, "Column %s converted to varchar(%d)",
296 colname, len);
297 PushWarning(g, thd);
298 v = 'V';
299 } else
300 len = MY_MIN(len, 4096);
301
302 } // endif type
303
304 qrp->Nblin++;
305 crp = crp->Next; // Data_Type
306 crp->Kdata->SetValue(type, i);
307
308 switch (nf) {
309 case 5: crp->Nulls[i] = 'Z'; break;
310 case 4: crp->Nulls[i] = 'U'; break;
311 default: crp->Nulls[i] = v; break;
312 } // endswitch nf
313
314 if (b) // enum or set
315 nf = sscanf(fld, "%s ", buf); // get values
316
317 crp = crp->Next; // Type_Name
318 crp->Kdata->SetValue(buf, i);
319
320 if (type == TYPE_DATE) {
321 // When creating tables we do need info about date columns
322 fmt = MyDateFmt(buf);
323 len = strlen(fmt);
324 } else
325 fmt = NULL;
326
327 crp = crp->Next; // Precision
328 crp->Kdata->SetValue(len, i);
329
330 crp = crp->Next; // key (was Length)
331 fld = myc.GetCharField(4);
332 crp->Kdata->SetValue(fld, i);
333
334 crp = crp->Next; // Scale
335 crp->Kdata->SetValue(prec, i);
336
337 crp = crp->Next; // Radix
338 crp->Kdata->SetValue(0, i);
339
340 crp = crp->Next; // Nullable
341 fld = myc.GetCharField(3);
342 crp->Kdata->SetValue((toupper(*fld) == 'Y') ? 1 : 0, i);
343
344 crp = crp->Next; // Remark
345 fld = myc.GetCharField(8);
346 crp->Kdata->SetValue(fld, i);
347
348 crp = crp->Next; // Date format
349// crp->Kdata->SetValue((fmt) ? fmt : (char*) "", i);
350 crp->Kdata->SetValue(fmt, i);
351
352 crp = crp->Next; // New (default)
353 fld = myc.GetCharField(5);
354 crp->Kdata->SetValue(fld, i);
355
356 crp = crp->Next; // New (extra)
357 fld = myc.GetCharField(6);
358 crp->Kdata->SetValue(fld, i);
359
360 crp = crp->Next; // New (charset)
361 fld = chset;
362 crp->Kdata->SetValue(fld, i);
363
364 i++; // Can be skipped
365 } // endfor i
366
367#if 0
368 if (k > 1) {
369 // Multicolumn primary key
370 PVBLK vbp = qrp->Colresp->Next->Next->Next->Next->Kdata;
371
372 for (i = 0; i < n; i++)
373 if (vbp->GetIntValue(i))
374 vbp->SetValue(k, i);
375
376 } // endif k
377#endif // 0
378
379 /**********************************************************************/
380 /* Close MySQL connection. */
381 /**********************************************************************/
382 myc.Close();
383
384 /**********************************************************************/
385 /* Return the result pointer for use by GetData routines. */
386 /**********************************************************************/
387 return qrp;
388 } // end of MyColumns
389
390/************************************************************************/
391/* SrcColumns: constructs the result blocks containing all columns */
392/* resulting from an SQL source definition query execution. */
393/************************************************************************/
394PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db,
395 const char *user, const char *pwd,
396 const char *srcdef, int port)
397 {
398 char *query;
399 int w;
400 MYSQLC myc;
401 PQRYRES qrp = NULL;
402
403 if (!port)
404 port = mysqld_port;
405
406 if (!strnicmp(srcdef, "select ", 7) || strstr(srcdef, "%s")) {
407 query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 10);
408
409 if (strstr(srcdef, "%s"))
410 sprintf(query, srcdef, "1=1"); // dummy where clause
411 else
412 strcpy(query, srcdef);
413
414 if (!strnicmp(srcdef, "select ", 7))
415 strcat(query, " LIMIT 0");
416
417 } else
418 query = (char *)srcdef;
419
420 // Open a MySQL connection for this table
421 if (myc.Open(g, host, db, user, pwd, port))
422 return NULL;
423
424 // Send the source command to MySQL
425 if (myc.ExecSQL(g, query, &w) == RC_OK)
426 qrp = myc.GetResult(g, true);
427
428 myc.Close();
429 return qrp;
430 } // end of SrcColumns
431
432/* -------------------------- Class MYSQLC --------------------------- */
433
434/***********************************************************************/
435/* Implementation of the MYSQLC class. */
436/***********************************************************************/
437MYSQLC::MYSQLC(void)
438 {
439 m_DB = NULL;
440#if defined (MYSQL_PREPARED_STATEMENTS)
441 m_Stmt = NULL;
442#endif // MYSQL_PREPARED_STATEMENTS
443 m_Res = NULL;
444 m_Rows = -1;
445 m_Row = NULL;
446 m_Fields = -1;
447 N = 0;
448 m_Use = false;
449 } // end of MYSQLC constructor
450
451/***********************************************************************/
452/* Get the number of lines of the result set. */
453/* Currently we send the Select command and return m_Rows */
454/* Perhaps should we use Select count(*) ... (?????) */
455/* No because here we execute only one query instead of two */
456/* (the select count(*) plus the normal query) */
457/***********************************************************************/
458int MYSQLC::GetResultSize(PGLOBAL g, PSZ sql)
459 {
460 if (m_Rows < 0)
461 if (ExecSQL(g, sql) != RC_OK)
462 return -1;
463
464 return m_Rows;
465 } // end of GetResultSize
466
467/***********************************************************************/
468/* Open a MySQL (remote) connection. */
469/***********************************************************************/
470int MYSQLC::Open(PGLOBAL g, const char *host, const char *db,
471 const char *user, const char *pwd,
472 int pt, const char *csname)
473 {
474 const char *pipe = NULL;
475 uint cto = 10, nrt = 20;
476 my_bool my_true= 1;
477
478 m_DB = mysql_init(NULL);
479
480 if (!m_DB) {
481 strcpy(g->Message, "mysql_init failed: no memory");
482 return RC_FX;
483 } // endif m_DB
484
485 if (trace(1))
486 htrc("MYSQLC Open: m_DB=%.4X size=%d\n", m_DB, (int)sizeof(*m_DB));
487
488 // Removed to do like FEDERATED do
489//mysql_options(m_DB, MYSQL_READ_DEFAULT_GROUP, "client-mariadb");
490 mysql_options(m_DB, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL);
491 mysql_options(m_DB, MYSQL_OPT_CONNECT_TIMEOUT, &cto);
492 mysql_options(m_DB, MYSQL_OPT_READ_TIMEOUT, &nrt);
493//mysql_options(m_DB, MYSQL_OPT_WRITE_TIMEOUT, ...);
494
495#if defined(__WIN__)
496 if (!strcmp(host, ".")) {
497 mysql_options(m_DB, MYSQL_OPT_NAMED_PIPE, NULL);
498 pipe = mysqld_unix_port;
499 } // endif host
500#else // !__WIN__
501 if (!strcmp(host, "localhost"))
502 pipe = mysqld_unix_port;
503#endif // !__WIN__
504
505#if 0
506 if (pwd && !strcmp(pwd, "*")) {
507 if (GetPromptAnswer(g, "*Enter password:")) {
508 m_DB = NULL;
509 return RC_FX;
510 } else
511 pwd = g->Message;
512
513 } // endif pwd
514#endif // 0
515
516/***********************************************************************/
517/* BUG# 17044 Federated Storage Engine is not UTF8 clean */
518/* Add set names to whatever charset the table is at open of table */
519/* this sets the csname like 'set names utf8'. */
520/***********************************************************************/
521 if (csname)
522 mysql_options(m_DB, MYSQL_SET_CHARSET_NAME, csname);
523
524 // Don't know what this one do but FEDERATED does it
525 mysql_options(m_DB, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY,
526 (char*)&my_true);
527
528 if (!mysql_real_connect(m_DB, host, user, pwd, db, pt, pipe,
529 CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS)) {
530#if defined(_DEBUG)
531 sprintf(g->Message, "mysql_real_connect failed: (%d) %s",
532 mysql_errno(m_DB), mysql_error(m_DB));
533#else // !_DEBUG
534 sprintf(g->Message, "(%d) %s", mysql_errno(m_DB), mysql_error(m_DB));
535#endif // !_DEBUG
536 mysql_close(m_DB);
537 m_DB = NULL;
538 return RC_FX;
539 } // endif mysql_real_connect
540
541 return RC_OK;
542 } // end of Open
543
544/***********************************************************************/
545/* Returns true if the connection is still alive. */
546/***********************************************************************/
547bool MYSQLC::Connected(void)
548 {
549//int rc;
550
551 if (!m_DB)
552 return FALSE;
553//else if ((rc = mysql_ping(m_DB)) == CR_SERVER_GONE_ERROR)
554// return FALSE;
555 else
556 return TRUE;
557
558 } // end of Connected
559
560#if 0 // Not used
561/***********************************************************************/
562/* Returns the thread ID of the current MySQL connection. */
563/***********************************************************************/
564ulong MYSQLC::GetThreadID(void)
565 {
566 return (m_DB) ? mysql_thread_id(m_DB) : 0;
567 } // end of GetThreadID
568
569/***********************************************************************/
570/* Returns a string that represents the server version number. */
571/***********************************************************************/
572const char *MYSQLC::ServerInfo(void)
573 {
574 return (m_DB) ? mysql_get_server_info(m_DB) : NULL;
575 } // end of ServerInfo
576
577/***********************************************************************/
578/* Returns the version number of the server as a number that */
579/* represents the MySQL server version in this format: */
580/* major_version*10000 + minor_version *100 + sub_version */
581/***********************************************************************/
582ulong MYSQLC::ServerVersion(void)
583 {
584 return (m_DB) ? mysql_get_server_version(m_DB) : 0;
585 } // end of ServerVersion
586#endif // 0
587
588/**************************************************************************/
589/* KillQuery: Send MySQL a Kill Query command. */
590/**************************************************************************/
591int MYSQLC::KillQuery(ulong id)
592 {
593 char kill[20];
594
595 sprintf(kill, "KILL QUERY %u", (unsigned int) id);
596//return (m_DB) ? mysql_query(m_DB, kill) : 1;
597 return (m_DB) ? mysql_real_query(m_DB, kill, strlen(kill)) : 1;
598 } // end of KillQuery
599
600#if defined (MYSQL_PREPARED_STATEMENTS)
601/***********************************************************************/
602/* Prepare the SQL statement used to insert into a MySQL table. */
603/***********************************************************************/
604int MYSQLC::PrepareSQL(PGLOBAL g, const char *stmt)
605 {
606 if (!m_DB) {
607 strcpy(g->Message, "MySQL not connected");
608 return -4;
609 } else if (m_Stmt)
610 return -1; // should not append
611
612#if defined(ALPHA)
613 if (!(m_Stmt = mysql_prepare(m_DB, stmt, strlen(stmt)))) {
614
615 sprintf(g->Message, "mysql_prepare failed: %s [%s]",
616 mysql_error(m_DB), stmt);
617 return -1;
618 } // endif m_Stmt
619
620 // Return the parameter count from the statement
621 return mysql_param_count(m_Stmt);
622#else // !ALPHA
623 if (!(m_Stmt = mysql_stmt_init(m_DB))) {
624 strcpy(g->Message, "mysql_stmt_init(), out of memory");
625 return -2;
626 } // endif m_Stmt
627
628 if (mysql_stmt_prepare(m_Stmt, stmt, strlen(stmt))) {
629 sprintf(g->Message, "mysql_stmt_prepare() failed: (%d) %s",
630 mysql_stmt_errno(m_Stmt), mysql_stmt_error(m_Stmt));
631 return -3;
632 } // endif prepare
633
634 // Return the parameter count from the statement
635 return mysql_stmt_param_count(m_Stmt);
636#endif // !ALPHA
637 } // end of PrepareSQL
638
639/***********************************************************************/
640/* Bind the parameter buffers. */
641/***********************************************************************/
642int MYSQLC::BindParams(PGLOBAL g, MYSQL_BIND *bind)
643 {
644 if (!m_DB) {
645 strcpy(g->Message, "MySQL not connected");
646 return RC_FX;
647 } else
648 assert(m_Stmt);
649
650#if defined(ALPHA)
651 if (mysql_bind_param(m_Stmt, bind)) {
652 sprintf(g->Message, "mysql_bind_param() failed: %s",
653 mysql_stmt_error(m_Stmt));
654#else // !ALPHA
655 if (mysql_stmt_bind_param(m_Stmt, bind)) {
656 sprintf(g->Message, "mysql_stmt_bind_param() failed: %s",
657 mysql_stmt_error(m_Stmt));
658#endif // !ALPHA
659 return RC_FX;
660 } // endif bind
661
662 return RC_OK;
663
664/***********************************************************************/
665/* Execute a prepared statement. */
666/***********************************************************************/
667int MYSQLC::ExecStmt(PGLOBAL g)
668 {
669 if (!m_DB) {
670 strcpy(g->Message, "MySQL not connected");
671 return RC_FX;
672 } // endif m_DB
673
674#if defined(ALPHA)
675 if (mysql_execute(m_Stmt)) {
676 sprintf(g->Message, "mysql_execute() failed: %s",
677 mysql_stmt_error(m_Stmt));
678 return RC_FX;
679 } // endif execute
680#else // !ALPHA
681 if (mysql_stmt_execute(m_Stmt)) {
682 sprintf(g->Message, "mysql_stmt_execute() failed: %s",
683 mysql_stmt_error(m_Stmt));
684 return RC_FX;
685 } // endif execute
686#endif // !ALPHA
687
688 // Check the total number of affected rows
689 if (mysql_stmt_affected_rows(m_Stmt) != 1) {
690 sprintf(g->Message, "Invalid affected rows by MySQL");
691 return RC_FX;
692 } // endif affected_rows
693
694 return RC_OK;
695 } // end of ExecStmt
696#endif // MYSQL_PREPARED_STATEMENTS
697
698/***********************************************************************/
699/* Exec the Select SQL command and get back the result size in rows. */
700/***********************************************************************/
701int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w)
702 {
703 int rc = RC_OK;
704
705 if (!m_DB) {
706 strcpy(g->Message, "MySQL not connected");
707 return RC_FX;
708 } // endif m_DB
709
710 if (w)
711 *w = 0;
712
713 if (m_Rows >= 0)
714 return RC_OK; // Already done
715
716//if (mysql_query(m_DB, query) != 0) {
717 if (mysql_real_query(m_DB, query, strlen(query))) {
718 char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
719
720 sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
721 mysql_error(m_DB), query);
722 strncpy(g->Message, msg, sizeof(g->Message) - 1);
723 g->Message[sizeof(g->Message) - 1] = 0;
724 rc = RC_FX;
725//} else if (mysql_field_count(m_DB) > 0) {
726 } else if (m_DB->field_count > 0) {
727 if (m_Use)
728#if defined(MYSQL_PREPARED_STATEMENTS)
729 m_Res = mysql_use_result(m_DB);
730#else // !MYSQL_PREPARED_STATEMENTS)
731 m_Res = connect_use_result(m_DB);
732#endif // !MYSQL_PREPARED_STATEMENTS
733 else
734 m_Res = mysql_store_result(m_DB);
735
736 if (!m_Res) {
737 char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
738
739 sprintf(msg, "mysql_store_result failed: %s", mysql_error(m_DB));
740 strncpy(g->Message, msg, sizeof(g->Message) - 1);
741 g->Message[sizeof(g->Message) - 1] = 0;
742 rc = RC_FX;
743 } else {
744 m_Fields = mysql_num_fields(m_Res);
745 m_Rows = (!m_Use) ? (int)mysql_num_rows(m_Res) : 0;
746
747 if (trace(1))
748 htrc("ExecSQL: m_Res=%.4X size=%d m_Fields=%d m_Rows=%d\n",
749 m_Res, sizeof(*m_Res), m_Fields, m_Rows);
750
751 } // endif m_Res
752
753 } else {
754// m_Rows = (int)mysql_affected_rows(m_DB);
755 m_Rows = (int)m_DB->affected_rows;
756 sprintf(g->Message, "Affected rows: %d\n", m_Rows);
757 rc = RC_NF;
758 } // endif field count
759
760 if (w)
761// *w = mysql_warning_count(m_DB);
762 *w = m_DB->warning_count;
763
764 return rc;
765 } // end of ExecSQL
766
767/***********************************************************************/
768/* Get table size by executing "select count(*) from table_name". */
769/***********************************************************************/
770int MYSQLC::GetTableSize(PGLOBAL g __attribute__((unused)), PSZ query)
771 {
772 if (mysql_real_query(m_DB, query, strlen(query))) {
773#if defined(_DEBUG)
774 char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
775
776 sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
777 mysql_error(m_DB), query);
778 strncpy(g->Message, msg, sizeof(g->Message) - 1);
779 g->Message[sizeof(g->Message) - 1] = 0;
780#endif // _DEBUG
781 return -2;
782 } // endif mysql_real_query
783
784 if (!(m_Res = mysql_store_result(m_DB)))
785 return -3;
786
787 // Get the resulting count value
788 m_Rows = (int)mysql_num_rows(m_Res); // Should be 1
789
790 if (m_Rows && (m_Row = mysql_fetch_row(m_Res)))
791 return atoi(*m_Row);
792
793 return -4;
794 } // end of GetTableSize
795
796/***********************************************************************/
797/* Move to a specific row and column */
798/***********************************************************************/
799void MYSQLC::DataSeek(my_ulonglong row)
800 {
801 MYSQL_ROWS *tmp = 0;
802//DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row));
803
804 if (m_Res->data)
805 for (tmp = m_Res->data->data; row-- && tmp; tmp = tmp->next) ;
806
807 m_Res->current_row = 0;
808 m_Res->data_cursor = tmp;
809 } // end of DataSeek
810
811/***********************************************************************/
812/* Fetch one result line from the query result set. */
813/***********************************************************************/
814int MYSQLC::Fetch(PGLOBAL g, int pos)
815 {
816 if (!m_DB) {
817 strcpy(g->Message, "MySQL not connected");
818 return RC_FX;
819 } // endif m_DB
820
821 if (!m_Res) {
822 // Result set was not initialized
823 strcpy(g->Message, MSG(FETCH_NO_RES));
824 return RC_FX;
825 } else
826 N++;
827
828 if (pos >= 0)
829// mysql_data_seek(m_Res, (my_ulonglong)pos);
830 DataSeek((my_ulonglong)pos);
831
832 m_Row = mysql_fetch_row(m_Res);
833 return (m_Row) ? RC_OK : RC_EF;
834 } // end of Fetch
835
836/***********************************************************************/
837/* Get one field of the current row. */
838/***********************************************************************/
839char *MYSQLC::GetCharField(int i)
840 {
841 if (m_Res && m_Row) {
842#if defined(_DEBUG)
843// MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i);
844#endif // _DEBUG
845 MYSQL_ROW row = m_Row + i;
846
847 return (row) ? (char*)*row : (char*)"<null>";
848 } else
849 return NULL;
850
851 } // end of GetCharField
852
853/***********************************************************************/
854/* Get the max length of the field. */
855/***********************************************************************/
856int MYSQLC::GetFieldLength(int i)
857 {
858 if (m_Res) {
859// MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i);
860// return fld->max_length;
861 return (m_Res)->fields[i].max_length;
862 } else
863 return 0;
864
865 } // end of GetFieldLength
866
867/***********************************************************************/
868/* Return next field of the query results. */
869/***********************************************************************/
870MYSQL_FIELD *MYSQLC::GetNextField(void)
871 {
872 return (m_Res->current_field >= m_Res->field_count) ? NULL
873 : &m_Res->fields[m_Res->current_field++];
874 } // end of GetNextField
875
876/***********************************************************************/
877/* Make a CONNECT result structure from the MySQL result. */
878/***********************************************************************/
879PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb)
880 {
881 PCSZ fmt;
882 char *name, v;
883 int n;
884 bool uns;
885 PCOLRES *pcrp, crp;
886 PQRYRES qrp;
887 MYSQL_FIELD *fld;
888 MYSQL_ROW row;
889
890 if (!m_Res || !m_Fields) {
891 sprintf(g->Message, "%s result", (m_Res) ? "Void" : "No");
892 return NULL;
893 } // endif m_Res
894
895 /*********************************************************************/
896 /* Put the result in storage for future retrieval. */
897 /*********************************************************************/
898 qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
899 pcrp = &qrp->Colresp;
900 qrp->Continued = FALSE;
901 qrp->Truncated = FALSE;
902 qrp->Info = FALSE;
903 qrp->Suball = TRUE;
904 qrp->BadLines = 0;
905 qrp->Maxsize = m_Rows;
906 qrp->Maxres = m_Rows;
907 qrp->Nbcol = 0;
908 qrp->Nblin = 0;
909 qrp->Cursor = 0;
910
911//for (fld = mysql_fetch_field(m_Res); fld;
912// fld = mysql_fetch_field(m_Res)) {
913 for (fld = GetNextField(); fld; fld = GetNextField()) {
914 *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
915 crp = *pcrp;
916 pcrp = &crp->Next;
917 memset(crp, 0, sizeof(COLRES));
918 crp->Ncol = ++qrp->Nbcol;
919
920 name = (char*)PlugSubAlloc(g, NULL, fld->name_length + 1);
921 strcpy(name, fld->name);
922 crp->Name = name;
923
924 if ((crp->Type = MYSQLtoPLG(fld->type, &v)) == TYPE_ERROR) {
925 sprintf(g->Message, "Type %d not supported for column %s",
926 fld->type, crp->Name);
927 return NULL;
928 } else if (crp->Type == TYPE_DATE && !pdb)
929 // For direct MySQL connection, display the MySQL date string
930 crp->Type = TYPE_STRING;
931 else
932 crp->Var = v;
933
934 crp->Prec = (crp->Type == TYPE_DOUBLE || crp->Type == TYPE_DECIM)
935 ? fld->decimals : 0;
936 CHARSET_INFO *cs= get_charset(fld->charsetnr, MYF(0));
937 crp->Clen = GetTypeSize(crp->Type, fld->length);
938 crp->Length = fld->length / (cs ? cs->mbmaxlen : 1);
939 uns = (fld->flags & (UNSIGNED_FLAG | ZEROFILL_FLAG)) ? true : false;
940
941 if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
942 crp->Clen, 0, FALSE, TRUE, uns))) {
943 sprintf(g->Message, MSG(INV_RESULT_TYPE),
944 GetFormatType(crp->Type));
945 return NULL;
946 } else if (crp->Type == TYPE_DATE) {
947 fmt = MyDateFmt(fld->type);
948 crp->Kdata->SetFormat(g, fmt, strlen(fmt));
949 } // endif's
950
951 if (fld->flags & NOT_NULL_FLAG)
952 crp->Nulls = NULL;
953 else {
954 if (m_Rows) {
955 crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
956 memset(crp->Nulls, ' ', m_Rows);
957 } // endif m_Rows
958
959 crp->Kdata->SetNullable(true);
960 } // endelse fld->flags
961
962 } // endfor fld
963
964 *pcrp = NULL;
965 assert(qrp->Nbcol == m_Fields);
966
967 /*********************************************************************/
968 /* Now fill the allocated result structure. */
969 /*********************************************************************/
970 for (n = 0; n < m_Rows; n++) {
971 if (!(m_Row = mysql_fetch_row(m_Res))) {
972 sprintf(g->Message, "Missing row %d from result", n + 1);
973 return NULL;
974 } // endif m_Row
975
976 for (crp = qrp->Colresp; crp; crp = crp->Next) {
977 if ((row = m_Row + (crp->Ncol - 1))) {
978 if (*row)
979 crp->Kdata->SetValue((PSZ)*row, n);
980 else {
981 if (!*row && crp->Nulls)
982 crp->Nulls[n] = '*'; // Null value
983
984 crp->Kdata->Reset(n);
985 } // endelse *row
986 }
987
988 } // endfor crp
989
990 } // endfor n
991
992 qrp->Nblin = n;
993 return qrp;
994 } // end of GetResult
995
996/***********************************************************************/
997/* Free the current result. */
998/***********************************************************************/
999void MYSQLC::FreeResult(void)
1000 {
1001 if (m_Res) {
1002 mysql_free_result(m_Res);
1003 m_Res = NULL;
1004 } // endif m_Res
1005
1006 // Reset the connection
1007 m_Row = NULL;
1008 m_Rows = -1;
1009 m_Fields = -1;
1010 N = 0;
1011 } // end of FreeResult
1012
1013/***********************************************************************/
1014/* Place the cursor at the beginning of the result set. */
1015/***********************************************************************/
1016int MYSQLC::Rewind(PGLOBAL g, PSZ sql)
1017 {
1018 int rc = RC_OK;
1019
1020 if (m_Res)
1021 DataSeek(0);
1022 else if (sql)
1023 rc = ExecSQL(g, sql);
1024
1025 return rc;
1026 } // end of Rewind
1027
1028/***********************************************************************/
1029/* Exec the Select SQL command and return ncol or afrws (TDBMYEXC). */
1030/***********************************************************************/
1031int MYSQLC::ExecSQLcmd(PGLOBAL g, const char *query, int *w)
1032 {
1033 int rc = RC_OK;
1034
1035 if (!m_DB) {
1036 strcpy(g->Message, "MySQL not connected");
1037 return RC_FX;
1038 } else
1039 *w = 0;
1040
1041 if (!stricmp(query, "Warning") || !stricmp(query, "Note")
1042 || !stricmp(query, "Error"))
1043 return RC_INFO;
1044 else
1045 m_Afrw = 0;
1046
1047//if (mysql_query(m_DB, query) != 0) {
1048 if (mysql_real_query(m_DB, query, strlen(query))) {
1049 m_Afrw = (int)mysql_errno(m_DB);
1050 sprintf(g->Message, "Remote: %s", mysql_error(m_DB));
1051 rc = RC_FX;
1052//} else if (!(m_Fields = mysql_field_count(m_DB))) {
1053 } else if (!(m_Fields = (int)m_DB->field_count)) {
1054// m_Afrw = (int)mysql_affected_rows(m_DB);
1055 m_Afrw = (int)m_DB->affected_rows;
1056 rc = RC_NF;
1057 } // endif's
1058
1059//*w = mysql_warning_count(m_DB);
1060 *w = m_DB->warning_count;
1061 return rc;
1062 } // end of ExecSQLcmd
1063
1064/***********************************************************************/
1065/* Close the connection. */
1066/***********************************************************************/
1067void MYSQLC::Close(void)
1068 {
1069 FreeResult();
1070
1071 if (trace(1))
1072 htrc("MYSQLC Close: m_DB=%.4X\n", m_DB);
1073
1074 mysql_close(m_DB);
1075 m_DB = NULL;
1076 } // end of Close
1077
1078#if 0 // not used yet
1079/***********************************************************************/
1080/* Discard additional results from a stored procedure. */
1081/***********************************************************************/
1082void MYSQLC::DiscardResults(void)
1083 {
1084 MYSQL_RES *res;
1085
1086 while (!mysql_next_result(m_DB)) {
1087 res = mysql_store_result(m_DB);
1088 mysql_free_result(res);
1089 } // endwhile next result
1090
1091 } // end of DiscardResults
1092#endif // 0
1093