mariadb/storage/connect/tabpivot.cpp

1203 lines
39 KiB
C++
Raw Normal View History

/************ TabPivot C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABPIVOT */
/* ------------- */
/* Version 1.3 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */
/* */
/* 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"
#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 "table.h" // MySQL table definitions
#include "sql_const.h"
#include "field.h"
#include "global.h"
#include "plgdbsem.h"
#include "xtable.h"
#include "xindex.h"
#include "colblk.h"
#include "tabmysql.h"
#include "csort.h"
#include "tabpivot.h"
#include "valblk.h"
#include "ha_connect.h"
#include "mycat.h" // For GetHandler
extern "C" int trace;
/* --------------- Implementation of the PIVOT classes --------------- */
/***********************************************************************/
/* DefineAM: define specific AM block values from PIVOT table. */
/***********************************************************************/
bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
char *p1, *p2;
PHC hc = ((MYCAT*)Cat)->GetHandler();
Host = Cat->GetStringCatInfo(g, Name, "Host", "localhost");
User = Cat->GetStringCatInfo(g, Name, "User", "root");
Pwd = Cat->GetStringCatInfo(g, Name, "Password", NULL);
DB = Cat->GetStringCatInfo(g, Name, "Database", (PSZ)hc->GetDBName(NULL));
Tabsrc = Cat->GetStringCatInfo(g, Name, "SrcDef", NULL);
Tabname = Cat->GetStringCatInfo(g, Name, "Name", NULL);
Picol = Cat->GetStringCatInfo(g, Name, "PivotCol", NULL);
Fncol = Cat->GetStringCatInfo(g, Name, "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 = Cat->GetStringCatInfo(g, Name, "Function", "SUM");
GBdone = Cat->GetIntCatInfo(Name, "Groupby", 0) ? TRUE : FALSE;
Port = Cat->GetIntCatInfo(Name, "Port", 3306);
Desc = (*Tabname) ? Tabname : Tabsrc;
return FALSE;
} // end of DefineAM
/***********************************************************************/
/* GetTable: makes a new TDB of the proper type. */
/***********************************************************************/
PTDB PIVOTDEF::GetTable(PGLOBAL g, MODE m)
{
return new(g) TDBPIVOT(this);
} // end of GetTable
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Implementation of the TDBPIVOT class. */
/***********************************************************************/
TDBPIVOT::TDBPIVOT(PPIVOTDEF tdp) : TDBASE(tdp), CSORT(FALSE)
{
Tqrp = NULL;
Host = tdp->Host;
Database = tdp->DB;
User = tdp->User;
Pwd = tdp->Pwd;
Port = tdp->Port;
Qryp = NULL;
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
GBdone = tdp->GBdone;
Mult = -1; // Estimated table size
N = 0; // The current table index
M = 0; // The occurence rank
FileStatus = 0; // Logical End-of-File
RowFlag = 0; // 0: Ok, 1: Same, 2: Skip
} // end of TDBPIVOT constructor
#if 0
TDBPIVOT::TDBPIVOT(PTDBPIVOT tdbp) : TDBASE(tdbp), CSORT(FALSE)
{
Tdbp = tdbp->Tdbp;
Sqlp = tdbp->Sqlp;
Qryp = tdbp->Qryp;
Tabname = tdbp->Tabname;
Tabsrc = tdbp->Tabsrc;
Picol = tdbp->Picol;
Fncol = tdbp->Fncol;
Function = tdbp->Function;
Xcolp = tdbp->Xcolp;
Xresp = tdbp->Xresp;
Rblkp = tdbp->Rblkp;
Fcolp = tdbp->Fcolp;
Mult = tdbp->Mult;
N = tdbp->N;
M = tdbp->M;
FileStatus = tdbp->FileStatus;
RowFlag = tdbp->RowFlag;
} // end of TDBPIVOT copy constructor
// Is this really useful ???
PTDB TDBPIVOT::CopyOne(PTABS t)
{
PTDB tp = new(t->G) TDBPIVOT(this);
tp->SetColumns(Columns);
return tp;
} // end of CopyOne
#endif // 0
/***********************************************************************/
/* Prepare the source table Query. */
/***********************************************************************/
PQRYRES TDBPIVOT::GetSourceTable(PGLOBAL g)
{
if (Qryp)
return Qryp; // Already done
if (!Tabsrc && Tabname) {
char *def, *colist;
size_t len = 0;
PCOL colp;
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
// Evaluate the length of the column list
for (colp = Columns; colp; colp = colp->GetNext())
len += (strlen(colp->GetName()) + 2);
*(colist = (char*)PlugSubAlloc(g, NULL, len)) = 0;
// Locate the suballocated string (size is not known yet)
def = (char*)PlugSubAlloc(g, NULL, 0);
strcpy(def, "SELECT ");
if (!Fncol) {
for (colp = Columns; colp; colp = colp->GetNext())
if (!Picol || stricmp(Picol, colp->GetName()))
Fncol = colp->GetName();
if (!Fncol) {
strcpy(g->Message, MSG(NO_DEF_FNCCOL));
return NULL;
} // endif Fncol
} else if (!(ColDB(g, Fncol, 0))) {
// Function column not found in table
sprintf(g->Message, MSG(COL_ISNOT_TABLE), Fncol, Tabname);
return NULL;
} // endif Fcolp
if (!Picol) {
// Find default Picol as the last one not equal to Fncol
for (colp = Columns; colp; colp = colp->GetNext())
if (!Fncol || stricmp(Fncol, colp->GetName()))
Picol = colp->GetName();
if (!Picol) {
strcpy(g->Message, MSG(NO_DEF_PIVOTCOL));
return NULL;
} // endif Picol
} else if (!(ColDB(g, Picol, 0))) {
// Pivot column not found in table
sprintf(g->Message, MSG(COL_ISNOT_TABLE), Picol, Tabname);
return NULL;
} // endif Xcolp
// Make the other column list
for (colp = Columns; colp; colp = colp->GetNext())
if (stricmp(Picol, colp->GetName()) &&
stricmp(Fncol, colp->GetName()))
strcat(strcat(colist, colp->GetName()), ", ");
// Add the Pivot column at the end of the list
strcat(strcat(def, strcat(colist, Picol)), ", ");
// Continue making the definition
if (!GBdone) {
// Make it suitable for Pivot by doing the group by
strcat(strcat(def, Function), "(");
strcat(strcat(strcat(def, Fncol), ") "), Fncol);
strcat(strcat(def, " FROM "), Tabname);
strcat(strcat(def, " GROUP BY "), colist);
} else // Gbdone
strcat(strcat(strcat(def, Fncol), " FROM "), Tabname);
// Now we know how much was suballocated
Tabsrc = (char*)PlugSubAlloc(g, NULL, strlen(def));
} else {
strcpy(g->Message, MSG(SRC_TABLE_UNDEF));
return NULL;
} // endif Tabsrc
int w;
// Open a MySQL connection for this table
if (Myc.Open(g, Host, Database, User, Pwd, Port))
return NULL;
// Send the source command to MySQL
if (Myc.ExecSQL(g, Tabsrc, &w) == RC_FX) {
Myc.Close();
return NULL;
} // endif Exec
// We must have a storage query to get pivot column values
Qryp = Myc.GetResult(g);
Myc.Close();
Tqrp = new(g) TDBQRS(Qryp);
Tqrp->OpenDB(g);
if (MakePivotColumns(g) < 0)
return NULL;
return Qryp;
} // end of GetSourceTable
/***********************************************************************/
/* Allocate PIVOT columns description block. */
/***********************************************************************/
int TDBPIVOT::MakePivotColumns(PGLOBAL g)
{
if (Mult < 0) {
int ndif, n = 0, nblin = Qryp->Nblin;
PVAL valp;
PCOL cp;
PSRCCOL colp;
PFNCCOL cfnp;
// Allocate all the source columns
Tqrp->ColDB(g, NULL, 0);
Columns = NULL; // Discard dummy columns blocks
for (cp = Tqrp->GetColumns(); cp; cp = cp->GetNext()) {
if (cp->InitValue(g))
return -1;
if (!stricmp(cp->GetName(), Picol)) {
Xcolp = (PQRSCOL)cp;
Xresp = Xcolp->GetCrp();
Rblkp = Xresp->Kdata;
} else if (!stricmp(cp->GetName(), Fncol)) {
Fcolp = (PQRSCOL)cp;
} else
if ((colp = new(g) SRCCOL(cp, this, ++n))->Init(g, this))
return -1;
} // endfor cp
if (!Xcolp) {
sprintf(g->Message, MSG(COL_ISNOT_TABLE),
Picol, Tabname ? Tabname : "TabSrc");
return -1;
} else if (!Fcolp) {
sprintf(g->Message, MSG(COL_ISNOT_TABLE),
Fncol, Tabname ? Tabname : "TabSrc");
return -1;
} // endif Fcolp
// Before calling sort, initialize all
Index.Size = nblin * sizeof(int);
Index.Sub = TRUE; // Should be small enough
if (!PlgDBalloc(g, NULL, Index))
return -1;
Offset.Size = (nblin + 1) * sizeof(int);
Offset.Sub = TRUE; // Should be small enough
if (!PlgDBalloc(g, NULL, Offset))
return -2;
ndif = Qsort(g, nblin);
if (ndif < 0) { // error
return -3;
} else
Ncol = ndif;
// Now make the functional columns
for (int i = 0; i < Ncol; i++) {
// Allocate the Value used to retieve column names
if (!(valp = AllocateValue(g, Xcolp->GetResultType(),
Xcolp->GetLengthEx(), Xcolp->GetPrecision(),
Xcolp->GetDomain(), Xcolp->GetTo_Tdb()->GetCat())))
return -4;
// Get the value that will be the generated column name
valp->SetValue_pvblk(Rblkp, Pex[Pof[i]]);
// Copy the functional column with new Name and new Value
cfnp = (PFNCCOL)new(g) FNCCOL(Fcolp, this);
// Initialize the generated column
if (cfnp->InitColumn(g, valp))
return -5;
} // endfor i
// Fields must be updated for ha_connect
// if (UpdateTableFields(g, n + Ncol))
// return -6;
// This should be refined later
Mult = nblin;
} // endif Mult
return Mult;
} // end of MakePivotColumns
#if 0
/***********************************************************************/
/* Update fields in the MySQL table structure */
/* Note: this does not work. Indeed the new rows are correctly made */
/* but the final result still specify the unmodified table and the */
/* returned table only contains the original column values. */
/* In addition, a new query on the table, when it is into the cache, */
/* specifies all the new columns and fails because they do not belong */
/* to the original table. */
/***********************************************************************/
bool TDBPIVOT::UpdateTableFields(PGLOBAL g, int n)
{
uchar *trec, *srec, *tptr, *sptr;
int i = 0, k = 0;
uint len;
uint32 nmp, lwm;
size_t buffsize;
PCOL colp;
PHC hc = ((MYCAT*)((PIVOTDEF*)To_Def)->Cat)->GetHandler();
TABLE *table = hc->GetTable();
st_mem_root *tmr = &table->mem_root;
st_mem_root *smr = &table->s->mem_root;
Field* *field;
Field *fp, *tfncp, *sfncp;
Field* *ntf;
Field* *nsf;
//my_bitmap_map *org_bitmap;
const MY_BITMAP *map;
// When sorting read_set selects all columns, so we use def_read_set
map= (const MY_BITMAP *)&table->def_read_set;
// Find the function field
for (field= table->field; *field; field++) {
fp= *field;
if (bitmap_is_set(map, fp->field_index))
if (!stricmp(fp->field_name, Fncol)) {
tfncp = fp;
break;
} // endif Name
} // endfor field
for (field= table->s->field; *field; field++) {
fp= *field;
if (bitmap_is_set(map, fp->field_index))
if (!stricmp(fp->field_name, Fncol)) {
sfncp = fp;
break;
} // endif Name
} // endfor field
// Calculate the new buffer size
len = tfncp->max_data_length();
buffsize = table->s->rec_buff_length + len * Ncol;
// Allocate the new record space
if (!(tptr = trec = (uchar*)alloc_root(tmr, 2 * buffsize)))
return TRUE;
if (!(sptr = srec = (uchar*)alloc_root(smr, 2 * buffsize)))
return TRUE;
// Allocate the array of all new table field pointers
if (!(ntf = (Field**)alloc_root(tmr, (uint)((n+1) * sizeof(Field*)))))
return TRUE;
// Allocate the array of all new table share field pointers
if (!(nsf = (Field**)alloc_root(smr, (uint)((n+1) * sizeof(Field*)))))
return TRUE;
// First fields are the the ones of the source columns
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->GetAmType() == TYPE_AM_SRC) {
for (field= table->field; *field; field++) {
fp= *field;
if (bitmap_is_set(map, fp->field_index))
if (!stricmp(colp->GetName(), fp->field_name)) {
ntf[i] = fp;
fp->field_index = i++;
fp->ptr = tptr;
tptr += fp->max_data_length();
break;
} // endif Name
} // endfor field
for (field= table->s->field; *field; field++) {
fp= *field;
if (bitmap_is_set(map, fp->field_index))
if (!stricmp(colp->GetName(), fp->field_name)) {
nsf[k] = fp;
fp->field_index = k++;
fp->ptr = srec;
srec += fp->max_data_length();
break;
} // endif Name
} // endfor field
} // endif AmType
// Now add the pivot generated columns
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->GetAmType() == TYPE_AM_FNC) {
if ((fp = (Field*)memdup_root(tmr, (char*)tfncp, tfncp->size_of()))) {
ntf[i] = fp;
fp->ptr = tptr;
fp->field_name = colp->GetName();
fp->field_index = i++;
fp->vcol_info = NULL;
fp->stored_in_db = TRUE;
tptr += len;
} else
return TRUE;
if ((fp = (Field*)memdup_root(smr, (char*)sfncp, sfncp->size_of()))) {
nsf[i] = fp;
fp->ptr = sptr;
fp->field_name = colp->GetName();
fp->field_index = k++;
fp->vcol_info = NULL;
fp->stored_in_db = TRUE;
sptr += len;
} else
return TRUE;
} // endif AM_FNC
// Mark end of the list
ntf[i] = NULL;
nsf[k] = NULL;
// Update the table fields
nmp = (uint32)((1<<i) - 1);
lwm = (uint32)((-1)<<i);
table->field = ntf;
table->used_fields = i;
table->record[0] = trec;
table->record[1] = trec + buffsize;
*table->def_read_set.bitmap = nmp;
*table->def_read_set.last_word_ptr = nmp;
table->def_read_set.last_word_mask = lwm;
table->def_read_set.n_bits = i;
*table->read_set->bitmap = nmp;
*table->read_set->last_word_ptr = nmp;
table->read_set->last_word_mask = lwm;
table->read_set->n_bits = i;
table->write_set->n_bits = i;
*table->vcol_set->bitmap = 0;
table->vcol_set->n_bits = i;
// and the share fields
table->s->field = nsf;
table->s->reclength = sptr - srec;
table->s->stored_rec_length = sptr - srec;
table->s->fields = k;
table->s->stored_fields = k;
table->s->rec_buff_length = buffsize;
//table->s->varchar_fields = ???;
//table->s->db_record_offset = ???;
//table->s->null_field_first = ???;
return FALSE;
} // end of UpdateTableFields
#endif // 0
/***********************************************************************/
/* Allocate source column description block. */
/***********************************************************************/
PCOL TDBPIVOT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
{
PCOL colp = NULL;
//if (stricmp(cdp->GetName(), Picol) && stricmp(cdp->GetName(), Fncol)) {
colp = new(g) COLBLK(cdp, this, n);
// if (((PSRCCOL)colp)->Init(g, this))
// return NULL;
//} else {
// sprintf(g->Message, MSG(NO_MORE_COL), cdp->GetName());
// return NULL;
//} // endif Name
if (cprec) {
colp->SetNext(cprec->GetNext());
cprec->SetNext(colp);
} else {
colp->SetNext(Columns);
Columns = colp;
} // endif cprec
return colp;
} // end of MakeCol
/***********************************************************************/
/* PIVOT GetMaxSize: returns the maximum number of rows in the table. */
/***********************************************************************/
int TDBPIVOT::GetMaxSize(PGLOBAL g)
{
#if 0
if (MaxSize < 0)
MaxSize = MakePivotColumns(g);
return MaxSize;
#endif // 0
return 0;
} // end of GetMaxSize
/***********************************************************************/
/* In this sample, ROWID will be the (virtual) row number, */
/* while ROWNUM will be the occurence rank in the multiple column. */
/***********************************************************************/
int TDBPIVOT::RowNumber(PGLOBAL g, bool b)
{
return (b) ? M : N;
} // end of RowNumber
/***********************************************************************/
/* PIVOT Access Method opening routine. */
/***********************************************************************/
bool TDBPIVOT::OpenDB(PGLOBAL g)
{
//PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open, just replace it at its beginning. */
/*******************************************************************/
N = M = 0;
RowFlag = 0;
FileStatus = 0;
return FALSE;
} // endif use
/*********************************************************************/
/* Do it here if not done yet (should not be the case). */
/*********************************************************************/
//if (MakePivotColumns(g) < 0)
if (!(Qryp = GetSourceTable(g)))
return TRUE;
if (Mode != MODE_READ) {
/*******************************************************************/
/* Currently PIVOT tables cannot be modified. */
/*******************************************************************/
sprintf(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. */
/*******************************************************************/
strcpy(g->Message, MSG(NO_PIV_DIR_ACC));
return TRUE;
} // endif To_Key_Col
return FALSE;
} // 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;
PVAL vp1, vp2;
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 = Tqrp->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 = Tqrp->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)->CompareColumn())
newrow = (RowFlag) ? TRUE : FALSE;
} else
((PSRCCOL)colp)->SetColumn();
FileStatus = 1;
} // endif RowFlag
if (newrow) {
RowFlag = 1;
break;
} else
RowFlag = 2;
vp1 = Xcolp->GetValue();
// Look for the column having this header
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->GetAmType() == TYPE_AM_FNC) {
vp2 = ((PFNCCOL)colp)->Hval;
if (!vp1->CompareValue(vp2))
break;
} // endif AmType
if (!colp) {
strcpy(g->Message, MSG(NO_MATCH_COL));
return RC_FX;
} // 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)
{
sprintf(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 irc)
{
sprintf(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)
{
//Tdbp->CloseDB(g);
} // end of CloseDB
/***********************************************************************/
/* TDBPIVOT: Compare routine for sorting pivot column values. */
/***********************************************************************/
int TDBPIVOT::Qcompare(int *i1, int *i2)
{
// TODO: the actual comparison between pivot column result values.
return Rblkp->CompVal(*i1, *i2);
} // end of Qcompare
// ------------------------ FNCCOL functions ----------------------------
/***********************************************************************/
/* FNCCOL public constructor. */
/***********************************************************************/
FNCCOL::FNCCOL(PCOL col1, PTDBPIVOT tdbp)
: COLBLK(col1, tdbp)
{
Value = NULL; // We'll get a new one later
Hval = NULL; // The unconverted header value
} // end of FNCCOL constructor
/***********************************************************************/
/* FNCCOL initialization function. */
/***********************************************************************/
bool FNCCOL::InitColumn(PGLOBAL g, PVAL valp)
{
char *p, buf[128];
int len;
// Must have its own value block
if (InitValue(g))
return TRUE;
// Convert header value to a null terminated character string
Hval = valp;
p = Hval->GetCharString(buf);
len = strlen(p) + 1;
if (len > (signed)sizeof(buf)) {
strcpy(g->Message, MSG(COLNAM_TOO_LONG));
return TRUE;
} // endif buf
// Set the name of the functional pivot column
Name = (PSZ)PlugSubAlloc(g, NULL, len);
strcpy(Name, p);
AddStatus(BUF_READ); // All is done here
return FALSE;
} // end of InitColumn
// ------------------------ SRCCOL functions ----------------------------
#if 0
/***********************************************************************/
/* SRCCOL public constructor. */
/***********************************************************************/
SRCCOL::SRCCOL(PCOLDEF cdp, PTDBPIVOT tdbp, int n)
: COLBLK(cdp, tdbp, n)
{
// Set additional SRC access method information for column.
Colp = NULL;
Cnval = NULL;
} // end of SRCCOL constructor
#endif // 0
/***********************************************************************/
/* SRCCOL public constructor. */
/***********************************************************************/
SRCCOL::SRCCOL(PCOL cp, PTDBPIVOT tdbp, int n)
: COLBLK(cp, tdbp)
{
Index = n;
// Set additional SRC access method information for column.
Colp = (PQRSCOL)cp;
// Don't share Value with the source column so we can compare
Cnval = Value = NULL;
} // end of SRCCOL constructor
#if 0
/***********************************************************************/
/* SRCCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
SRCCOL::SRCCOL(SRCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
Colp = col1->Colp;
} // end of SRCCOL copy constructor
#endif // 0
/***********************************************************************/
/* Initialize the column as pointing to the source column. */
/***********************************************************************/
bool SRCCOL::Init(PGLOBAL g, PTDBPIVOT tdbp)
{
// Currently we ignore the type of the create table column
bool conv = FALSE;
#if 0
if (!Colp) {
// Column was defined in the Create Table statement
if (!(Colp = tdbp->ColDB(g, Name, 0)))
return TRUE;
// We can have a conversion problem converting from numeric to
// character because GetCharValue does not exist for numeric types.
conv = (IsTypeChar(Buf_Type) && IsTypeNum(Colp->GetResultType()));
} else
#endif // 0
conv = FALSE;
if (InitValue(g))
return TRUE;
//if (conv)
// Cnval = AllocateValue(g, Colp->GetValue());
//else
Cnval = Value;
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)
{
#if defined(_DEBUG)
Cnval->SetValue_pval(Colp->GetValue(), TRUE);
#else
Cnval->SetValue_pval(Colp->GetValue());
#endif // _DEBUG
if (Value != Cnval)
// Convert value
Value->SetValue_pval(Cnval);
} // end of SetColumn
/***********************************************************************/
/* SetColumn: Compare column value with source column value. */
/***********************************************************************/
bool SRCCOL::CompareColumn(void)
{
// Compare the unconverted values
return Cnval->CompareValue(Colp->GetValue());
} // end of CompareColumn
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Implementation of the TDBQRS class. */
/***********************************************************************/
TDBQRS::TDBQRS(PTDBQRS tdbp) : TDBASE(tdbp)
{
Qrp = tdbp->Qrp;
CurPos = tdbp->CurPos;
} // end of TDBQRS copy constructor
// Method
PTDB TDBQRS::CopyOne(PTABS t)
{
PTDB tp;
PQRSCOL cp1, cp2;
PGLOBAL g = t->G; // Is this really useful ???
tp = new(g) TDBQRS(this);
for (cp1 = (PQRSCOL)Columns; cp1; cp1 = (PQRSCOL)cp1->GetNext()) {
cp2 = new(g) QRSCOL(cp1, tp); // Make a copy
NewPointer(t, cp1, cp2);
} // endfor cp1
return tp;
} // end of CopyOne
#if 0 // The TDBASE functions return NULL when To_Def is NULL
/***********************************************************************/
/* Return the pointer on the DB catalog this table belongs to. */
/***********************************************************************/
PCATLG TDBQRS::GetCat(void)
{
// To_Def is null for QRYRES tables
return NULL;
} // end of GetCat
/***********************************************************************/
/* Return the datapath of the DB this table belongs to. */
/***********************************************************************/
PSZ TDBQRS::GetPath(void)
{
// To_Def is null for QRYRES tables
return NULL;
} // end of GetPath
#endif // 0
/***********************************************************************/
/* Initialize QRS column description block construction. */
/* name is used to call columns by name. */
/* num is used by LNA to construct columns by index number. */
/* Note: name=Null and num=0 for constructing all columns (select *) */
/***********************************************************************/
PCOL TDBQRS::ColDB(PGLOBAL g, PSZ name, int num)
{
int i;
PCOLRES crp;
PCOL cp, colp = NULL, cprec = NULL;
if (trace)
htrc("QRS ColDB: colname=%s tabname=%s num=%d\n",
SVP(name), Name, num);
for (crp = Qrp->Colresp, i = 1; crp; crp = crp->Next, i++)
if ((!name && !num) ||
(name && !stricmp(crp->Name, name)) || num == i) {
// Check for existence of desired column
// Also find where to insert the new block
for (cp = Columns; cp; cp = cp->GetNext())
if (cp->GetIndex() < i)
cprec = cp;
else if (cp->GetIndex() == i)
break;
if (trace) {
if (cp)
htrc("cp(%d).Name=%s cp=%p\n", i, cp->GetName(), cp);
else
htrc("cp(%d) cp=%p\n", i, cp);
} // endif trace
// Now take care of Column Description Block
if (cp)
colp = cp;
else
colp = new(g) QRSCOL(g, crp, this, cprec, i);
if (name || num)
break;
else
cprec = colp;
} // endif Name
return (colp);
} // end of ColDB
/***********************************************************************/
/* QRS GetMaxSize: returns maximum table size in number of lines. */
/***********************************************************************/
int TDBQRS::GetMaxSize(PGLOBAL g)
{
MaxSize = Qrp->Maxsize;
return MaxSize;
} // end of GetMaxSize
/***********************************************************************/
/* RowNumber: returns the current row ordinal number. */
/***********************************************************************/
int TDBQRS::RowNumber(PGLOBAL g, BOOL b)
{
return (CurPos + 1);
} // end of RowNumber
/***********************************************************************/
/* QRS Access Method opening routine. */
/* New method now that this routine is called recursively (last table */
/* first in reverse order): index blocks are immediately linked to */
/* join block of next table if it exists or else are discarted. */
/***********************************************************************/
bool TDBQRS::OpenDB(PGLOBAL g)
{
if (trace)
htrc("QRS OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n",
this, Tdb_No, Use, To_Key_Col, Mode);
if (Mode != MODE_READ) {
sprintf(g->Message, MSG(BAD_QUERY_OPEN), Mode);
return TRUE;
} // endif Mode
CurPos = -1;
if (Use == USE_OPEN) {
/*******************************************************************/
/* Table already open, just replace it at its beginning. */
/*******************************************************************/
if (To_Kindex)
/*****************************************************************/
/* Table is to be accessed through a sorted index table. */
/*****************************************************************/
To_Kindex->Reset();
return FALSE;
} // endif use
/*********************************************************************/
/* Open (retrieve data from) the query if not already open. */
/*********************************************************************/
Use = USE_OPEN; // Do it now in case we are recursively called
return FALSE;
} // end of OpenDB
/***********************************************************************/
/* GetRecpos: returns current position of next sequential read. */
/***********************************************************************/
int TDBQRS::GetRecpos(void)
{
return (CurPos);
} // end of GetRecpos
/***********************************************************************/
/* ReadDB: Data Base read routine for QRS access method. */
/***********************************************************************/
int TDBQRS::ReadDB(PGLOBAL g)
{
int rc = RC_OK;
if (trace)
htrc("QRS ReadDB: R%d CurPos=%d key=%p link=%p Kindex=%p\n",
GetTdb_No(), CurPos, To_Key_Col, To_Link, To_Kindex);
if (To_Kindex) {
/*******************************************************************/
/* Reading is by an index table. */
/*******************************************************************/
int recpos = To_Kindex->Fetch(g);
switch (recpos) {
case -1: // End of file reached
rc = RC_EF;
break;
case -2: // No match for join
rc = RC_NF;
break;
case -3: // Same record as last non null one
rc = RC_OK;
break;
default:
/***************************************************************/
/* Set the file position according to record to read. */
/***************************************************************/
CurPos = recpos;
} // endswitch recpos
if (trace)
htrc("Position is now %d\n", CurPos);
} else
/*******************************************************************/
/* !To_Kindex ---> sequential reading */
/*******************************************************************/
rc = (++CurPos < Qrp->Nblin) ? RC_OK : RC_EF;
return rc;
} // end of ReadDB
/***********************************************************************/
/* Dummy WriteDB: just send back an error return. */
/***********************************************************************/
int TDBQRS::WriteDB(PGLOBAL g)
{
strcpy(g->Message, MSG(QRY_READ_ONLY));
return RC_FX;
} // end of WriteDB
/***********************************************************************/
/* Dummy DeleteDB routine, just returns an error code. */
/***********************************************************************/
int TDBQRS::DeleteDB(PGLOBAL g, int irc)
{
strcpy(g->Message, MSG(NO_QRY_DELETE));
return RC_FX;
} // end of DeleteDB
/***********************************************************************/
/* Data Base close routine for QRS access method. */
/***********************************************************************/
void TDBQRS::CloseDB(PGLOBAL g)
{
if (To_Kindex) {
To_Kindex->Close();
To_Kindex = NULL;
} // endif
if (trace)
htrc("Qryres CloseDB");
//Qryp->Sqlp->CloseDB();
} // end of CloseDB
// ------------------------ QRSCOL functions ----------------------------
/***********************************************************************/
/* QRSCOL public constructor. */
/***********************************************************************/
QRSCOL::QRSCOL(PGLOBAL g, PCOLRES crp, PTDB tdbp, PCOL cprec, int i)
: COLBLK(NULL, tdbp, i)
{
if (cprec) {
Next = cprec->GetNext();
cprec->SetNext(this);
} else {
Next = tdbp->GetColumns();
tdbp->SetColumns(this);
} // endif cprec
// Set additional QRS access method information for column.
Crp = crp;
Name = Crp->Name;
Long = Crp->Clen;
Buf_Type = crp->Type;
strcpy(Format.Type, GetFormatType(Buf_Type));
Format.Length = (SHORT)Long;
Format.Prec = (SHORT)Crp->Prec;
if (trace) {
htrc("Making new QRSCOL C%d %s at %p\n", Index, Name, this);
htrc(" BufType=%d Long=%d length=%d clen=%d\n",
Buf_Type, Long, Format.Length, Crp->Clen);
} // endif trace
} // end of QRSCOL constructor
/***********************************************************************/
/* QRSCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
QRSCOL::QRSCOL(QRSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
Crp = col1->Crp;
} // end of QRSCOL copy constructor
/***********************************************************************/
/* ReadColumn: what this routine does is to extract the RESCOL block */
/* current value and convert it to the column buffer type. */
/***********************************************************************/
void QRSCOL::ReadColumn(PGLOBAL g)
{
PTDBQRS tdbp = (PTDBQRS)To_Tdb;
if (trace)
htrc("QRS RC: col %s R%d type=%d CurPos=%d Len=%d\n",
Name, tdbp->GetTdb_No(), Buf_Type, tdbp->CurPos, Crp->Clen);
if (Crp->Kdata)
Value->SetValue_pvblk(Crp->Kdata, tdbp->CurPos);
else
Value->Reset();
} // end of ReadColumn
/***********************************************************************/
/* Make file output of a Dos column descriptor block. */
/***********************************************************************/
void QRSCOL::Print(PGLOBAL g, FILE *f, UINT n)
{
COLBLK::Print(g, f, n);
fprintf(f, " Crp=%p\n", Crp);
} // end of Print
/* --------------------- End of TabPivot/TabQrs ---------------------- */