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 | |