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(_WIN32)
39 //#include <windows.h>
40 #else   // !_WIN32
41 #include "osutil.h"
42 #endif  // !_WIN32
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;
55 int GetConvSize(void);
56 extern MYSQL_PLUGIN_IMPORT uint  mysqld_port;
57 extern MYSQL_PLUGIN_IMPORT char *mysqld_unix_port;
58 
59 DllExport void PushWarning(PGLOBAL, THD*, int level = 1);
60 
61 // Returns the current used port
GetDefaultPort(void)62 uint 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 **************************************************************************/
connect_use_result(MYSQL * mysql)78 static 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(PSI_NOT_INSTRUMENTED,
92                   sizeof(*result) + 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(PSI_NOT_INSTRUMENTED,
101                 sizeof(result->row[0]) * (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 information.                     */
124 /************************************************************************/
MyColumns(PGLOBAL g,THD * thd,const char * host,const char * db,const char * user,const char * pwd,const char * table,const char * colpat,int port,bool info)125 PQRYRES 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;
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 /************************************************************************/
SrcColumns(PGLOBAL g,const char * host,const char * db,const char * user,const char * pwd,const char * srcdef,int port)394 PQRYRES 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   const char *p;
403 
404   if (!port)
405     port = mysqld_port;
406 
407 	if (!strnicmp(srcdef, "select ", 7) || strstr(srcdef, "%s")) {
408 		query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 10);
409 
410 		if ((p= strstr(srcdef, "%s")))
411 		{
412 			/* Replace %s with 1=1 */
413 			sprintf(query, "%.*s1=1%s", (int) (p - srcdef), srcdef, p + 2); // dummy where clause
414 		}
415 		else
416 		  strcpy(query, srcdef);
417 
418 		if (!strnicmp(srcdef, "select ", 7))
419 		  strcat(query, " LIMIT 0");
420 
421 	} else
422     query = (char *)srcdef;
423 
424   // Open a MySQL connection for this table
425   if (myc.Open(g, host, db, user, pwd, port))
426     return NULL;
427 
428   // Send the source command to MySQL
429   if (myc.ExecSQL(g, query, &w) == RC_OK)
430     qrp = myc.GetResult(g, true);
431 
432   myc.Close();
433   return qrp;
434   } // end of SrcColumns
435 
436 /* -------------------------- Class MYSQLC --------------------------- */
437 
438 /***********************************************************************/
439 /*  Implementation of the MYSQLC class.                                */
440 /***********************************************************************/
MYSQLC(void)441 MYSQLC::MYSQLC(void)
442   {
443   m_DB = NULL;
444 #if defined (MYSQL_PREPARED_STATEMENTS)
445 	m_Stmt = NULL;
446 #endif    // MYSQL_PREPARED_STATEMENTS
447 	m_Res = NULL;
448   m_Rows = -1;
449   m_Row = NULL;
450   m_Fields = -1;
451   N = 0;
452   m_Use = false;
453   } // end of MYSQLC constructor
454 
455 /***********************************************************************/
456 /*  Get the number of lines of the result set.                         */
457 /*  Currently we send the Select command and return m_Rows             */
458 /*  Perhaps should we use Select count(*) ... (?????)                  */
459 /*  No because here we execute only one query instead of two           */
460 /*  (the select count(*) plus the normal query)                        */
461 /***********************************************************************/
GetResultSize(PGLOBAL g,PSZ sql)462 int MYSQLC::GetResultSize(PGLOBAL g, PSZ sql)
463   {
464   if (m_Rows < 0)
465     if (ExecSQL(g, sql) != RC_OK)
466       return -1;
467 
468   return m_Rows;
469   } // end of GetResultSize
470 
471 /***********************************************************************/
472 /*  Open a MySQL (remote) connection.                                  */
473 /***********************************************************************/
Open(PGLOBAL g,const char * host,const char * db,const char * user,const char * pwd,int pt,const char * csname)474 int MYSQLC::Open(PGLOBAL g, const char *host, const char *db,
475                             const char *user, const char *pwd,
476                             int pt, const char *csname)
477   {
478   const char *pipe = NULL;
479   //uint      cto = 10, nrt = 20;
480   my_bool     my_true= 1;
481 
482   m_DB = mysql_init(NULL);
483 
484   if (!m_DB) {
485     strcpy(g->Message, "mysql_init failed: no memory");
486     return RC_FX;
487     } // endif m_DB
488 
489 	if (trace(1))
490 		htrc("MYSQLC Open: m_DB=%.4X size=%d\n", m_DB, (int)sizeof(*m_DB));
491 
492 	// Removed to do like FEDERATED does
493 //mysql_options(m_DB, MYSQL_READ_DEFAULT_GROUP, "client-mariadb");
494 //mysql_options(m_DB, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL);
495 //mysql_options(m_DB, MYSQL_OPT_CONNECT_TIMEOUT, &cto);
496 //mysql_options(m_DB, MYSQL_OPT_READ_TIMEOUT, &nrt);
497 //mysql_options(m_DB, MYSQL_OPT_WRITE_TIMEOUT, ...);
498 
499 #if defined(_WIN32)
500   if (!strcmp(host, ".")) {
501     mysql_options(m_DB, MYSQL_OPT_NAMED_PIPE, NULL);
502     pipe = mysqld_unix_port;
503     } // endif host
504 #else   // !_WIN32
505   if (!strcmp(host, "localhost"))
506     pipe = mysqld_unix_port;
507 #endif  // !_WIN32
508 
509 #if 0
510   if (pwd && !strcmp(pwd, "*")) {
511     if (GetPromptAnswer(g, "*Enter password:")) {
512       m_DB = NULL;
513       return RC_FX;
514     } else
515       pwd = g->Message;
516 
517     } // endif pwd
518 #endif // 0
519 
520 /***********************************************************************/
521 /*	BUG# 17044 Federated Storage Engine is not UTF8 clean              */
522 /*	Add set names to whatever charset the table is at open of table    */
523 /*  this sets the csname like 'set names utf8'.                        */
524 /***********************************************************************/
525   if (csname)
526     mysql_options(m_DB, MYSQL_SET_CHARSET_NAME, csname);
527 
528   // Don't know what this one do but FEDERATED does it
529   mysql_options(m_DB, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY,
530                   (char*)&my_true);
531 
532   if (!mysql_real_connect(m_DB, host, user, pwd, db, pt, pipe,
533 		CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS)) {
534 #if defined(_DEBUG)
535     sprintf(g->Message, "mysql_real_connect failed: (%d) %s",
536                         mysql_errno(m_DB), mysql_error(m_DB));
537 #else   // !_DEBUG
538     sprintf(g->Message, "(%d) %s", mysql_errno(m_DB), mysql_error(m_DB));
539 #endif  // !_DEBUG
540     mysql_close(m_DB);
541     m_DB = NULL;
542     return RC_FX;
543     } // endif mysql_real_connect
544 
545   return RC_OK;
546   } // end of Open
547 
548 /***********************************************************************/
549 /*  Returns true if the connection is still alive.                     */
550 /***********************************************************************/
Connected(void)551 bool MYSQLC::Connected(void)
552   {
553 //int rc;
554 
555   if (!m_DB)
556     return FALSE;
557 //else if ((rc = mysql_ping(m_DB)) == CR_SERVER_GONE_ERROR)
558 //  return FALSE;
559   else
560     return TRUE;
561 
562   } // end of Connected
563 
564 #if 0                         // Not used
565 /***********************************************************************/
566 /*  Returns the thread ID of the current MySQL connection.             */
567 /***********************************************************************/
GetThreadID(void)568 ulong MYSQLC::GetThreadID(void)
569   {
570   return (m_DB) ? mysql_thread_id(m_DB) : 0;
571   } // end of GetThreadID
572 
573 /***********************************************************************/
574 /*  Returns a string that represents the server version number.        */
575 /***********************************************************************/
ServerInfo(void)576 const char *MYSQLC::ServerInfo(void)
577   {
578   return (m_DB) ? mysql_get_server_info(m_DB) : NULL;
579   } // end of ServerInfo
580 
581 /***********************************************************************/
582 /*  Returns the version number of the server as a number that          */
583 /*  represents the MySQL server version in this format:                */
584 /*  major_version*10000 + minor_version *100 + sub_version             */
585 /***********************************************************************/
ServerVersion(void)586 ulong MYSQLC::ServerVersion(void)
587   {
588   return (m_DB) ? mysql_get_server_version(m_DB) : 0;
589   } // end of ServerVersion
590 #endif // 0
591 
592 /**************************************************************************/
593 /*  KillQuery: Send MySQL a Kill Query command.                           */
594 /**************************************************************************/
KillQuery(ulong id)595 int MYSQLC::KillQuery(ulong id)
596   {
597   char kill[20];
598 
599   sprintf(kill, "KILL QUERY %u", (unsigned int) id);
600 //return (m_DB) ? mysql_query(m_DB, kill) : 1;
601   return (m_DB) ? mysql_real_query(m_DB, kill, strlen(kill)) : 1;
602   } // end of KillQuery
603 
604 #if defined (MYSQL_PREPARED_STATEMENTS)
605 /***********************************************************************/
606 /*  Prepare the SQL statement used to insert into a MySQL table.       */
607 /***********************************************************************/
PrepareSQL(PGLOBAL g,const char * stmt)608 int MYSQLC::PrepareSQL(PGLOBAL g, const char *stmt)
609   {
610   if (!m_DB) {
611     strcpy(g->Message, "MySQL not connected");
612     return -4;
613   } else if (m_Stmt)
614     return -1;              // should not append
615 
616 #if defined(ALPHA)
617   if (!(m_Stmt = mysql_prepare(m_DB, stmt, strlen(stmt)))) {
618 
619     sprintf(g->Message, "mysql_prepare failed: %s [%s]",
620                          mysql_error(m_DB), stmt);
621     return -1;
622     } // endif m_Stmt
623 
624   // Return the parameter count from the statement
625   return mysql_param_count(m_Stmt);
626 #else   // !ALPHA
627   if (!(m_Stmt = mysql_stmt_init(m_DB))) {
628     strcpy(g->Message, "mysql_stmt_init(), out of memory");
629     return -2;
630     } // endif m_Stmt
631 
632   if (mysql_stmt_prepare(m_Stmt, stmt, strlen(stmt))) {
633     sprintf(g->Message, "mysql_stmt_prepare() failed: (%d) %s",
634             mysql_stmt_errno(m_Stmt), mysql_stmt_error(m_Stmt));
635     return -3;
636     } // endif prepare
637 
638   // Return the parameter count from the statement
639   return mysql_stmt_param_count(m_Stmt);
640 #endif   // !ALPHA
641   } // end of PrepareSQL
642 
643 /***********************************************************************/
644 /*  Bind the parameter buffers.                                        */
645 /***********************************************************************/
BindParams(PGLOBAL g,MYSQL_BIND * bind)646 int MYSQLC::BindParams(PGLOBAL g, MYSQL_BIND *bind)
647   {
648   if (!m_DB) {
649     strcpy(g->Message, "MySQL not connected");
650     return RC_FX;
651   } else
652     assert(m_Stmt);
653 
654 #if defined(ALPHA)
655   if (mysql_bind_param(m_Stmt, bind)) {
656     sprintf(g->Message, "mysql_bind_param() failed: %s",
657                         mysql_stmt_error(m_Stmt));
658 #else   // !ALPHA
659   if (mysql_stmt_bind_param(m_Stmt, bind)) {
660     sprintf(g->Message, "mysql_stmt_bind_param() failed: %s",
661                         mysql_stmt_error(m_Stmt));
662 #endif  // !ALPHA
663     return RC_FX;
664     } // endif bind
665 
666   return RC_OK;
667 
668 /***********************************************************************/
669 /*  Execute a prepared statement.                                      */
670 /***********************************************************************/
671 int MYSQLC::ExecStmt(PGLOBAL g)
672   {
673   if (!m_DB) {
674     strcpy(g->Message, "MySQL not connected");
675     return RC_FX;
676     } // endif m_DB
677 
678 #if defined(ALPHA)
679   if (mysql_execute(m_Stmt)) {
680     sprintf(g->Message, "mysql_execute() failed: %s",
681                         mysql_stmt_error(m_Stmt));
682     return RC_FX;
683     } // endif execute
684 #else   // !ALPHA
685   if (mysql_stmt_execute(m_Stmt)) {
686     sprintf(g->Message, "mysql_stmt_execute() failed: %s",
687                         mysql_stmt_error(m_Stmt));
688     return RC_FX;
689     } // endif execute
690 #endif  // !ALPHA
691 
692   // Check the total number of affected rows
693   if (mysql_stmt_affected_rows(m_Stmt) != 1) {
694     sprintf(g->Message, "Invalid affected rows by MySQL");
695     return RC_FX;
696     } // endif affected_rows
697 
698   return RC_OK;
699   } // end of ExecStmt
700 #endif   // MYSQL_PREPARED_STATEMENTS
701 
702 /***********************************************************************/
703 /*  Exec the Select SQL command and get back the result size in rows.  */
704 /***********************************************************************/
705 int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w)
706   {
707   int rc = RC_OK;
708 
709   if (!m_DB) {
710     strcpy(g->Message, "MySQL not connected");
711     return RC_FX;
712     } // endif m_DB
713 
714   if (w)
715     *w = 0;
716 
717   if (m_Rows >= 0)
718     return RC_OK;                  // Already done
719 
720 //if (mysql_query(m_DB, query) != 0) {
721   if (mysql_real_query(m_DB, query, strlen(query))) {
722     char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
723 
724     sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
725                                  mysql_error(m_DB), query);
726     strncpy(g->Message, msg, sizeof(g->Message) - 1);
727     g->Message[sizeof(g->Message) - 1] = 0;
728     rc = RC_FX;
729 //} else if (mysql_field_count(m_DB) > 0) {
730   } else if (m_DB->field_count > 0) {
731     if (m_Use)
732 #if defined(MYSQL_PREPARED_STATEMENTS)
733       m_Res = mysql_use_result(m_DB);
734 #else   // !MYSQL_PREPARED_STATEMENTS)
735       m_Res = connect_use_result(m_DB);
736 #endif  // !MYSQL_PREPARED_STATEMENTS
737     else
738       m_Res = mysql_store_result(m_DB);
739 
740     if (!m_Res) {
741       char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
742 
743       sprintf(msg, "mysql_store_result failed: %s", mysql_error(m_DB));
744       strncpy(g->Message, msg, sizeof(g->Message) - 1);
745       g->Message[sizeof(g->Message) - 1] = 0;
746       rc = RC_FX;
747     } else {
748       m_Fields = mysql_num_fields(m_Res);
749       m_Rows = (!m_Use) ? (int)mysql_num_rows(m_Res) : 0;
750 
751 			if (trace(1))
752 				htrc("ExecSQL: m_Res=%.4X size=%d m_Fields=%d m_Rows=%d\n",
753 				               m_Res, sizeof(*m_Res), m_Fields, m_Rows);
754 
755     } // endif m_Res
756 
757   } else {
758 //  m_Rows = (int)mysql_affected_rows(m_DB);
759     m_Rows = (int)m_DB->affected_rows;
760     sprintf(g->Message, "Affected rows: %d\n", m_Rows);
761     rc = RC_NF;
762   } // endif field count
763 
764   if (w)
765 //  *w = mysql_warning_count(m_DB);
766     *w = m_DB->warning_count;
767 
768   return rc;
769   } // end of ExecSQL
770 
771 /***********************************************************************/
772 /*  Get table size by executing "select count(*) from table_name".     */
773 /***********************************************************************/
774 int MYSQLC::GetTableSize(PGLOBAL g __attribute__((unused)), PSZ query)
775   {
776   if (mysql_real_query(m_DB, query, strlen(query))) {
777 #if defined(_DEBUG)
778     char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query));
779 
780     sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB),
781                                  mysql_error(m_DB), query);
782     strncpy(g->Message, msg, sizeof(g->Message) - 1);
783     g->Message[sizeof(g->Message) - 1] = 0;
784 #endif   // _DEBUG
785     return -2;
786     } // endif mysql_real_query
787 
788   if (!(m_Res = mysql_store_result(m_DB)))
789     return -3;
790 
791   // Get the resulting count value
792   m_Rows = (int)mysql_num_rows(m_Res);     // Should be 1
793 
794   if (m_Rows && (m_Row = mysql_fetch_row(m_Res)))
795     return atoi(*m_Row);
796 
797   return -4;
798   } // end of GetTableSize
799 
800 /***********************************************************************/
801 /*  Move to a specific row and column                                  */
802 /***********************************************************************/
803 void MYSQLC::DataSeek(my_ulonglong row)
804   {
805   MYSQL_ROWS *tmp = 0;
806 //DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row));
807 
808   if (m_Res->data)
809     for (tmp = m_Res->data->data; row-- && tmp; tmp = tmp->next) ;
810 
811   m_Res->current_row = 0;
812   m_Res->data_cursor = tmp;
813   } // end of DataSeek
814 
815 /***********************************************************************/
816 /*  Fetch one result line from the query result set.                   */
817 /***********************************************************************/
818 int MYSQLC::Fetch(PGLOBAL g, int pos)
819   {
820   if (!m_DB) {
821     strcpy(g->Message, "MySQL not connected");
822     return RC_FX;
823     } // endif m_DB
824 
825   if (!m_Res) {
826     // Result set was not initialized
827     strcpy(g->Message, MSG(FETCH_NO_RES));
828     return RC_FX;
829   } else
830     N++;
831 
832   if (pos >= 0)
833 //  mysql_data_seek(m_Res, (my_ulonglong)pos);
834     DataSeek((my_ulonglong)pos);
835 
836   m_Row = mysql_fetch_row(m_Res);
837   return (m_Row) ? RC_OK : RC_EF;
838   } // end of Fetch
839 
840 /***********************************************************************/
841 /*  Get one field of the current row.                                  */
842 /***********************************************************************/
843 char *MYSQLC::GetCharField(int i)
844   {
845   if (m_Res && m_Row) {
846 #if defined(_DEBUG)
847 //  MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i);
848 #endif   // _DEBUG
849     MYSQL_ROW row = m_Row + i;
850 
851     return (row) ? (char*)*row : (char*)"<null>";
852   } else
853     return NULL;
854 
855   } // end of GetCharField
856 
857 /***********************************************************************/
858 /*  Get the max length of the field.                                   */
859 /***********************************************************************/
860 int MYSQLC::GetFieldLength(int i)
861   {
862   if (m_Res) {
863 //  MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i);
864 //  return fld->max_length;
865     return (m_Res)->fields[i].max_length;
866   } else
867     return 0;
868 
869   } // end of GetFieldLength
870 
871 /***********************************************************************/
872 /*  Return next field of the query results.                            */
873 /***********************************************************************/
874 MYSQL_FIELD *MYSQLC::GetNextField(void)
875   {
876   return (m_Res->current_field >= m_Res->field_count) ? NULL
877        : &m_Res->fields[m_Res->current_field++];
878   } // end of GetNextField
879 
880 /***********************************************************************/
881 /*  Make a CONNECT result structure from the MySQL result.             */
882 /***********************************************************************/
883 PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb)
884   {
885 	PCSZ         fmt;
886   char        *name, v= 0;
887   int          n;
888   bool         uns;
889   PCOLRES     *pcrp, crp;
890   PQRYRES      qrp;
891   MYSQL_FIELD *fld;
892   MYSQL_ROW    row;
893 
894   if (!m_Res || !m_Fields) {
895     sprintf(g->Message, "%s result", (m_Res) ? "Void" : "No");
896     return NULL;
897     } // endif m_Res
898 
899   /*********************************************************************/
900   /*  Put the result in storage for future retrieval.                  */
901   /*********************************************************************/
902   qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
903   pcrp = &qrp->Colresp;
904   qrp->Continued = FALSE;
905   qrp->Truncated = FALSE;
906   qrp->Info = FALSE;
907   qrp->Suball = TRUE;
908   qrp->BadLines = 0;
909   qrp->Maxsize = m_Rows;
910   qrp->Maxres = m_Rows;
911   qrp->Nbcol = 0;
912   qrp->Nblin = 0;
913   qrp->Cursor = 0;
914 
915 //for (fld = mysql_fetch_field(m_Res); fld;
916 //     fld = mysql_fetch_field(m_Res)) {
917   for (fld = GetNextField(); fld; fld = GetNextField()) {
918     *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
919     crp = *pcrp;
920     pcrp = &crp->Next;
921     memset(crp, 0, sizeof(COLRES));
922     crp->Ncol = ++qrp->Nbcol;
923 
924     name = (char*)PlugSubAlloc(g, NULL, fld->name_length + 1);
925     strcpy(name, fld->name);
926 		crp->Name = name;
927 
928     if ((crp->Type = MYSQLtoPLG(fld->type, &v)) == TYPE_ERROR) {
929       sprintf(g->Message, "Type %d not supported for column %s",
930                           fld->type, crp->Name);
931       return NULL;
932     } else if (crp->Type == TYPE_DATE && !pdb)
933       // For direct MySQL connection, display the MySQL date string
934       crp->Type = TYPE_STRING;
935     else
936       crp->Var = v;
937 
938     crp->Prec = (crp->Type == TYPE_DOUBLE || crp->Type == TYPE_DECIM)
939               ? fld->decimals : 0;
940     CHARSET_INFO *cs= get_charset(fld->charsetnr, MYF(0));
941     crp->Clen = GetTypeSize(crp->Type, fld->length);
942     crp->Length = fld->length / (cs ? cs->mbmaxlen : 1);
943     uns = (fld->flags & (UNSIGNED_FLAG | ZEROFILL_FLAG)) ? true : false;
944 
945     if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
946                                      crp->Clen, 0, FALSE, TRUE, uns))) {
947       sprintf(g->Message, MSG(INV_RESULT_TYPE),
948                           GetFormatType(crp->Type));
949       return NULL;
950     } else if (crp->Type == TYPE_DATE) {
951       fmt = MyDateFmt(fld->type);
952       crp->Kdata->SetFormat(g, fmt, strlen(fmt));
953     } // endif's
954 
955     if (fld->flags & NOT_NULL_FLAG)
956       crp->Nulls = NULL;
957     else {
958 			if (m_Rows) {
959 				crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
960 				memset(crp->Nulls, ' ', m_Rows);
961 			} // endif m_Rows
962 
963 			crp->Kdata->SetNullable(true);
964     } // endelse fld->flags
965 
966     } // endfor fld
967 
968   *pcrp = NULL;
969   assert(qrp->Nbcol == m_Fields);
970 
971   /*********************************************************************/
972   /*  Now fill the allocated result structure.                         */
973   /*********************************************************************/
974   for (n = 0; n < m_Rows; n++) {
975     if (!(m_Row = mysql_fetch_row(m_Res))) {
976       sprintf(g->Message, "Missing row %d from result", n + 1);
977       return NULL;
978       } // endif m_Row
979 
980     for (crp = qrp->Colresp; crp; crp = crp->Next) {
981       if ((row = m_Row + (crp->Ncol - 1))) {
982         if (*row)
983           crp->Kdata->SetValue((PSZ)*row, n);
984         else {
985           if (!*row && crp->Nulls)
986             crp->Nulls[n] = '*';           // Null value
987 
988           crp->Kdata->Reset(n);
989         } // endelse *row
990       }
991 
992     } // endfor crp
993 
994   } // endfor n
995 
996   qrp->Nblin = n;
997   return qrp;
998   } // end of GetResult
999 
1000 /***********************************************************************/
1001 /*  Free the current result.                                           */
1002 /***********************************************************************/
1003 void MYSQLC::FreeResult(void)
1004   {
1005   if (m_Res) {
1006     mysql_free_result(m_Res);
1007     m_Res = NULL;
1008     } // endif m_Res
1009 
1010   // Reset the connection
1011   m_Row = NULL;
1012   m_Rows = -1;
1013   m_Fields = -1;
1014   N = 0;
1015   } // end of FreeResult
1016 
1017 /***********************************************************************/
1018 /*  Place the cursor at the beginning of the result set.               */
1019 /***********************************************************************/
1020 int MYSQLC::Rewind(PGLOBAL g, PSZ sql)
1021   {
1022 		int rc = RC_OK;
1023 
1024 		if (m_Res)
1025 			DataSeek(0);
1026 		else if (sql)
1027 			rc = ExecSQL(g, sql);
1028 
1029 		return rc;
1030   } // end of Rewind
1031 
1032 /***********************************************************************/
1033 /*  Exec the Select SQL command and return ncol or afrws (TDBMYEXC).   */
1034 /***********************************************************************/
1035 int MYSQLC::ExecSQLcmd(PGLOBAL g, const char *query, int *w)
1036   {
1037   int rc = RC_OK;
1038 
1039   if (!m_DB) {
1040     strcpy(g->Message, "MySQL not connected");
1041     return RC_FX;
1042   } else
1043     *w = 0;
1044 
1045   if (!stricmp(query, "Warning") || !stricmp(query, "Note")
1046                                  || !stricmp(query, "Error"))
1047     return RC_INFO;
1048   else
1049     m_Afrw = 0;
1050 
1051 //if (mysql_query(m_DB, query) != 0) {
1052   if (mysql_real_query(m_DB, query, strlen(query))) {
1053     m_Afrw = (int)mysql_errno(m_DB);
1054     sprintf(g->Message, "Remote: %s", mysql_error(m_DB));
1055     rc = RC_FX;
1056 //} else if (!(m_Fields = mysql_field_count(m_DB))) {
1057   } else if (!(m_Fields = (int)m_DB->field_count)) {
1058 //  m_Afrw = (int)mysql_affected_rows(m_DB);
1059     m_Afrw = (int)m_DB->affected_rows;
1060     rc = RC_NF;
1061   } // endif's
1062 
1063 //*w = mysql_warning_count(m_DB);
1064   *w = m_DB->warning_count;
1065   return rc;
1066   } // end of ExecSQLcmd
1067 
1068 /***********************************************************************/
1069 /*  Close the connection.                                              */
1070 /***********************************************************************/
1071 void MYSQLC::Close(void)
1072   {
1073   FreeResult();
1074 
1075 	if (trace(1))
1076 		htrc("MYSQLC Close: m_DB=%.4X\n", m_DB);
1077 
1078 	mysql_close(m_DB);
1079   m_DB = NULL;
1080   } // end of Close
1081 
1082 #if 0                       // not used yet
1083 /***********************************************************************/
1084 /*  Discard additional results from a stored procedure.                */
1085 /***********************************************************************/
1086 void MYSQLC::DiscardResults(void)
1087   {
1088   MYSQL_RES *res;
1089 
1090   while (!mysql_next_result(m_DB)) {
1091     res = mysql_store_result(m_DB);
1092     mysql_free_result(res);
1093     } // endwhile next result
1094 
1095   } // end of DiscardResults
1096 #endif // 0
1097