| 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; | 
| 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 | 
| 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 | **************************************************************************/ | 
| 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(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 | /************************************************************************/ | 
| 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, 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 | /************************************************************************/ | 
| 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 |  | 
| 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 | /***********************************************************************/ | 
| 437 | MYSQLC::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 | /***********************************************************************/ | 
| 458 | int 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 | /***********************************************************************/ | 
| 470 | int 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 | /***********************************************************************/ | 
| 547 | bool 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 | /***********************************************************************/ | 
| 564 | ulong 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 | /***********************************************************************/ | 
| 572 | const 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 | /***********************************************************************/ | 
| 582 | ulong 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 | /**************************************************************************/ | 
| 591 | int 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 | /***********************************************************************/ | 
| 604 | int 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 | /***********************************************************************/ | 
| 642 | int 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 | /***********************************************************************/ | 
| 667 | int 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 | /***********************************************************************/ | 
| 701 | int 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 | /***********************************************************************/ | 
| 770 | int 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 | /***********************************************************************/ | 
| 799 | void 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 | /***********************************************************************/ | 
| 814 | int 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 | /***********************************************************************/ | 
| 839 | char *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 | /***********************************************************************/ | 
| 856 | int 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 | /***********************************************************************/ | 
| 870 | MYSQL_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 | /***********************************************************************/ | 
| 879 | PQRYRES 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 | /***********************************************************************/ | 
| 999 | void 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 | /***********************************************************************/ | 
| 1016 | int 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 | /***********************************************************************/ | 
| 1031 | int 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 | /***********************************************************************/ | 
| 1067 | void 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 | /***********************************************************************/ | 
| 1082 | void 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 |  |