1/************* Tabext C++ Functions Source Code File (.CPP) ************/
2/* Name: TABEXT.CPP Version 1.0 */
3/* */
4/* (C) Copyright to the author Olivier BERTRAND 2017 */
5/* */
6/* This file contains the TBX, TDB and OPJOIN classes functions. */
7/***********************************************************************/
8
9/***********************************************************************/
10/* Include relevant MariaDB header file. */
11/***********************************************************************/
12#define MYSQL_SERVER 1
13#include "my_global.h"
14#include "sql_class.h"
15#include "sql_servers.h"
16#include "sql_string.h"
17#if !defined(__WIN__)
18#include "osutil.h"
19#endif
20
21/***********************************************************************/
22/* Include required application header files */
23/* global.h is header containing all global Plug declarations. */
24/* plgdbsem.h is header containing the DB applic. declarations. */
25/* xobject.h is header containing XOBJECT derived classes declares. */
26/***********************************************************************/
27#include "global.h"
28#include "plgdbsem.h"
29#include "xtable.h"
30#include "tabext.h"
31#include "ha_connect.h"
32
33/* -------------------------- Class CONDFIL -------------------------- */
34
35/***********************************************************************/
36/* CONDFIL Constructor. */
37/***********************************************************************/
38CONDFIL::CONDFIL(uint idx, AMT type)
39{
40//Cond = cond;
41 Idx = idx;
42 Type = type;
43 Op = OP_XX;
44 Cmds = NULL;
45 Alist = NULL;
46 All = true;
47 Bd = false;
48 Hv = false;
49 Body = NULL,
50 Having = NULL;
51} // end of CONDFIL constructor
52
53/***********************************************************************/
54/* Make and allocate the alias list. */
55/***********************************************************************/
56int CONDFIL::Init(PGLOBAL g, PHC hc)
57{
58 PTOS options = hc->GetTableOptionStruct();
59 char *p, *cn, *cal, *alt = NULL;
60 int rc = RC_OK;
61 bool h;
62
63 if (options)
64 alt = (char*)GetListOption(g, "Alias", options->oplist, NULL);
65
66 while (alt) {
67 if (!(p = strchr(alt, '='))) {
68 strcpy(g->Message, "Invalid alias list");
69 rc = RC_FX;
70 break;
71 } // endif !p
72
73 cal = alt; // Alias
74 *p++ = 0;
75
76 if ((h = *p == '*')) {
77 rc = RC_INFO;
78 p++;
79 } // endif h
80
81 cn = p; // Remote column name
82
83 if ((alt = strchr(p, ';')))
84 *alt++ = 0;
85
86 if (*cn == 0)
87 cn = alt;
88
89 Alist = new(g) ALIAS(Alist, cn, cal, h);
90 } // endwhile alt
91
92 return rc;
93} // end of Init
94
95/***********************************************************************/
96/* Make and allocate the alias list. */
97/***********************************************************************/
98const char *CONDFIL::Chk(const char *fln, bool *h)
99{
100 for (PAL pal = Alist; pal; pal = pal->Next)
101 if (!stricmp(fln, pal->Alias)) {
102 *h = pal->Having;
103 return pal->Name;
104 } // endif fln
105
106 *h = false;
107 return fln;
108} // end of Chk
109
110/* --------------------------- Class EXTDEF -------------------------- */
111
112/***********************************************************************/
113/* EXTDEF Constructor. */
114/***********************************************************************/
115EXTDEF::EXTDEF(void)
116{
117 Tabname = Tabschema = Username = Password = Tabcat = Tabtyp = NULL;
118 Colpat = Srcdef = Qchar = Qrystr = Sep = Phpos = NULL;
119 Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0;
120 Scrollable = Xsrc = false;
121} // end of EXTDEF constructor
122
123/***********************************************************************/
124/* DefineAM: define specific AM block values from XDB file. */
125/***********************************************************************/
126bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
127{
128 Desc = NULL;
129 Tabname = GetStringCatInfo(g, "Name",
130 (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
131 Tabname = GetStringCatInfo(g, "Tabname", Tabname);
132 Tabschema = GetStringCatInfo(g, "Dbname", NULL);
133 Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
134 Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
135 Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
136 Username = GetStringCatInfo(g, "User", NULL);
137 Password = GetStringCatInfo(g, "Password", NULL);
138
139 if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
140 Read_Only = true;
141
142 Qrystr = GetStringCatInfo(g, "Query_String", "?");
143 Sep = GetStringCatInfo(g, "Separator", NULL);
144//Alias = GetStringCatInfo(g, "Alias", NULL);
145 Phpos = GetStringCatInfo(g, "Phpos", NULL);
146 Xsrc = GetBoolCatInfo("Execsrc", FALSE);
147 Maxerr = GetIntCatInfo("Maxerr", 0);
148 Maxres = GetIntCatInfo("Maxres", 0);
149 Quoted = GetIntCatInfo("Quoted", 0);
150 Options = 0;
151 Cto = 0;
152 Qto = 0;
153
154 if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
155 Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch
156
157 if (Catfunc == FNC_COL)
158 Colpat = GetStringCatInfo(g, "Colpat", NULL);
159
160 if (Catfunc == FNC_TABLE)
161 Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
162
163 // Memory was Boolean, it is now integer
164 if (!(Memory = GetIntCatInfo("Memory", 0)))
165 Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
166
167 Pseudo = 2; // FILID is Ok but not ROWID
168 return false;
169} // end of DefineAM
170
171/* ---------------------------TDBEXT class --------------------------- */
172
173/***********************************************************************/
174/* Implementation of the TDBEXT class. */
175/***********************************************************************/
176TDBEXT::TDBEXT(EXTDEF *tdp) : TDB(tdp)
177{
178 Qrp = NULL;
179
180 if (tdp) {
181 TableName = tdp->Tabname;
182 Schema = tdp->Tabschema;
183 User = tdp->Username;
184 Pwd = tdp->Password;
185 Catalog = tdp->Tabcat;
186 Srcdef = tdp->Srcdef;
187 Qrystr = tdp->Qrystr;
188 Sep = tdp->GetSep();
189 Options = tdp->Options;
190 Cto = tdp->Cto;
191 Qto = tdp->Qto;
192 Quoted = MY_MAX(0, tdp->GetQuoted());
193 Rows = tdp->GetElemt();
194 Memory = tdp->Memory;
195 Scrollable = tdp->Scrollable;
196 } else {
197 TableName = NULL;
198 Schema = NULL;
199 User = NULL;
200 Pwd = NULL;
201 Catalog = NULL;
202 Srcdef = NULL;
203 Qrystr = NULL;
204 Sep = 0;
205 Options = 0;
206 Cto = 0;
207 Qto = 0;
208 Quoted = 0;
209 Rows = 0;
210 Memory = 0;
211 Scrollable = false;
212 } // endif tdp
213
214 Quote = NULL;
215 Query = NULL;
216 Count = NULL;
217 //Where = NULL;
218 MulConn = NULL;
219 DBQ = NULL;
220 Qrp = NULL;
221 Fpos = 0;
222 Curpos = 0;
223 AftRows = 0;
224 CurNum = 0;
225 Rbuf = 0;
226 BufSize = 0;
227 Nparm = 0;
228 Ncol = 0;
229 Placed = false;
230} // end of TDBEXT constructor
231
232TDBEXT::TDBEXT(PTDBEXT tdbp) : TDB(tdbp)
233{
234 Qrp = tdbp->Qrp;
235 TableName = tdbp->TableName;
236 Schema = tdbp->Schema;
237 User = tdbp->User;
238 Pwd = tdbp->Pwd;
239 Catalog = tdbp->Catalog;
240 Srcdef = tdbp->Srcdef;
241 Qrystr = tdbp->Qrystr;
242 Sep = tdbp->Sep;
243 Options = tdbp->Options;
244 Cto = tdbp->Cto;
245 Qto = tdbp->Qto;
246 Quoted = tdbp->Quoted;
247 Rows = tdbp->Rows;
248 Memory = tdbp->Memory;
249 Scrollable = tdbp->Scrollable;
250 Quote = tdbp->Quote;
251 Query = tdbp->Query;
252 Count = tdbp->Count;
253 //Where = tdbp->Where;
254 MulConn = tdbp->MulConn;
255 DBQ = tdbp->DBQ;
256 Fpos = 0;
257 Curpos = 0;
258 AftRows = 0;
259 CurNum = 0;
260 Rbuf = 0;
261 BufSize = tdbp->BufSize;
262 Nparm = tdbp->Nparm;
263 Ncol = tdbp->Ncol;
264 Placed = false;
265} // end of TDBEXT copy constructor
266
267/******************************************************************/
268/* Convert an UTF-8 string to latin characters. */
269/******************************************************************/
270int TDBEXT::Decode(PCSZ txt, char *buf, size_t n)
271{
272 uint dummy_errors;
273 uint32 len = copy_and_convert(buf, n, &my_charset_latin1,
274 txt, strlen(txt),
275 &my_charset_utf8_general_ci,
276 &dummy_errors);
277 buf[len] = '\0';
278 return 0;
279} // end of Decode
280
281/***********************************************************************/
282/* MakeSrcdef: make the SQL statement from SRDEF option. */
283/***********************************************************************/
284bool TDBEXT::MakeSrcdef(PGLOBAL g)
285{
286 char *catp = strstr(Srcdef, "%s");
287
288 if (catp) {
289 char *fil1 = 0, *fil2;
290 PCSZ ph = ((EXTDEF*)To_Def)->Phpos;
291
292 if (!ph)
293 ph = (strstr(catp + 2, "%s")) ? "WH" : "W";
294
295 if (stricmp(ph, "H")) {
296 fil1 = (To_CondFil && *To_CondFil->Body)
297 ? To_CondFil->Body : PlugDup(g, "1=1");
298 } // endif ph
299
300 if (stricmp(ph, "W")) {
301 fil2 = (To_CondFil && To_CondFil->Having && *To_CondFil->Having)
302 ? To_CondFil->Having : PlugDup(g, "1=1");
303 } // endif ph
304
305 if (!stricmp(ph, "W")) {
306 Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1));
307 Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1));
308 } else if (!stricmp(ph, "WH")) {
309 Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
310 Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1, fil2));
311 } else if (!stricmp(ph, "H")) {
312 Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil2));
313 Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2));
314 } else if (!stricmp(ph, "HW")) {
315 Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
316 Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1));
317 } else {
318 strcpy(g->Message, "MakeSQL: Wrong place holders specification");
319 return true;
320 } // endif's ph
321
322 } else
323 Query = new(g)STRING(g, 0, Srcdef);
324
325 return false;
326} // end of MakeSrcdef
327
328 /***********************************************************************/
329 /* MakeSQL: make the SQL statement use with remote connection. */
330 /* TODO: when implementing remote filtering, column only used in */
331 /* local filter should be removed from column list. */
332 /***********************************************************************/
333bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt)
334{
335 PCSZ schmp = NULL;
336 char *catp = NULL, buf[NAM_LEN * 3];
337 int len;
338 bool first = true;
339 PTABLE tablep = To_Table;
340 PCOL colp;
341
342 if (Srcdef)
343 return MakeSrcdef(g);
344
345 // Allocate the string used to contain the Query
346 Query = new(g)STRING(g, 1023, "SELECT ");
347
348 if (!cnt) {
349 if (Columns) {
350 // Normal SQL statement to retrieve results
351 for (colp = Columns; colp; colp = colp->GetNext())
352 if (!colp->IsSpecial()) {
353 if (!first)
354 Query->Append(", ");
355 else
356 first = false;
357
358 // Column name can be encoded in UTF-8
359 Decode(colp->GetName(), buf, sizeof(buf));
360
361 if (Quote) {
362 // Put column name between identifier quotes in case in contains blanks
363 Query->Append(Quote);
364 Query->Append(buf);
365 Query->Append(Quote);
366 } else
367 Query->Append(buf);
368
369 ((PEXTCOL)colp)->SetRank(++Ncol);
370 } // endif colp
371
372 } else
373 // !Columns can occur for queries such that sql count(*) from...
374 // for which we will count the rows from sql * from...
375 Query->Append('*');
376
377 } else
378 // SQL statement used to retrieve the size of the result
379 Query->Append("count(*)");
380
381 Query->Append(" FROM ");
382
383 if (Catalog && *Catalog)
384 catp = Catalog;
385
386 //if (tablep->GetSchema())
387 // schmp = (char*)tablep->GetSchema();
388 //else
389 if (Schema && *Schema)
390 schmp = Schema;
391
392 if (catp) {
393 Query->Append(catp);
394
395 if (schmp) {
396 Query->Append('.');
397 Query->Append(schmp);
398 } // endif schmp
399
400 Query->Append('.');
401 } else if (schmp) {
402 Query->Append(schmp);
403 Query->Append('.');
404 } // endif schmp
405
406 // Table name can be encoded in UTF-8
407 Decode(TableName, buf, sizeof(buf));
408
409 if (Quote) {
410 // Put table name between identifier quotes in case in contains blanks
411 Query->Append(Quote);
412 Query->Append(buf);
413 Query->Append(Quote);
414 } else
415 Query->Append(buf);
416
417 len = Query->GetLength();
418
419 if (To_CondFil) {
420 if (Mode == MODE_READ) {
421 Query->Append(" WHERE ");
422 Query->Append(To_CondFil->Body);
423 len = Query->GetLength() + 1;
424 } else
425 len += (strlen(To_CondFil->Body) + 256);
426
427 } else
428 len += ((Mode == MODE_READX) ? 256 : 1);
429
430 if (Query->IsTruncated()) {
431 strcpy(g->Message, "MakeSQL: Out of memory");
432 return true;
433 } else
434 Query->Resize(len);
435
436 if (trace(33))
437 htrc("Query=%s\n", Query->GetStr());
438
439 return false;
440} // end of MakeSQL
441
442/***********************************************************************/
443/* MakeCommand: make the Update or Delete statement to send to the */
444/* MySQL server. Limited to remote values and filtering. */
445/***********************************************************************/
446bool TDBEXT::MakeCommand(PGLOBAL g)
447{
448 PCSZ schmp = NULL;
449 char *p, *stmt, name[132], *body = NULL;
450 char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
451 bool qtd = Quoted > 0;
452 char q = qtd ? *Quote : ' ';
453 int i = 0, k = 0;
454
455 // Make a lower case copy of the originale query and change
456 // back ticks to the data source identifier quoting character
457 do {
458 qrystr[i] = (Qrystr[i] == '`') ? q : tolower(Qrystr[i]);
459 } while (Qrystr[i++]);
460
461 if (To_CondFil && (p = strstr(qrystr, " where "))) {
462 p[7] = 0; // Remove where clause
463 Qrystr[(p - qrystr) + 7] = 0;
464 body = To_CondFil->Body;
465 stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
466 + strlen(body) + 64);
467 } else
468 stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
469
470 // Check whether the table name is equal to a keyword
471 // If so, it must be quoted in the original query
472 strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
473
474 if (strstr(" update delete low_priority ignore quick from ", name)) {
475 if (Quote) {
476 strlwr(strcat(strcat(strcpy(name, Quote), Name), Quote));
477 k += 2;
478 } else {
479 strcpy(g->Message, "Quoted must be specified");
480 return true;
481 } // endif Quote
482
483 } else
484 strlwr(strcpy(name, Name)); // Not a keyword
485
486 if ((p = strstr(qrystr, name))) {
487 for (i = 0; i < p - qrystr; i++)
488 stmt[i] = (Qrystr[i] == '`') ? q : Qrystr[i];
489
490 stmt[i] = 0;
491
492 k += i + (int)strlen(Name);
493
494 if (Schema && *Schema)
495 schmp = Schema;
496
497 if (qtd && *(p - 1) == ' ') {
498 if (schmp)
499 strcat(strcat(stmt, schmp), ".");
500
501 strcat(strcat(strcat(stmt, Quote), TableName), Quote);
502 } else {
503 if (schmp) {
504 if (qtd && *(p - 1) != ' ') {
505 stmt[i - 1] = 0;
506 strcat(strcat(strcat(stmt, schmp), "."), Quote);
507 } else
508 strcat(strcat(stmt, schmp), ".");
509
510 } // endif schmp
511
512 strcat(stmt, TableName);
513 } // endif's
514
515 i = (int)strlen(stmt);
516
517 do {
518 stmt[i++] = (Qrystr[k] == '`') ? q : Qrystr[k];
519 } while (Qrystr[k++]);
520
521 if (body)
522 strcat(stmt, body);
523
524 } else {
525 sprintf(g->Message, "Cannot use this %s command",
526 (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
527 return true;
528 } // endif p
529
530 if (trace(33))
531 htrc("Command=%s\n", stmt);
532
533 Query = new(g)STRING(g, 0, stmt);
534 return (!Query->GetSize());
535} // end of MakeCommand
536
537/***********************************************************************/
538/* GetRecpos: return the position of last read record. */
539/***********************************************************************/
540int TDBEXT::GetRecpos(void)
541{
542 return Fpos;
543} // end of GetRecpos
544
545/***********************************************************************/
546/* ODBC GetMaxSize: returns table size estimate in number of lines. */
547/***********************************************************************/
548int TDBEXT::GetMaxSize(PGLOBAL g)
549{
550 if (MaxSize < 0) {
551 if (Mode == MODE_DELETE)
552 // Return 0 in mode DELETE in case of delete all.
553 MaxSize = 0;
554 else if (!Cardinality(NULL))
555 MaxSize = 10; // To make MySQL happy
556 else if ((MaxSize = Cardinality(g)) < 0)
557 MaxSize = 12; // So we can see an error occurred
558
559 } // endif MaxSize
560
561 return MaxSize;
562} // end of GetMaxSize
563
564/***********************************************************************/
565/* Return max size value. */
566/***********************************************************************/
567int TDBEXT::GetProgMax(PGLOBAL g)
568{
569 return GetMaxSize(g);
570} // end of GetProgMax
571
572/* ---------------------------EXTCOL class --------------------------- */
573
574/***********************************************************************/
575/* EXTCOL public constructor. */
576/***********************************************************************/
577EXTCOL::EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
578 : COLBLK(cdp, tdbp, i)
579{
580 if (cprec) {
581 Next = cprec->GetNext();
582 cprec->SetNext(this);
583 } else {
584 Next = tdbp->GetColumns();
585 tdbp->SetColumns(this);
586 } // endif cprec
587
588 if (trace(1))
589 htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
590
591 // Set additional remote access method information for column.
592 Crp = NULL;
593 Long = Precision;
594 To_Val = NULL;
595 Bufp = NULL;
596 Blkp = NULL;
597 Rank = 0; // Not known yet
598} // end of JDBCCOL constructor
599
600/***********************************************************************/
601/* EXTCOL private constructor. */
602/***********************************************************************/
603EXTCOL::EXTCOL(void) : COLBLK()
604{
605 Crp = NULL;
606 Buf_Type = TYPE_INT; // This is a count(*) column
607
608 // Set additional Dos access method information for column.
609 Long = sizeof(int);
610 To_Val = NULL;
611 Bufp = NULL;
612 Blkp = NULL;
613 Rank = 1;
614} // end of EXTCOL constructor
615
616/***********************************************************************/
617/* EXTCOL constructor used for copying columns. */
618/* tdbp is the pointer to the new table descriptor. */
619/***********************************************************************/
620EXTCOL::EXTCOL(PEXTCOL col1, PTDB tdbp) : COLBLK(col1, tdbp)
621{
622 Crp = col1->Crp;
623 Long = col1->Long;
624 To_Val = col1->To_Val;
625 Bufp = col1->Bufp;
626 Blkp = col1->Blkp;
627 Rank = col1->Rank;
628} // end of JDBCCOL copy constructor
629
630/***********************************************************************/
631/* SetBuffer: prepare a column block for write operation. */
632/***********************************************************************/
633bool EXTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
634{
635 if (!(To_Val = value)) {
636 sprintf(g->Message, MSG(VALUE_ERROR), Name);
637 return true;
638 } else if (Buf_Type == value->GetType()) {
639 // Values are of the (good) column type
640 if (Buf_Type == TYPE_DATE) {
641 // If any of the date values is formatted
642 // output format must be set for the receiving table
643 if (GetDomain() || ((DTVAL *)value)->IsFormatted())
644 goto newval; // This will make a new value;
645
646 } else if (Buf_Type == TYPE_DOUBLE)
647 // Float values must be written with the correct (column) precision
648 // Note: maybe this should be forced by ShowValue instead of this ?
649 value->SetPrec(GetScale());
650
651 Value = value; // Directly access the external value
652 } else {
653 // Values are not of the (good) column type
654 if (check) {
655 sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
656 GetTypeName(Buf_Type), GetTypeName(value->GetType()));
657 return true;
658 } // endif check
659
660 newval:
661 if (InitValue(g)) // Allocate the matching value block
662 return true;
663
664 } // endif's Value, Buf_Type
665
666 // Because Colblk's have been made from a copy of the original TDB in
667 // case of Update, we must reset them to point to the original one.
668 if (To_Tdb->GetOrig())
669 To_Tdb = (PTDB)To_Tdb->GetOrig();
670
671 // Set the Column
672 Status = (ok) ? BUF_EMPTY : BUF_NO;
673 return false;
674} // end of SetBuffer
675
676