1/************ TabOccur CPP Declares Source Code File (.CPP) ************/
2/* Name: TABOCCUR.CPP Version 1.2 */
3/* */
4/* (C) Copyright to the author Olivier BERTRAND 2013 - 2017 */
5/* */
6/* OCCUR: Table that provides a view of a source table where the */
7/* contain of several columns of the source table is placed in only */
8/* one column, the OCCUR column, this resulting into several rows. */
9/***********************************************************************/
10
11/***********************************************************************/
12/* Include relevant section of system dependant header files. */
13/***********************************************************************/
14#include "my_global.h"
15#include "table.h" // MySQL table definitions
16#if defined(__WIN__)
17#include <stdlib.h>
18#include <stdio.h>
19#if defined(__BORLANDC__)
20#define __MFC_COMPAT__ // To define min/max as macro
21#endif
22//#include <windows.h>
23#else
24#if defined(UNIX)
25#include <fnmatch.h>
26#include <errno.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include "osutil.h"
31#else
32//#include <io.h>
33#endif
34//#include <fcntl.h>
35#endif
36
37/***********************************************************************/
38/* Include application header files: */
39/***********************************************************************/
40#include "global.h"
41#include "plgdbsem.h"
42#include "xtable.h"
43#include "tabext.h"
44//#include "reldef.h"
45#include "filamtxt.h"
46#include "tabdos.h"
47#include "tabcol.h"
48#include "taboccur.h"
49#include "tabmysql.h"
50#include "ha_connect.h"
51
52/***********************************************************************/
53/* Prepare and count columns in the column list. */
54/***********************************************************************/
55static int PrepareColist(char *colist)
56 {
57 char *p, *pn;
58 int n = 0;
59
60 // Count the number of columns and change separator into null char
61 for (pn = colist; ; pn += (strlen(pn) + 1))
62 // Separator can be ; if colist was specified in the option_list
63 if ((p = strchr(pn, ',')) || (p = strchr(pn, ';'))) {
64 *p++ = '\0';
65 n++;
66 } else {
67 if (*pn)
68 n++;
69
70 break;
71 } // endif p
72
73 return n;
74 } // end of PrepareColist
75
76/************************************************************************/
77/* OcrColumns: constructs the result blocks containing all the columns */
78/* of the object table that will be retrieved by GetData commands. */
79/************************************************************************/
80bool OcrColumns(PGLOBAL g, PQRYRES qrp, const char *col,
81 const char *ocr, const char *rank)
82 {
83 char *pn, *colist;
84 int i, k, m, n = 0, c = 0, j = qrp->Nblin;
85 bool rk, b = false;
86 PCOLRES crp;
87
88 if (!col || !*col) {
89 strcpy(g->Message, "Missing colist");
90 return true;
91 } // endif col
92
93 // Prepare the column list
94 colist = PlugDup(g, col);
95 m = PrepareColist(colist);
96
97 if ((rk = (rank && *rank))) {
98 if (m == 1) {
99 strcpy(g->Message, "Cannot handle one column colist and rank");
100 return true;
101 } // endif m
102
103 for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
104 n = MY_MAX(n, (signed)strlen(pn));
105
106 } // endif k
107
108 // Default occur column name is the 1st colist column name
109 if (!ocr || !*ocr)
110 ocr = colist;
111
112 /**********************************************************************/
113 /* Replace the columns of the colist by the rank and occur columns. */
114 /**********************************************************************/
115 for (i = 0; i < qrp->Nblin; i++) {
116 for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
117 if (!stricmp(pn, qrp->Colresp->Kdata->GetCharValue(i)))
118 break;
119
120 if (k < m) {
121 // This column belongs to colist
122 if (rk) {
123 // Place the rank column here
124 for (crp = qrp->Colresp; crp; crp = crp->Next)
125 switch (crp->Fld) {
126 case FLD_NAME: crp->Kdata->SetValue((char*)rank, i); break;
127 case FLD_TYPE: crp->Kdata->SetValue(TYPE_STRING, i); break;
128 case FLD_PREC: crp->Kdata->SetValue(n, i); break;
129 case FLD_SCALE: crp->Kdata->SetValue(0, i); break;
130 case FLD_NULL: crp->Kdata->SetValue(0, i); break;
131 case FLD_REM: crp->Kdata->Reset(i); break;
132 default: ; // Ignored by CONNECT
133 } // endswich Fld
134
135 rk = false;
136 } else if (!b) {
137 // First remaining listed column, will be the occur column
138 for (crp = qrp->Colresp; crp; crp = crp->Next)
139 switch (crp->Fld) {
140 case FLD_NAME: crp->Kdata->SetValue((char*)ocr, i); break;
141 case FLD_REM: crp->Kdata->Reset(i); break;
142 default: ; // Nothing to do
143 } // endswich Fld
144
145 b = true;
146 } else if (j == qrp->Nblin)
147 j = i; // Column to remove
148
149 c++;
150 } else if (j < i) {
151 // Move this column in empty spot
152 for (crp = qrp->Colresp; crp; crp = crp->Next)
153 crp->Kdata->Move(i, j);
154
155 j++;
156 } // endif k
157
158 } // endfor i
159
160 // Check whether all columns of the list where found
161 if (c < m) {
162 strcpy(g->Message, "Some colist columns are not in the source table");
163 return true;
164 } // endif crp
165
166 /**********************************************************************/
167 /* Set the number of columns of the table. */
168 /**********************************************************************/
169 qrp->Nblin = j;
170 return false;
171 } // end of OcrColumns
172
173/************************************************************************/
174/* OcrSrcCols: constructs the result blocks containing all the columns */
175/* of the object table that will be retrieved by GetData commands. */
176/************************************************************************/
177bool OcrSrcCols(PGLOBAL g, PQRYRES qrp, const char *col,
178 const char *ocr, const char *rank)
179 {
180 char *pn, *colist;
181 int i, k, m, n = 0, c = 0;
182 bool rk, b = false;
183 PCOLRES crp, rcrp, *pcrp;
184
185 if (!col || !*col) {
186 strcpy(g->Message, "Missing colist");
187 return true;
188 } // endif col
189
190 // Prepare the column list
191 colist = PlugDup(g, col);
192 m = PrepareColist(colist);
193
194 if ((rk = (rank && *rank)))
195 for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
196 n = MY_MAX(n, (signed)strlen(pn));
197
198 // Default occur column name is the 1st colist column name
199 if (!ocr || !*ocr)
200 ocr = colist;
201
202 /**********************************************************************/
203 /* Replace the columns of the colist by the rank and occur columns. */
204 /**********************************************************************/
205 for (i = 0, pcrp = &qrp->Colresp; crp = *pcrp; ) {
206 for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
207 if (!stricmp(pn, crp->Name))
208 break;
209
210 if (k < m) {
211 // This column belongs to colist
212 c++;
213
214 if (!b) {
215 if (rk) {
216 // Add the rank column here
217 rcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
218 memset(rcrp, 0, sizeof(COLRES));
219 rcrp->Next = crp;
220 rcrp->Name = (char*)rank;
221 rcrp->Type = TYPE_STRING;
222 rcrp->Length = n;
223 rcrp->Ncol = ++i;
224 *pcrp = rcrp;
225 } // endif rk
226
227 // First remaining listed column, will be the occur column
228 crp->Name = (char*)ocr;
229 b = true;
230 } else {
231 *pcrp = crp->Next; // Remove this column
232 continue;
233 } // endif b
234
235 } // endif k
236
237 crp->Ncol = ++i;
238 pcrp = &crp->Next;
239 } // endfor pcrp
240
241 // Check whether all columns of the list where found
242 if (c < m) {
243 strcpy(g->Message, "Some colist columns are not in the source table");
244 return true;
245 } // endif crp
246
247 /**********************************************************************/
248 /* Set the number of columns of the table. */
249 /**********************************************************************/
250 qrp->Nblin = i;
251 return false;
252 } // end of OcrSrcCols
253
254/* -------------- Implementation of the OCCUR classes ---------------- */
255
256/***********************************************************************/
257/* DefineAM: define specific AM block values from OCCUR table. */
258/***********************************************************************/
259bool OCCURDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
260 {
261 Rcol = GetStringCatInfo(g, "RankCol", "");
262 Colist = GetStringCatInfo(g, "Colist", "");
263 Xcol = GetStringCatInfo(g, "OccurCol", Colist);
264 return PRXDEF::DefineAM(g, am, poff);
265 } // end of DefineAM
266
267/***********************************************************************/
268/* GetTable: makes a new TDB of the proper type. */
269/***********************************************************************/
270PTDB OCCURDEF::GetTable(PGLOBAL g, MODE)
271 {
272 if (Catfunc != FNC_COL)
273 return new(g) TDBOCCUR(this);
274 else
275 return new(g) TDBTBC(this);
276
277 } // end of GetTable
278
279/* ------------------------------------------------------------------- */
280
281/***********************************************************************/
282/* Implementation of the TDBOCCUR class. */
283/***********************************************************************/
284TDBOCCUR::TDBOCCUR(POCCURDEF tdp) : TDBPRX(tdp)
285 {
286//Tdbp = NULL; // Source table (in TDBPRX)
287 Tabname = tdp->Tablep->GetName(); // Name of source table
288 Colist = tdp->Colist; // List of source columns
289 Xcolumn = tdp->Xcol; // Occur column name
290 Rcolumn = tdp->Rcol; // Rank column name
291 Xcolp = NULL; // To the OCCURCOL column
292 Col = NULL; // To source column blocks array
293 Mult = PrepareColist(Colist); // Multiplication factor
294 N = 0; // The current table index
295 M = 0; // The occurence rank
296 RowFlag = 0; // 0: Ok, 1: Same, 2: Skip
297 } // end of TDBOCCUR constructor
298
299/***********************************************************************/
300/* Allocate OCCUR/SRC column description block. */
301/***********************************************************************/
302PCOL TDBOCCUR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
303 {
304 PCOL colp = NULL;
305
306 if (!stricmp(cdp->GetName(), Rcolumn)) {
307 // Allocate a RANK column
308 colp = new(g) RANKCOL(cdp, this, n);
309 } else if (!stricmp(cdp->GetName(), Xcolumn)) {
310 // Allocate the OCCUR column
311 colp = Xcolp = new(g) OCCURCOL(cdp, this, n);
312 } else
313 return new(g) PRXCOL(cdp, this, cprec, n);
314
315 if (cprec) {
316 colp->SetNext(cprec->GetNext());
317 cprec->SetNext(colp);
318 } else {
319 colp->SetNext(Columns);
320 Columns = colp;
321 } // endif cprec
322
323 return colp;
324 } // end of MakeCol
325
326/***********************************************************************/
327/* Initializes the table. */
328/***********************************************************************/
329bool TDBOCCUR::InitTable(PGLOBAL g)
330 {
331 if (!Tdbp)
332 // Get the table description block of this table
333 if (!(Tdbp = GetSubTable(g, ((POCCURDEF)To_Def)->Tablep, TRUE)))
334 return TRUE;
335
336 if (!Tdbp->IsView())
337 if (MakeColumnList(g))
338 return TRUE;
339
340 return FALSE;
341 } // end of InitTable
342
343/***********************************************************************/
344/* Allocate OCCUR column description block. */
345/***********************************************************************/
346bool TDBOCCUR::MakeColumnList(PGLOBAL g)
347 {
348 char *pn;
349 int i;
350 PCOL colp;
351
352 for (colp = Columns; colp; colp = colp->GetNext())
353 if (colp->GetAmType() == TYPE_AM_PRX)
354 if (((PPRXCOL)colp)->Init(g, NULL))
355 return true;
356
357 Col = (PCOL*)PlugSubAlloc(g, NULL, Mult * sizeof(PCOL));
358
359 for (i = 0, pn = Colist; i < Mult; i++, pn += (strlen(pn) + 1)) {
360 if (!(Col[i] = Tdbp->ColDB(g, pn, 0))) {
361 // Column not found in table
362 sprintf(g->Message, MSG(COL_ISNOT_TABLE), pn, Tabname);
363 return true;
364 } // endif Col
365
366 if (Col[i]->InitValue(g)) {
367 strcpy(g->Message, "OCCUR InitValue failed");
368 return true;
369 } // endif InitValue
370
371 } // endfor i
372
373 return false;
374 } // end of MakeColumnList
375
376/***********************************************************************/
377/* Allocate OCCUR column description block for a view. */
378/***********************************************************************/
379bool TDBOCCUR::ViewColumnList(PGLOBAL g)
380 {
381 char *pn;
382 int i;
383 PCOL colp, cp;
384 PTDBMY tdbp;
385
386 if (!Tdbp->IsView())
387 return false;
388
389 if (Tdbp->GetAmType() != TYPE_AM_MYSQL) {
390 strcpy(g->Message, "View is not MySQL");
391 return true;
392 } else
393 tdbp = (PTDBMY)Tdbp;
394
395 for (cp = Columns; cp; cp = cp->GetNext())
396 if (cp->GetAmType() == TYPE_AM_PRX) {
397 if ((colp = tdbp->MakeFieldColumn(g, cp->GetName()))) {
398 ((PPRXCOL)cp)->Colp = colp;
399 ((PPRXCOL)cp)->To_Val = colp->GetValue();
400 } else
401 return true;
402
403 } // endif Type
404
405 Col = (PCOL*)PlugSubAlloc(g, NULL, Mult * sizeof(PCOL));
406
407 for (i = 0, pn = Colist; i < Mult; i++, pn += (strlen(pn) + 1))
408 if (!(Col[i] = tdbp->MakeFieldColumn(g, pn))) {
409 // Column not found in table
410 sprintf(g->Message, MSG(COL_ISNOT_TABLE), pn, Tabname);
411 return true;
412 } // endif Col
413
414 return false;
415 } // end of ViewColumnList
416
417/***********************************************************************/
418/* OCCUR GetMaxSize: returns the maximum number of rows in the table. */
419/***********************************************************************/
420int TDBOCCUR::GetMaxSize(PGLOBAL g)
421 {
422 if (MaxSize < 0) {
423 if (!(Tdbp = GetSubTable(g, ((POCCURDEF)To_Def)->Tablep, TRUE)))
424 return 0;
425
426 MaxSize = Mult * Tdbp->GetMaxSize(g);
427 } // endif MaxSize
428
429 return MaxSize;
430 } // end of GetMaxSize
431
432/***********************************************************************/
433/* In this sample, ROWID will be the (virtual) row number, */
434/* while ROWNUM will be the occurence rank in the multiple column. */
435/***********************************************************************/
436int TDBOCCUR::RowNumber(PGLOBAL, bool b)
437 {
438 return (b) ? M : N;
439 } // end of RowNumber
440
441/***********************************************************************/
442/* OCCUR Access Method opening routine. */
443/***********************************************************************/
444bool TDBOCCUR::OpenDB(PGLOBAL g)
445 {
446 if (Use == USE_OPEN) {
447 /*******************************************************************/
448 /* Table already open, just replace it at its beginning. */
449 /*******************************************************************/
450 N = M = 0;
451 RowFlag = 0;
452
453 if (Xcolp)
454 Xcolp->Xreset();
455
456 return Tdbp->OpenDB(g);
457 } // endif use
458
459
460 if (Mode != MODE_READ) {
461 /*******************************************************************/
462 /* Currently OCCUR tables cannot be modified. */
463 /*******************************************************************/
464 strcpy(g->Message, "OCCUR tables are read only");
465 return TRUE;
466 } // endif Mode
467
468 /*********************************************************************/
469 /* Do it here if not done yet. */
470 /*********************************************************************/
471 if (InitTable(g))
472 return TRUE;
473
474 if (Xcolp)
475 // Lock this column so it is evaluated by its table only
476 Xcolp->AddStatus(BUF_READ);
477
478 if (To_Key_Col || To_Kindex) {
479 /*******************************************************************/
480 /* Direct access of OCCUR tables is not implemented yet. */
481 /*******************************************************************/
482 strcpy(g->Message, "No direct access to OCCUR tables");
483 return TRUE;
484 } // endif To_Key_Col
485
486 /*********************************************************************/
487 /* Do open the source table. */
488 /*********************************************************************/
489 if (Tdbp->OpenDB(g))
490 return TRUE;
491
492 Use = USE_OPEN;
493 return ViewColumnList(g);
494 } // end of OpenDB
495
496/***********************************************************************/
497/* Data Base read routine for OCCUR access method. */
498/***********************************************************************/
499int TDBOCCUR::ReadDB(PGLOBAL g)
500 {
501 int rc = RC_OK;
502
503 /*********************************************************************/
504 /* Now start the multi reading process. */
505 /*********************************************************************/
506 do {
507 if (RowFlag != 1)
508 if ((rc = Tdbp->ReadDB(g)) != RC_OK)
509 break;
510
511 if (Xcolp) {
512 RowFlag = 0;
513 Xcolp->ReadColumn(g);
514 M = Xcolp->GetI();
515 } // endif Xcolp
516
517 } while (RowFlag == 2);
518
519 N++;
520 return rc;
521 } // end of ReadDB
522
523// ------------------------ OCCURCOL functions ----------------------------
524
525/***********************************************************************/
526/* OCCURCOL public constructor. */
527/***********************************************************************/
528OCCURCOL::OCCURCOL(PCOLDEF cdp, PTDBOCCUR tdbp, int n)
529 : COLBLK(cdp, tdbp, n)
530 {
531 // Set additional OCCUR access method information for column.
532 I = 0;
533 } // end of OCCURCOL constructor
534
535/***********************************************************************/
536/* ReadColumn: what this routine does is to access the columns of */
537/* list, extract their value and convert it to buffer type. */
538/***********************************************************************/
539void OCCURCOL::ReadColumn(PGLOBAL g)
540 {
541 PTDBOCCUR tdbp = (PTDBOCCUR)To_Tdb;
542 PCOL *col = tdbp->Col;
543
544 for (; I < tdbp->Mult; I++) {
545 col[I]->ReadColumn(g);
546
547 if (Nullable || !col[I]->GetValue()->IsZero())
548 break;
549
550 } // endfor I
551
552 if (I == tdbp->Mult) {
553 // No more values, go to next source row
554 tdbp->RowFlag = 2;
555 I = 0;
556 return;
557 } // endif I
558
559 // Set the OCCUR column value from the Ith source column value
560 Value->SetValue_pval(col[I++]->GetValue());
561 tdbp->RowFlag = 1;
562 } // end of ReadColumn
563
564
565// ------------------------ RANKCOL functions ---------------------------
566
567/***********************************************************************/
568/* ReadColumn: what this routine does is to access the Mth columns of */
569/* list, extract its name and set to it the rank column value. */
570/***********************************************************************/
571void RANKCOL::ReadColumn(PGLOBAL)
572 {
573 PTDBOCCUR tdbp = (PTDBOCCUR)To_Tdb;
574 PCOL *col = tdbp->Col;
575
576 // Set the RANK column value from the Mth source column name
577 if (tdbp->M)
578 Value->SetValue_psz(col[tdbp->M - 1]->GetName());
579 else {
580 Value->Reset();
581
582 if (Nullable)
583 Value->SetNull(true);
584
585 } // endelse
586
587 } // end of ReadColumn
588