/************** Table C++ Functions Source Code File (.CPP) ************/ /* Name: TABLE.CPP Version 2.8 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 1999-2017 */ /* */ /* This file contains the TBX, TDB and OPJOIN classes functions. */ /***********************************************************************/ /***********************************************************************/ /* Include relevant MariaDB header file. */ /***********************************************************************/ #include "my_global.h" #include "sql_string.h" /***********************************************************************/ /* Include required application header files */ /* global.h is header containing all global Plug declarations. */ /* plgdbsem.h is header containing the DB applic. declarations. */ /* xobject.h is header containing XOBJECT derived classes declares. */ /***********************************************************************/ #include "global.h" #include "plgdbsem.h" #include "xtable.h" #include "tabcol.h" #include "filamtxt.h" #include "tabdos.h" //#include "catalog.h" #include "reldef.h" int TDB::Tnum = 0; /***********************************************************************/ /* Utility routines. */ /***********************************************************************/ void NewPointer(PTABS, void *, void *); void AddPointer(PTABS, void *); /* ---------------------------- class TDB ---------------------------- */ /***********************************************************************/ /* TDB public constructors. */ /***********************************************************************/ TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum) { To_Def = tdp; Use = USE_NO; To_Orig = NULL; To_Filter = NULL; To_CondFil = NULL; Cond = NULL; Next = NULL; Name = (tdp) ? tdp->GetName() : NULL; To_Table = NULL; Columns = NULL; To_SetCols = NULL; Degree = (tdp) ? tdp->GetDegree() : 0; Mode = MODE_ANY; Cardinal = -1; MaxSize = -1; Read_Only = (tdp) ? tdp->IsReadOnly() : false; m_data_charset = (tdp) ? tdp->data_charset() : NULL; csname = (tdp) ? tdp->csname : NULL; } // end of TDB standard constructor TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum) { To_Def = tdbp->To_Def; Use = tdbp->Use; To_Orig = tdbp; To_Filter = NULL; To_CondFil = NULL; Cond = NULL; Next = NULL; Name = tdbp->Name; To_Table = tdbp->To_Table; Columns = NULL; To_SetCols = tdbp->To_SetCols; // ??? Degree = tdbp->Degree; Mode = tdbp->Mode; Cardinal = tdbp->Cardinal; MaxSize = tdbp->MaxSize; Read_Only = tdbp->IsReadOnly(); m_data_charset = tdbp->data_charset(); csname = tdbp->csname; } // end of TDB copy constructor // Methods /***********************************************************************/ /* Return the pointer on the charset of this table. */ /***********************************************************************/ CHARSET_INFO *TDB::data_charset(void) { // If no DATA_CHARSET is specified, we assume that character // set of the remote data is the same with CHARACTER SET // definition of the SQL column. return m_data_charset ? m_data_charset : &my_charset_bin; } // end of data_charset /***********************************************************************/ /* Return the datapath of the DB this table belongs to. */ /***********************************************************************/ PCSZ TDB::GetPath(void) { return To_Def->GetPath(); } // end of GetPath /***********************************************************************/ /* Return true if name is a special column of this table. */ /***********************************************************************/ bool TDB::IsSpecial(PSZ name) { for (PCOLDEF cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) if (!stricmp(cdp->GetName(), name) && (cdp->Flags & U_SPECIAL)) return true; // Special column to ignore while inserting return false; // Not found or not special or not inserting } // end of IsSpecial /***********************************************************************/ /* Initialize TDB based column description block construction. */ /* name is used to call columns by name. */ /* num is used by TBL to construct columns by index number. */ /* Note: name=Null and num=0 for constructing all columns (select *) */ /***********************************************************************/ PCOL TDB::ColDB(PGLOBAL g, PSZ name, int num) { int i; PCOLDEF cdp; PCOL cp, colp = NULL, cprec = NULL; if (trace(1)) htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n", GetAmType(), SVP(name), Name, num); for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) if ((!name && !num) || (name && !stricmp(cdp->GetName(), 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 ((num && cp->GetIndex() == i) || (name && !stricmp(cp->GetName(), name))) break; // Found else if (cp->GetIndex() < i) cprec = cp; if (trace(1)) htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp); /*****************************************************************/ /* Now take care of Column Description Block. */ /*****************************************************************/ if (cp) colp = cp; else if (!(cdp->Flags & U_SPECIAL)) colp = MakeCol(g, cdp, cprec, i); else if (Mode != MODE_INSERT) colp = InsertSpcBlk(g, cdp); if (trace(1)) htrc("colp=%p\n", colp); if (name || num) break; else if (colp && !colp->IsSpecial()) cprec = colp; } // endif Name return (colp); } // end of ColDB /***********************************************************************/ /* InsertSpecialColumn: Put a special column ahead of the column list.*/ /***********************************************************************/ PCOL TDB::InsertSpecialColumn(PCOL colp) { if (!colp->IsSpecial()) return NULL; colp->SetNext(Columns); Columns = colp; return colp; } // end of InsertSpecialColumn /***********************************************************************/ /* Make a special COLBLK to insert in a table. */ /***********************************************************************/ PCOL TDB::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp) { //char *name = cdp->GetName(); char *name = cdp->GetFmt(); PCOLUMN cp; PCOL colp; cp = new(g)COLUMN(cdp->GetName()); if (!To_Table) { strcpy(g->Message, "Cannot make special column: To_Table is NULL"); return NULL; } else cp->SetTo_Table(To_Table); if (!stricmp(name, "FILEID") || !stricmp(name, "FDISK") || !stricmp(name, "FPATH") || !stricmp(name, "FNAME") || !stricmp(name, "FTYPE") || !stricmp(name, "SERVID")) { if (!To_Def || !(To_Def->GetPseudo() & 2)) { sprintf(g->Message, MSG(BAD_SPEC_COLUMN)); return NULL; } // endif Pseudo if (!stricmp(name, "FILEID")) colp = new(g)FIDBLK(cp, OP_XX); else if (!stricmp(name, "FDISK")) colp = new(g)FIDBLK(cp, OP_FDISK); else if (!stricmp(name, "FPATH")) colp = new(g)FIDBLK(cp, OP_FPATH); else if (!stricmp(name, "FNAME")) colp = new(g)FIDBLK(cp, OP_FNAME); else if (!stricmp(name, "FTYPE")) colp = new(g)FIDBLK(cp, OP_FTYPE); else colp = new(g)SIDBLK(cp); } else if (!stricmp(name, "TABID")) { colp = new(g)TIDBLK(cp); } else if (!stricmp(name, "PARTID")) { colp = new(g)PRTBLK(cp); //} else if (!stricmp(name, "CONID")) { // colp = new(g) CIDBLK(cp); } else if (!stricmp(name, "ROWID")) { colp = new(g)RIDBLK(cp, false); } else if (!stricmp(name, "ROWNUM")) { colp = new(g)RIDBLK(cp, true); } else { sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); return NULL; } // endif's name if (!(colp = InsertSpecialColumn(colp))) { sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); return NULL; } // endif Insert return (colp); } // end of InsertSpcBlk /***********************************************************************/ /* Marks DOS/MAP table columns used in internal joins. */ /* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */ /* points to the currently marked tdb. */ /* Two questions here: exact meaning of U_J_INT ? */ /* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */ /***********************************************************************/ void TDB::MarkDB(PGLOBAL, PTDB tdb2) { if (trace(1)) htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2); } // end of MarkDB /***********************************************************************/ /* RowNumber: returns the current row ordinal number. */ /***********************************************************************/ int TDB::RowNumber(PGLOBAL g, bool) { sprintf(g->Message, MSG(ROWID_NOT_IMPL), GetAmName(g, GetAmType())); return 0; } // end of RowNumber PTDB TDB::Copy(PTABS t) { PTDB tp, tdb1, tdb2 = NULL, outp = NULL; //PGLOBAL g = t->G; // Is this really useful ??? for (tdb1 = this; tdb1; tdb1 = tdb1->Next) { tp = tdb1->Clone(t); if (!outp) outp = tp; else tdb2->Next = tp; tdb2 = tp; NewPointer(t, tdb1, tdb2); } // endfor tdb1 return outp; } // end of Copy /***********************************************************************/ /* SetRecpos: Replace the table at the specified position. */ /***********************************************************************/ bool TDB::SetRecpos(PGLOBAL g, int) { strcpy(g->Message, MSG(SETRECPOS_NIY)); return true; } // end of SetRecpos void TDB::Printf(PGLOBAL g, FILE *f, uint n) { PCOL cp; char m[64]; memset(m, ' ', n); // Make margin string m[n] = '\0'; for (PTDB tp = this; tp; tp = tp->Next) { fprintf(f, "%sTDB (%p) %s no=%d use=%d type=%d\n", m, tp, tp->Name, tp->Tdb_No, tp->Use, tp->GetAmType()); tp->PrintAM(f, m); fprintf(f, "%s Columns (deg=%d):\n", m, tp->Degree); for (cp = tp->Columns; cp; cp = cp->GetNext()) cp->Printf(g, f, n); } /* endfor tp */ } // end of Printf void TDB::Prints(PGLOBAL, char *ps, uint) { sprintf(ps, "R%d.%s", Tdb_No, Name); } // end of Prints /* -------------------------- class TDBASE --------------------------- */ /***********************************************************************/ /* Implementation of the TDBASE class. This is the base class to all */ /* classes for tables that can be joined together. */ /***********************************************************************/ TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp) { //To_Def = tdp; To_Link = NULL; To_Key_Col = NULL; To_Kindex = NULL; To_Xdp = NULL; //To_SetCols = NULL; Ftype = RECFM_NAF; //MaxSize = -1; Knum = 0; //Read_Only = (tdp) ? tdp->IsReadOnly() : false; //m_data_charset= (tdp) ? tdp->data_charset() : NULL; //csname = (tdp) ? tdp->csname : NULL; } // end of TDBASE constructor TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp) { //To_Def = tdbp->To_Def; To_Link = tdbp->To_Link; To_Key_Col = tdbp->To_Key_Col; To_Kindex = tdbp->To_Kindex; To_Xdp = tdbp->To_Xdp; //To_SetCols = tdbp->To_SetCols; // ??? Ftype = tdbp->Ftype; //MaxSize = tdbp->MaxSize; Knum = tdbp->Knum; //Read_Only = tdbp->Read_Only; //m_data_charset= tdbp->m_data_charset; //csname = tdbp->csname; } // end of TDBASE copy constructor /***********************************************************************/ /* Return the pointer on the DB catalog this table belongs to. */ /***********************************************************************/ PCATLG TDBASE::GetCat(void) { return (To_Def) ? To_Def->GetCat() : NULL; } // end of GetCat #if 0 /***********************************************************************/ /* Return the pointer on the charset of this table. */ /***********************************************************************/ CHARSET_INFO *TDBASE::data_charset(void) { // If no DATA_CHARSET is specified, we assume that character // set of the remote data is the same with CHARACTER SET // definition of the SQL column. return m_data_charset ? m_data_charset : &my_charset_bin; } // end of data_charset /***********************************************************************/ /* Return the datapath of the DB this table belongs to. */ /***********************************************************************/ PSZ TDBASE::GetPath(void) { return To_Def->GetPath(); } // end of GetPath /***********************************************************************/ /* Return true if name is a special column of this table. */ /***********************************************************************/ bool TDBASE::IsSpecial(PSZ name) { for (PCOLDEF cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) if (!stricmp(cdp->GetName(), name) && (cdp->Flags & U_SPECIAL)) return true; // Special column to ignore while inserting return false; // Not found or not special or not inserting } // end of IsSpecial /***********************************************************************/ /* Initialize TDBASE based column description block construction. */ /* name is used to call columns by name. */ /* num is used by TBL to construct columns by index number. */ /* Note: name=Null and num=0 for constructing all columns (select *) */ /***********************************************************************/ PCOL TDBASE::ColDB(PGLOBAL g, PSZ name, int num) { int i; PCOLDEF cdp; PCOL cp, colp = NULL, cprec = NULL; if (trace(1)) htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n", GetAmType(), SVP(name), Name, num); for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) if ((!name && !num) || (name && !stricmp(cdp->GetName(), 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 ((num && cp->GetIndex() == i) || (name && !stricmp(cp->GetName(), name))) break; // Found else if (cp->GetIndex() < i) cprec = cp; if (trace(1)) htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp); /*****************************************************************/ /* Now take care of Column Description Block. */ /*****************************************************************/ if (cp) colp = cp; else if (!(cdp->Flags & U_SPECIAL)) colp = MakeCol(g, cdp, cprec, i); else if (Mode != MODE_INSERT) colp = InsertSpcBlk(g, cdp); if (trace(1)) htrc("colp=%p\n", colp); if (name || num) break; else if (colp && !colp->IsSpecial()) cprec = colp; } // endif Name return (colp); } // end of ColDB /***********************************************************************/ /* InsertSpecialColumn: Put a special column ahead of the column list.*/ /***********************************************************************/ PCOL TDBASE::InsertSpecialColumn(PCOL colp) { if (!colp->IsSpecial()) return NULL; colp->SetNext(Columns); Columns = colp; return colp; } // end of InsertSpecialColumn /***********************************************************************/ /* Make a special COLBLK to insert in a table. */ /***********************************************************************/ PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp) { //char *name = cdp->GetName(); char *name = cdp->GetFmt(); PCOLUMN cp; PCOL colp; cp= new(g) COLUMN(cdp->GetName()); if (! To_Table) { strcpy(g->Message, "Cannot make special column: To_Table is NULL"); return NULL; } else cp->SetTo_Table(To_Table); if (!stricmp(name, "FILEID") || !stricmp(name, "FDISK") || !stricmp(name, "FPATH") || !stricmp(name, "FNAME") || !stricmp(name, "FTYPE") || !stricmp(name, "SERVID")) { if (!To_Def || !(To_Def->GetPseudo() & 2)) { sprintf(g->Message, MSG(BAD_SPEC_COLUMN)); return NULL; } // endif Pseudo if (!stricmp(name, "FILEID")) colp = new(g) FIDBLK(cp, OP_XX); else if (!stricmp(name, "FDISK")) colp = new(g) FIDBLK(cp, OP_FDISK); else if (!stricmp(name, "FPATH")) colp = new(g) FIDBLK(cp, OP_FPATH); else if (!stricmp(name, "FNAME")) colp = new(g) FIDBLK(cp, OP_FNAME); else if (!stricmp(name, "FTYPE")) colp = new(g) FIDBLK(cp, OP_FTYPE); else colp = new(g) SIDBLK(cp); } else if (!stricmp(name, "TABID")) { colp = new(g) TIDBLK(cp); } else if (!stricmp(name, "PARTID")) { colp = new(g) PRTBLK(cp); //} else if (!stricmp(name, "CONID")) { // colp = new(g) CIDBLK(cp); } else if (!stricmp(name, "ROWID")) { colp = new(g) RIDBLK(cp, false); } else if (!stricmp(name, "ROWNUM")) { colp = new(g) RIDBLK(cp, true); } else { sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); return NULL; } // endif's name if (!(colp = InsertSpecialColumn(colp))) { sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); return NULL; } // endif Insert return (colp); } // end of InsertSpcBlk #endif // 0 /***********************************************************************/ /* ResetTableOpt: Wrong for this table type. */ /***********************************************************************/ int TDBASE::ResetTableOpt(PGLOBAL g, bool, bool) { strcpy(g->Message, "This table is not indexable"); return RC_INFO; } // end of ResetTableOpt /***********************************************************************/ /* ResetKindex: set or reset the index pointer. */ /***********************************************************************/ void TDBASE::ResetKindex(PGLOBAL g, PKXBASE kxp) { if (To_Kindex) { int pos = GetRecpos(); // To be reset in Txfp for (PCOL colp= Columns; colp; colp= colp->GetNext()) colp->SetKcol(NULL); To_Kindex->Close(); // Discard old index SetRecpos(g, pos); // Ignore return value } // endif To_Kindex To_Kindex = kxp; } // end of ResetKindex #if 0 /***********************************************************************/ /* SetRecpos: Replace the table at the specified position. */ /***********************************************************************/ bool TDBASE::SetRecpos(PGLOBAL g, int) { strcpy(g->Message, MSG(SETRECPOS_NIY)); return true; } // end of SetRecpos #endif // 0 /***********************************************************************/ /* Methods */ /***********************************************************************/ void TDBASE::PrintAM(FILE *f, char *m) { fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode); } // end of PrintAM #if 0 /***********************************************************************/ /* Marks DOS/MAP table columns used in internal joins. */ /* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */ /* points to the currently marked tdb. */ /* Two questions here: exact meaning of U_J_INT ? */ /* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */ /***********************************************************************/ void TDBASE::MarkDB(PGLOBAL, PTDB tdb2) { if (trace(1)) htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2); } // end of MarkDB #endif // 0 /* ---------------------------TDBCAT class --------------------------- */ /***********************************************************************/ /* Implementation of the TDBCAT class. */ /***********************************************************************/ TDBCAT::TDBCAT(PTABDEF tdp) : TDBASE(tdp) { Qrp = NULL; Init = false; N = -1; } // end of TDBCAT constructor /***********************************************************************/ /* Allocate CAT column description block. */ /***********************************************************************/ PCOL TDBCAT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) { PCATCOL colp; colp = (PCATCOL)new(g) CATCOL(cdp, this, n); if (cprec) { colp->SetNext(cprec->GetNext()); cprec->SetNext(colp); } else { colp->SetNext(Columns); Columns = colp; } // endif cprec return colp; } // end of MakeCol /***********************************************************************/ /* Initialize: Get the result query block. */ /***********************************************************************/ bool TDBCAT::Initialize(PGLOBAL g) { if (Init) return false; if (!(Qrp = GetResult(g))) return true; if (Qrp->Truncated) { sprintf(g->Message, "Result limited to %d lines", Qrp->Maxres); PushWarning(g, this); } // endif Truncated if (Qrp->BadLines) { sprintf(g->Message, "%d bad lines in result", Qrp->BadLines); PushWarning(g, this); } // endif Badlines Init = true; return false; } // end of Initialize /***********************************************************************/ /* CAT: Get the number of properties. */ /***********************************************************************/ int TDBCAT::GetMaxSize(PGLOBAL g __attribute__((unused))) { if (MaxSize < 0) { // if (Initialize(g)) // return -1; // MaxSize = Qrp->Nblin; MaxSize = 10; // To make MariaDB happy } // endif MaxSize return MaxSize; } // end of GetMaxSize /***********************************************************************/ /* CAT Access Method opening routine. */ /***********************************************************************/ bool TDBCAT::OpenDB(PGLOBAL g) { if (Use == USE_OPEN) { /*******************************************************************/ /* Table already open. */ /*******************************************************************/ N = -1; return false; } // endif use if (Mode != MODE_READ) { /*******************************************************************/ /* ODBC Info tables cannot be modified. */ /*******************************************************************/ strcpy(g->Message, "CAT tables are read only"); return true; } // endif Mode /*********************************************************************/ /* Initialize the ODBC processing. */ /*********************************************************************/ if (Initialize(g)) return true; Use = USE_OPEN; return InitCol(g); } // end of OpenDB /***********************************************************************/ /* Initialize columns. */ /***********************************************************************/ bool TDBCAT::InitCol(PGLOBAL g) { PCATCOL colp; PCOLRES crp; for (colp = (PCATCOL)Columns; colp; colp = (PCATCOL)colp->GetNext()) { for (crp = Qrp->Colresp; crp; crp = crp->Next) if ((colp->Flag && colp->Flag == crp->Fld) || (!colp->Flag && !stricmp(colp->Name, crp->Name))) { colp->Crp = crp; break; } // endif Flag if (!colp->Crp /*&& !colp->GetValue()->IsConstant()*/) { sprintf(g->Message, "Invalid flag %d for column %s", colp->Flag, colp->Name); return true; } else if (crp->Fld == FLD_SCALE || crp->Fld == FLD_RADIX) colp->Value->SetNullable(true); } // endfor colp return false; } // end of InitCol /***********************************************************************/ /* SetRecpos: Replace the table at the specified position. */ /***********************************************************************/ bool TDBCAT::SetRecpos(PGLOBAL, int recpos) { N = recpos - 1; return false; } // end of SetRecpos /***********************************************************************/ /* Data Base read routine for CAT access method. */ /***********************************************************************/ int TDBCAT::ReadDB(PGLOBAL) { return (++N < Qrp->Nblin) ? RC_OK : RC_EF; } // end of ReadDB /***********************************************************************/ /* WriteDB: Data Base write routine for CAT access methods. */ /***********************************************************************/ int TDBCAT::WriteDB(PGLOBAL g) { strcpy(g->Message, "CAT tables are read only"); return RC_FX; } // end of WriteDB /***********************************************************************/ /* Data Base delete line routine for CAT access methods. */ /***********************************************************************/ int TDBCAT::DeleteDB(PGLOBAL g, int) { strcpy(g->Message, "Delete not enabled for CAT tables"); return RC_FX; } // end of DeleteDB /***********************************************************************/ /* Data Base close routine for WMI access method. */ /***********************************************************************/ void TDBCAT::CloseDB(PGLOBAL) { // Nothing to do } // end of CloseDB // ------------------------ CATCOL functions ---------------------------- /***********************************************************************/ /* CATCOL public constructor. */ /***********************************************************************/ CATCOL::CATCOL(PCOLDEF cdp, PTDB tdbp, int n) : COLBLK(cdp, tdbp, n) { Tdbp = (PTDBCAT)tdbp; Crp = NULL; Flag = cdp->GetOffset(); } // end of WMICOL constructor /***********************************************************************/ /* Read the next Data Source elements. */ /***********************************************************************/ void CATCOL::ReadColumn(PGLOBAL) { bool b = (!Crp->Kdata || Crp->Kdata->IsNull(Tdbp->N)); // Get the value of the Name or Description property if (!b) Value->SetValue_pvblk(Crp->Kdata, Tdbp->N); else Value->Reset(); Value->SetNull(b); } // end of ReadColumn