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