1/************ TabPivot C++ Program Source Code File (.CPP) *************/
2/* PROGRAM NAME: TABPIVOT */
3/* ------------- */
4/* Version 1.7 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* This program are the PIVOT classes DB execution routines. */
13/***********************************************************************/
14
15/***********************************************************************/
16/* Include relevant sections of the operating system header file. */
17/***********************************************************************/
18#include "my_global.h"
19#include "table.h" // MySQL table definitions
20#if defined(__WIN__)
21#if defined(__BORLANDC__)
22#define __MFC_COMPAT__ // To define min/max as macro
23#endif
24//#include <windows.h>
25#elif defined(UNIX)
26#include <errno.h>
27#include <unistd.h>
28#include "osutil.h"
29#else
30#include <io.h>
31#endif
32
33/***********************************************************************/
34/* Include application header files: */
35/* global.h is header containing all global declarations. */
36/* plgdbsem.h is header containing the DB application declarations. */
37/***********************************************************************/
38#define FRM_VER 6
39#include "sql_const.h"
40#include "field.h"
41#include "global.h"
42#include "plgdbsem.h"
43#include "xtable.h"
44#include "tabext.h"
45#include "tabcol.h"
46#include "colblk.h"
47#include "tabmysql.h"
48#include "csort.h"
49#include "tabutil.h"
50#include "tabpivot.h"
51#include "valblk.h"
52#include "ha_connect.h"
53
54/***********************************************************************/
55/* Make the Pivot table column list. */
56/***********************************************************************/
57PQRYRES PivotColumns(PGLOBAL g, const char *tab, const char *src,
58 const char *picol, const char *fncol,
59 const char *skcol, const char *host,
60 const char *db, const char *user,
61 const char *pwd, int port)
62 {
63 PIVAID pvd(tab, src, picol, fncol, skcol, host, db, user, pwd, port);
64
65 return pvd.MakePivotColumns(g);
66 } // end of PivotColumns
67
68/* --------------- Implementation of the PIVAID classe --------------- */
69
70/***********************************************************************/
71/* PIVAID constructor. */
72/***********************************************************************/
73PIVAID::PIVAID(const char *tab, const char *src, const char *picol,
74 const char *fncol, const char *skcol, const char *host,
75 const char *db, const char *user, const char *pwd,
76 int port) : CSORT(false)
77 {
78 Host = (char*)host;
79 User = (char*)user;
80 Pwd = (char*)pwd;
81 Qryp = NULL;
82 Database = (char*)db;
83 Tabname = (char*)tab;
84 Tabsrc = (char*)src;
85 Picol = (char*)picol;
86 Fncol = (char*)fncol;
87 Skcol = (char*)skcol;
88 Rblkp = NULL;
89 Port = (port) ? port : GetDefaultPort();
90 } // end of PIVAID constructor
91
92/***********************************************************************/
93/* Skip columns that are in the skipped column list. */
94/***********************************************************************/
95bool PIVAID::SkipColumn(PCOLRES crp, char *skc)
96 {
97 if (skc)
98 for (char *p = skc; *p; p += (strlen(p) + 1))
99 if (!stricmp(crp->Name, p))
100 return true;
101
102 return false;
103 } // end of SkipColumn
104
105/***********************************************************************/
106/* Make the Pivot table column list. */
107/***********************************************************************/
108PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
109{
110 char *p, *query, *colname, *skc, buf[64];
111 int ndif, nblin, w = 0;
112 bool b = false;
113 PVAL valp;
114 PQRYRES qrp;
115 PCOLRES *pcrp, crp, fncrp = NULL;
116
117 try {
118 // Are there columns to skip?
119 if (Skcol) {
120 uint n = strlen(Skcol);
121
122 skc = (char*)PlugSubAlloc(g, NULL, n + 2);
123 strcpy(skc, Skcol);
124 skc[n + 1] = 0;
125
126 // Replace ; by nulls in skc
127 for (p = strchr(skc, ';'); p; p = strchr(p, ';'))
128 *p++ = 0;
129
130 } else
131 skc = NULL;
132
133 if (!Tabsrc && Tabname) {
134 // Locate the query
135 query = (char*)PlugSubAlloc(g, NULL, strlen(Tabname) + 26);
136 sprintf(query, "SELECT * FROM `%s` LIMIT 1", Tabname);
137 } else if (!Tabsrc) {
138 strcpy(g->Message, MSG(SRC_TABLE_UNDEF));
139 goto err;
140 } else
141 query = (char*)Tabsrc;
142
143 // Open a MySQL connection for this table
144 if (!Myc.Open(g, Host, Database, User, Pwd, Port)) {
145 b = true;
146
147 // Returned values must be in their original character set
148 if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
149 goto err;
150 else
151 Myc.FreeResult();
152
153 } else
154 goto err;
155
156 // Send the source command to MySQL
157 if (Myc.ExecSQL(g, query, &w) == RC_FX)
158 goto err;
159
160 // We must have a storage query to get pivot column values
161 if (!(Qryp = Myc.GetResult(g, true)))
162 goto err;
163
164 if (!Fncol) {
165 for (crp = Qryp->Colresp; crp; crp = crp->Next)
166 if ((!Picol || stricmp(Picol, crp->Name)) && !SkipColumn(crp, skc))
167 Fncol = crp->Name;
168
169 if (!Fncol) {
170 strcpy(g->Message, MSG(NO_DEF_FNCCOL));
171 goto err;
172 } // endif Fncol
173
174 } // endif Fncol
175
176 if (!Picol) {
177 // Find default Picol as the last one not equal to Fncol
178 for (crp = Qryp->Colresp; crp; crp = crp->Next)
179 if (stricmp(Fncol, crp->Name) && !SkipColumn(crp, skc))
180 Picol = crp->Name;
181
182 if (!Picol) {
183 strcpy(g->Message, MSG(NO_DEF_PIVOTCOL));
184 goto err;
185 } // endif Picol
186
187 } // endif picol
188
189 // Prepare the column list
190 for (pcrp = &Qryp->Colresp; crp = *pcrp; )
191 if (SkipColumn(crp, skc)) {
192 // Ignore this column
193 *pcrp = crp->Next;
194 } else if (!stricmp(Picol, crp->Name)) {
195 if (crp->Nulls) {
196 sprintf(g->Message, "Pivot column %s cannot be nullable", Picol);
197 goto err;
198 } // endif Nulls
199
200 Rblkp = crp->Kdata;
201 *pcrp = crp->Next;
202 } else if (!stricmp(Fncol, crp->Name)) {
203 fncrp = crp;
204 *pcrp = crp->Next;
205 } else
206 pcrp = &crp->Next;
207
208 if (!Rblkp) {
209 strcpy(g->Message, MSG(NO_DEF_PIVOTCOL));
210 goto err;
211 } else if (!fncrp) {
212 strcpy(g->Message, MSG(NO_DEF_FNCCOL));
213 goto err;
214 } // endif
215
216 if (Tabsrc) {
217 Myc.Close();
218 b = false;
219
220 // Before calling sort, initialize all
221 nblin = Qryp->Nblin;
222
223 Index.Size = nblin * sizeof(int);
224 Index.Sub = TRUE; // Should be small enough
225
226 if (!PlgDBalloc(g, NULL, Index))
227 goto err;
228
229 Offset.Size = (nblin + 1) * sizeof(int);
230 Offset.Sub = TRUE; // Should be small enough
231
232 if (!PlgDBalloc(g, NULL, Offset))
233 goto err;
234
235 ndif = Qsort(g, nblin);
236
237 if (ndif < 0) // error
238 goto err;
239
240 } else {
241 // The query was limited, we must get pivot column values
242 // Returned values must be in their original character set
243 // if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
244 // goto err;
245
246 query = (char*)PlugSubAlloc(g, NULL, 0);
247 sprintf(query, "SELECT DISTINCT `%s` FROM `%s`", Picol, Tabname);
248 PlugSubAlloc(g, NULL, strlen(query) + 1);
249 Myc.FreeResult();
250
251 // Send the source command to MySQL
252 if (Myc.ExecSQL(g, query, &w) == RC_FX)
253 goto err;
254
255 // We must have a storage query to get pivot column values
256 if (!(qrp = Myc.GetResult(g, true)))
257 goto err;
258
259 Myc.Close();
260 b = false;
261
262 // Get the column list
263 crp = qrp->Colresp;
264 Rblkp = crp->Kdata;
265 ndif = qrp->Nblin;
266 } // endif Tabsrc
267
268 // Allocate the Value used to retieve column names
269 if (!(valp = AllocateValue(g, Rblkp->GetType(),
270 Rblkp->GetVlen(),
271 Rblkp->GetPrec())))
272 goto err;
273
274 // Now make the functional columns
275 for (int i = 0; i < ndif; i++) {
276 if (i) {
277 crp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
278 memcpy(crp, fncrp, sizeof(COLRES));
279 } else
280 crp = fncrp;
281
282 // Get the value that will be the generated column name
283 if (Tabsrc)
284 valp->SetValue_pvblk(Rblkp, Pex[Pof[i]]);
285 else
286 valp->SetValue_pvblk(Rblkp, i);
287
288 colname = valp->GetCharString(buf);
289 crp->Name = PlugDup(g, colname);
290 crp->Flag = 1;
291
292 // Add this column
293 *pcrp = crp;
294 crp->Next = NULL;
295 pcrp = &crp->Next;
296 } // endfor i
297
298 // We added ndif columns and removed 2 (picol and fncol)
299 Qryp->Nbcol += (ndif - 2);
300 return Qryp;
301 } catch (int n) {
302 if (trace(1))
303 htrc("Exception %d: %s\n", n, g->Message);
304 } catch (const char *msg) {
305 strcpy(g->Message, msg);
306 } // end catch
307
308err:
309 if (b)
310 Myc.Close();
311
312 return NULL;
313} // end of MakePivotColumns
314
315/***********************************************************************/
316/* PIVAID: Compare routine for sorting pivot column values. */
317/***********************************************************************/
318int PIVAID::Qcompare(int *i1, int *i2)
319 {
320 // TODO: the actual comparison between pivot column result values.
321 return Rblkp->CompVal(*i1, *i2);
322 } // end of Qcompare
323
324/* --------------- Implementation of the PIVOT classes --------------- */
325
326/***********************************************************************/
327/* PIVOTDEF constructor. */
328/***********************************************************************/
329 PIVOTDEF::PIVOTDEF(void)
330 {
331 Host = User = Pwd = DB = NULL;
332 Tabname = Tabsrc = Picol = Fncol = Function = NULL;
333 GBdone = Accept = false;
334 Port = 0;
335 } // end of PIVOTDEF constructor
336
337/***********************************************************************/
338/* DefineAM: define specific AM block values from PIVOT table. */
339/***********************************************************************/
340bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
341 {
342 char *p1, *p2;
343 PHC hc = ((MYCAT*)Cat)->GetHandler();
344
345 if (PRXDEF::DefineAM(g, am, poff))
346 return TRUE;
347
348 Tabname = (char*)Tablep->GetName();
349 DB = (char*)Tablep->GetSchema();
350 Tabsrc = (char*)Tablep->GetSrc();
351
352 Host = GetStringCatInfo(g, "Host", "localhost");
353 User = GetStringCatInfo(g, "User", "*");
354 Pwd = GetStringCatInfo(g, "Password", NULL);
355 Picol = GetStringCatInfo(g, "PivotCol", NULL);
356 Fncol = GetStringCatInfo(g, "FncCol", NULL);
357
358 // If fncol is like avg(colname), separate Fncol and Function
359 if (Fncol && (p1 = strchr(Fncol, '(')) && (p2 = strchr(p1, ')')) &&
360 (*Fncol != '"') && (!*(p2+1))) {
361 *p1++ = '\0'; *p2 = '\0';
362 Function = Fncol;
363 Fncol = p1;
364 } else
365 Function = GetStringCatInfo(g, "Function", "SUM");
366
367 GBdone = GetBoolCatInfo("Groupby", false);
368 Accept = GetBoolCatInfo("Accept", false);
369 Port = GetIntCatInfo("Port", 3306);
370 Desc = (Tabsrc) ? Tabsrc : Tabname;
371 return FALSE;
372 } // end of DefineAM
373
374/***********************************************************************/
375/* GetTable: makes a new TDB of the proper type. */
376/***********************************************************************/
377PTDB PIVOTDEF::GetTable(PGLOBAL g, MODE)
378 {
379 return new(g) TDBPIVOT(this);
380 } // end of GetTable
381
382/* ------------------------------------------------------------------- */
383
384/***********************************************************************/
385/* Implementation of the TDBPIVOT class. */
386/***********************************************************************/
387TDBPIVOT::TDBPIVOT(PPIVOTDEF tdp) : TDBPRX(tdp)
388 {
389 Host = tdp->Host;
390 Database = tdp->DB;
391 User = tdp->User;
392 Pwd = tdp->Pwd;
393 Port = tdp->Port;
394 Tabname = tdp->Tabname; // Name of source table
395 Tabsrc = tdp->Tabsrc; // SQL description of source table
396 Picol = tdp->Picol; // Pivot column name
397 Fncol = tdp->Fncol; // Function column name
398 Function = tdp->Function; // Aggregate function name
399 Xcolp = NULL; // To the FNCCOL column
400//Xresp = NULL; // To the pivot result column
401//Rblkp = NULL; // The value block of the pivot column
402 Fcolp = NULL; // To the function column
403 Dcolp = NULL; // To the dump column
404 GBdone = tdp->GBdone;
405 Accept = tdp->Accept;
406 Mult = -1; // Estimated table size
407 N = 0; // The current table index
408 M = 0; // The occurence rank
409 FileStatus = 0; // Logical End-of-File
410 RowFlag = 0; // 0: Ok, 1: Same, 2: Skip
411 } // end of TDBPIVOT constructor
412
413/***********************************************************************/
414/* Allocate source column description block. */
415/***********************************************************************/
416PCOL TDBPIVOT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
417 {
418 PCOL colp;
419
420 if (cdp->GetOffset()) {
421 colp = new(g) FNCCOL(cdp, this, cprec, n);
422
423 if (cdp->GetOffset() > 1)
424 Dcolp = colp;
425
426 } else
427 colp = new(g) SRCCOL(cdp, this, cprec, n);
428
429 return colp;
430 } // end of MakeCol
431
432/***********************************************************************/
433/* Find default fonction and pivot columns. */
434/***********************************************************************/
435bool TDBPIVOT::FindDefaultColumns(PGLOBAL g)
436 {
437 PCOLDEF cdp;
438 PTABDEF defp = Tdbp->GetDef();
439
440 if (!Fncol) {
441 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
442 if (!Picol || stricmp(Picol, cdp->GetName()))
443 Fncol = cdp->GetName();
444
445 if (!Fncol) {
446 strcpy(g->Message, MSG(NO_DEF_FNCCOL));
447 return true;
448 } // endif Fncol
449
450 } // endif Fncol
451
452 if (!Picol) {
453 // Find default Picol as the last one not equal to Fncol
454 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
455 if (stricmp(Fncol, cdp->GetName()))
456 Picol = cdp->GetName();
457
458 if (!Picol) {
459 strcpy(g->Message, MSG(NO_DEF_PIVOTCOL));
460 return true;
461 } // endif Picol
462
463 } // endif Picol
464
465 return false;
466 } // end of FindDefaultColumns
467
468/***********************************************************************/
469/* Prepare the source table Query. */
470/***********************************************************************/
471bool TDBPIVOT::GetSourceTable(PGLOBAL g)
472 {
473 if (Tdbp)
474 return false; // Already done
475
476 if (!Tabsrc && Tabname) {
477 // Get the table description block of this table
478 if (!(Tdbp = GetSubTable(g, ((PPIVOTDEF)To_Def)->Tablep, true)))
479 return true;
480
481 if (!GBdone) {
482 char *colist;
483 PCOLDEF cdp;
484
485 if (FindDefaultColumns(g))
486 return true;
487
488 // Locate the suballocated colist (size is not known yet)
489 *(colist = (char*)PlugSubAlloc(g, NULL, 0)) = 0;
490
491 // Make the column list
492 for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
493 if (!cdp->GetOffset())
494 strcat(strcat(colist, cdp->GetName()), ", ");
495
496 // Add the Pivot column at the end of the list
497 strcat(colist, Picol);
498
499 // Now we know how much was suballocated
500 PlugSubAlloc(g, NULL, strlen(colist) + 1);
501
502 // Locate the source string (size is not known yet)
503 Tabsrc = (char*)PlugSubAlloc(g, NULL, 0);
504
505 // Start making the definition
506 strcat(strcat(strcpy(Tabsrc, "SELECT "), colist), ", ");
507
508 // Make it suitable for Pivot by doing the group by
509 strcat(strcat(Tabsrc, Function), "(");
510 strcat(strcat(strcat(Tabsrc, Fncol), ") "), Fncol);
511 strcat(strcat(Tabsrc, " FROM "), Tabname);
512 strcat(strcat(Tabsrc, " GROUP BY "), colist);
513
514 if (Tdbp->IsView()) // Until MariaDB bug is fixed
515 strcat(strcat(Tabsrc, " ORDER BY "), colist);
516
517 // Now we know how much was suballocated
518 PlugSubAlloc(g, NULL, strlen(Tabsrc) + 1);
519 } // endif !GBdone
520
521 } else if (!Tabsrc) {
522 strcpy(g->Message, MSG(SRC_TABLE_UNDEF));
523 return true;
524 } // endif
525
526 if (Tabsrc) {
527 // Get the new table description block of this source table
528 PTABLE tablep = new(g) XTAB("whatever", Tabsrc);
529
530 tablep->SetSchema(Database);
531
532 if (!(Tdbp = GetSubTable(g, tablep, true)))
533 return true;
534
535 } // endif Tabsrc
536
537 return false;
538 } // end of GetSourceTable
539
540/***********************************************************************/
541/* Make the required pivot columns. */
542/***********************************************************************/
543bool TDBPIVOT::MakePivotColumns(PGLOBAL g)
544 {
545 if (!Tdbp->IsView()) {
546 // This was not done yet if GBdone is true
547 if (FindDefaultColumns(g))
548 return true;
549
550 // Now it is time to allocate the pivot and function columns
551 if (!(Fcolp = Tdbp->ColDB(g, Fncol, 0))) {
552 // Function column not found in table
553 sprintf(g->Message, MSG(COL_ISNOT_TABLE), Fncol, Tabname);
554 return true;
555 } else if (Fcolp->InitValue(g))
556 return true;
557
558 if (!(Xcolp = Tdbp->ColDB(g, Picol, 0))) {
559 // Pivot column not found in table
560 sprintf(g->Message, MSG(COL_ISNOT_TABLE), Picol, Tabname);
561 return true;
562 } else if (Xcolp->InitValue(g))
563 return true;
564
565 // Check and initialize the subtable columns
566 for (PCOL cp = Columns; cp; cp = cp->GetNext())
567 if (cp->GetAmType() == TYPE_AM_SRC) {
568 if (((PSRCCOL)cp)->Init(g, NULL))
569 return TRUE;
570
571 } else if (cp->GetAmType() == TYPE_AM_FNC)
572 if (((PFNCCOL)cp)->InitColumn(g))
573 return TRUE;
574
575 } // endif isview
576
577 return false;
578 } // end of MakePivotColumns
579
580/***********************************************************************/
581/* Make the required pivot columns for an object view. */
582/***********************************************************************/
583bool TDBPIVOT::MakeViewColumns(PGLOBAL g)
584 {
585 if (Tdbp->IsView()) {
586 // Tdbp is a view ColDB cannot be used
587 PCOL colp, cp;
588 PTDBMY tdbp;
589
590 if (Tdbp->GetAmType() != TYPE_AM_MYSQL) {
591 strcpy(g->Message, "View is not MySQL");
592 return true;
593 } else
594 tdbp = (PTDBMY)Tdbp;
595
596 if (!Fncol && !(Fncol = tdbp->FindFieldColumn(Picol))) {
597 strcpy(g->Message, MSG(NO_DEF_FNCCOL));
598 return true;
599 } // endif Fncol
600
601 if (!Picol && !(Picol = tdbp->FindFieldColumn(Fncol))) {
602 strcpy(g->Message, MSG(NO_DEF_PIVOTCOL));
603 return true;
604 } // endif Picol
605
606 // Now it is time to allocate the pivot and function columns
607 if (!(Fcolp = tdbp->MakeFieldColumn(g, Fncol)))
608 return true;
609
610 if (!(Xcolp = tdbp->MakeFieldColumn(g, Picol)))
611 return true;
612
613 // Check and initialize the subtable columns
614 for (cp = Columns; cp; cp = cp->GetNext())
615 if (cp->GetAmType() == TYPE_AM_SRC) {
616 if ((colp = tdbp->MakeFieldColumn(g, cp->GetName()))) {
617 ((PSRCCOL)cp)->Colp = colp;
618 ((PSRCCOL)cp)->To_Val = colp->GetValue();
619 cp->AddStatus(BUF_READ); // All is done here
620 } else
621 return true;
622
623 } else if (cp->GetAmType() == TYPE_AM_FNC)
624 if (((PFNCCOL)cp)->InitColumn(g))
625 return TRUE;
626
627 } // endif isview
628
629 return false;
630 } // end of MakeViewColumns
631
632/***********************************************************************/
633/* PIVOT GetMaxSize: returns the maximum number of rows in the table. */
634/***********************************************************************/
635int TDBPIVOT::GetMaxSize(PGLOBAL g __attribute__((unused)))
636 {
637#if 0
638 if (MaxSize < 0)
639 MaxSize = MakePivotColumns(g);
640
641 return MaxSize;
642#endif // 0
643 return 10;
644 } // end of GetMaxSize
645
646/***********************************************************************/
647/* In this sample, ROWID will be the (virtual) row number, */
648/* while ROWNUM will be the occurence rank in the multiple column. */
649/***********************************************************************/
650int TDBPIVOT::RowNumber(PGLOBAL, bool b)
651 {
652 return (b) ? M : N;
653 } // end of RowNumber
654
655/***********************************************************************/
656/* PIVOT Access Method opening routine. */
657/***********************************************************************/
658bool TDBPIVOT::OpenDB(PGLOBAL g)
659 {
660 if (Use == USE_OPEN) {
661 /*******************************************************************/
662 /* Table already open, just replace it at its beginning. */
663 /*******************************************************************/
664 N = M = 0;
665 RowFlag = 0;
666 FileStatus = 0;
667 return FALSE;
668 } // endif use
669
670 if (Mode != MODE_READ) {
671 /*******************************************************************/
672 /* Currently PIVOT tables cannot be modified. */
673 /*******************************************************************/
674 sprintf(g->Message, MSG(TABLE_READ_ONLY), "PIVOT");
675 return TRUE;
676 } // endif Mode
677
678 if (To_Key_Col || To_Kindex) {
679 /*******************************************************************/
680 /* Direct access of PIVOT tables is not implemented yet. */
681 /*******************************************************************/
682 strcpy(g->Message, MSG(NO_PIV_DIR_ACC));
683 return TRUE;
684 } // endif To_Key_Col
685
686 /*********************************************************************/
687 /* Do it here if not done yet (should not be the case). */
688 /*********************************************************************/
689 if (GetSourceTable(g))
690 return TRUE;
691
692 // For tables, columns must be allocated before opening
693 if (MakePivotColumns(g))
694 return TRUE;
695
696 /*********************************************************************/
697 /* Physically open the object table. */
698 /*********************************************************************/
699 if (Tdbp->OpenDB(g))
700 return TRUE;
701
702 Use = USE_OPEN; // Do it now in case we are recursively called
703
704 /*********************************************************************/
705 /* Make all required pivot columns for object views. */
706 /*********************************************************************/
707 return MakeViewColumns(g);
708 } // end of OpenDB
709
710/***********************************************************************/
711/* Data Base read routine for PIVOT access method. */
712/***********************************************************************/
713int TDBPIVOT::ReadDB(PGLOBAL g)
714 {
715 int rc = RC_OK;
716 bool newrow = FALSE;
717 PCOL colp;
718
719 if (FileStatus == 2)
720 return RC_EF;
721
722 if (FileStatus)
723 for (colp = Columns; colp; colp = colp->GetNext())
724 if (colp->GetAmType() == TYPE_AM_SRC)
725 ((PSRCCOL)colp)->SetColumn();
726
727 // New row, reset all function column values
728 for (colp = Columns; colp; colp = colp->GetNext())
729 if (colp->GetAmType() == TYPE_AM_FNC)
730 colp->GetValue()->Reset();
731
732 /*********************************************************************/
733 /* Now start the multi reading process. */
734 /*********************************************************************/
735 do {
736 if (RowFlag != 1) {
737 if ((rc = Tdbp->ReadDB(g)) != RC_OK) {
738 if (FileStatus && rc == RC_EF) {
739 // A prepared row remains to be sent
740 FileStatus = 2;
741 rc = RC_OK;
742 } // endif FileStatus
743
744 break;
745 } // endif rc
746
747 for (colp = Tdbp->GetColumns(); colp; colp = colp->GetNext())
748 colp->ReadColumn(g);
749
750 for (colp = Columns; colp; colp = colp->GetNext())
751 if (colp->GetAmType() == TYPE_AM_SRC)
752 if (FileStatus) {
753 if (((PSRCCOL)colp)->CompareLast()) {
754 newrow = (RowFlag) ? TRUE : FALSE;
755 break;
756 } // endif CompareLast
757
758 } else
759 ((PSRCCOL)colp)->SetColumn();
760
761 FileStatus = 1;
762 } // endif RowFlag
763
764 if (newrow) {
765 RowFlag = 1;
766 break;
767 } else
768 RowFlag = 2;
769
770 // Look for the column having this header
771 for (colp = Columns; colp; colp = colp->GetNext())
772 if (colp->GetAmType() == TYPE_AM_FNC) {
773 if (((PFNCCOL)colp)->CompareColumn())
774 break;
775
776 } // endif AmType
777
778 if (!colp && !(colp = Dcolp)) {
779 if (!Accept) {
780 strcpy(g->Message, MSG(NO_MATCH_COL));
781 return RC_FX;
782 } else
783 continue;
784
785 } // endif colp
786
787 // Set the value of the matching column from the fonction value
788 colp->GetValue()->SetValue_pval(Fcolp->GetValue());
789 } while (RowFlag == 2);
790
791 N++;
792 return rc;
793 } // end of ReadDB
794
795/***********************************************************************/
796/* WriteDB: Data Base write routine for PIVOT access methods. */
797/***********************************************************************/
798int TDBPIVOT::WriteDB(PGLOBAL g)
799 {
800 sprintf(g->Message, MSG(TABLE_READ_ONLY), "PIVOT");
801 return RC_FX;
802 } // end of WriteDB
803
804/***********************************************************************/
805/* Data Base delete line routine for PIVOT access methods. */
806/***********************************************************************/
807int TDBPIVOT::DeleteDB(PGLOBAL g, int)
808 {
809 sprintf(g->Message, MSG(NO_TABLE_DEL), "PIVOT");
810 return RC_FX;
811 } // end of DeleteDB
812
813/***********************************************************************/
814/* Data Base close routine for PIVOT access method. */
815/***********************************************************************/
816void TDBPIVOT::CloseDB(PGLOBAL g)
817 {
818 if (Tdbp)
819 Tdbp->CloseDB(g);
820
821 } // end of CloseDB
822
823// ------------------------ FNCCOL functions ----------------------------
824
825/***********************************************************************/
826/* FNCCOL public constructor. */
827/***********************************************************************/
828FNCCOL::FNCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
829 : COLBLK(cdp, tdbp, i)
830 {
831 if (cprec) {
832 Next = cprec->GetNext();
833 cprec->SetNext(this);
834 } else {
835 Next = tdbp->GetColumns();
836 tdbp->SetColumns(this);
837 } // endif cprec
838
839 Value = NULL; // We'll get a new one later
840 Hval = NULL; // The unconverted header value
841 Xcolp = NULL;
842 } // end of FNCCOL constructor
843
844/***********************************************************************/
845/* FNCCOL initialization function. */
846/***********************************************************************/
847bool FNCCOL::InitColumn(PGLOBAL g)
848{
849 // Must have its own value block
850 if (InitValue(g))
851 return TRUE;
852
853 // Make a value from the column name
854 Hval = AllocateValue(g, Name, TYPE_STRING);
855 Hval->SetPrec(1); // Case insensitive
856
857 Xcolp = ((PTDBPIVOT)To_Tdb)->Xcolp;
858 AddStatus(BUF_READ); // All is done here
859 return FALSE;
860} // end of InitColumn
861
862/***********************************************************************/
863/* CompareColumn: Compare column value with source column value. */
864/***********************************************************************/
865bool FNCCOL::CompareColumn(void)
866 {
867 // Compare the unconverted values
868 return Hval->IsEqual(Xcolp->GetValue(), false);
869 } // end of CompareColumn
870
871// ------------------------ SRCCOL functions ----------------------------
872
873/***********************************************************************/
874/* SRCCOL public constructor. */
875/***********************************************************************/
876SRCCOL::SRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int n)
877 : PRXCOL(cdp, tdbp, cprec, n)
878 {
879 } // end of SRCCOL constructor
880
881/***********************************************************************/
882/* Initialize the column as pointing to the source column. */
883/***********************************************************************/
884bool SRCCOL::Init(PGLOBAL g, PTDB tp)
885 {
886 if (PRXCOL::Init(g, tp))
887 return true;
888
889 AddStatus(BUF_READ); // All is done here
890 return false;
891 } // end of SRCCOL constructor
892
893/***********************************************************************/
894/* SetColumn: have the column value set from the source column. */
895/***********************************************************************/
896void SRCCOL::SetColumn(void)
897 {
898 Value->SetValue_pval(To_Val);
899 } // end of SetColumn
900
901/***********************************************************************/
902/* SetColumn: Compare column value with source column value. */
903/***********************************************************************/
904bool SRCCOL::CompareLast(void)
905 {
906 // Compare the unconverted values
907 return !Value->IsEqual(To_Val, true);
908 } // end of CompareColumn
909
910/* --------------------- End of TabPivot/TabQrs ---------------------- */
911