1 /************* TabMySQL C++ Program Source Code File (.CPP) *************/
2 /* PROGRAM NAME: TABMYSQL */
3 /* ------------- */
4 /* Version 2.0 */
5 /* */
6 /* AUTHOR: */
7 /* ------- */
8 /* Olivier BERTRAND 2007-2017 */
9 /* */
10 /* WHAT THIS PROGRAM DOES: */
11 /* ----------------------- */
12 /* Implements a table type that are MySQL tables. */
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 /* TABMYSQL.CPP - Source code */
21 /* PLGDBSEM.H - DB application declaration file */
22 /* TABMYSQL.H - TABMYSQL classes declaration file */
23 /* GLOBAL.H - Global declaration file */
24 /* */
25 /* REQUIRED LIBRARIES: */
26 /* ------------------- */
27 /* Large model C library */
28 /* */
29 /* REQUIRED PROGRAMS: */
30 /* ------------------ */
31 /* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
32 /* */
33 /************************************************************************/
34 #define MYSQL_SERVER 1
35 #include "my_global.h"
36 #include "sql_class.h"
37 #include "sql_servers.h"
38 #if defined(_WIN32)
39 //#include <windows.h>
40 #else // !_WIN32
41 //#include <fnmatch.h>
42 //#include <errno.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include "osutil.h"
47 //#include <io.h>
48 //#include <fcntl.h>
49 #endif // !_WIN32
50
51 /***********************************************************************/
52 /* Include application header files: */
53 /***********************************************************************/
54 #include "global.h"
55 #include "plgdbsem.h"
56 #include "xtable.h"
57 #include "tabext.h"
58 #include "tabcol.h"
59 #include "colblk.h"
60 //#include "reldef.h"
61 #include "tabmysql.h"
62 #include "valblk.h"
63 #include "tabutil.h"
64 #include "ha_connect.h"
65
66 #if defined(_CONSOLE)
67 void PrintResult(PGLOBAL, PSEM, PQRYRES);
68 #endif // _CONSOLE
69
70 // Used to check whether a MYSQL table is created on itself
71 bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host, PCSZ db,
72 PCSZ tab, PCSZ src, int port);
73
74 /***********************************************************************/
75 /* External function. */
76 /***********************************************************************/
77 bool ExactInfo(void);
78
79 /* -------------- Implementation of the MYSQLDEF class --------------- */
80
81 /***********************************************************************/
82 /* Constructor. */
83 /***********************************************************************/
MYSQLDEF(void)84 MYSQLDEF::MYSQLDEF(void)
85 {
86 Pseudo = 2; // SERVID is Ok but not ROWID
87 Hostname = NULL;
88 //Tabschema = NULL;
89 //Tabname = NULL;
90 //Srcdef = NULL;
91 //Username = NULL;
92 //Password = NULL;
93 Portnumber = 0;
94 Isview = false;
95 Bind = false;
96 Delayed = false;
97 //Xsrc = false;
98 Huge = false;
99 } // end of MYSQLDEF constructor
100
101 /***********************************************************************/
102 /* Get connection info from the declared server. */
103 /***********************************************************************/
GetServerInfo(PGLOBAL g,const char * server_name)104 bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
105 {
106 THD *thd= current_thd;
107 MEM_ROOT *mem= thd->mem_root;
108 FOREIGN_SERVER *server, server_buffer;
109 DBUG_ENTER("GetServerInfo");
110 DBUG_PRINT("info", ("server_name %s", server_name));
111
112 if (!server_name || !strlen(server_name)) {
113 DBUG_PRINT("info", ("server_name not defined!"));
114 strcpy(g->Message, "server_name not defined!");
115 DBUG_RETURN(true);
116 } // endif server_name
117
118 // get_server_by_name() clones the server if exists and allocates
119 // copies of strings in the supplied mem_root
120 if (!(server= get_server_by_name(mem, server_name, &server_buffer))) {
121 DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!"));
122 /* need to come up with error handling */
123 strcpy(g->Message, "get_server_by_name returned > 0 error condition!");
124 DBUG_RETURN(true);
125 } // endif server
126
127 DBUG_PRINT("info", ("get_server_by_name returned server at %p",
128 server));
129
130 // TODO: We need to examine which of these can really be NULL
131 Hostname = PlugDup(g, server->host);
132 Tabschema = PlugDup(g, server->db);
133 Username = PlugDup(g, server->username);
134 Password = PlugDup(g, server->password);
135 Portnumber = (server->port) ? server->port : GetDefaultPort();
136
137 DBUG_RETURN(false);
138 } // end of GetServerInfo
139
140 /***********************************************************************/
141 /* Parse connection string */
142 /* */
143 /* SYNOPSIS */
144 /* ParseURL() */
145 /* url The connection string to parse */
146 /* */
147 /* DESCRIPTION */
148 /* Populates the table with information about the connection */
149 /* to the foreign database that will serve as the data source. */
150 /* This string must be specified (currently) in the "CONNECTION" */
151 /* field, listed in the CREATE TABLE statement. */
152 /* */
153 /* This string MUST be in the format of any of these: */
154 /* */
155 /* CONNECTION="scheme://user:pwd@host:port/database/table" */
156 /* CONNECTION="scheme://user@host/database/table" */
157 /* CONNECTION="scheme://user@host:port/database/table" */
158 /* CONNECTION="scheme://user:pwd@host/database/table" */
159 /* */
160 /* _OR_ */
161 /* */
162 /* CONNECTION="connection name" (NIY) */
163 /* */
164 /* An Example: */
165 /* */
166 /* CREATE TABLE t1 (id int(32)) */
167 /* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
168 /* CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname"; */
169 /* */
170 /* CREATE TABLE t2 ( */
171 /* id int(4) NOT NULL auto_increment, */
172 /* name varchar(32) NOT NULL, */
173 /* PRIMARY KEY(id) */
174 /* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
175 /* CONNECTION="my_conn"; (NIY) */
176 /* */
177 /* 'password' and 'port' are both optional. */
178 /* */
179 /* RETURN VALUE */
180 /* false success */
181 /* true error */
182 /* */
183 /***********************************************************************/
ParseURL(PGLOBAL g,char * url,bool b)184 bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
185 {
186 char *tabn, *pwd, *schema;
187
188 if ((!strstr(url, "://") && (!strchr(url, '@')))) {
189 // No :// or @ in connection string. Must be a straight
190 // connection name of either "server" or "server/table"
191 // ok, so we do a little parsing, but not completely!
192 if ((tabn= strchr(url, '/'))) {
193 // If there is a single '/' in the connection string,
194 // this means the user is specifying a table name
195 *tabn++= '\0';
196
197 // there better not be any more '/'s !
198 if (strchr(tabn, '/'))
199 return true;
200
201 Tabname = tabn;
202 } else
203 // Otherwise, straight server name,
204 Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
205
206 if (trace(1))
207 htrc("server: %s TableName: %s", url, Tabname);
208
209 Server = url;
210 return GetServerInfo(g, url);
211 } else {
212 // URL, parse it
213 char *sport, *scheme = url;
214
215 if (!(Username = strstr(url, "://"))) {
216 strcpy(g->Message, "Connection is not an URL");
217 return true;
218 } // endif User
219
220 scheme[Username - scheme] = 0;
221
222 if (stricmp(scheme, "mysql")) {
223 strcpy(g->Message, "scheme must be mysql");
224 return true;
225 } // endif scheme
226
227 Username += 3;
228
229 if (!(Hostname = (char*)strchr(Username, '@'))) {
230 strcpy(g->Message, "No host specified in URL");
231 return true;
232 } else {
233 *Hostname++ = 0; // End Username
234 Server = Hostname;
235 } // endif Hostname
236
237 if ((pwd = (char*)strchr(Username, ':'))) {
238 *pwd++ = 0; // End username
239
240 // Make sure there isn't an extra /
241 if (strchr(pwd, '/')) {
242 strcpy(g->Message, "Syntax error in URL");
243 return true;
244 } // endif
245
246 // Found that if the string is:
247 // user:@hostname:port/db/table
248 // Then password is a null string, so set to NULL
249 if ((pwd[0] == 0))
250 Password = NULL;
251 else
252 Password = pwd;
253
254 } // endif password
255
256 // Make sure there isn't an extra / or @ */
257 if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) {
258 strcpy(g->Message, "Syntax error in URL");
259 return true;
260 } // endif
261
262 if ((schema = strchr(Hostname, '/'))) {
263 *schema++ = 0;
264
265 if ((tabn = strchr(schema, '/'))) {
266 *tabn++ = 0;
267
268 // Make sure there's not an extra /
269 if ((strchr(tabn, '/'))) {
270 strcpy(g->Message, "Syntax error in URL");
271 return true;
272 } // endif /
273
274 Tabname = tabn;
275 } // endif TableName
276
277 Tabschema = schema;
278 } // endif database
279
280 if ((sport = strchr(Hostname, ':')))
281 *sport++ = 0;
282
283 // For unspecified values, get the values of old style options
284 // but only if called from MYSQLDEF, else set them to NULL
285 Portnumber = (sport && sport[0]) ? atoi(sport)
286 : (b) ? GetIntCatInfo("Port", GetDefaultPort()) : 0;
287
288 if (Username[0] == 0)
289 Username = (b) ? GetStringCatInfo(g, "User", "*") : NULL;
290
291 if (Hostname[0] == 0)
292 Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL;
293
294 if (!Tabschema || !*Tabschema)
295 Tabschema = (b) ? GetStringCatInfo(g, "Database", "*") : NULL;
296
297 if (!Tabname || !*Tabname)
298 Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
299
300 if (!Password)
301 Password = (b) ? GetStringCatInfo(g, "Password", NULL) : NULL;
302 } // endif URL
303
304 #if 0
305 if (!share->port)
306 if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
307 share->socket= (char *) MYSQL_UNIX_ADDR;
308 else
309 share->port= MYSQL_PORT;
310 #endif // 0
311
312 return false;
313 } // end of ParseURL
314
315 /***********************************************************************/
316 /* DefineAM: define specific AM block values from XCV file. */
317 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int)318 bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
319 {
320 char *url;
321
322 Desc = "MySQL Table";
323
324 if (stricmp(am, "MYPRX")) {
325 // Normal case of specific MYSQL table
326 url = GetStringCatInfo(g, "Connect", NULL);
327
328 if (!url || !*url) {
329 // Not using the connection URL
330 Hostname = GetStringCatInfo(g, "Host", "localhost");
331 Tabschema = GetStringCatInfo(g, "Database", "*");
332 Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated
333 Tabname = GetStringCatInfo(g, "Tabname", Tabname);
334 Username = GetStringCatInfo(g, "User", "*");
335 Password = GetStringCatInfo(g, "Password", NULL);
336 Portnumber = GetIntCatInfo("Port", GetDefaultPort());
337 Server = Hostname;
338 } else if (ParseURL(g, url))
339 return true;
340
341 Bind = !!GetIntCatInfo("Bind", 0);
342 Delayed = !!GetIntCatInfo("Delayed", 0);
343 } else {
344 // MYSQL access from a PROXY table
345 TABLE_SHARE* s;
346
347 Tabschema = GetStringCatInfo(g, "Database", Tabschema ? Tabschema : PlugDup(g, "*"));
348 Isview = GetBoolCatInfo("View", false);
349
350 // We must get other connection parms from the calling table
351 s = Remove_tshp(Cat);
352 url = GetStringCatInfo(g, "Connect", NULL);
353
354 if (!url || !*url) {
355 Hostname = GetStringCatInfo(g, "Host", "localhost");
356 Username = GetStringCatInfo(g, "User", "*");
357 Password = GetStringCatInfo(g, "Password", NULL);
358 Portnumber = GetIntCatInfo("Port", GetDefaultPort());
359 Server = Hostname;
360 } else {
361 PCSZ locdb = Tabschema;
362
363 if (ParseURL(g, url))
364 return true;
365
366 Tabschema = locdb;
367 } // endif url
368
369 Tabname = Name;
370
371 // Needed for column description
372 Restore_tshp(Cat, s);
373 } // endif am
374
375 if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) {
376 Read_Only = true;
377 Isview = true;
378 } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Tabschema,
379 Tabname, Srcdef, Portnumber))
380 return true;
381
382 // Used for Update and Delete
383 Qrystr = GetStringCatInfo(g, "Query_String", "?");
384 Quoted = GetIntCatInfo("Quoted", 0);
385
386 // Specific for command executing tables
387 Xsrc = GetBoolCatInfo("Execsrc", false);
388 Maxerr = GetIntCatInfo("Maxerr", 0);
389 Huge = GetBoolCatInfo("Huge", false);
390 return false;
391 } // end of DefineAM
392
393 /***********************************************************************/
394 /* GetTable: makes a new TDB of the proper type. */
395 /***********************************************************************/
GetTable(PGLOBAL g,MODE)396 PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE)
397 {
398 if (Xsrc)
399 return new(g) TDBMYEXC(this);
400 else if (Catfunc == FNC_COL)
401 return new(g) TDBMCL(this);
402 else
403 return new(g) TDBMYSQL(this);
404
405 } // end of GetTable
406
407 /* ------------------------------------------------------------------- */
408
409 /***********************************************************************/
410 /* Implementation of the TDBMYSQL class. */
411 /***********************************************************************/
TDBMYSQL(PMYDEF tdp)412 TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBEXT(tdp)
413 {
414 if (tdp) {
415 Host = tdp->Hostname;
416 // Schema = tdp->Tabschema;
417 // TableName = tdp->Tabname;
418 // Srcdef = tdp->Srcdef;
419 // User = tdp->Username;
420 // Pwd = tdp->Password;
421 Server = tdp->Server;
422 // Qrystr = tdp->Qrystr;
423 Quoted = MY_MAX(0, tdp->Quoted);
424 Port = tdp->Portnumber;
425 Isview = tdp->Isview;
426 Prep = tdp->Bind;
427 Delayed = tdp->Delayed;
428 Myc.m_Use = tdp->Huge;
429 } else {
430 Host = NULL;
431 // Schema = NULL;
432 // TableName = NULL;
433 // Srcdef = NULL;
434 // User = NULL;
435 // Pwd = NULL;
436 Server = NULL;
437 // Qrystr = NULL;
438 // Quoted = 0;
439 Port = 0;
440 Isview = false;
441 Prep = false;
442 Delayed = false;
443 } // endif tdp
444
445 Bind = NULL;
446 //Query = NULL;
447 Fetched = false;
448 m_Rc = RC_FX;
449 //AftRows = 0;
450 N = -1;
451 //Nparm = 0;
452 } // end of TDBMYSQL constructor
453
TDBMYSQL(PTDBMY tdbp)454 TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBEXT(tdbp)
455 {
456 Host = tdbp->Host;
457 //Schema = tdbp->Schema;
458 //TableName = tdbp->TableName;
459 //Srcdef = tdbp->Srcdef;
460 //User = tdbp->User;
461 //Pwd = tdbp->Pwd;
462 //Qrystr = tdbp->Qrystr;
463 //Quoted = tdbp->Quoted;
464 Server = tdbp->Server;
465 Port = tdbp->Port;
466 Isview = tdbp->Isview;
467 Prep = tdbp->Prep;
468 Delayed = tdbp->Delayed;
469 Bind = NULL;
470 //Query = tdbp->Query;
471 Fetched = tdbp->Fetched;
472 m_Rc = tdbp->m_Rc;
473 //AftRows = tdbp->AftRows;
474 N = tdbp->N;
475 //Nparm = tdbp->Nparm;
476 } // end of TDBMYSQL copy constructor
477
478 // Is this really useful ??? --> Yes for UPDATE
Clone(PTABS t)479 PTDB TDBMYSQL::Clone(PTABS t)
480 {
481 PTDB tp;
482 PCOL cp1, cp2;
483 PGLOBAL g = t->G;
484
485 tp = new(g) TDBMYSQL(this);
486
487 for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
488 cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp);
489
490 NewPointer(t, cp1, cp2);
491 } // endfor cp1
492
493 return tp;
494 } // end of Clone
495
496 /***********************************************************************/
497 /* Allocate MYSQL column description block. */
498 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)499 PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
500 {
501 return new(g) MYSQLCOL(cdp, this, cprec, n);
502 } // end of MakeCol
503
504 /***********************************************************************/
505 /* MakeSelect: make the Select statement use with MySQL connection. */
506 /* Note: when implementing EOM filtering, column only used in local */
507 /* filter should be removed from column list. */
508 /***********************************************************************/
MakeSelect(PGLOBAL g,bool mx)509 bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
510 {
511 //char *tk = "`";
512 char tk = '`';
513 int len = 0, rank = 0;
514 bool b = false;
515 PCOL colp;
516 //PDBUSER dup = PlgGetUser(g);
517
518 if (Query)
519 return false; // already done
520
521 if (Srcdef)
522 return MakeSrcdef(g);
523
524 // Allocate the string used to contain Query
525 Query = new(g) STRING(g, 1023, "SELECT ");
526
527 if (Columns) {
528 for (colp = Columns; colp; colp = colp->GetNext())
529 if (!colp->IsSpecial()) {
530 if (b)
531 Query->Append(", ");
532 else
533 b = true;
534
535 Query->Append(tk);
536 Query->Append(colp->GetName());
537 Query->Append(tk);
538 ((PMYCOL)colp)->Rank = rank++;
539 } // endif colp
540
541 } else {
542 // ncol == 0 can occur for views or queries such as
543 // Query count(*) from... for which we will count the rows from
544 // Query '*' from...
545 // (the use of a char constant minimize the result storage)
546 if (Isview)
547 Query->Append('*');
548 else
549 Query->Append("'*'");
550
551 } // endif ncol
552
553 Query->Append(" FROM ");
554 Query->Append(tk);
555 Query->Append(TableName);
556 Query->Append(tk);
557 len = Query->GetLength();
558
559 if (To_CondFil) {
560 if (!mx) {
561 Query->Append(" WHERE ");
562 Query->Append(To_CondFil->Body);
563 len = Query->GetLength() + 1;
564 } else
565 len += (strlen(To_CondFil->Body) + 256);
566
567 } else
568 len += (mx ? 256 : 1);
569
570 if (Query->IsTruncated() || Query->Resize(len)) {
571 strcpy(g->Message, "MakeSelect: Out of memory");
572 return true;
573 } // endif Query
574
575 if (trace(33))
576 htrc("Query=%s\n", Query->GetStr());
577
578 return false;
579 } // end of MakeSelect
580
581 /***********************************************************************/
582 /* MakeInsert: make the Insert statement used with MySQL connection. */
583 /***********************************************************************/
MakeInsert(PGLOBAL g)584 bool TDBMYSQL::MakeInsert(PGLOBAL g)
585 {
586 const char *tk = "`";
587 uint len = 0;
588 bool oom, b = false;
589 PCOL colp;
590
591 if (Query)
592 return false; // already done
593
594 if (Prep) {
595 #if !defined(MYSQL_PREPARED_STATEMENTS)
596 strcpy(g->Message, "Prepared statements not used (not supported)");
597 PushWarning(g, this);
598 Prep = false;
599 #endif // !MYSQL_PREPARED_STATEMENTS
600 } // endif Prep
601
602 for (colp = Columns; colp; colp = colp->GetNext())
603 if (colp->IsSpecial()) {
604 strcpy(g->Message, MSG(NO_SPEC_COL));
605 return true;
606 } else {
607 len += (strlen(colp->GetName()) + 4);
608
609 // Parameter marker
610 if (!Prep) {
611 if (colp->GetResultType() == TYPE_DATE)
612 len += 20;
613 else
614 len += colp->GetLength();
615
616 } else
617 len += 2;
618
619 ((PMYCOL)colp)->Rank = Nparm++;
620 } // endif colp
621
622 // Below 40 is enough to contain the fixed part of the query
623 len += (strlen(TableName) + 40);
624 Query = new(g) STRING(g, len);
625
626 if (Delayed)
627 Query->Set("INSERT DELAYED INTO ");
628 else
629 Query->Set("INSERT INTO ");
630
631 Query->Append(tk);
632 Query->Append(TableName);
633 Query->Append("` (");
634
635 for (colp = Columns; colp; colp = colp->GetNext()) {
636 if (b)
637 Query->Append(", ");
638 else
639 b = true;
640
641 Query->Append(tk);
642 Query->Append(colp->GetName());
643 Query->Append(tk);
644 } // endfor colp
645
646 Query->Append(") VALUES (");
647
648 #if defined(MYSQL_PREPARED_STATEMENTS)
649 if (Prep) {
650 for (int i = 0; i < Nparm; i++)
651 Query->Append("?,");
652
653 Query->RepLast(')');
654 Query->Trim();
655 } // endif Prep
656 #endif // MYSQL_PREPARED_STATEMENTS
657
658 if ((oom = Query->IsTruncated()))
659 strcpy(g->Message, "MakeInsert: Out of memory");
660
661 return oom;
662 } // end of MakeInsert
663
664 /***********************************************************************/
665 /* MakeCommand: make the Update or Delete statement to send to the */
666 /* MySQL server. Limited to remote values and filtering. */
667 /***********************************************************************/
MakeCommand(PGLOBAL g)668 bool TDBMYSQL::MakeCommand(PGLOBAL g)
669 {
670 Query = new(g) STRING(g, strlen(Qrystr) + 64);
671
672 if (Quoted > 0 || stricmp(Name, TableName)) {
673 char *p, *qrystr, name[68];
674 bool qtd = Quoted > 0;
675
676
677 // Make a lower case copy of the originale query
678 qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 5);
679 strlwr(strcpy(qrystr, Qrystr));
680
681 // Check whether the table name is equal to a keyword
682 // If so, it must be quoted in the original query
683 strlwr(strcat(strcat(strcpy(name, "`"), Name), "`"));
684
685 if (!strstr("`update`delete`low_priority`ignore`quick`from`", name))
686 strlwr(strcpy(name, Name)); // Not a keyword
687
688 if ((p = strstr(qrystr, name))) {
689 Query->Set(Qrystr, (uint)(p - qrystr));
690
691 if (qtd && *(p-1) == ' ') {
692 Query->Append('`');
693 Query->Append(TableName);
694 Query->Append('`');
695 } else
696 Query->Append(TableName);
697
698 Query->Append(Qrystr + (p - qrystr) + strlen(name));
699
700 if (Query->IsTruncated()) {
701 strcpy(g->Message, "MakeCommand: Out of memory");
702 return true;
703 } else
704 strlwr(strcpy(qrystr, Query->GetStr()));
705
706 } else {
707 sprintf(g->Message, "Cannot use this %s command",
708 (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
709 return true;
710 } // endif p
711
712 } else
713 (void)Query->Set(Qrystr);
714
715 return false;
716 } // end of MakeCommand
717
718 #if 0
719 /***********************************************************************/
720 /* MakeUpdate: make the Update statement use with MySQL connection. */
721 /* Limited to remote values and filtering. */
722 /***********************************************************************/
723 int TDBMYSQL::MakeUpdate(PGLOBAL g)
724 {
725 char *qc, cmd[8], tab[96], end[1024];
726
727 Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
728 memset(end, 0, sizeof(end));
729
730 if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
731 sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
732 qc = "`";
733 else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
734 && !stricmp(tab, Name))
735 qc = (Quoted) ? "`" : "";
736 else {
737 strcpy(g->Message, "Cannot use this UPDATE command");
738 return RC_FX;
739 } // endif sscanf
740
741 assert(!stricmp(cmd, "update"));
742 strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), TableName), qc);
743 strcat(Query, end);
744 return RC_OK;
745 } // end of MakeUpdate
746
747 /***********************************************************************/
748 /* MakeDelete: make the Delete statement used with MySQL connection. */
749 /* Limited to remote filtering. */
750 /***********************************************************************/
751 int TDBMYSQL::MakeDelete(PGLOBAL g)
752 {
753 char *qc, cmd[8], from[8], tab[96], end[512];
754
755 Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
756 memset(end, 0, sizeof(end));
757
758 if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
759 sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
760 qc = "`";
761 else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
762 qc = (Quoted) ? "`" : "";
763 else {
764 strcpy(g->Message, "Cannot use this DELETE command");
765 return RC_FX;
766 } // endif sscanf
767
768 assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
769 strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), TableName), qc);
770
771 if (*end)
772 strcat(Query, end);
773
774 return RC_OK;
775 } // end of MakeDelete
776 #endif // 0
777
778 /***********************************************************************/
779 /* MYSQL Cardinality: returns the number of rows in the table. */
780 /***********************************************************************/
Cardinality(PGLOBAL g)781 int TDBMYSQL::Cardinality(PGLOBAL g)
782 {
783 if (!g)
784 return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
785
786 if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
787 // Info command, we must return the exact table row number
788 char query[96];
789 MYSQLC myc;
790
791 if (myc.Open(g, Host, Schema, User, Pwd, Port, csname))
792 return -1;
793
794 strcpy(query, "SELECT COUNT(*) FROM ");
795
796 if (Quoted > 0)
797 strcat(strcat(strcat(query, "`"), TableName), "`");
798 else
799 strcat(query, TableName);
800
801 Cardinal = myc.GetTableSize(g, query);
802 myc.Close();
803 } else
804 Cardinal = 10; // To make MySQL happy
805
806 return Cardinal;
807 } // end of Cardinality
808
809 #if 0
810 /***********************************************************************/
811 /* MYSQL GetMaxSize: returns the maximum number of rows in the table. */
812 /***********************************************************************/
813 int TDBMYSQL::GetMaxSize(PGLOBAL g)
814 {
815 if (MaxSize < 0) {
816 if (Mode == MODE_DELETE)
817 // Return 0 in mode DELETE in case of delete all.
818 MaxSize = 0;
819 else if (!Cardinality(NULL))
820 MaxSize = 10; // To make MySQL happy
821 else if ((MaxSize = Cardinality(g)) < 0)
822 MaxSize = 12; // So we can see an error occurred
823
824 } // endif MaxSize
825
826 return MaxSize;
827 } // end of GetMaxSize
828 #endif // 0
829
830 /***********************************************************************/
831 /* This a fake routine as ROWID does not exist in MySQL. */
832 /***********************************************************************/
RowNumber(PGLOBAL,bool)833 int TDBMYSQL::RowNumber(PGLOBAL, bool)
834 {
835 return N + 1;
836 } // end of RowNumber
837
838 /***********************************************************************/
839 /* Return 0 in mode UPDATE to tell that the update is done. */
840 /***********************************************************************/
GetProgMax(PGLOBAL g)841 int TDBMYSQL::GetProgMax(PGLOBAL g)
842 {
843 return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
844 } // end of GetProgMax
845
846 /***********************************************************************/
847 /* MySQL Bind Parameter function. */
848 /***********************************************************************/
BindColumns(PGLOBAL g)849 int TDBMYSQL::BindColumns(PGLOBAL g __attribute__((unused)))
850 {
851 #if defined(MYSQL_PREPARED_STATEMENTS)
852 if (Prep) {
853 Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND));
854
855 for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
856 colp->InitBind(g);
857
858 return Myc.BindParams(g, Bind);
859 } // endif prep
860 #endif // MYSQL_PREPARED_STATEMENTS
861
862 return RC_OK;
863 } // end of BindColumns
864
865 /***********************************************************************/
866 /* MySQL Access Method opening routine. */
867 /***********************************************************************/
OpenDB(PGLOBAL g)868 bool TDBMYSQL::OpenDB(PGLOBAL g)
869 {
870 if (Use == USE_OPEN) {
871 /*******************************************************************/
872 /* Table already open, just replace it at its beginning. */
873 /*******************************************************************/
874 if (Myc.Rewind(g, (Mode == MODE_READX) ? Query->GetStr() : NULL) != RC_OK)
875 return true;
876
877 N = -1;
878 return false;
879 } // endif use
880
881 /*********************************************************************/
882 /* Open a MySQL connection for this table. */
883 /* Note: this may not be the proper way to do. Perhaps it is better */
884 /* to test whether a connection is already open for this server */
885 /* and if so to allocate just a new result set. But this only for */
886 /* servers allowing concurency in getting results ??? */
887 /*********************************************************************/
888 if (!Myc.Connected()) {
889 if (Myc.Open(g, Host, Schema, User, Pwd, Port, csname))
890 return true;
891
892 } // endif Connected
893
894 /*********************************************************************/
895 /* Take care of DATE columns. */
896 /*********************************************************************/
897 for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
898 if (colp->Buf_Type == TYPE_DATE)
899 // Format must match DATETIME MySQL type
900 ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
901
902 /*********************************************************************/
903 /* Allocate whatever is used for getting results. */
904 /*********************************************************************/
905 if (Mode == MODE_READ || Mode == MODE_READX) {
906 MakeSelect(g, Mode == MODE_READX);
907 if (Mode == MODE_READ && !Query)
908 {
909 Myc.Close();
910 return true;
911 }
912 m_Rc = (Mode == MODE_READ)
913 ? Myc.ExecSQL(g, Query->GetStr()) : RC_OK;
914
915 #if 0
916 if (!Myc.m_Res || !Myc.m_Fields) {
917 sprintf(g->Message, "%s result", (Myc.m_Res) ? "Void" : "No");
918 Myc.Close();
919 return true;
920 } // endif m_Res
921 #endif // 0
922
923 if (!m_Rc && Srcdef)
924 if (SetColumnRanks(g))
925 return true;
926
927 } else if (Mode == MODE_INSERT) {
928 if (Srcdef) {
929 strcpy(g->Message, "No insert into anonym views");
930 Myc.Close();
931 return true;
932 } // endif Srcdef
933
934 if (!MakeInsert(g)) {
935 #if defined(MYSQL_PREPARED_STATEMENTS)
936 int n = (Prep)
937 ? Myc.PrepareSQL(g, Query->GetCharValue()) : Nparm;
938
939 if (Nparm != n) {
940 if (n >= 0) // Other errors return negative values
941 strcpy(g->Message, MSG(BAD_PARM_COUNT));
942
943 } else
944 #endif // MYSQL_PREPARED_STATEMENTS
945 m_Rc = BindColumns(g);
946
947 } // endif MakeInsert
948
949 if (m_Rc != RC_FX) {
950 char cmd[64];
951 int w;
952
953 sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", TableName);
954
955 m_Rc = Myc.ExecSQL(g, cmd, &w); // may fail for some engines
956 } // endif m_Rc
957
958 } else
959 // m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
960 m_Rc = (MakeCommand(g)) ? RC_FX : RC_OK;
961
962 if (m_Rc == RC_FX) {
963 Myc.Close();
964 return true;
965 } // endif rc
966
967 Use = USE_OPEN;
968 return false;
969 } // end of OpenDB
970
971 /***********************************************************************/
972 /* Set the rank of columns in the result set. */
973 /***********************************************************************/
SetColumnRanks(PGLOBAL g)974 bool TDBMYSQL::SetColumnRanks(PGLOBAL g)
975 {
976 for (PCOL colp = Columns; colp; colp = colp->GetNext())
977 if (((PMYCOL)colp)->FindRank(g))
978 return true;
979
980 return false;
981 } // end of SetColumnRanks
982
983 /***********************************************************************/
984 /* Called by Parent table to make the columns of a View. */
985 /***********************************************************************/
MakeFieldColumn(PGLOBAL g,char * name)986 PCOL TDBMYSQL::MakeFieldColumn(PGLOBAL g, char *name)
987 {
988 int n;
989 MYSQL_FIELD *fld;
990 PCOL cp, colp = NULL;
991
992 for (n = 0; n < Myc.m_Fields; n++) {
993 fld = &Myc.m_Res->fields[n];
994
995 if (!stricmp(name, fld->name)) {
996 colp = new(g) MYSQLCOL(fld, this, n);
997
998 if (colp->InitValue(g))
999 return NULL;
1000
1001 if (!Columns)
1002 Columns = colp;
1003 else for (cp = Columns; cp; cp = cp->GetNext())
1004 if (!cp->GetNext()) {
1005 cp->SetNext(colp);
1006 break;
1007 } // endif Next
1008
1009 break;
1010 } // endif name
1011
1012 } // endfor n
1013
1014 if (!colp)
1015 sprintf(g->Message, "Column %s is not in view", name);
1016
1017 return colp;
1018 } // end of MakeFieldColumn
1019
1020 /***********************************************************************/
1021 /* Called by Pivot tables to find default column names in a View */
1022 /* as the name of last field not equal to the passed name. */
1023 /***********************************************************************/
FindFieldColumn(char * name)1024 char *TDBMYSQL::FindFieldColumn(char *name)
1025 {
1026 int n;
1027 MYSQL_FIELD *fld;
1028 char *cp = NULL;
1029
1030 for (n = Myc.m_Fields - 1; n >= 0; n--) {
1031 fld = &Myc.m_Res->fields[n];
1032
1033 if (!name || stricmp(name, fld->name)) {
1034 cp = fld->name;
1035 break;
1036 } // endif name
1037
1038 } // endfor n
1039
1040 return cp;
1041 } // end of FindFieldColumn
1042
1043 /***********************************************************************/
1044 /* Send an UPDATE or DELETE command to the remote server. */
1045 /***********************************************************************/
SendCommand(PGLOBAL g)1046 int TDBMYSQL::SendCommand(PGLOBAL g)
1047 {
1048 int w;
1049
1050 if (Myc.ExecSQLcmd(g, Query->GetStr(), &w) == RC_NF) {
1051 AftRows = Myc.m_Afrw;
1052 sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
1053 PushWarning(g, this, 0); // 0 means a Note
1054
1055 if (trace(1))
1056 htrc("%s\n", g->Message);
1057
1058 if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
1059 // We got warnings from the remote server
1060 while (Myc.Fetch(g, -1) == RC_OK) {
1061 sprintf(g->Message, "%s: (%s) %s", TableName,
1062 Myc.GetCharField(1), Myc.GetCharField(2));
1063 PushWarning(g, this);
1064 } // endwhile Fetch
1065
1066 Myc.FreeResult();
1067 } // endif w
1068
1069 return RC_EF; // Nothing else to do
1070 } else
1071 return RC_FX; // Error
1072
1073 } // end of SendCommand
1074
1075 /***********************************************************************/
1076 /* Data Base indexed read routine for MYSQL access method. */
1077 /***********************************************************************/
ReadKey(PGLOBAL g,OPVAL op,const key_range * kr)1078 bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
1079 {
1080 int oldlen = Query->GetLength();
1081 PHC hc = To_Def->GetHandler();
1082
1083 if (!(kr || hc->end_range) || op == OP_NEXT ||
1084 Mode == MODE_UPDATE || Mode == MODE_DELETE) {
1085 if (!kr && Mode == MODE_READX) {
1086 // This is a false indexed read
1087 m_Rc = Myc.ExecSQL(g, Query->GetStr());
1088 Mode = MODE_READ;
1089 return (m_Rc == RC_FX) ? true : false;
1090 } // endif key
1091
1092 return false;
1093 } else {
1094 if (Myc.m_Res)
1095 Myc.FreeResult();
1096
1097 if (hc->MakeKeyWhere(g, Query, op, '`', kr))
1098 return true;
1099
1100 if (To_CondFil) {
1101 if (To_CondFil->Idx != hc->active_index) {
1102 To_CondFil->Idx = hc->active_index;
1103 To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
1104 *To_CondFil->Body= 0;
1105
1106 if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond)))
1107 PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
1108
1109 } // endif active_index
1110
1111 if (To_CondFil)
1112 if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
1113 strcpy(g->Message, "Readkey: Out of memory");
1114 return true;
1115 } // endif Append
1116
1117 } // endif To_Condfil
1118
1119 Mode = MODE_READ;
1120 } // endif's op
1121
1122 if (trace(33))
1123 htrc("MYSQL ReadKey: Query=%s\n", Query->GetStr());
1124
1125 m_Rc = Myc.ExecSQL(g, Query->GetStr());
1126 Query->Truncate(oldlen);
1127 return (m_Rc == RC_FX) ? true : false;
1128 } // end of ReadKey
1129
1130 /***********************************************************************/
1131 /* Data Base read routine for MYSQL access method. */
1132 /***********************************************************************/
ReadDB(PGLOBAL g)1133 int TDBMYSQL::ReadDB(PGLOBAL g)
1134 {
1135 int rc;
1136
1137 if (trace(2))
1138 htrc("MySQL ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
1139
1140 if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
1141 return SendCommand(g);
1142
1143 /*********************************************************************/
1144 /* Now start the reading process. */
1145 /* Here is the place to fetch the line. */
1146 /*********************************************************************/
1147 N++;
1148 Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK);
1149
1150 if (trace(2))
1151 htrc(" Read: rc=%d\n", rc);
1152
1153 return rc;
1154 } // end of ReadDB
1155
1156 /***********************************************************************/
1157 /* WriteDB: Data Base write routine for MYSQL access methods. */
1158 /***********************************************************************/
WriteDB(PGLOBAL g)1159 int TDBMYSQL::WriteDB(PGLOBAL g)
1160 {
1161 #if defined(MYSQL_PREPARED_STATEMENTS)
1162 if (Prep)
1163 return Myc.ExecStmt(g);
1164 #endif // MYSQL_PREPARED_STATEMENTS
1165
1166 // Statement was not prepared, we must construct and execute
1167 // an insert query for each line to insert
1168 int rc;
1169 uint len = Query->GetLength();
1170 char buf[64];
1171
1172 // Make the Insert command value list
1173 for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
1174 if (!colp->GetValue()->IsNull()) {
1175 if (colp->GetResultType() == TYPE_STRING ||
1176 colp->GetResultType() == TYPE_DATE)
1177 Query->Append_quoted(colp->GetValue()->GetCharString(buf));
1178 else
1179 Query->Append(colp->GetValue()->GetCharString(buf));
1180
1181 } else
1182 Query->Append("NULL");
1183
1184 Query->Append(',');
1185 } // endfor colp
1186
1187 if (unlikely(Query->IsTruncated())) {
1188 strcpy(g->Message, "WriteDB: Out of memory");
1189 rc = RC_FX;
1190 } else {
1191 Query->RepLast(')');
1192 Myc.m_Rows = -1; // To execute the query
1193 rc = Myc.ExecSQL(g, Query->GetStr());
1194 Query->Truncate(len); // Restore query
1195 } // endif Query
1196
1197 return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok
1198 } // end of WriteDB
1199
1200 /***********************************************************************/
1201 /* Data Base delete all routine for MYSQL access methods. */
1202 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)1203 int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
1204 {
1205 if (irc == RC_FX)
1206 // Send the DELETE (all) command to the remote table
1207 return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
1208 else
1209 return RC_OK; // Ignore
1210
1211 } // end of DeleteDB
1212
1213 /***********************************************************************/
1214 /* Data Base close routine for MySQL access method. */
1215 /***********************************************************************/
CloseDB(PGLOBAL g)1216 void TDBMYSQL::CloseDB(PGLOBAL g)
1217 {
1218 if (Myc.Connected()) {
1219 if (Mode == MODE_INSERT) {
1220 char cmd[64];
1221 int w;
1222 PDBUSER dup = PlgGetUser(g);
1223
1224 dup->Step = "Enabling indexes";
1225 sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", TableName);
1226 Myc.m_Rows = -1; // To execute the query
1227 m_Rc = Myc.ExecSQL(g, cmd, &w); // May fail for some engines
1228 } // endif m_Rc
1229
1230 Myc.Close();
1231 } // endif Myc
1232
1233 if (trace(1))
1234 htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
1235
1236 } // end of CloseDB
1237
1238 // ------------------------ MYSQLCOL functions --------------------------
1239
1240 /***********************************************************************/
1241 /* MYSQLCOL public constructor. */
1242 /***********************************************************************/
MYSQLCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)1243 MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1244 : COLBLK(cdp, tdbp, i)
1245 {
1246 if (cprec) {
1247 Next = cprec->GetNext();
1248 cprec->SetNext(this);
1249 } else {
1250 Next = tdbp->GetColumns();
1251 tdbp->SetColumns(this);
1252 } // endif cprec
1253
1254 // Set additional MySQL access method information for column.
1255 Precision = Long = cdp->GetLong();
1256 Bind = NULL;
1257 To_Val = NULL;
1258 Slen = 0;
1259 Rank = -1; // Not known yet
1260
1261 if (trace(1))
1262 htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
1263
1264 } // end of MYSQLCOL constructor
1265
1266 /***********************************************************************/
1267 /* MYSQLCOL public constructor. */
1268 /***********************************************************************/
MYSQLCOL(MYSQL_FIELD * fld,PTDB tdbp,int i,PCSZ am)1269 MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PCSZ am)
1270 : COLBLK(NULL, tdbp, i)
1271 {
1272 const char *chset = get_charset_name(fld->charsetnr);
1273 //char v = (!strcmp(chset, "binary")) ? 'B' : 0;
1274 char v = 0;
1275
1276 Name = fld->name;
1277 Opt = 0;
1278 Precision = Long = fld->length;
1279 Buf_Type = MYSQLtoPLG(fld->type, &v);
1280 strcpy(Format.Type, GetFormatType(Buf_Type));
1281 Format.Length = Long;
1282 Format.Prec = fld->decimals;
1283 ColUse = U_P;
1284 Nullable = !IS_NOT_NULL(fld->flags);
1285
1286 // Set additional MySQL access method information for column.
1287 Bind = NULL;
1288 To_Val = NULL;
1289 Slen = 0;
1290 Rank = i;
1291
1292 if (trace(1))
1293 htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
1294
1295 } // end of MYSQLCOL constructor
1296
1297 /***********************************************************************/
1298 /* MYSQLCOL constructor used for copying columns. */
1299 /* tdbp is the pointer to the new table descriptor. */
1300 /***********************************************************************/
MYSQLCOL(MYSQLCOL * col1,PTDB tdbp)1301 MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
1302 {
1303 Long = col1->Long;
1304 Bind = NULL;
1305 To_Val = NULL;
1306 Slen = col1->Slen;
1307 Rank = col1->Rank;
1308 } // end of MYSQLCOL copy constructor
1309
1310 /***********************************************************************/
1311 /* FindRank: Find the rank of this column in the result set. */
1312 /***********************************************************************/
FindRank(PGLOBAL g)1313 bool MYSQLCOL::FindRank(PGLOBAL g)
1314 {
1315 int n;
1316 MYSQLC myc = ((PTDBMY)To_Tdb)->Myc;
1317
1318 for (n = 0; n < myc.m_Fields; n++)
1319 if (!stricmp(Name, myc.m_Res->fields[n].name)) {
1320 Rank = n;
1321 return false;
1322 } // endif Name
1323
1324 sprintf(g->Message, "Column %s not in result set", Name);
1325 return true;
1326 } // end of FindRank
1327
1328 /***********************************************************************/
1329 /* SetBuffer: prepare a column block for write operation. */
1330 /***********************************************************************/
SetBuffer(PGLOBAL g,PVAL value,bool ok,bool check)1331 bool MYSQLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
1332 {
1333 if (!(To_Val = value)) {
1334 sprintf(g->Message, MSG(VALUE_ERROR), Name);
1335 return true;
1336 } else if (Buf_Type == value->GetType()) {
1337 // Values are of the (good) column type
1338 if (Buf_Type == TYPE_DATE) {
1339 // If any of the date values is formatted
1340 // output format must be set for the receiving table
1341 if (GetDomain() || ((DTVAL *)value)->IsFormatted())
1342 goto newval; // This will make a new value;
1343
1344 } else if (Buf_Type == TYPE_DOUBLE)
1345 // Float values must be written with the correct (column) precision
1346 // Note: maybe this should be forced by ShowValue instead of this ?
1347 value->SetPrec(GetScale());
1348
1349 Value = value; // Directly access the external value
1350 } else {
1351 // Values are not of the (good) column type
1352 if (check) {
1353 sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
1354 GetTypeName(Buf_Type), GetTypeName(value->GetType()));
1355 return true;
1356 } // endif check
1357
1358 newval:
1359 if (InitValue(g)) // Allocate the matching value block
1360 return true;
1361
1362 } // endif's Value, Buf_Type
1363
1364 // Because Colblk's have been made from a copy of the original TDB in
1365 // case of Update, we must reset them to point to the original one.
1366 if (To_Tdb->GetOrig())
1367 To_Tdb = (PTDB)To_Tdb->GetOrig();
1368
1369 // Set the Column
1370 Status = (ok) ? BUF_EMPTY : BUF_NO;
1371 return false;
1372 } // end of SetBuffer
1373
1374 /***********************************************************************/
1375 /* InitBind: Initialize the bind structure according to type. */
1376 /***********************************************************************/
InitBind(PGLOBAL g)1377 void MYSQLCOL::InitBind(PGLOBAL g)
1378 {
1379 PTDBMY tdbp = (PTDBMY)To_Tdb;
1380
1381 assert(tdbp->Bind && Rank < tdbp->Nparm);
1382
1383 Bind = &tdbp->Bind[Rank];
1384 memset(Bind, 0, sizeof(MYSQL_BIND));
1385
1386 if (Buf_Type == TYPE_DATE) {
1387 Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
1388 Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
1389 Bind->buffer_length = 20;
1390 Bind->length = &Slen;
1391 } else {
1392 Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
1393 Bind->buffer = (char *)Value->GetTo_Val();
1394 Bind->buffer_length = Value->GetClen();
1395 Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL;
1396 } // endif Buf_Type
1397
1398 } // end of InitBind
1399
1400 /***********************************************************************/
1401 /* ReadColumn: */
1402 /***********************************************************************/
ReadColumn(PGLOBAL g)1403 void MYSQLCOL::ReadColumn(PGLOBAL g)
1404 {
1405 char *p, *buf, tim[20];
1406 int rc;
1407 PTDBMY tdbp = (PTDBMY)To_Tdb;
1408
1409 /*********************************************************************/
1410 /* If physical fetching of the line was deferred, do it now. */
1411 /*********************************************************************/
1412 if (!tdbp->Fetched)
1413 if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) {
1414 if (rc == RC_EF)
1415 sprintf(g->Message, MSG(INV_DEF_READ), rc);
1416
1417 throw 11;
1418 } else
1419 tdbp->Fetched = true;
1420
1421 if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
1422 if (trace(2))
1423 htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);
1424
1425 // TODO: have a true way to differenciate temporal values
1426 if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
1427 // This is a TIME value
1428 p = strcat(strcpy(tim, "1970-01-01 "), buf);
1429 else
1430 p = buf;
1431
1432 if (Value->SetValue_char(p, strlen(p))) {
1433 sprintf(g->Message, "Out of range value for column %s at row %d",
1434 Name, tdbp->RowNumber(g));
1435 PushWarning(g, tdbp);
1436 } // endif SetValue_char
1437
1438 } else {
1439 if (Nullable)
1440 Value->SetNull(true);
1441
1442 Value->Reset(); // Null value
1443 } // endif buf
1444
1445 } // end of ReadColumn
1446
1447 /***********************************************************************/
1448 /* WriteColumn: make sure the bind buffer is updated. */
1449 /***********************************************************************/
WriteColumn(PGLOBAL)1450 void MYSQLCOL::WriteColumn(PGLOBAL)
1451 {
1452 /*********************************************************************/
1453 /* Do convert the column value if necessary. */
1454 /*********************************************************************/
1455 if (Value != To_Val)
1456 Value->SetValue_pval(To_Val, false); // Convert the inserted value
1457
1458 #if defined(MYSQL_PREPARED_STATEMENTS)
1459 if (((PTDBMY)To_Tdb)->Prep) {
1460 if (Buf_Type == TYPE_DATE) {
1461 Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
1462 Slen = strlen((char *)Bind->buffer);
1463 } else if (IsTypeChar(Buf_Type))
1464 Slen = strlen(Value->GetCharValue());
1465
1466 } // endif Prep
1467 #endif // MYSQL_PREPARED_STATEMENTS
1468
1469 } // end of WriteColumn
1470
1471 /* ------------------------------------------------------------------- */
1472
1473 /***********************************************************************/
1474 /* Implementation of the TDBMYEXC class. */
1475 /***********************************************************************/
TDBMYEXC(PMYDEF tdp)1476 TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
1477 {
1478 Cmdlist = NULL;
1479 Cmdcol = NULL;
1480 Shw = false;
1481 Havew = false;
1482 Isw = false;
1483 Warnings = 0;
1484 Mxr = tdp->Maxerr;
1485 Nerr = 0;
1486 } // end of TDBMYEXC constructor
1487
TDBMYEXC(PTDBMYX tdbp)1488 TDBMYEXC::TDBMYEXC(PTDBMYX tdbp) : TDBMYSQL(tdbp)
1489 {
1490 Cmdlist = tdbp->Cmdlist;
1491 Cmdcol = tdbp->Cmdcol;
1492 Shw = tdbp->Shw;
1493 Havew = tdbp->Havew;
1494 Isw = tdbp->Isw;
1495 Mxr = tdbp->Mxr;
1496 Nerr = tdbp->Nerr;
1497 } // end of TDBMYEXC copy constructor
1498
1499 // Is this really useful ???
Clone(PTABS t)1500 PTDB TDBMYEXC::Clone(PTABS t)
1501 {
1502 PTDB tp;
1503 PCOL cp1, cp2;
1504 PGLOBAL g = t->G;
1505
1506 tp = new(g) TDBMYEXC(this);
1507
1508 for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
1509 cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
1510
1511 NewPointer(t, cp1, cp2);
1512 } // endfor cp1
1513
1514 return tp;
1515 } // end of Clone
1516
1517 /***********************************************************************/
1518 /* Allocate MYSQL column description block. */
1519 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)1520 PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1521 {
1522 PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
1523
1524 if (!colp->Flag)
1525 Cmdcol = colp->GetName();
1526
1527 return colp;
1528 } // end of MakeCol
1529
1530 /***********************************************************************/
1531 /* MakeCMD: make the SQL statement to send to MYSQL connection. */
1532 /***********************************************************************/
MakeCMD(PGLOBAL g)1533 PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
1534 {
1535 PCMD xcmd = NULL;
1536
1537 if (To_CondFil) {
1538 if (Cmdcol) {
1539 if (!stricmp(Cmdcol, To_CondFil->Body) &&
1540 (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
1541 xcmd = To_CondFil->Cmds;
1542 } else
1543 strcpy(g->Message, "Invalid command specification filter");
1544
1545 } else
1546 strcpy(g->Message, "No command column in select list");
1547
1548 } else if (!Srcdef)
1549 strcpy(g->Message, "No Srcdef default command");
1550 else
1551 xcmd = new(g) CMD(g, Srcdef);
1552
1553 return xcmd;
1554 } // end of MakeCMD
1555
1556 /***********************************************************************/
1557 /* EXC GetMaxSize: returns the maximum number of rows in the table. */
1558 /***********************************************************************/
GetMaxSize(PGLOBAL)1559 int TDBMYEXC::GetMaxSize(PGLOBAL)
1560 {
1561 if (MaxSize < 0) {
1562 MaxSize = 10; // a guess
1563 } // endif MaxSize
1564
1565 return MaxSize;
1566 } // end of GetMaxSize
1567
1568 /***********************************************************************/
1569 /* MySQL Exec Access Method opening routine. */
1570 /***********************************************************************/
OpenDB(PGLOBAL g)1571 bool TDBMYEXC::OpenDB(PGLOBAL g)
1572 {
1573 if (Use == USE_OPEN) {
1574 strcpy(g->Message, "Multiple execution is not allowed");
1575 return true;
1576 } // endif use
1577
1578 /*********************************************************************/
1579 /* Open a MySQL connection for this table. */
1580 /* Note: this may not be the proper way to do. Perhaps it is better */
1581 /* to test whether a connection is already open for this server */
1582 /* and if so to allocate just a new result set. But this only for */
1583 /* servers allowing concurency in getting results ??? */
1584 /*********************************************************************/
1585 if (!Myc.Connected())
1586 if (Myc.Open(g, Host, Schema, User, Pwd, Port))
1587 return true;
1588
1589 Use = USE_OPEN; // Do it now in case we are recursively called
1590
1591 if (Mode != MODE_READ && Mode != MODE_READX) {
1592 strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
1593 return true;
1594 } // endif Mode
1595
1596 /*********************************************************************/
1597 /* Get the command to execute. */
1598 /*********************************************************************/
1599 if (!(Cmdlist = MakeCMD(g))) {
1600 // Next lines commented out because of CHECK TABLE
1601 //Myc.Close();
1602 //return true;
1603 } // endif Cmdlist
1604
1605 return false;
1606 } // end of OpenDB
1607
1608 /***********************************************************************/
1609 /* Data Base read routine for MYSQL access method. */
1610 /***********************************************************************/
ReadDB(PGLOBAL g)1611 int TDBMYEXC::ReadDB(PGLOBAL g)
1612 {
1613 if (Havew) {
1614 // Process result set from SHOW WARNINGS
1615 if (Myc.Fetch(g, -1) != RC_OK) {
1616 Myc.FreeResult();
1617 Havew = Isw = false;
1618 } else {
1619 N++;
1620 Isw = true;
1621 return RC_OK;
1622 } // endif Fetch
1623
1624 } // endif m_Res
1625
1626 if (Cmdlist) {
1627 // Process query to send
1628 int rc;
1629
1630 do {
1631 if (Query)
1632 Query->Set(Cmdlist->Cmd);
1633 else
1634 Query = new(g) STRING(g, 0, Cmdlist->Cmd);
1635
1636 switch (rc = Myc.ExecSQLcmd(g, Query->GetStr(), &Warnings)) {
1637 case RC_NF:
1638 AftRows = Myc.m_Afrw;
1639 strcpy(g->Message, "Affected rows");
1640 break;
1641 case RC_OK:
1642 AftRows = Myc.m_Fields;
1643 strcpy(g->Message, "Result set columns");
1644 break;
1645 case RC_FX:
1646 AftRows = Myc.m_Afrw;
1647 Nerr++;
1648 break;
1649 case RC_INFO:
1650 Shw = true;
1651 } // endswitch rc
1652
1653 Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
1654 } while (rc == RC_INFO);
1655
1656 if (Shw && Warnings)
1657 Havew = (Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK);
1658
1659 ++N;
1660 return RC_OK;
1661 } else {
1662 PushWarning(g, this, 1);
1663 return RC_EF;
1664 } // endif Cmdlist
1665
1666 } // end of ReadDB
1667
1668 /***********************************************************************/
1669 /* WriteDB: Data Base write routine for Exec MYSQL access methods. */
1670 /***********************************************************************/
WriteDB(PGLOBAL g)1671 int TDBMYEXC::WriteDB(PGLOBAL g)
1672 {
1673 strcpy(g->Message, "EXEC MYSQL tables are read only");
1674 return RC_FX;
1675 } // end of WriteDB
1676
1677 // ------------------------- MYXCOL functions ---------------------------
1678
1679 /***********************************************************************/
1680 /* MYXCOL public constructor. */
1681 /***********************************************************************/
MYXCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)1682 MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1683 : MYSQLCOL(cdp, tdbp, cprec, i, am)
1684 {
1685 // Set additional EXEC MYSQL access method information for column.
1686 Flag = cdp->GetOffset();
1687 } // end of MYSQLCOL constructor
1688
1689 /***********************************************************************/
1690 /* MYSQLCOL public constructor. */
1691 /***********************************************************************/
MYXCOL(MYSQL_FIELD * fld,PTDB tdbp,int i,PCSZ am)1692 MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PCSZ am)
1693 : MYSQLCOL(fld, tdbp, i, am)
1694 {
1695 if (trace(1))
1696 htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
1697
1698 } // end of MYSQLCOL constructor
1699
1700 /***********************************************************************/
1701 /* MYXCOL constructor used for copying columns. */
1702 /* tdbp is the pointer to the new table descriptor. */
1703 /***********************************************************************/
MYXCOL(MYXCOL * col1,PTDB tdbp)1704 MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
1705 {
1706 Flag = col1->Flag;
1707 } // end of MYXCOL copy constructor
1708
1709 /***********************************************************************/
1710 /* ReadColumn: */
1711 /***********************************************************************/
ReadColumn(PGLOBAL g)1712 void MYXCOL::ReadColumn(PGLOBAL g)
1713 {
1714 PTDBMYX tdbp = (PTDBMYX)To_Tdb;
1715
1716 if (tdbp->Isw) {
1717 char *buf = NULL;
1718
1719 if (Flag < 3) {
1720 buf = tdbp->Myc.GetCharField(Flag);
1721 Value->SetValue_psz(buf);
1722 } else
1723 Value->Reset();
1724
1725 } else
1726 switch (Flag) {
1727 case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
1728 case 1: Value->SetValue(tdbp->AftRows); break;
1729 case 2: Value->SetValue_psz(g->Message); break;
1730 case 3: Value->SetValue(tdbp->Warnings); break;
1731 default: Value->SetValue_psz("Invalid Flag"); break;
1732 } // endswitch Flag
1733
1734 } // end of ReadColumn
1735
1736 /***********************************************************************/
1737 /* WriteColumn: should never be called. */
1738 /***********************************************************************/
WriteColumn(PGLOBAL)1739 void MYXCOL::WriteColumn(PGLOBAL)
1740 {
1741 assert(false);
1742 } // end of WriteColumn
1743
1744 /* ---------------------------TDBMCL class --------------------------- */
1745
1746 /***********************************************************************/
1747 /* TDBMCL class constructor. */
1748 /***********************************************************************/
TDBMCL(PMYDEF tdp)1749 TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
1750 {
1751 Host = tdp->Hostname;
1752 Db = tdp->Tabschema;
1753 Tab = tdp->Tabname;
1754 User = tdp->Username;
1755 Pwd = tdp->Password;
1756 Port = tdp->Portnumber;
1757 } // end of TDBMCL constructor
1758
1759 /***********************************************************************/
1760 /* GetResult: Get the list the MYSQL table columns. */
1761 /***********************************************************************/
GetResult(PGLOBAL g)1762 PQRYRES TDBMCL::GetResult(PGLOBAL g)
1763 {
1764 return MyColumns(g, NULL, Host, Db, User, Pwd, Tab, NULL, Port, false);
1765 } // end of GetResult
1766