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 | /***********************************************************************/ |
55 | static 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 | /************************************************************************/ |
80 | bool 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 | /************************************************************************/ |
177 | bool 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 | /***********************************************************************/ |
259 | bool 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 | /***********************************************************************/ |
270 | PTDB 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 | /***********************************************************************/ |
284 | TDBOCCUR::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 | /***********************************************************************/ |
302 | PCOL 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 | /***********************************************************************/ |
329 | bool 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 | /***********************************************************************/ |
346 | bool 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 | /***********************************************************************/ |
379 | bool 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 | /***********************************************************************/ |
420 | int 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 | /***********************************************************************/ |
436 | int TDBOCCUR::RowNumber(PGLOBAL, bool b) |
437 | { |
438 | return (b) ? M : N; |
439 | } // end of RowNumber |
440 | |
441 | /***********************************************************************/ |
442 | /* OCCUR Access Method opening routine. */ |
443 | /***********************************************************************/ |
444 | bool 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 | /***********************************************************************/ |
499 | int 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 | /***********************************************************************/ |
528 | OCCURCOL::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 | /***********************************************************************/ |
539 | void 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 | /***********************************************************************/ |
571 | void 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 | |