text: /************** 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
  
  