mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			912 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			912 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /************ TabPivot C++ Program Source Code File (.CPP) *************/
 | |
| /* PROGRAM NAME: TABPIVOT                                              */
 | |
| /* -------------                                                       */
 | |
| /*  Version 1.7                                                        */
 | |
| /*                                                                     */
 | |
| /* COPYRIGHT:                                                          */
 | |
| /* ----------                                                          */
 | |
| /*  (C) Copyright to the author Olivier BERTRAND          2005-2017    */
 | |
| /*                                                                     */
 | |
| /* WHAT THIS PROGRAM DOES:                                             */
 | |
| /* -----------------------                                             */
 | |
| /*  This program are the PIVOT classes DB execution routines.          */
 | |
| /***********************************************************************/
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include relevant sections of the operating system header file.     */
 | |
| /***********************************************************************/
 | |
| #include "my_global.h"
 | |
| #include "table.h"       // MySQL table definitions
 | |
| #if defined(_WIN32)
 | |
| #if defined(__BORLANDC__)
 | |
| #define __MFC_COMPAT__                   // To define min/max as macro
 | |
| #endif
 | |
| //#include <windows.h>
 | |
| #elif defined(UNIX)
 | |
| #include <errno.h>
 | |
| #include <unistd.h>
 | |
| #include "osutil.h"
 | |
| #else
 | |
| #include <io.h>
 | |
| #endif
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include application header files:                                  */
 | |
| /*  global.h    is header containing all global declarations.          */
 | |
| /*  plgdbsem.h  is header containing the DB application declarations.  */
 | |
| /***********************************************************************/
 | |
| #define FRM_VER 6
 | |
| #include "sql_const.h"
 | |
| #include "field.h"
 | |
| #include "global.h"
 | |
| #include "plgdbsem.h"
 | |
| #include "xtable.h"
 | |
| #include "tabext.h"
 | |
| #include "tabcol.h"
 | |
| #include "colblk.h"
 | |
| #include "tabmysql.h"
 | |
| #include "csort.h"
 | |
| #include "tabutil.h"
 | |
| #include "tabpivot.h"
 | |
| #include "valblk.h"
 | |
| #include "ha_connect.h"
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Make the Pivot table column list.                                  */
 | |
| /***********************************************************************/
 | |
| PQRYRES PivotColumns(PGLOBAL g, const char *tab,   const char *src, 
 | |
|                                 const char *picol, const char *fncol,
 | |
|                                 const char *skcol, const char *host,  
 | |
|                                 const char *db,    const char *user,
 | |
|                                 const char *pwd,   int port)
 | |
|   {
 | |
|   PIVAID pvd(tab, src, picol, fncol, skcol, host, db, user, pwd, port);
 | |
| 
 | |
|   return pvd.MakePivotColumns(g);
 | |
|   } // end of PivotColumns
 | |
| 
 | |
| /* --------------- Implementation of the PIVAID classe --------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PIVAID constructor.                                                */
 | |
| /***********************************************************************/
 | |
| PIVAID::PIVAID(const char *tab,   const char *src,   const char *picol,
 | |
|                const char *fncol, const char *skcol, const char *host,
 | |
|                const char *db,    const char *user,  const char *pwd,
 | |
|                int port) : CSORT(false)
 | |
|   {
 | |
|   Host = (char*)host;
 | |
|   User = (char*)user;
 | |
|   Pwd = (char*)pwd;
 | |
|   Qryp = NULL;
 | |
|   Database = (char*)db;
 | |
|   Tabname = (char*)tab;
 | |
|   Tabsrc = (char*)src;
 | |
|   Picol = (char*)picol;
 | |
|   Fncol = (char*)fncol;
 | |
|   Skcol = (char*)skcol;
 | |
|   Rblkp = NULL;
 | |
|   Port = (port) ? port : GetDefaultPort();
 | |
|   } // end of PIVAID constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Skip columns that are in the skipped column list.                  */
 | |
| /***********************************************************************/
 | |
| bool PIVAID::SkipColumn(PCOLRES crp, char *skc)
 | |
|   {
 | |
|   if (skc)
 | |
|     for (char *p = skc; *p; p += (strlen(p) + 1))
 | |
|       if (!stricmp(crp->Name, p))
 | |
|         return true;
 | |
| 
 | |
|   return false;
 | |
|   } // end of SkipColumn
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Make the Pivot table column list.                                  */
 | |
| /***********************************************************************/
 | |
| PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
 | |
| {
 | |
| 	char    *p, *query, *colname, *skc, buf[64];
 | |
| 	int      ndif, nblin, w = 0;
 | |
| 	bool     b = false;
 | |
| 	PVAL     valp;
 | |
| 	PQRYRES  qrp;
 | |
| 	PCOLRES *pcrp, crp, fncrp = NULL;
 | |
| 
 | |
| 	try {
 | |
| 		// Are there columns to skip?
 | |
| 		if (Skcol) {
 | |
| 			uint n = strlen(Skcol);
 | |
| 
 | |
| 			skc = (char*)PlugSubAlloc(g, NULL, n + 2);
 | |
| 			snprintf(skc, n + 2, "%s", Skcol);
 | |
| 			skc[n + 1] = 0;
 | |
| 
 | |
| 			// Replace ; by nulls in skc
 | |
| 			for (p = strchr(skc, ';'); p; p = strchr(p, ';'))
 | |
| 				*p++ = 0;
 | |
| 
 | |
| 		} else
 | |
| 			skc = NULL;
 | |
| 
 | |
| 		if (!Tabsrc && Tabname) {
 | |
| 			// Locate the  query
 | |
| 			query = (char*)PlugSubAlloc(g, NULL, strlen(Tabname) + 26);
 | |
| 			snprintf(query, strlen(Tabname) + 26, "SELECT * FROM `%s` LIMIT 1", Tabname);
 | |
| 		} else if (!Tabsrc) {
 | |
| 			snprintf(g->Message, sizeof(g->Message), MSG(SRC_TABLE_UNDEF));
 | |
| 			goto err;
 | |
| 		} else
 | |
| 			query = (char*)Tabsrc;
 | |
| 
 | |
| 		// Open a MySQL connection for this table
 | |
| 		if (!Myc.Open(g, Host, Database, User, Pwd, Port)) {
 | |
| 			b = true;
 | |
| 
 | |
| 			// Returned values must be in their original character set
 | |
| 			if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
 | |
| 				goto err;
 | |
| 			else
 | |
| 				Myc.FreeResult();
 | |
| 
 | |
| 		} else
 | |
| 			goto err;
 | |
| 
 | |
| 		// Send the source command to MySQL
 | |
| 		if (Myc.ExecSQL(g, query, &w) == RC_FX)
 | |
| 			goto err;
 | |
| 
 | |
| 		// We must have a storage query to get pivot column values
 | |
| 		if (!(Qryp = Myc.GetResult(g, true)))
 | |
| 			goto err;
 | |
| 
 | |
| 		if (!Fncol) {
 | |
| 			for (crp = Qryp->Colresp; crp; crp = crp->Next)
 | |
| 				if ((!Picol || stricmp(Picol, crp->Name)) && !SkipColumn(crp, skc))
 | |
| 					Fncol = crp->Name;
 | |
| 
 | |
| 			if (!Fncol) {
 | |
| 				snprintf(g->Message, sizeof(g->Message), MSG(NO_DEF_FNCCOL));
 | |
| 				goto err;
 | |
| 			} // endif Fncol
 | |
| 
 | |
| 		} // endif Fncol
 | |
| 
 | |
| 		if (!Picol) {
 | |
| 			// Find default Picol as the last one not equal to Fncol
 | |
| 			for (crp = Qryp->Colresp; crp; crp = crp->Next)
 | |
| 				if (stricmp(Fncol, crp->Name) && !SkipColumn(crp, skc))
 | |
| 					Picol = crp->Name;
 | |
| 
 | |
| 			if (!Picol) {
 | |
| 				snprintf(g->Message, sizeof(g->Message),  MSG(NO_DEF_PIVOTCOL));
 | |
| 				goto err;
 | |
| 			} // endif Picol
 | |
| 
 | |
| 		} // endif picol
 | |
| 
 | |
| 	  // Prepare the column list
 | |
| 		for (pcrp = &Qryp->Colresp; (crp = *pcrp); ) {
 | |
| 			if (SkipColumn(crp, skc)) {
 | |
| 				// Ignore this column
 | |
| 				*pcrp = crp->Next;
 | |
| 			} else if (!stricmp(Picol, crp->Name)) {
 | |
| 				if (crp->Nulls) {
 | |
| 					snprintf(g->Message, sizeof(g->Message), "Pivot column %s cannot be nullable", Picol);
 | |
| 					goto err;
 | |
| 				} // endif Nulls
 | |
| 
 | |
| 				Rblkp = crp->Kdata;
 | |
| 				*pcrp = crp->Next;
 | |
| 			} else if (!stricmp(Fncol, crp->Name)) {
 | |
| 				fncrp = crp;
 | |
| 				*pcrp = crp->Next;
 | |
| 			} else
 | |
| 				pcrp = &crp->Next;
 | |
| 		}
 | |
| 		if (!Rblkp) {
 | |
| 			snprintf(g->Message, sizeof(g->Message), MSG(NO_DEF_PIVOTCOL));
 | |
| 			goto err;
 | |
| 		} else if (!fncrp) {
 | |
| 			snprintf(g->Message, sizeof(g->Message), MSG(NO_DEF_FNCCOL));
 | |
| 			goto err;
 | |
| 		} // endif
 | |
| 
 | |
| 		if (Tabsrc) {
 | |
| 			Myc.Close();
 | |
| 			b = false;
 | |
| 
 | |
| 			// Before calling sort, initialize all
 | |
| 			nblin = Qryp->Nblin;
 | |
| 
 | |
| 			Index.Size = nblin * sizeof(int);
 | |
| 			Index.Sub = TRUE;                  // Should be small enough
 | |
| 
 | |
| 			if (!PlgDBalloc(g, NULL, Index))
 | |
| 				goto err;
 | |
| 
 | |
| 			Offset.Size = (nblin + 1) * sizeof(int);
 | |
| 			Offset.Sub = TRUE;                 // Should be small enough
 | |
| 
 | |
| 			if (!PlgDBalloc(g, NULL, Offset))
 | |
| 				goto err;
 | |
| 
 | |
| 			ndif = Qsort(g, nblin);
 | |
| 
 | |
| 			if (ndif < 0)           // error
 | |
| 				goto err;
 | |
| 
 | |
| 		} else {
 | |
| 			// The query was limited, we must get pivot column values
 | |
| 			// Returned values must be in their original character set
 | |
| 	    //  if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
 | |
| 	    //    goto err;
 | |
| 
 | |
| 			query = (char*)PlugSubAlloc(g, NULL, 0);
 | |
| 			sprintf(query, "SELECT DISTINCT `%s` FROM `%s`", Picol, Tabname);
 | |
| 			PlugSubAlloc(g, NULL, strlen(query) + 1);
 | |
| 			Myc.FreeResult();
 | |
| 
 | |
| 			// Send the source command to MySQL
 | |
| 			if (Myc.ExecSQL(g, query, &w) == RC_FX)
 | |
| 				goto err;
 | |
| 
 | |
| 			// We must have a storage query to get pivot column values
 | |
| 			if (!(qrp = Myc.GetResult(g, true)))
 | |
| 				goto err;
 | |
| 
 | |
| 			Myc.Close();
 | |
| 			b = false;
 | |
| 
 | |
| 			// Get the column list
 | |
| 			crp = qrp->Colresp;
 | |
| 			Rblkp = crp->Kdata;
 | |
| 			ndif = qrp->Nblin;
 | |
| 		} // endif Tabsrc
 | |
| 
 | |
| 		// Allocate the Value used to retieve column names
 | |
| 		if (!(valp = AllocateValue(g, Rblkp->GetType(),
 | |
| 				                          Rblkp->GetVlen(),
 | |
| 				                          Rblkp->GetPrec())))
 | |
| 			goto err;
 | |
| 
 | |
| 		// Now make the functional columns
 | |
| 		for (int i = 0; i < ndif; i++) {
 | |
| 			if (i) {
 | |
| 				crp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
 | |
| 				memcpy(crp, fncrp, sizeof(COLRES));
 | |
| 			} else
 | |
| 				crp = fncrp;
 | |
| 
 | |
| 			// Get the value that will be the generated column name
 | |
| 			if (Tabsrc)
 | |
| 				valp->SetValue_pvblk(Rblkp, Pex[Pof[i]]);
 | |
| 			else
 | |
| 				valp->SetValue_pvblk(Rblkp, i);
 | |
| 
 | |
| 			colname = valp->GetCharString(buf);
 | |
| 			crp->Name = PlugDup(g, colname);
 | |
| 			crp->Flag = 1;
 | |
| 
 | |
| 			// Add this column
 | |
| 			*pcrp = crp;
 | |
| 			crp->Next = NULL;
 | |
| 			pcrp = &crp->Next;
 | |
| 		} // endfor i
 | |
| 
 | |
| 		// We added ndif columns and removed 2 (picol and fncol)
 | |
| 		Qryp->Nbcol += (ndif - 2);
 | |
| 		return Qryp;
 | |
| 	} catch (int n) {
 | |
| 		if (trace(1))
 | |
| 			htrc("Exception %d: %s\n", n, g->Message);
 | |
| 	} catch (const char *msg) {
 | |
| 		snprintf(g->Message, sizeof(g->Message), "%s", msg);
 | |
| 	} // end catch
 | |
| 
 | |
| err:
 | |
| 	if (b)
 | |
| 		Myc.Close();
 | |
| 
 | |
| 	return NULL;
 | |
| } // end of MakePivotColumns
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PIVAID: Compare routine for sorting pivot column values.           */
 | |
| /***********************************************************************/
 | |
| int PIVAID::Qcompare(int *i1, int *i2)
 | |
|   {
 | |
|   // TODO: the actual comparison between pivot column result values.
 | |
|   return Rblkp->CompVal(*i1, *i2);
 | |
|   } // end of Qcompare
 | |
| 
 | |
| /* --------------- Implementation of the PIVOT classes --------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PIVOTDEF constructor.                                              */
 | |
| /***********************************************************************/
 | |
|   PIVOTDEF::PIVOTDEF(void) 
 | |
|   {
 | |
|   Host = User = Pwd = DB = NULL;
 | |
|   Tabname = Tabsrc = Picol = Fncol = Function = NULL;
 | |
|   GBdone = Accept = false;
 | |
|   Port = 0;
 | |
|   } // end of PIVOTDEF constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  DefineAM: define specific AM block values from PIVOT table.        */
 | |
| /***********************************************************************/
 | |
| bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
 | |
|   {
 | |
|   char *p1, *p2;
 | |
| 
 | |
|   if (PRXDEF::DefineAM(g, am, poff))
 | |
|     return TRUE;
 | |
| 
 | |
|   Tabname = (char*)Tablep->GetName();
 | |
|   DB = (char*)Tablep->GetSchema();
 | |
|   Tabsrc = (char*)Tablep->GetSrc();
 | |
| 
 | |
|   Host = GetStringCatInfo(g, "Host", "localhost");
 | |
|   User = GetStringCatInfo(g, "User", "*");
 | |
|   Pwd = GetStringCatInfo(g, "Password", NULL);
 | |
|   Picol = GetStringCatInfo(g, "PivotCol", NULL);
 | |
|   Fncol = GetStringCatInfo(g, "FncCol", NULL);
 | |
|   
 | |
|   // If fncol is like avg(colname), separate Fncol and Function
 | |
|   if (Fncol && (p1 = strchr(Fncol, '(')) && (p2 = strchr(p1, ')')) &&
 | |
|       (*Fncol != '"') &&  (!*(p2+1))) {
 | |
|     *p1++ = '\0'; *p2 = '\0';
 | |
|     Function = Fncol;
 | |
|     Fncol = p1;
 | |
|   } else
 | |
|     Function = GetStringCatInfo(g, "Function", "SUM");
 | |
| 
 | |
|   GBdone = GetBoolCatInfo("Groupby", false);
 | |
|   Accept = GetBoolCatInfo("Accept", false);
 | |
|   Port = GetIntCatInfo("Port", 3306);
 | |
|   Desc = (Tabsrc) ? Tabsrc : Tabname;
 | |
|   return FALSE;
 | |
|   } // end of DefineAM
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  GetTable: makes a new TDB of the proper type.                      */
 | |
| /***********************************************************************/
 | |
| PTDB PIVOTDEF::GetTable(PGLOBAL g, MODE)
 | |
|   {
 | |
|   return new(g) TDBPIVOT(this);
 | |
|   } // end of GetTable
 | |
| 
 | |
| /* ------------------------------------------------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the TDBPIVOT class.                              */
 | |
| /***********************************************************************/
 | |
| TDBPIVOT::TDBPIVOT(PPIVOTDEF tdp) : TDBPRX(tdp)
 | |
|   {
 | |
|   Host = tdp->Host;
 | |
|   Database = tdp->DB;
 | |
|   User = tdp->User;
 | |
|   Pwd = tdp->Pwd;
 | |
|   Port = tdp->Port;
 | |
|   Tabname = tdp->Tabname;    // Name of source table
 | |
|   Tabsrc = tdp->Tabsrc;      // SQL description of source table
 | |
|   Picol = tdp->Picol;        // Pivot column name
 | |
|   Fncol = tdp->Fncol;        // Function column name
 | |
|   Function = tdp->Function;  // Aggregate function name
 | |
|   Xcolp = NULL;              // To the FNCCOL column
 | |
| //Xresp = NULL;              // To the pivot result column
 | |
| //Rblkp = NULL;              // The value block of the pivot column
 | |
|   Fcolp = NULL;              // To the function column
 | |
|   Dcolp = NULL;              // To the dump column
 | |
|   GBdone = tdp->GBdone;
 | |
|   Accept = tdp->Accept;
 | |
|   Mult = -1;                // Estimated table size
 | |
|   N = 0;                    // The current table index
 | |
|   M = 0;                    // The occurrence rank
 | |
|   FileStatus = 0;           // Logical End-of-File
 | |
|   RowFlag = 0;              // 0: Ok, 1: Same, 2: Skip
 | |
|   } // end of TDBPIVOT constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate source column description block.                          */
 | |
| /***********************************************************************/
 | |
| PCOL TDBPIVOT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
 | |
|   {
 | |
|   PCOL colp;
 | |
| 
 | |
|   if (cdp->GetOffset()) {
 | |
|     colp = new(g) FNCCOL(cdp, this, cprec, n);
 | |
| 
 | |
|     if (cdp->GetOffset() > 1)
 | |
|       Dcolp = colp;
 | |
| 
 | |
|   } else
 | |
|     colp = new(g) SRCCOL(cdp, this, cprec, n);
 | |
| 
 | |
|   return colp;
 | |
|   } // end of MakeCol
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Find default fonction and pivot columns.                           */
 | |
| /***********************************************************************/
 | |
| bool TDBPIVOT::FindDefaultColumns(PGLOBAL g)
 | |
|   {
 | |
|   PCOLDEF cdp;
 | |
|   PTABDEF defp = Tdbp->GetDef();
 | |
| 
 | |
|   if (!Fncol) {
 | |
|     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|       if (!Picol || stricmp(Picol, cdp->GetName()))
 | |
|         Fncol = cdp->GetName();
 | |
|   
 | |
|     if (!Fncol) {
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(NO_DEF_FNCCOL));
 | |
|       return true;
 | |
|       } // endif Fncol
 | |
|   
 | |
|     } // endif Fncol
 | |
|   
 | |
|   if (!Picol) {
 | |
|     // Find default Picol as the last one not equal to Fncol
 | |
|     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|       if (stricmp(Fncol, cdp->GetName()))
 | |
|         Picol = cdp->GetName();
 | |
|   
 | |
|     if (!Picol) {
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(NO_DEF_PIVOTCOL));
 | |
|       return true;
 | |
|       } // endif Picol
 | |
|   
 | |
|     } // endif Picol
 | |
|   
 | |
|   return false;
 | |
|   } // end of FindDefaultColumns
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Prepare the source table Query.                                    */
 | |
| /***********************************************************************/
 | |
| bool TDBPIVOT::GetSourceTable(PGLOBAL g)
 | |
|   {
 | |
|   if (Tdbp)
 | |
|     return false;             // Already done
 | |
| 
 | |
|   if (!Tabsrc && Tabname) {
 | |
|     // Get the table description block of this table
 | |
|     if (!(Tdbp = GetSubTable(g, ((PPIVOTDEF)To_Def)->Tablep, true)))
 | |
|       return true;
 | |
| 
 | |
|     if (!GBdone) {
 | |
|       char   *colist;
 | |
|       PCOLDEF cdp;
 | |
| 
 | |
|       if (FindDefaultColumns(g))
 | |
|         return true;
 | |
|   
 | |
|       // Locate the suballocated colist (size is not known yet)
 | |
|       *(colist = (char*)PlugSubAlloc(g, NULL, 0)) = 0;
 | |
|   
 | |
|       // Make the column list
 | |
|       for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|         if (!cdp->GetOffset())
 | |
|           strcat(strcat(colist, cdp->GetName()), ", ");
 | |
|   
 | |
|       // Add the Pivot column at the end of the list
 | |
|       strcat(colist, Picol);
 | |
|   
 | |
|       // Now we know how much was suballocated
 | |
|       PlugSubAlloc(g, NULL, strlen(colist) + 1);
 | |
|   
 | |
|       // Locate the source string (size is not known yet)
 | |
|       Tabsrc = (char*)PlugSubAlloc(g, NULL, 0);
 | |
|   
 | |
|       // Start making the definition
 | |
|       strcat(strcat(strcpy(Tabsrc, "SELECT "), colist), ", ");
 | |
|   
 | |
|       // Make it suitable for Pivot by doing the group by
 | |
|       strcat(strcat(Tabsrc, Function), "(");
 | |
|       strcat(strcat(strcat(Tabsrc, Fncol), ") "), Fncol);
 | |
|       strcat(strcat(Tabsrc, " FROM "), Tabname);
 | |
|       strcat(strcat(Tabsrc, " GROUP BY "), colist);
 | |
|   
 | |
|       if (Tdbp->IsView())     // Until MariaDB bug is fixed
 | |
|         strcat(strcat(Tabsrc, " ORDER BY "), colist);
 | |
| 
 | |
|       // Now we know how much was suballocated
 | |
|       PlugSubAlloc(g, NULL, strlen(Tabsrc) + 1);
 | |
|       } // endif !GBdone
 | |
| 
 | |
|   } else if (!Tabsrc) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(SRC_TABLE_UNDEF));
 | |
|     return true;
 | |
|   } // endif
 | |
| 
 | |
|   if (Tabsrc) {
 | |
|     // Get the new table description block of this source table
 | |
|     PTABLE tablep = new(g) XTAB("whatever", Tabsrc);
 | |
| 
 | |
|     tablep->SetSchema(Database);
 | |
| 
 | |
|     if (!(Tdbp = GetSubTable(g, tablep, true)))
 | |
|       return true;
 | |
| 
 | |
|     } // endif Tabsrc
 | |
| 
 | |
|   return false;
 | |
|   } // end of GetSourceTable
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Make the required pivot columns.                                   */
 | |
| /***********************************************************************/
 | |
| bool TDBPIVOT::MakePivotColumns(PGLOBAL g)
 | |
|   {
 | |
|   if (!Tdbp->IsView()) {
 | |
|     // This was not done yet if GBdone is true
 | |
|     if (FindDefaultColumns(g))
 | |
|       return true;
 | |
|   
 | |
|     // Now it is time to allocate the pivot and function columns
 | |
|     if (!(Fcolp = Tdbp->ColDB(g, Fncol, 0))) {
 | |
|       // Function column not found in table                                       
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(COL_ISNOT_TABLE), Fncol, Tabname);
 | |
|       return true;
 | |
|     } else if (Fcolp->InitValue(g))
 | |
|       return true;
 | |
| 
 | |
|     if (!(Xcolp = Tdbp->ColDB(g, Picol, 0))) {
 | |
|       // Pivot column not found in table                                       
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(COL_ISNOT_TABLE), Picol, Tabname);
 | |
|       return true;
 | |
|     } else if (Xcolp->InitValue(g))
 | |
|       return true;
 | |
| 
 | |
|     //  Check and initialize the subtable columns
 | |
|     for (PCOL cp = Columns; cp; cp = cp->GetNext())
 | |
|       if (cp->GetAmType() == TYPE_AM_SRC) {
 | |
|         if (((PSRCCOL)cp)->Init(g, NULL))
 | |
|           return TRUE;
 | |
| 
 | |
|       } else if (cp->GetAmType() == TYPE_AM_FNC)
 | |
|         if (((PFNCCOL)cp)->InitColumn(g))
 | |
|           return TRUE;
 | |
| 
 | |
|     } // endif isview
 | |
| 
 | |
|   return false;
 | |
|   } // end of MakePivotColumns
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Make the required pivot columns for an object view.                */
 | |
| /***********************************************************************/
 | |
| bool TDBPIVOT::MakeViewColumns(PGLOBAL g)
 | |
|   {
 | |
|   if (Tdbp->IsView()) {
 | |
|     // Tdbp is a view ColDB cannot be used
 | |
|     PCOL   colp, cp;
 | |
|     PTDBMY tdbp;
 | |
| 
 | |
|     if (Tdbp->GetAmType() != TYPE_AM_MYSQL) {
 | |
|       snprintf(g->Message, sizeof(g->Message),"View is not MySQL");
 | |
|       return true;
 | |
|     } else
 | |
|       tdbp = (PTDBMY)Tdbp;
 | |
| 
 | |
|     if (!Fncol && !(Fncol = tdbp->FindFieldColumn(Picol))) {
 | |
|       snprintf(g->Message, sizeof(g->Message),  MSG(NO_DEF_FNCCOL));
 | |
|       return true;
 | |
|       } // endif Fncol
 | |
| 
 | |
|     if (!Picol && !(Picol = tdbp->FindFieldColumn(Fncol))) {
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(NO_DEF_PIVOTCOL));
 | |
|       return true;
 | |
|       } // endif Picol
 | |
| 
 | |
|     // Now it is time to allocate the pivot and function columns
 | |
|     if (!(Fcolp = tdbp->MakeFieldColumn(g, Fncol)))
 | |
|     	return true;
 | |
| 
 | |
|     if (!(Xcolp = tdbp->MakeFieldColumn(g, Picol)))
 | |
|     	return true;
 | |
| 
 | |
|     //  Check and initialize the subtable columns
 | |
|     for (cp = Columns; cp; cp = cp->GetNext())
 | |
|       if (cp->GetAmType() == TYPE_AM_SRC) {
 | |
|         if ((colp = tdbp->MakeFieldColumn(g, cp->GetName()))) {
 | |
|           ((PSRCCOL)cp)->Colp = colp;        
 | |
|           ((PSRCCOL)cp)->To_Val = colp->GetValue();
 | |
|           cp->AddStatus(BUF_READ);     // All is done here
 | |
|         } else
 | |
|     			return true;
 | |
| 
 | |
|       } else if (cp->GetAmType() == TYPE_AM_FNC)
 | |
|         if (((PFNCCOL)cp)->InitColumn(g))
 | |
|           return TRUE;
 | |
| 
 | |
|     } // endif isview
 | |
| 
 | |
|   return false;
 | |
|   } // end of MakeViewColumns
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PIVOT GetMaxSize: returns the maximum number of rows in the table. */
 | |
| /***********************************************************************/
 | |
| int TDBPIVOT::GetMaxSize(PGLOBAL g __attribute__((unused)))
 | |
|   {     
 | |
| #if  0
 | |
|   if (MaxSize < 0)
 | |
|     MaxSize = MakePivotColumns(g);
 | |
| 
 | |
|   return MaxSize;
 | |
| #endif // 0
 | |
|   return 10;
 | |
|   } // end of GetMaxSize
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  In this sample, ROWID will be the (virtual) row number,            */
 | |
| /*  while ROWNUM will be the occurrence rank in the multiple column.    */
 | |
| /***********************************************************************/
 | |
| int TDBPIVOT::RowNumber(PGLOBAL, bool b)
 | |
|   {
 | |
|   return (b) ? M : N;
 | |
|   } // end of RowNumber
 | |
|  
 | |
| /***********************************************************************/
 | |
| /*  PIVOT Access Method opening routine.                               */
 | |
| /***********************************************************************/
 | |
| bool TDBPIVOT::OpenDB(PGLOBAL g)
 | |
|   {
 | |
|   if (Use == USE_OPEN) {
 | |
|     /*******************************************************************/
 | |
|     /*  Table already open, just replace it at its beginning.          */
 | |
|     /*******************************************************************/
 | |
|     N = M = 0;
 | |
|     RowFlag = 0;
 | |
|     FileStatus = 0;
 | |
|     return FALSE;
 | |
|     } // endif use
 | |
| 
 | |
|   if (Mode != MODE_READ) {
 | |
|     /*******************************************************************/
 | |
|     /* Currently PIVOT tables cannot be modified.                      */
 | |
|     /*******************************************************************/
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(TABLE_READ_ONLY), "PIVOT");
 | |
|     return TRUE;
 | |
|     } // endif Mode
 | |
| 
 | |
|   if (To_Key_Col || To_Kindex) {
 | |
|     /*******************************************************************/
 | |
|     /* Direct access of PIVOT tables is not implemented yet.           */
 | |
|     /*******************************************************************/
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(NO_PIV_DIR_ACC));
 | |
|     return TRUE;
 | |
|     } // endif To_Key_Col
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Do it here if not done yet (should not be the case).             */
 | |
|   /*********************************************************************/
 | |
|   if (GetSourceTable(g))
 | |
|     return TRUE;
 | |
|                 
 | |
|   // For tables, columns must be allocated before opening
 | |
|   if (MakePivotColumns(g))
 | |
|     return TRUE;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Physically open the object table.                                */
 | |
|   /*********************************************************************/
 | |
| 	if (Tdbp->OpenDB(g))
 | |
| 		return TRUE;
 | |
| 
 | |
|   Use = USE_OPEN;       // Do it now in case we are recursively called
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Make all required pivot columns for object views.                */
 | |
|   /*********************************************************************/
 | |
|   return MakeViewColumns(g);
 | |
|   } // end of OpenDB
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base read routine for PIVOT access method.                    */
 | |
| /***********************************************************************/
 | |
| int TDBPIVOT::ReadDB(PGLOBAL g)
 | |
|   {
 | |
|   int  rc = RC_OK;
 | |
|   bool newrow = FALSE;
 | |
|   PCOL colp;
 | |
| 
 | |
|   if (FileStatus == 2)
 | |
|     return RC_EF;
 | |
| 
 | |
|   if (FileStatus)
 | |
|     for (colp = Columns; colp; colp = colp->GetNext())
 | |
|       if (colp->GetAmType() == TYPE_AM_SRC)
 | |
|         ((PSRCCOL)colp)->SetColumn();
 | |
| 
 | |
|   // New row, reset all function column values
 | |
|   for (colp = Columns; colp; colp = colp->GetNext())
 | |
|     if (colp->GetAmType() == TYPE_AM_FNC)
 | |
|       colp->GetValue()->Reset();
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Now start the multi reading process.                             */
 | |
|   /*********************************************************************/
 | |
|   do {
 | |
|     if (RowFlag != 1) {
 | |
|       if ((rc = Tdbp->ReadDB(g)) != RC_OK) {
 | |
|         if (FileStatus && rc == RC_EF) {
 | |
|           // A prepared row remains to be sent
 | |
|           FileStatus = 2;
 | |
|           rc = RC_OK;
 | |
|           } // endif FileStatus
 | |
| 
 | |
|         break;
 | |
|         } // endif rc
 | |
| 
 | |
|       for (colp = Tdbp->GetColumns(); colp; colp = colp->GetNext())
 | |
|         colp->ReadColumn(g);
 | |
| 
 | |
|       for (colp = Columns; colp; colp = colp->GetNext())
 | |
|       {
 | |
|         if (colp->GetAmType() == TYPE_AM_SRC)
 | |
|         {
 | |
|           if (FileStatus) {
 | |
|             if (((PSRCCOL)colp)->CompareLast()) {
 | |
|               newrow = (RowFlag) ? TRUE : FALSE;
 | |
|               break;
 | |
|               } // endif CompareLast
 | |
| 
 | |
|           } else
 | |
|             ((PSRCCOL)colp)->SetColumn();
 | |
|         }
 | |
|       }
 | |
|       FileStatus = 1;
 | |
|       } // endif RowFlag
 | |
| 
 | |
|     if (newrow) {
 | |
|       RowFlag = 1;
 | |
|       break;
 | |
|     } else
 | |
|       RowFlag = 2;
 | |
| 
 | |
|     // Look for the column having this header
 | |
|     for (colp = Columns; colp; colp = colp->GetNext())
 | |
|       if (colp->GetAmType() == TYPE_AM_FNC) {
 | |
|         if (((PFNCCOL)colp)->CompareColumn())
 | |
|           break;
 | |
| 
 | |
|         } // endif AmType
 | |
| 
 | |
|     if (!colp && !(colp = Dcolp)) {
 | |
|       if (!Accept) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(NO_MATCH_COL));
 | |
|         return RC_FX;
 | |
|       } else
 | |
|         continue;
 | |
| 
 | |
|       } // endif colp
 | |
| 
 | |
|     // Set the value of the matching column from the fonction value
 | |
|     colp->GetValue()->SetValue_pval(Fcolp->GetValue());
 | |
|     } while (RowFlag == 2);
 | |
| 
 | |
|   N++;
 | |
|   return rc;
 | |
|   } // end of ReadDB
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  WriteDB: Data Base write routine for PIVOT access methods.         */
 | |
| /***********************************************************************/
 | |
| int TDBPIVOT::WriteDB(PGLOBAL g)
 | |
|   {
 | |
|   snprintf(g->Message, sizeof(g->Message), MSG(TABLE_READ_ONLY), "PIVOT");
 | |
|   return RC_FX;
 | |
|   } // end of WriteDB
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base delete line routine for PIVOT access methods.            */
 | |
| /***********************************************************************/
 | |
| int TDBPIVOT::DeleteDB(PGLOBAL g, int)
 | |
|   {
 | |
|   snprintf(g->Message, sizeof(g->Message), MSG(NO_TABLE_DEL), "PIVOT");
 | |
|   return RC_FX;
 | |
|   } // end of DeleteDB
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for PIVOT access method.                   */
 | |
| /***********************************************************************/
 | |
| void TDBPIVOT::CloseDB(PGLOBAL g)
 | |
|   {
 | |
|   if (Tdbp)
 | |
|     Tdbp->CloseDB(g);
 | |
| 
 | |
|   } // end of CloseDB
 | |
| 
 | |
| // ------------------------ FNCCOL functions ----------------------------
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  FNCCOL public constructor.                                       */
 | |
| /***********************************************************************/
 | |
| FNCCOL::FNCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
 | |
|       : COLBLK(cdp, tdbp, i)
 | |
|   {
 | |
|   if (cprec) {
 | |
|     Next = cprec->GetNext();
 | |
|     cprec->SetNext(this);
 | |
|   } else {
 | |
|     Next = tdbp->GetColumns();
 | |
|     tdbp->SetColumns(this);
 | |
|   } // endif cprec
 | |
| 
 | |
|   Value = NULL;     // We'll get a new one later
 | |
|   Hval = NULL;      // The unconverted header value
 | |
|   Xcolp = NULL;
 | |
|   } // end of FNCCOL constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  FNCCOL initialization function.                                    */
 | |
| /***********************************************************************/
 | |
| bool FNCCOL::InitColumn(PGLOBAL g)
 | |
| {
 | |
|   // Must have its own value block
 | |
|   if (InitValue(g))
 | |
|     return TRUE;
 | |
| 
 | |
|   // Make a value from the column name
 | |
|   Hval = AllocateValue(g, Name, TYPE_STRING);
 | |
|   Hval->SetPrec(1);         // Case insensitive
 | |
| 
 | |
|   Xcolp = ((PTDBPIVOT)To_Tdb)->Xcolp;
 | |
|   AddStatus(BUF_READ);      // All is done here
 | |
|   return FALSE;
 | |
| } // end of InitColumn
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  CompareColumn: Compare column value with source column value.      */
 | |
| /***********************************************************************/
 | |
| bool FNCCOL::CompareColumn(void)
 | |
|   {
 | |
|   // Compare the unconverted values
 | |
|   return Hval->IsEqual(Xcolp->GetValue(), false);
 | |
|   } // end of CompareColumn
 | |
| 
 | |
| // ------------------------ SRCCOL functions ----------------------------
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  SRCCOL public constructor.                                         */
 | |
| /***********************************************************************/
 | |
| SRCCOL::SRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int n)
 | |
|       : PRXCOL(cdp, tdbp, cprec, n)
 | |
|   {
 | |
|   } // end of SRCCOL constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Initialize the column as pointing to the source column.            */
 | |
| /***********************************************************************/
 | |
| bool SRCCOL::Init(PGLOBAL g, PTDB tp)
 | |
|   {
 | |
|   if (PRXCOL::Init(g, tp))
 | |
|     return true;
 | |
| 
 | |
|   AddStatus(BUF_READ);     // All is done here
 | |
|   return false;
 | |
|   } // end of SRCCOL constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  SetColumn: have the column value set from the source column.       */
 | |
| /***********************************************************************/
 | |
| void SRCCOL::SetColumn(void)
 | |
|   {
 | |
|   Value->SetValue_pval(To_Val);
 | |
|   } // end of SetColumn
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  SetColumn: Compare column value with source column value.          */
 | |
| /***********************************************************************/
 | |
| bool SRCCOL::CompareLast(void)
 | |
|   {
 | |
|   // Compare the unconverted values
 | |
|   return !Value->IsEqual(To_Val, true);
 | |
|   } // end of CompareColumn
 | |
| 
 | |
| /* --------------------- End of TabPivot/TabQrs ---------------------- */
 | 
