mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
589 lines
19 KiB
C++
589 lines
19 KiB
C++
/************ TabOccur CPP Declares Source Code File (.CPP) ************/
|
|
/* Name: TABOCCUR.CPP Version 1.2 */
|
|
/* */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2013 - 2021 */
|
|
/* */
|
|
/* OCCUR: Table that provides a view of a source table where the */
|
|
/* contain of several columns of the source table is placed in only */
|
|
/* one column, the OCCUR column, this resulting into several rows. */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant section of system dependant header files. */
|
|
/***********************************************************************/
|
|
#include "my_global.h"
|
|
#include "table.h" // MySQL table definitions
|
|
#if defined(_WIN32)
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#if defined(__BORLANDC__)
|
|
#define __MFC_COMPAT__ // To define min/max as macro
|
|
#endif
|
|
//#include <windows.h>
|
|
#else
|
|
#if defined(UNIX)
|
|
#include <fnmatch.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "osutil.h"
|
|
#else
|
|
//#include <io.h>
|
|
#endif
|
|
//#include <fcntl.h>
|
|
#endif
|
|
|
|
/***********************************************************************/
|
|
/* Include application header files: */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "xtable.h"
|
|
#include "tabext.h"
|
|
//#include "reldef.h"
|
|
#include "filamtxt.h"
|
|
#include "tabdos.h"
|
|
#include "tabcol.h"
|
|
#include "taboccur.h"
|
|
#include "tabmysql.h"
|
|
#include "ha_connect.h"
|
|
|
|
int PrepareColist(char *colist);
|
|
|
|
/***********************************************************************/
|
|
/* Prepare and count columns in the column list. */
|
|
/***********************************************************************/
|
|
int PrepareColist(char *colist)
|
|
{
|
|
char *p, *pn;
|
|
int n = 0;
|
|
|
|
// Count the number of columns and change separator into null char
|
|
for (pn = colist; ; pn += (strlen(pn) + 1))
|
|
// Separator can be ; if colist was specified in the option_list
|
|
if ((p = strchr(pn, ',')) || (p = strchr(pn, ';'))) {
|
|
*p++ = '\0';
|
|
n++;
|
|
} else {
|
|
if (*pn)
|
|
n++;
|
|
|
|
break;
|
|
} // endif p
|
|
|
|
return n;
|
|
} // end of PrepareColist
|
|
|
|
/************************************************************************/
|
|
/* OcrColumns: constructs the result blocks containing all the columns */
|
|
/* of the object table that will be retrieved by GetData commands. */
|
|
/************************************************************************/
|
|
bool OcrColumns(PGLOBAL g, PQRYRES qrp, const char *col,
|
|
const char *ocr, const char *rank)
|
|
{
|
|
char *pn, *colist;
|
|
int i, k, m, n = 0, c = 0, j = qrp->Nblin;
|
|
bool rk, b = false;
|
|
PCOLRES crp;
|
|
|
|
if (!col || !*col) {
|
|
strcpy(g->Message, "Missing colist");
|
|
return true;
|
|
} // endif col
|
|
|
|
// Prepare the column list
|
|
colist = PlugDup(g, col);
|
|
m = PrepareColist(colist);
|
|
|
|
if ((rk = (rank && *rank))) {
|
|
if (m == 1) {
|
|
strcpy(g->Message, "Cannot handle one column colist and rank");
|
|
return true;
|
|
} // endif m
|
|
|
|
for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
|
|
n = MY_MAX(n, (signed)strlen(pn));
|
|
|
|
} // endif k
|
|
|
|
// Default occur column name is the 1st colist column name
|
|
if (!ocr || !*ocr)
|
|
ocr = colist;
|
|
|
|
/**********************************************************************/
|
|
/* Replace the columns of the colist by the rank and occur columns. */
|
|
/**********************************************************************/
|
|
for (i = 0; i < qrp->Nblin; i++) {
|
|
for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
|
|
if (!stricmp(pn, qrp->Colresp->Kdata->GetCharValue(i)))
|
|
break;
|
|
|
|
if (k < m) {
|
|
// This column belongs to colist
|
|
if (rk) {
|
|
// Place the rank column here
|
|
for (crp = qrp->Colresp; crp; crp = crp->Next)
|
|
switch (crp->Fld) {
|
|
case FLD_NAME: crp->Kdata->SetValue((char*)rank, i); break;
|
|
case FLD_TYPE: crp->Kdata->SetValue(TYPE_STRING, i); break;
|
|
case FLD_PREC: crp->Kdata->SetValue(n, i); break;
|
|
case FLD_SCALE: crp->Kdata->SetValue(0, i); break;
|
|
case FLD_NULL: crp->Kdata->SetValue(0, i); break;
|
|
case FLD_REM: crp->Kdata->Reset(i); break;
|
|
default: ; // Ignored by CONNECT
|
|
} // endswich Fld
|
|
|
|
rk = false;
|
|
} else if (!b) {
|
|
// First remaining listed column, will be the occur column
|
|
for (crp = qrp->Colresp; crp; crp = crp->Next)
|
|
switch (crp->Fld) {
|
|
case FLD_NAME: crp->Kdata->SetValue((char*)ocr, i); break;
|
|
case FLD_REM: crp->Kdata->Reset(i); break;
|
|
default: ; // Nothing to do
|
|
} // endswich Fld
|
|
|
|
b = true;
|
|
} else if (j == qrp->Nblin)
|
|
j = i; // Column to remove
|
|
|
|
c++;
|
|
} else if (j < i) {
|
|
// Move this column in empty spot
|
|
for (crp = qrp->Colresp; crp; crp = crp->Next)
|
|
crp->Kdata->Move(i, j);
|
|
|
|
j++;
|
|
} // endif k
|
|
|
|
} // endfor i
|
|
|
|
// Check whether all columns of the list where found
|
|
if (c < m) {
|
|
strcpy(g->Message, "Some colist columns are not in the source table");
|
|
return true;
|
|
} // endif crp
|
|
|
|
/**********************************************************************/
|
|
/* Set the number of columns of the table. */
|
|
/**********************************************************************/
|
|
qrp->Nblin = j;
|
|
return false;
|
|
} // end of OcrColumns
|
|
|
|
/************************************************************************/
|
|
/* OcrSrcCols: constructs the result blocks containing all the columns */
|
|
/* of the object table that will be retrieved by GetData commands. */
|
|
/************************************************************************/
|
|
bool OcrSrcCols(PGLOBAL g, PQRYRES qrp, const char *col,
|
|
const char *ocr, const char *rank)
|
|
{
|
|
char *pn, *colist;
|
|
int i, k, m, n = 0, c = 0;
|
|
bool rk, b = false;
|
|
PCOLRES crp, rcrp, *pcrp;
|
|
|
|
if (!col || !*col) {
|
|
strcpy(g->Message, "Missing colist");
|
|
return true;
|
|
} // endif col
|
|
|
|
// Prepare the column list
|
|
colist = PlugDup(g, col);
|
|
m = PrepareColist(colist);
|
|
|
|
if ((rk = (rank && *rank)))
|
|
for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
|
|
n = MY_MAX(n, (signed)strlen(pn));
|
|
|
|
// Default occur column name is the 1st colist column name
|
|
if (!ocr || !*ocr)
|
|
ocr = colist;
|
|
|
|
/**********************************************************************/
|
|
/* Replace the columns of the colist by the rank and occur columns. */
|
|
/**********************************************************************/
|
|
for (i = 0, pcrp = &qrp->Colresp; (crp = *pcrp); ) {
|
|
for (k = 0, pn = colist; k < m; k++, pn += (strlen(pn) + 1))
|
|
if (!stricmp(pn, crp->Name))
|
|
break;
|
|
|
|
if (k < m) {
|
|
// This column belongs to colist
|
|
c++;
|
|
|
|
if (!b) {
|
|
if (rk) {
|
|
// Add the rank column here
|
|
rcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
|
|
memset(rcrp, 0, sizeof(COLRES));
|
|
rcrp->Next = crp;
|
|
rcrp->Name = (char*)rank;
|
|
rcrp->Type = TYPE_STRING;
|
|
rcrp->Length = n;
|
|
rcrp->Ncol = ++i;
|
|
*pcrp = rcrp;
|
|
} // endif rk
|
|
|
|
// First remaining listed column, will be the occur column
|
|
crp->Name = (char*)ocr;
|
|
b = true;
|
|
} else {
|
|
*pcrp = crp->Next; // Remove this column
|
|
continue;
|
|
} // endif b
|
|
|
|
} // endif k
|
|
|
|
crp->Ncol = ++i;
|
|
pcrp = &crp->Next;
|
|
} // endfor pcrp
|
|
|
|
// Check whether all columns of the list where found
|
|
if (c < m) {
|
|
strcpy(g->Message, "Some colist columns are not in the source table");
|
|
return true;
|
|
} // endif crp
|
|
|
|
/**********************************************************************/
|
|
/* Set the number of columns of the table. */
|
|
/**********************************************************************/
|
|
qrp->Nblin = i;
|
|
return false;
|
|
} // end of OcrSrcCols
|
|
|
|
/* -------------- Implementation of the OCCUR classes ---------------- */
|
|
|
|
/***********************************************************************/
|
|
/* DefineAM: define specific AM block values from OCCUR table. */
|
|
/***********************************************************************/
|
|
bool OCCURDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|
{
|
|
Rcol = GetStringCatInfo(g, "RankCol", "");
|
|
Colist = GetStringCatInfo(g, "Colist", "");
|
|
Xcol = GetStringCatInfo(g, "OccurCol", Colist);
|
|
return PRXDEF::DefineAM(g, am, poff);
|
|
} // end of DefineAM
|
|
|
|
/***********************************************************************/
|
|
/* GetTable: makes a new TDB of the proper type. */
|
|
/***********************************************************************/
|
|
PTDB OCCURDEF::GetTable(PGLOBAL g, MODE)
|
|
{
|
|
if (Catfunc != FNC_COL)
|
|
return new(g) TDBOCCUR(this);
|
|
else
|
|
return new(g) TDBTBC(this);
|
|
|
|
} // end of GetTable
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Implementation of the TDBOCCUR class. */
|
|
/***********************************************************************/
|
|
TDBOCCUR::TDBOCCUR(POCCURDEF tdp) : TDBPRX(tdp)
|
|
{
|
|
//Tdbp = NULL; // Source table (in TDBPRX)
|
|
Tabname = tdp->Tablep->GetName(); // Name of source table
|
|
Colist = tdp->Colist; // List of source columns
|
|
Xcolumn = tdp->Xcol; // Occur column name
|
|
Rcolumn = tdp->Rcol; // Rank column name
|
|
Xcolp = NULL; // To the OCCURCOL column
|
|
Col = NULL; // To source column blocks array
|
|
Mult = PrepareColist(Colist); // Multiplication factor
|
|
N = 0; // The current table index
|
|
M = 0; // The occurrence rank
|
|
RowFlag = 0; // 0: Ok, 1: Same, 2: Skip
|
|
} // end of TDBOCCUR constructor
|
|
|
|
/***********************************************************************/
|
|
/* Allocate OCCUR/SRC column description block. */
|
|
/***********************************************************************/
|
|
PCOL TDBOCCUR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
|
{
|
|
PCOL colp = NULL;
|
|
|
|
if (!stricmp(cdp->GetName(), Rcolumn)) {
|
|
// Allocate a RANK column
|
|
colp = new(g) RANKCOL(cdp, this, n);
|
|
} else if (!stricmp(cdp->GetName(), Xcolumn)) {
|
|
// Allocate the OCCUR column
|
|
colp = Xcolp = new(g) OCCURCOL(cdp, this, n);
|
|
} else
|
|
return new(g) PRXCOL(cdp, this, cprec, n);
|
|
|
|
if (cprec) {
|
|
colp->SetNext(cprec->GetNext());
|
|
cprec->SetNext(colp);
|
|
} else {
|
|
colp->SetNext(Columns);
|
|
Columns = colp;
|
|
} // endif cprec
|
|
|
|
return colp;
|
|
} // end of MakeCol
|
|
|
|
/***********************************************************************/
|
|
/* Initializes the table. */
|
|
/***********************************************************************/
|
|
bool TDBOCCUR::InitTable(PGLOBAL g)
|
|
{
|
|
if (!Tdbp)
|
|
// Get the table description block of this table
|
|
if (!(Tdbp = GetSubTable(g, ((POCCURDEF)To_Def)->Tablep, TRUE)))
|
|
return TRUE;
|
|
|
|
if (!Tdbp->IsView())
|
|
if (MakeColumnList(g))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
} // end of InitTable
|
|
|
|
/***********************************************************************/
|
|
/* Allocate OCCUR column description block. */
|
|
/***********************************************************************/
|
|
bool TDBOCCUR::MakeColumnList(PGLOBAL g)
|
|
{
|
|
char *pn;
|
|
int i;
|
|
PCOL colp;
|
|
|
|
for (colp = Columns; colp; colp = colp->GetNext())
|
|
if (colp->GetAmType() == TYPE_AM_PRX)
|
|
if (((PPRXCOL)colp)->Init(g, NULL))
|
|
return true;
|
|
|
|
Col = (PCOL*)PlugSubAlloc(g, NULL, Mult * sizeof(PCOL));
|
|
|
|
for (i = 0, pn = Colist; i < Mult; i++, pn += (strlen(pn) + 1)) {
|
|
if (!(Col[i] = Tdbp->ColDB(g, pn, 0))) {
|
|
// Column not found in table
|
|
snprintf(g->Message, sizeof(g->Message), MSG(COL_ISNOT_TABLE), pn, Tabname);
|
|
return true;
|
|
} // endif Col
|
|
|
|
if (Col[i]->InitValue(g)) {
|
|
strcpy(g->Message, "OCCUR InitValue failed");
|
|
return true;
|
|
} // endif InitValue
|
|
|
|
} // endfor i
|
|
|
|
return false;
|
|
} // end of MakeColumnList
|
|
|
|
/***********************************************************************/
|
|
/* Allocate OCCUR column description block for a view. */
|
|
/***********************************************************************/
|
|
bool TDBOCCUR::ViewColumnList(PGLOBAL g)
|
|
{
|
|
char *pn;
|
|
int i;
|
|
PCOL colp, cp;
|
|
PTDBMY tdbp;
|
|
|
|
if (!Tdbp->IsView())
|
|
return false;
|
|
|
|
if (Tdbp->GetAmType() != TYPE_AM_MYSQL) {
|
|
strcpy(g->Message, "View is not MySQL");
|
|
return true;
|
|
} else
|
|
tdbp = (PTDBMY)Tdbp;
|
|
|
|
for (cp = Columns; cp; cp = cp->GetNext())
|
|
if (cp->GetAmType() == TYPE_AM_PRX) {
|
|
if ((colp = tdbp->MakeFieldColumn(g, cp->GetName()))) {
|
|
((PPRXCOL)cp)->Colp = colp;
|
|
((PPRXCOL)cp)->To_Val = colp->GetValue();
|
|
} else
|
|
return true;
|
|
|
|
} // endif Type
|
|
|
|
Col = (PCOL*)PlugSubAlloc(g, NULL, Mult * sizeof(PCOL));
|
|
|
|
for (i = 0, pn = Colist; i < Mult; i++, pn += (strlen(pn) + 1))
|
|
if (!(Col[i] = tdbp->MakeFieldColumn(g, pn))) {
|
|
// Column not found in table
|
|
snprintf(g->Message, sizeof(g->Message), MSG(COL_ISNOT_TABLE), pn, Tabname);
|
|
return true;
|
|
} // endif Col
|
|
|
|
return false;
|
|
} // end of ViewColumnList
|
|
|
|
/***********************************************************************/
|
|
/* OCCUR GetMaxSize: returns the maximum number of rows in the table. */
|
|
/***********************************************************************/
|
|
int TDBOCCUR::GetMaxSize(PGLOBAL g)
|
|
{
|
|
if (MaxSize < 0) {
|
|
if (!(Tdbp = GetSubTable(g, ((POCCURDEF)To_Def)->Tablep, TRUE)))
|
|
return 0;
|
|
|
|
MaxSize = Mult * Tdbp->GetMaxSize(g);
|
|
} // endif MaxSize
|
|
|
|
return MaxSize;
|
|
} // 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 TDBOCCUR::RowNumber(PGLOBAL, bool b)
|
|
{
|
|
return (b) ? M : N;
|
|
} // end of RowNumber
|
|
|
|
/***********************************************************************/
|
|
/* OCCUR Access Method opening routine. */
|
|
/***********************************************************************/
|
|
bool TDBOCCUR::OpenDB(PGLOBAL g)
|
|
{
|
|
if (Use == USE_OPEN) {
|
|
/*******************************************************************/
|
|
/* Table already open, just replace it at its beginning. */
|
|
/*******************************************************************/
|
|
N = M = 0;
|
|
RowFlag = 0;
|
|
|
|
if (Xcolp)
|
|
Xcolp->Xreset();
|
|
|
|
return Tdbp->OpenDB(g);
|
|
} // endif use
|
|
|
|
|
|
if (Mode != MODE_READ) {
|
|
/*******************************************************************/
|
|
/* Currently OCCUR tables cannot be modified. */
|
|
/*******************************************************************/
|
|
strcpy(g->Message, "OCCUR tables are read only");
|
|
return TRUE;
|
|
} // endif Mode
|
|
|
|
/*********************************************************************/
|
|
/* Do it here if not done yet. */
|
|
/*********************************************************************/
|
|
if (InitTable(g))
|
|
return TRUE;
|
|
|
|
if (Xcolp)
|
|
// Lock this column so it is evaluated by its table only
|
|
Xcolp->AddStatus(BUF_READ);
|
|
|
|
if (To_Key_Col || To_Kindex) {
|
|
/*******************************************************************/
|
|
/* Direct access of OCCUR tables is not implemented yet. */
|
|
/*******************************************************************/
|
|
strcpy(g->Message, "No direct access to OCCUR tables");
|
|
return TRUE;
|
|
} // endif To_Key_Col
|
|
|
|
/*********************************************************************/
|
|
/* Do open the source table. */
|
|
/*********************************************************************/
|
|
if (Tdbp->OpenDB(g))
|
|
return TRUE;
|
|
|
|
Use = USE_OPEN;
|
|
return ViewColumnList(g);
|
|
} // end of OpenDB
|
|
|
|
/***********************************************************************/
|
|
/* Data Base read routine for OCCUR access method. */
|
|
/***********************************************************************/
|
|
int TDBOCCUR::ReadDB(PGLOBAL g)
|
|
{
|
|
int rc = RC_OK;
|
|
|
|
/*********************************************************************/
|
|
/* Now start the multi reading process. */
|
|
/*********************************************************************/
|
|
do {
|
|
if (RowFlag != 1)
|
|
if ((rc = Tdbp->ReadDB(g)) != RC_OK)
|
|
break;
|
|
|
|
if (Xcolp) {
|
|
RowFlag = 0;
|
|
Xcolp->ReadColumn(g);
|
|
M = Xcolp->GetI();
|
|
} // endif Xcolp
|
|
|
|
} while (RowFlag == 2);
|
|
|
|
N++;
|
|
return rc;
|
|
} // end of ReadDB
|
|
|
|
// ------------------------ OCCURCOL functions ----------------------------
|
|
|
|
/***********************************************************************/
|
|
/* OCCURCOL public constructor. */
|
|
/***********************************************************************/
|
|
OCCURCOL::OCCURCOL(PCOLDEF cdp, PTDBOCCUR tdbp, int n)
|
|
: COLBLK(cdp, tdbp, n)
|
|
{
|
|
// Set additional OCCUR access method information for column.
|
|
I = 0;
|
|
} // end of OCCURCOL constructor
|
|
|
|
/***********************************************************************/
|
|
/* ReadColumn: what this routine does is to access the columns of */
|
|
/* list, extract their value and convert it to buffer type. */
|
|
/***********************************************************************/
|
|
void OCCURCOL::ReadColumn(PGLOBAL g)
|
|
{
|
|
PTDBOCCUR tdbp = (PTDBOCCUR)To_Tdb;
|
|
PCOL *col = tdbp->Col;
|
|
|
|
for (; I < tdbp->Mult; I++) {
|
|
col[I]->ReadColumn(g);
|
|
|
|
if (Nullable || !col[I]->GetValue()->IsZero())
|
|
break;
|
|
|
|
} // endfor I
|
|
|
|
if (I == tdbp->Mult) {
|
|
// No more values, go to next source row
|
|
tdbp->RowFlag = 2;
|
|
I = 0;
|
|
return;
|
|
} // endif I
|
|
|
|
// Set the OCCUR column value from the Ith source column value
|
|
Value->SetValue_pval(col[I++]->GetValue());
|
|
tdbp->RowFlag = 1;
|
|
} // end of ReadColumn
|
|
|
|
|
|
// ------------------------ RANKCOL functions ---------------------------
|
|
|
|
/***********************************************************************/
|
|
/* ReadColumn: what this routine does is to access the Mth columns of */
|
|
/* list, extract its name and set to it the rank column value. */
|
|
/***********************************************************************/
|
|
void RANKCOL::ReadColumn(PGLOBAL)
|
|
{
|
|
PTDBOCCUR tdbp = (PTDBOCCUR)To_Tdb;
|
|
PCOL *col = tdbp->Col;
|
|
|
|
// Set the RANK column value from the Mth source column name
|
|
if (tdbp->M)
|
|
Value->SetValue_psz(col[tdbp->M - 1]->GetName());
|
|
else {
|
|
Value->Reset();
|
|
|
|
if (Nullable)
|
|
Value->SetNull(true);
|
|
|
|
} // endelse
|
|
|
|
} // end of ReadColumn
|