mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2303 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2303 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/************* Tabxml C++ Program Source Code File (.CPP) **************/
 | 
						|
/* PROGRAM NAME: TABXML                                                */
 | 
						|
/* -------------                                                       */
 | 
						|
/*  Version 3.0                                                        */
 | 
						|
/*                                                                     */
 | 
						|
/*  Author Olivier BERTRAND          2007 - 2020                       */
 | 
						|
/*                                                                     */
 | 
						|
/*  This program are the XML tables classes using MS-DOM or libxml2.   */
 | 
						|
/***********************************************************************/
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Include required compiler header files.                            */
 | 
						|
/***********************************************************************/
 | 
						|
#include "my_global.h"
 | 
						|
#include <m_string.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#if defined(_WIN32)
 | 
						|
#include <io.h>
 | 
						|
#include <winsock2.h>
 | 
						|
//#include <windows.h>
 | 
						|
#include <comdef.h>
 | 
						|
#else   // !_WIN32
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/socket.h>
 | 
						|
#include <netinet/in.h>
 | 
						|
#include <unistd.h>
 | 
						|
//#include <ctype.h>
 | 
						|
#ifdef LIBXML2_SUPPORT
 | 
						|
#include <libxml/tree.h>
 | 
						|
#endif
 | 
						|
#include "osutil.h"
 | 
						|
#define _O_RDONLY O_RDONLY
 | 
						|
#endif  // !_WIN32
 | 
						|
#include "resource.h"                        // for IDS_COLUMNS
 | 
						|
 | 
						|
#define INCLUDE_TDBXML
 | 
						|
#define NODE_TYPE_LIST
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Include application header files:                                  */
 | 
						|
/*  global.h    is header containing all global declarations.          */
 | 
						|
/*  plgdbsem.h  is header containing the DB application declarations.  */
 | 
						|
/*  tabdos.h    is header containing the TABDOS class declarations.    */
 | 
						|
/***********************************************************************/
 | 
						|
#include "global.h"
 | 
						|
#include "plgdbsem.h"
 | 
						|
//#include "reldef.h"
 | 
						|
#include "xtable.h"
 | 
						|
#include "colblk.h"
 | 
						|
#include "mycat.h"
 | 
						|
#include "xindex.h"
 | 
						|
#include "plgxml.h"
 | 
						|
#include "tabxml.h"
 | 
						|
#include "tabmul.h"
 | 
						|
 | 
						|
extern "C" char version[];
 | 
						|
 | 
						|
#if defined(_WIN32) && defined(DOMDOC_SUPPORT)
 | 
						|
#define XMLSUP "MS-DOM"
 | 
						|
#else   // !_WIN32
 | 
						|
#define XMLSUP "libxml2"
 | 
						|
#endif  // !_WIN32
 | 
						|
 | 
						|
#define TYPE_UNKNOWN     12        /* Must be greater than other types */
 | 
						|
#define XLEN(M)  sizeof(M) - strlen(M) - 1	       /* To avoid overflow*/
 | 
						|
 | 
						|
int GetDefaultDepth(void);
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/* Class and structure used by XMLColumns.                             */
 | 
						|
/***********************************************************************/
 | 
						|
typedef class XMCOL *PXCL;
 | 
						|
 | 
						|
class XMCOL : public BLOCK {
 | 
						|
 public:
 | 
						|
  // Constructors
 | 
						|
  XMCOL(void) {Next = NULL; 
 | 
						|
               Name[0] = 0; 
 | 
						|
               Fmt = NULL; 
 | 
						|
               Type = 1;
 | 
						|
               Len = Scale = 0;
 | 
						|
               Cbn = false; 
 | 
						|
               Found = true;}
 | 
						|
  XMCOL(PGLOBAL g, PXCL xp, char *fmt, int i) {
 | 
						|
               Next = NULL; 
 | 
						|
               strcpy(Name, xp->Name);
 | 
						|
               Fmt = (*fmt) ? PlugDup(g, fmt) : NULL; 
 | 
						|
               Type = xp->Type;
 | 
						|
               Len = xp->Len;
 | 
						|
               Scale = xp->Scale;
 | 
						|
               Cbn = (xp->Cbn || i > 1); 
 | 
						|
               Found = true;}
 | 
						|
 | 
						|
  // Members
 | 
						|
  PXCL  Next;
 | 
						|
  char  Name[64];
 | 
						|
  char *Fmt;
 | 
						|
  int   Type;
 | 
						|
  int   Len;
 | 
						|
  int   Scale;
 | 
						|
  bool  Cbn;
 | 
						|
  bool  Found;
 | 
						|
}; // end of class XMCOL
 | 
						|
 | 
						|
typedef struct LVL {
 | 
						|
  PXNODE  pn;
 | 
						|
  PXLIST  nl;
 | 
						|
  PXATTR  atp;
 | 
						|
  bool    b;
 | 
						|
  long    k;
 | 
						|
  int     m, n;
 | 
						|
} *PLVL;
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/* XMLColumns: construct the result blocks containing the description  */
 | 
						|
/* of all the columns of a table contained inside an XML file.         */
 | 
						|
/***********************************************************************/
 | 
						|
PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info)
 | 
						|
{
 | 
						|
  static int  buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, 
 | 
						|
                          TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
 | 
						|
  static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, 
 | 
						|
                          FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
 | 
						|
  static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
 | 
						|
  char    colname[65], fmt[129], buf[512];
 | 
						|
  int     i, j, lvl, n = 0;
 | 
						|
  int     ncol = sizeof(buftyp) / sizeof(int);
 | 
						|
  bool    ok = true;
 | 
						|
	PCSZ    fn, op;
 | 
						|
  PXCL    xcol, xcp, fxcp = NULL, pxcp = NULL;
 | 
						|
  PLVL   *lvlp, vp;
 | 
						|
  PXNODE  node = NULL;
 | 
						|
  PXMLDEF tdp;
 | 
						|
  PTDBXML txmp;
 | 
						|
  PQRYRES qrp;
 | 
						|
  PCOLRES crp;
 | 
						|
 | 
						|
  if (info) {
 | 
						|
    length[0] = 128;
 | 
						|
    length[7] = 256;
 | 
						|
    goto skipit;
 | 
						|
    } // endif info
 | 
						|
 | 
						|
	if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
 | 
						|
		snprintf(g->Message, sizeof(g->Message), "Cannot find column definition for multiple table");
 | 
						|
		return NULL;
 | 
						|
	}	// endif Multiple
 | 
						|
 | 
						|
	/*********************************************************************/
 | 
						|
  /*  Open the input file.                                             */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (!(fn = GetStringTableOption(g, topt, "Filename", NULL))) {
 | 
						|
    if (topt->http) // REST table can have default filename
 | 
						|
      fn = GetStringTableOption(g, topt, "Subtype", NULL);
 | 
						|
    
 | 
						|
    if (!fn) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
 | 
						|
      return NULL;
 | 
						|
    } else
 | 
						|
      topt->subtype = NULL;
 | 
						|
 | 
						|
  } // endif fn
 | 
						|
 | 
						|
  lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
 | 
						|
  lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
 | 
						|
  lvl = (lvl < 0) ? 0 : (lvl > 16) ? 16 : lvl;
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    htrc("File %s lvl=%d\n", topt->filename, lvl);
 | 
						|
 | 
						|
  tdp = new(g) XMLDEF;
 | 
						|
  tdp->Fn = fn;
 | 
						|
 | 
						|
	if (!(tdp->Database = SetPath(g, db)))
 | 
						|
		return NULL;
 | 
						|
 | 
						|
  tdp->Tabname = tab;
 | 
						|
	tdp->Tabname = (char*)GetStringTableOption(g, topt, "Tabname", tab);
 | 
						|
	tdp->Rowname = (char*)GetStringTableOption(g, topt, "Rownode", NULL);
 | 
						|
	tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
 | 
						|
	tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
 | 
						|
	tdp->Skip = GetBooleanTableOption(g, topt, "Skipnull", false);
 | 
						|
 | 
						|
  if (!(op = GetStringTableOption(g, topt, "Xmlsup", NULL)))
 | 
						|
#if defined(_WIN32)
 | 
						|
    tdp->Usedom = true;
 | 
						|
#else   // !_WIN32
 | 
						|
    tdp->Usedom = false;
 | 
						|
#endif  // !_WIN32
 | 
						|
  else
 | 
						|
    tdp->Usedom = (toupper(*op) == 'M' || toupper(*op) == 'D');
 | 
						|
 | 
						|
  txmp = new(g) TDBXML(tdp);
 | 
						|
 | 
						|
  if (txmp->Initialize(g))
 | 
						|
    goto err;
 | 
						|
 | 
						|
  xcol = new(g) XMCOL;
 | 
						|
  colname[64] = 0;
 | 
						|
  fmt[128] = 0;
 | 
						|
  lvlp = (PLVL*)PlugSubAlloc(g, NULL, sizeof(PLVL) * (lvl + 1));
 | 
						|
 | 
						|
  for (j = 0; j <= lvl; j++)
 | 
						|
    lvlp[j] = (PLVL)PlugSubAlloc(g, NULL, sizeof(LVL));
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Analyse the XML tree and define columns.                         */
 | 
						|
  /*********************************************************************/
 | 
						|
  for (i = 1; ; i++) {
 | 
						|
    // Get next row
 | 
						|
    switch (txmp->ReadDB(g)) {
 | 
						|
      case RC_EF:
 | 
						|
        vp = NULL;
 | 
						|
        break;
 | 
						|
      case RC_FX:
 | 
						|
        goto err;
 | 
						|
      default:
 | 
						|
        vp = lvlp[0];
 | 
						|
        vp->pn = txmp->RowNode;
 | 
						|
        vp->atp = vp->pn->GetAttribute(g, NULL);
 | 
						|
        vp->nl = vp->pn->GetChildElements(g);
 | 
						|
        vp->b = true;
 | 
						|
        vp->k = 0;
 | 
						|
        vp->m = vp->n = 0;
 | 
						|
        j = 0;
 | 
						|
      } // endswitch ReadDB
 | 
						|
 | 
						|
    if (!vp)
 | 
						|
      break;
 | 
						|
 | 
						|
    while (true) {
 | 
						|
      if (!vp->atp &&
 | 
						|
          !(node = (vp->nl) ? vp->nl->GetItem(g, vp->k++, tdp->Usedom ?
 | 
						|
                                              node : NULL)
 | 
						|
            : NULL))
 | 
						|
      {
 | 
						|
        if (j) {
 | 
						|
          vp = lvlp[--j];
 | 
						|
 | 
						|
          if (!tdp->Usedom)    // nl was destroyed
 | 
						|
            vp->nl = vp->pn->GetChildElements(g);
 | 
						|
 | 
						|
          if (!lvlp[j+1]->b) {
 | 
						|
            vp->k--;
 | 
						|
            ok = false;
 | 
						|
            } // endif b
 | 
						|
 | 
						|
          continue;
 | 
						|
        } else
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      xcol->Name[vp->n] = 0;
 | 
						|
      fmt[vp->m] = 0;
 | 
						|
 | 
						|
     more:
 | 
						|
      if (vp->atp) {
 | 
						|
			size_t z = sizeof(colname) - 1;
 | 
						|
                        size_t xlen= strlen(xcol->Name);
 | 
						|
                        strmake(colname, vp->atp->GetName(g), z);
 | 
						|
                        strmake(xcol->Name + xlen, colname,
 | 
						|
                                sizeof(xcol->Name) - 1 - xlen);
 | 
						|
 | 
						|
        switch (vp->atp->GetText(g, buf, sizeof(buf))) {
 | 
						|
          case RC_INFO:
 | 
						|
            PushWarning(g, txmp);
 | 
						|
            /* falls through */
 | 
						|
          case RC_OK:
 | 
						|
            strncat(fmt, "@", XLEN(fmt));
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            goto err;
 | 
						|
          } // enswitch rc
 | 
						|
 | 
						|
        if (j)
 | 
						|
          strncat(fmt, colname, XLEN(fmt));
 | 
						|
 | 
						|
      } else {
 | 
						|
        size_t xlen;
 | 
						|
        if (tdp->Usedom && node->GetType() != 1)
 | 
						|
          continue;
 | 
						|
 | 
						|
        xlen= strlen(xcol->Name);
 | 
						|
        strmake(colname, node->GetName(g), sizeof(colname)-1);
 | 
						|
        strmake(xcol->Name + xlen, colname, sizeof(xcol->Name) - 1 - xlen);
 | 
						|
 | 
						|
        if (j)
 | 
						|
          strncat(fmt, colname, XLEN(fmt));
 | 
						|
 | 
						|
        if (j < lvl && ok) {
 | 
						|
          vp = lvlp[j+1];
 | 
						|
          vp->k = 0;
 | 
						|
					vp->pn = node;
 | 
						|
					vp->atp = node->GetAttribute(g, NULL);
 | 
						|
          vp->nl = node->GetChildElements(g);
 | 
						|
 | 
						|
          if (tdp->Usedom && vp->nl->GetLength() == 1) {
 | 
						|
            node = vp->nl->GetItem(g, 0, node);
 | 
						|
            vp->b = (node->GetType() == 1);   // Must be ab element
 | 
						|
          } else
 | 
						|
            vp->b = (vp->nl && vp->nl->GetLength());
 | 
						|
 | 
						|
          if (vp->atp || vp->b) {
 | 
						|
            if (!vp->atp)
 | 
						|
							node = vp->nl->GetItem(g, vp->k++, tdp->Usedom ? node : NULL);
 | 
						|
 | 
						|
						if (!j)
 | 
						|
							strncat(fmt, colname, XLEN(fmt));
 | 
						|
 | 
						|
						strncat(fmt, "/", XLEN(fmt));
 | 
						|
						strncat(xcol->Name, "_", XLEN(xcol->Name));
 | 
						|
						j++;
 | 
						|
            vp->n = (int)strlen(xcol->Name);
 | 
						|
            vp->m = (int)strlen(fmt);
 | 
						|
            goto more;
 | 
						|
          } else {
 | 
						|
            vp = lvlp[j];
 | 
						|
 | 
						|
            if (!tdp->Usedom)    // nl was destroyed
 | 
						|
              vp->nl = vp->pn->GetChildElements(g);
 | 
						|
 | 
						|
          } // endif vp
 | 
						|
 | 
						|
        } else
 | 
						|
          ok = true;
 | 
						|
 | 
						|
        switch (node->GetContent(g, buf, sizeof(buf))) {
 | 
						|
          case RC_INFO:
 | 
						|
            PushWarning(g, txmp);
 | 
						|
            /* falls through */
 | 
						|
          case RC_OK:
 | 
						|
						xcol->Cbn = !strlen(buf);
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            goto err;
 | 
						|
          } // enswitch rc
 | 
						|
 | 
						|
      } // endif atp;
 | 
						|
 | 
						|
      xcol->Len = strlen(buf);
 | 
						|
 | 
						|
      // Check whether this column was already found
 | 
						|
      for (xcp = fxcp; xcp; xcp = xcp->Next)
 | 
						|
        if (!strcmp(xcol->Name, xcp->Name))
 | 
						|
          break;
 | 
						|
  
 | 
						|
      if (xcp) {
 | 
						|
        if (xcp->Type != xcol->Type)
 | 
						|
          xcp->Type = TYPE_STRING;
 | 
						|
 | 
						|
        if (*fmt && (!xcp->Fmt || strlen(xcp->Fmt) < strlen(fmt))) {
 | 
						|
          xcp->Fmt = PlugDup(g, fmt);
 | 
						|
          length[7] = MY_MAX(length[7], strlen(fmt));
 | 
						|
          } // endif *fmt
 | 
						|
 | 
						|
        xcp->Len = MY_MAX(xcp->Len, xcol->Len);
 | 
						|
        xcp->Scale = MY_MAX(xcp->Scale, xcol->Scale);
 | 
						|
        xcp->Cbn |= (xcol->Cbn || !xcol->Len);
 | 
						|
        xcp->Found = true;
 | 
						|
      } else if(xcol->Len || !tdp->Skip) {
 | 
						|
        // New column
 | 
						|
        xcp = new(g) XMCOL(g, xcol, fmt, i);
 | 
						|
        length[0] = MY_MAX(length[0], strlen(xcol->Name));
 | 
						|
        length[7] = MY_MAX(length[7], strlen(fmt));
 | 
						|
  
 | 
						|
        if (pxcp) {
 | 
						|
          xcp->Next = pxcp->Next;
 | 
						|
          pxcp->Next = xcp;
 | 
						|
        } else
 | 
						|
          fxcp = xcp;
 | 
						|
 | 
						|
        n++;
 | 
						|
      } // endif xcp
 | 
						|
 | 
						|
			if (xcp)
 | 
						|
				pxcp = xcp;
 | 
						|
 | 
						|
      if (vp->atp)
 | 
						|
        vp->atp = vp->atp->GetNext(g);
 | 
						|
 | 
						|
      } // endwhile
 | 
						|
 | 
						|
    // Missing column can be null
 | 
						|
    for (xcp = fxcp; xcp; xcp = xcp->Next) {
 | 
						|
      xcp->Cbn |= !xcp->Found;
 | 
						|
      xcp->Found = false;
 | 
						|
      } // endfor xcp
 | 
						|
 | 
						|
    } // endor i
 | 
						|
 | 
						|
  txmp->CloseDB(g);
 | 
						|
 | 
						|
 skipit:
 | 
						|
  if (trace(1))
 | 
						|
    htrc("XMLColumns: n=%d len=%d\n", n, length[0]);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Allocate the structures used to refer to the result set.         */
 | 
						|
  /*********************************************************************/
 | 
						|
  qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
 | 
						|
                          buftyp, fldtyp, length, false, false);
 | 
						|
 | 
						|
  crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
 | 
						|
  crp->Name = "Nullable";
 | 
						|
  crp->Next->Name = "Xpath";
 | 
						|
 | 
						|
  if (info || !qrp)
 | 
						|
    return qrp;
 | 
						|
 | 
						|
  qrp->Nblin = n;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Now get the results into blocks.                                 */
 | 
						|
  /*********************************************************************/
 | 
						|
  for (i = 0, xcp = fxcp; xcp; i++, xcp = xcp->Next) {
 | 
						|
    if (xcp->Type == TYPE_UNKNOWN)            // Void column
 | 
						|
      xcp->Type = TYPE_STRING;
 | 
						|
 | 
						|
    crp = qrp->Colresp;                    // Column Name
 | 
						|
    crp->Kdata->SetValue(xcp->Name, i);
 | 
						|
    crp = crp->Next;                       // Data Type
 | 
						|
    crp->Kdata->SetValue(xcp->Type, i);
 | 
						|
    crp = crp->Next;                       // Type Name
 | 
						|
    crp->Kdata->SetValue(GetTypeName(xcp->Type), i);
 | 
						|
    crp = crp->Next;                       // Precision
 | 
						|
    crp->Kdata->SetValue(xcp->Len, i);
 | 
						|
    crp = crp->Next;                       // Length
 | 
						|
    crp->Kdata->SetValue(xcp->Len, i);
 | 
						|
    crp = crp->Next;                       // Scale (precision)
 | 
						|
    crp->Kdata->SetValue(xcp->Scale, i);
 | 
						|
    crp = crp->Next;                       // Nullable
 | 
						|
    crp->Kdata->SetValue(xcp->Cbn ? 1 : 0, i);
 | 
						|
    crp = crp->Next;                       // Field format
 | 
						|
 | 
						|
    if (crp->Kdata)
 | 
						|
      crp->Kdata->SetValue(xcp->Fmt, i);
 | 
						|
 | 
						|
    } // endfor i
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Return the result pointer.                                       */
 | 
						|
  /*********************************************************************/
 | 
						|
  return qrp;
 | 
						|
 | 
						|
err:
 | 
						|
  txmp->CloseDB(g);
 | 
						|
  return NULL;
 | 
						|
  } // end of XMLColumns
 | 
						|
 | 
						|
/* -------------- Implementation of the XMLDEF class  ---------------- */
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Constructor.                                                       */
 | 
						|
/***********************************************************************/
 | 
						|
XMLDEF::XMLDEF(void)
 | 
						|
  {
 | 
						|
  Pseudo = 3;
 | 
						|
  Fn = NULL;
 | 
						|
  Encoding = NULL;
 | 
						|
  Tabname = NULL;
 | 
						|
  Rowname = NULL;
 | 
						|
  Colname = NULL;
 | 
						|
  Mulnode = NULL;
 | 
						|
  XmlDB = NULL;
 | 
						|
  Nslist = NULL;
 | 
						|
  DefNs = NULL;
 | 
						|
  Attrib = NULL;
 | 
						|
  Hdattr = NULL;
 | 
						|
	Entry = NULL;
 | 
						|
  Coltype = 1;
 | 
						|
  Limit = 0;
 | 
						|
  Header = 0;
 | 
						|
  Xpand = false;
 | 
						|
  Usedom = false;
 | 
						|
	Zipped = false;
 | 
						|
	Mulentries = false;
 | 
						|
	Skip = false;
 | 
						|
  } // end of XMLDEF constructor
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  DefineAM: define specific AM block values from XDB file.           */
 | 
						|
/***********************************************************************/
 | 
						|
bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
 | 
						|
  {
 | 
						|
	PCSZ defrow, defcol;
 | 
						|
	char buf[10];
 | 
						|
 | 
						|
  Fn = GetStringCatInfo(g, "Filename", NULL);
 | 
						|
  Encoding = GetStringCatInfo(g, "Encoding", "UTF-8");
 | 
						|
 | 
						|
  if (*Fn == '?') {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(MISSING_FNAME));
 | 
						|
    return true;
 | 
						|
    } // endif fn
 | 
						|
 | 
						|
  if ((signed)GetIntCatInfo("Flag", -1) != -1) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(DEPREC_FLAG));
 | 
						|
    return true;
 | 
						|
    } // endif flag
 | 
						|
 | 
						|
  defrow = defcol = NULL;
 | 
						|
  GetCharCatInfo("Coltype", "", buf, sizeof(buf));
 | 
						|
 | 
						|
  switch (toupper(*buf)) {
 | 
						|
    case 'A':                          // Attribute
 | 
						|
    case '@':
 | 
						|
    case '0':
 | 
						|
      Coltype = 0;
 | 
						|
      break;
 | 
						|
    case '\0':                         // Default
 | 
						|
    case 'T':                          // Tag
 | 
						|
    case 'N':                          // Node
 | 
						|
    case '1':
 | 
						|
      Coltype = 1;
 | 
						|
      break;
 | 
						|
    case 'C':                          // Column
 | 
						|
    case 'P':                          // Position
 | 
						|
    case 'H':                          // HTML
 | 
						|
    case '2':
 | 
						|
      Coltype = 2;
 | 
						|
      defrow = "TR";
 | 
						|
      defcol = "TD";
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(INV_COL_TYPE), buf);
 | 
						|
      return true;
 | 
						|
    } // endswitch typname
 | 
						|
 | 
						|
  Tabname = GetStringCatInfo(g, "Name", Name);  // Deprecated
 | 
						|
  Tabname = GetStringCatInfo(g, "Table_name", Tabname); // Deprecated
 | 
						|
  Tabname = GetStringCatInfo(g, "Tabname", Tabname);
 | 
						|
  Rowname = GetStringCatInfo(g, "Rownode", defrow);
 | 
						|
  Colname = GetStringCatInfo(g, "Colnode", defcol);
 | 
						|
  Mulnode = GetStringCatInfo(g, "Mulnode", NULL);
 | 
						|
  XmlDB = GetStringCatInfo(g, "XmlDB", NULL);
 | 
						|
  Nslist = GetStringCatInfo(g, "Nslist", NULL);
 | 
						|
  DefNs = GetStringCatInfo(g, "DefNs", NULL);
 | 
						|
  Limit = GetIntCatInfo("Limit", 50);
 | 
						|
  Xpand = GetBoolCatInfo("Expand", false);
 | 
						|
  Header = GetIntCatInfo("Header", 0);
 | 
						|
  GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf));
 | 
						|
 | 
						|
  // Note that if no support is specified, the default is MS-DOM
 | 
						|
  // on Windows and libxml2 otherwise
 | 
						|
  if (*buf == '*')
 | 
						|
#if defined(_WIN32)
 | 
						|
    Usedom = true;
 | 
						|
#else   // !_WIN32
 | 
						|
    Usedom = false;
 | 
						|
#endif  // !_WIN32
 | 
						|
  else
 | 
						|
    Usedom = (toupper(*buf) == 'M' || toupper(*buf) == 'D');
 | 
						|
 | 
						|
  // Get eventual table node attribute
 | 
						|
  Attrib = GetStringCatInfo(g, "Attribute", NULL);
 | 
						|
  Hdattr = GetStringCatInfo(g, "HeadAttr", NULL);
 | 
						|
 | 
						|
	// Specific for zipped files
 | 
						|
	if ((Zipped = GetBoolCatInfo("Zipped", false)))
 | 
						|
		Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL)))
 | 
						|
		? strchr(Entry, '*') || strchr(Entry, '?')
 | 
						|
		: GetBoolCatInfo("Mulentries", false);
 | 
						|
 | 
						|
	return false;
 | 
						|
  } // end of DefineAM
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  GetTable: makes a new TDB of the proper type.                      */
 | 
						|
/***********************************************************************/
 | 
						|
PTDB XMLDEF::GetTable(PGLOBAL g, MODE m)
 | 
						|
  {
 | 
						|
  if (Catfunc == FNC_COL)
 | 
						|
    return new(g) TDBXCT(this);
 | 
						|
 | 
						|
	if (Zipped && !(m == MODE_READ || m == MODE_ANY)) {
 | 
						|
		snprintf(g->Message, sizeof(g->Message), "ZIpped XML tables are read only");
 | 
						|
		return NULL;
 | 
						|
	}	// endif Zipped
 | 
						|
 | 
						|
  PTDBASE tdbp = new(g) TDBXML(this);
 | 
						|
 | 
						|
  if (Multiple)
 | 
						|
    tdbp = new(g) TDBMUL(tdbp);
 | 
						|
 | 
						|
  return tdbp;
 | 
						|
  } // end of GetTable
 | 
						|
 | 
						|
/* ------------------------- TDBXML Class ---------------------------- */
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Implementation of the TDBXML constuctor.                           */
 | 
						|
/***********************************************************************/
 | 
						|
TDBXML::TDBXML(PXMLDEF tdp) : TDBASE(tdp)
 | 
						|
  {
 | 
						|
  Docp = NULL;
 | 
						|
  Root = NULL;
 | 
						|
  Curp = NULL;
 | 
						|
  DBnode = NULL;
 | 
						|
  TabNode = NULL;
 | 
						|
  RowNode = NULL;
 | 
						|
  ColNode = NULL;
 | 
						|
  Nlist = NULL;
 | 
						|
  Clist = NULL;
 | 
						|
  To_Xb = NULL;
 | 
						|
  Colp = NULL;
 | 
						|
  Xfile = tdp->Fn;
 | 
						|
  Enc = tdp->Encoding;
 | 
						|
  Tabname = tdp->Tabname;
 | 
						|
#if 0 // why all these?
 | 
						|
  Rowname = (tdp->Rowname) ? tdp->Rowname : NULL;
 | 
						|
  Colname = (tdp->Colname) ? tdp->Colname : NULL;
 | 
						|
  Mulnode = (tdp->Mulnode) ? tdp->Mulnode : NULL;
 | 
						|
  XmlDB = (tdp->XmlDB) ? tdp->XmlDB : NULL;
 | 
						|
  Nslist = (tdp->Nslist) ? tdp->Nslist : NULL;
 | 
						|
  DefNs = (tdp->DefNs) ? tdp->DefNs : NULL;
 | 
						|
  Attrib = (tdp->Attrib) ? tdp->Attrib : NULL;
 | 
						|
  Hdattr = (tdp->Hdattr) ? tdp->Hdattr : NULL;
 | 
						|
#endif // 0
 | 
						|
	Rowname = tdp->Rowname;
 | 
						|
	Colname = tdp->Colname;
 | 
						|
	Mulnode = tdp->Mulnode;
 | 
						|
	XmlDB = tdp->XmlDB;
 | 
						|
	Nslist = tdp->Nslist;
 | 
						|
	DefNs = tdp->DefNs;
 | 
						|
	Attrib = tdp->Attrib;
 | 
						|
	Hdattr = tdp->Hdattr;
 | 
						|
	Entry = tdp->Entry;
 | 
						|
	Coltype = tdp->Coltype;
 | 
						|
  Limit = tdp->Limit;
 | 
						|
  Xpand = tdp->Xpand;
 | 
						|
	Zipped = tdp->Zipped;
 | 
						|
	Mulentries = tdp->Mulentries;
 | 
						|
	Changed = false;
 | 
						|
  Checked = false;
 | 
						|
  NextSame = false;
 | 
						|
  NewRow = false;
 | 
						|
  Hasnod = false;
 | 
						|
  Write = false;
 | 
						|
  Bufdone = false;
 | 
						|
  Nodedone = false;
 | 
						|
  Void = false;
 | 
						|
  Usedom = tdp->Usedom;
 | 
						|
  Header = tdp->Header;
 | 
						|
  Multiple = tdp->Multiple;
 | 
						|
  Nrow = -1;
 | 
						|
  Irow = Header - 1;
 | 
						|
  Nsub = 0;
 | 
						|
  N = 0;
 | 
						|
  } // end of TDBXML constructor
 | 
						|
 | 
						|
TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp)
 | 
						|
  {
 | 
						|
  Docp = tdbp->Docp;
 | 
						|
  Root = tdbp->Root;
 | 
						|
  Curp = tdbp->Curp;
 | 
						|
  DBnode = tdbp->DBnode;
 | 
						|
  TabNode = tdbp->TabNode;
 | 
						|
  RowNode = tdbp->RowNode;
 | 
						|
  ColNode = tdbp->ColNode;
 | 
						|
  Nlist = tdbp->Nlist;
 | 
						|
  Clist = tdbp->Clist;
 | 
						|
  To_Xb = tdbp->To_Xb;
 | 
						|
  Colp = tdbp->Colp;
 | 
						|
  Xfile = tdbp->Xfile;
 | 
						|
  Enc = tdbp->Enc;
 | 
						|
  Tabname = tdbp->Tabname;
 | 
						|
  Rowname = tdbp->Rowname;
 | 
						|
  Colname = tdbp->Colname;
 | 
						|
  Mulnode = tdbp->Mulnode;
 | 
						|
  XmlDB = tdbp->XmlDB;
 | 
						|
  Nslist = tdbp->Nslist;
 | 
						|
  DefNs = tdbp->DefNs;
 | 
						|
  Attrib = tdbp->Attrib;
 | 
						|
  Hdattr = tdbp->Hdattr;
 | 
						|
	Entry = tdbp->Entry;
 | 
						|
	Coltype = tdbp->Coltype;
 | 
						|
  Limit = tdbp->Limit;
 | 
						|
  Xpand = tdbp->Xpand;
 | 
						|
	Zipped = tdbp->Zipped;
 | 
						|
	Mulentries = tdbp->Mulentries;
 | 
						|
	Changed = tdbp->Changed;
 | 
						|
  Checked = tdbp->Checked;
 | 
						|
  NextSame = tdbp->NextSame;
 | 
						|
  NewRow = tdbp->NewRow;
 | 
						|
  Hasnod = tdbp->Hasnod;
 | 
						|
  Write = tdbp->Write;
 | 
						|
  Void = tdbp->Void;
 | 
						|
  Usedom = tdbp->Usedom;
 | 
						|
  Header = tdbp->Header;
 | 
						|
  Multiple = tdbp->Multiple;
 | 
						|
  Nrow = tdbp->Nrow;
 | 
						|
  Irow = tdbp->Irow;
 | 
						|
  Nsub = tdbp->Nsub;
 | 
						|
  N = tdbp->N;
 | 
						|
  } // end of TDBXML copy constructor
 | 
						|
 | 
						|
// Used for update
 | 
						|
PTDB TDBXML::Clone(PTABS t)
 | 
						|
  {
 | 
						|
  PTDB    tp;
 | 
						|
  PXMLCOL cp1, cp2;
 | 
						|
  PGLOBAL g = t->G;
 | 
						|
 | 
						|
  tp = new(g) TDBXML(this);
 | 
						|
 | 
						|
  for (cp1 = (PXMLCOL)Columns; cp1; cp1 = (PXMLCOL)cp1->GetNext()) {
 | 
						|
    cp2 = new(g) XMLCOL(cp1, tp);  // Make a copy
 | 
						|
    NewPointer(t, cp1, cp2);
 | 
						|
    } // endfor cp1
 | 
						|
 | 
						|
  return tp;
 | 
						|
  } // end of Clone
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Must not be in tabxml.h because of OEM tables                      */
 | 
						|
/***********************************************************************/
 | 
						|
const CHARSET_INFO *TDBXML::data_charset()
 | 
						|
{
 | 
						|
	return &my_charset_utf8mb3_general_ci;
 | 
						|
}	// end of data_charset
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Allocate XML column description block.                             */
 | 
						|
/***********************************************************************/
 | 
						|
PCOL TDBXML::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
 | 
						|
  {
 | 
						|
  if (trace(1))
 | 
						|
    htrc("TDBXML: MakeCol %s n=%d\n", (cdp) ? cdp->GetName() : "<null>", n);
 | 
						|
 | 
						|
  return new(g) XMLCOL(cdp, this, cprec, n);
 | 
						|
  } // end of MakeCol
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  InsertSpecialColumn: Put a special column ahead of the column list.*/
 | 
						|
/***********************************************************************/
 | 
						|
PCOL TDBXML::InsertSpecialColumn(PCOL colp)
 | 
						|
  {
 | 
						|
  if (!colp->IsSpecial())
 | 
						|
    return NULL;
 | 
						|
 | 
						|
//if (Xpand && ((SPCBLK*)colp)->GetRnm())
 | 
						|
//  colp->SetKey(0);               // Rownum is no more a key
 | 
						|
 | 
						|
  colp->SetNext(Columns);
 | 
						|
  Columns = colp;
 | 
						|
  return colp;
 | 
						|
  } // end of InsertSpecialColumn
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  LoadTableFile: Load and parse an XML file.                         */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
 | 
						|
  {
 | 
						|
  int     rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2;
 | 
						|
  PFBLOCK fp = NULL;
 | 
						|
  PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
 | 
						|
 | 
						|
  if (Docp)
 | 
						|
    return rc;               // Already done
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    htrc("TDBXML: loading %s\n", filename);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Firstly we check whether this file have been already loaded.     */
 | 
						|
  /*********************************************************************/
 | 
						|
  if ((Mode == MODE_READ || Mode == MODE_ANY) && !Zipped) 
 | 
						|
    for (fp = dup->Openlist; fp; fp = fp->Next)
 | 
						|
      if (fp->Type == type && fp->Length && fp->Count)
 | 
						|
        if (!stricmp(fp->Fname, filename))
 | 
						|
          break;
 | 
						|
 | 
						|
  if (fp) {
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  File already loaded. Just increment use count and get pointer. */
 | 
						|
    /*******************************************************************/
 | 
						|
    fp->Count++;
 | 
						|
    Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc, fp)
 | 
						|
                    : GetLibxmlDoc(g, Nslist, DefNs, Enc, fp);
 | 
						|
  } else {
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Parse the XML file.                                            */
 | 
						|
    /*******************************************************************/
 | 
						|
    if (!(Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc)
 | 
						|
                          : GetLibxmlDoc(g, Nslist, DefNs, Enc)))
 | 
						|
      return RC_FX;
 | 
						|
 | 
						|
    // Initialize the implementation
 | 
						|
    if (Docp->Initialize(g, Entry, Zipped)) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(INIT_FAILED), (Usedom) ? "DOM" : "libxml2");
 | 
						|
      return RC_FX;
 | 
						|
      } // endif init
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      htrc("TDBXML: parsing %s rc=%d\n", filename, rc);
 | 
						|
 | 
						|
    // Parse the XML file
 | 
						|
    if (Docp->ParseFile(g, filename)) {
 | 
						|
      // Does the file exist?
 | 
						|
      int h= global_open(g, MSGID_NONE, filename, _O_RDONLY);
 | 
						|
 | 
						|
      if (h != -1) {
 | 
						|
        rc = (!_filelength(h)) ? RC_EF : RC_INFO;
 | 
						|
        close(h);
 | 
						|
      } else
 | 
						|
        rc = (errno == ENOENT) ? RC_NF : RC_INFO;
 | 
						|
 | 
						|
      // Cannot make a Xblock until document is made
 | 
						|
      return rc;
 | 
						|
      } // endif Docp
 | 
						|
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Link a Xblock. This make possible to reuse already opened docs */
 | 
						|
    /*  and also to automatically close them in case of error g->jump. */
 | 
						|
    /*******************************************************************/
 | 
						|
    fp = Docp->LinkXblock(g, Mode, rc, filename);
 | 
						|
  } // endif xp
 | 
						|
 | 
						|
  To_Xb = fp;                                  // Useful when closing
 | 
						|
  return rc;
 | 
						|
  } // end of LoadTableFile
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Initialize the processing of the XML file.                         */
 | 
						|
/*  Note: this function can be called several times, eventally before  */
 | 
						|
/*  the columns are known (from TBL for instance)                      */
 | 
						|
/***********************************************************************/
 | 
						|
bool TDBXML::Initialize(PGLOBAL g)
 | 
						|
  {
 | 
						|
  int     rc;
 | 
						|
  PXMLCOL colp;
 | 
						|
 | 
						|
  if (Void)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if (Columns) {
 | 
						|
    // Allocate the buffers that will contain node values
 | 
						|
    for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext())
 | 
						|
			if (!colp->IsSpecial()) {            // Not a pseudo column
 | 
						|
				if (!Bufdone && colp->AllocBuf(g, Mode == MODE_INSERT))
 | 
						|
					return true;
 | 
						|
 | 
						|
				colp->Nx = colp->Sx = -1;
 | 
						|
			} // endif Special
 | 
						|
 | 
						|
    Bufdone = true;
 | 
						|
    } // endif Bufdone
 | 
						|
 | 
						|
#if !defined(UNIX)
 | 
						|
	if (!Root) try {
 | 
						|
#else
 | 
						|
	if (!Root) {
 | 
						|
#endif
 | 
						|
		char tabpath[64], filename[_MAX_PATH];
 | 
						|
 | 
						|
		//  We used the file name relative to recorded datapath
 | 
						|
		PlugSetPath(filename, Xfile, GetPath());
 | 
						|
 | 
						|
		// Load or re-use the table file
 | 
						|
		rc = LoadTableFile(g, filename);
 | 
						|
 | 
						|
		if (rc == RC_OK) {
 | 
						|
			// Get root node
 | 
						|
			if (!(Root = Docp->GetRoot(g))) {
 | 
						|
				// This should never happen as load should have failed
 | 
						|
				snprintf(g->Message, sizeof(g->Message), MSG(EMPTY_DOC));
 | 
						|
				goto error;
 | 
						|
			} // endif Root
 | 
						|
 | 
						|
		// If tabname is not an Xpath,
 | 
						|
		// construct one that will find it anywhere
 | 
						|
			if (!strchr(Tabname, '/'))
 | 
						|
				snprintf(tabpath, sizeof(tabpath), "//%s", Tabname);
 | 
						|
			else
 | 
						|
				snprintf(tabpath, sizeof(tabpath), "%s", Tabname);
 | 
						|
 | 
						|
			// Evaluate table xpath
 | 
						|
			if ((TabNode = Root->SelectSingleNode(g, tabpath))) {
 | 
						|
				if (TabNode->GetType() != XML_ELEMENT_NODE) {
 | 
						|
					snprintf(g->Message, sizeof(g->Message), MSG(BAD_NODE_TYPE), TabNode->GetType());
 | 
						|
					goto error;
 | 
						|
				} // endif Type
 | 
						|
 | 
						|
			} else if (Mode == MODE_INSERT && XmlDB) {
 | 
						|
				// We are adding a new table to a multi-table file
 | 
						|
 | 
						|
				// If XmlDB is not an Xpath,
 | 
						|
				// construct one that will find it anywhere
 | 
						|
				if (!strchr(XmlDB, '/'))
 | 
						|
					strcat(strcpy(tabpath, "//"), XmlDB);
 | 
						|
				else
 | 
						|
					strcpy(tabpath, XmlDB);
 | 
						|
 | 
						|
				if (!(DBnode = Root->SelectSingleNode(g, tabpath))) {
 | 
						|
					// DB node does not exist yet; we cannot create it
 | 
						|
					// because we don't know where it should be placed
 | 
						|
					snprintf(g->Message, sizeof(g->Message), MSG(MISSING_NODE), XmlDB, Xfile);
 | 
						|
					goto error;
 | 
						|
				} // endif DBnode
 | 
						|
 | 
						|
				if (!(TabNode = DBnode->AddChildNode(g, Tabname))) {
 | 
						|
					snprintf(g->Message, sizeof(g->Message), MSG(FAIL_ADD_NODE), Tabname);
 | 
						|
					goto error;
 | 
						|
				} // endif TabNode
 | 
						|
 | 
						|
				DBnode->AddText(g, "\n");
 | 
						|
			} else {
 | 
						|
				TabNode = Root;              // Try this ?
 | 
						|
				Tabname = TabNode->GetName(g);
 | 
						|
			} // endif's
 | 
						|
 | 
						|
		} else if (rc == RC_NF || rc == RC_EF) {
 | 
						|
			// The XML file does not exist or is void
 | 
						|
			if (Mode == MODE_INSERT) {
 | 
						|
				// New Document
 | 
						|
				char buf[64];
 | 
						|
 | 
						|
				// Create the XML node
 | 
						|
				if (Docp->NewDoc(g, "1.0")) {
 | 
						|
					snprintf(g->Message, sizeof(g->Message), MSG(NEW_DOC_FAILED));
 | 
						|
					goto error;
 | 
						|
				} // endif NewDoc
 | 
						|
 | 
						|
			//  Now we can link the Xblock
 | 
						|
				To_Xb = Docp->LinkXblock(g, Mode, rc, filename);
 | 
						|
 | 
						|
				// Add a CONNECT comment node
 | 
						|
				snprintf(buf, sizeof(buf), " Created by the MariaDB CONNECT Storage Engine");
 | 
						|
				Docp->AddComment(g, buf);
 | 
						|
 | 
						|
				if (XmlDB) {
 | 
						|
					// This is a multi-table file
 | 
						|
					DBnode = Root = Docp->NewRoot(g, XmlDB);
 | 
						|
					DBnode->AddText(g, "\n");
 | 
						|
					TabNode = DBnode->AddChildNode(g, Tabname);
 | 
						|
					DBnode->AddText(g, "\n");
 | 
						|
				} else
 | 
						|
					TabNode = Root = Docp->NewRoot(g, Tabname);
 | 
						|
 | 
						|
				if (TabNode == NULL || Root == NULL) {
 | 
						|
					snprintf(g->Message, sizeof(g->Message), MSG(XML_INIT_ERROR));
 | 
						|
					goto error;
 | 
						|
				} else if (SetTabNode(g))
 | 
						|
					goto error;
 | 
						|
 | 
						|
			} else {
 | 
						|
				snprintf(g->Message, sizeof(g->Message), MSG(FILE_UNFOUND), Xfile);
 | 
						|
 | 
						|
				if (Mode == MODE_READ) {
 | 
						|
					PushWarning(g, this);
 | 
						|
					Void = true;
 | 
						|
				} // endif Mode
 | 
						|
 | 
						|
				goto error;
 | 
						|
			} // endif Mode
 | 
						|
 | 
						|
		} else if (rc == RC_INFO) {
 | 
						|
			// Loading failed
 | 
						|
			snprintf(g->Message, sizeof(g->Message), MSG(LOADING_FAILED), Xfile);
 | 
						|
			goto error;
 | 
						|
		} else // (rc == RC_FX)
 | 
						|
			goto error;
 | 
						|
 | 
						|
		if (!Rowname) {
 | 
						|
			for (PXNODE n = TabNode->GetChild(g); n; n = n->GetNext(g))
 | 
						|
				if (n->GetType() == XML_ELEMENT_NODE) {
 | 
						|
					Rowname = n->GetName(g);
 | 
						|
					break;
 | 
						|
				} // endif Type
 | 
						|
 | 
						|
			if (!Rowname)
 | 
						|
				Rowname = TabNode->GetName(g);
 | 
						|
		} // endif Rowname
 | 
						|
 | 
						|
			// Get row node list
 | 
						|
		if (strcmp(Rowname, Tabname))
 | 
						|
			Nlist = TabNode->SelectNodes(g, Rowname);
 | 
						|
		else
 | 
						|
			Nrow = 1;
 | 
						|
 | 
						|
 | 
						|
		Docp->SetNofree(true);       // For libxml2
 | 
						|
#if defined(_WIN32)
 | 
						|
	} catch (_com_error e) {
 | 
						|
    // We come here if a DOM command threw an error
 | 
						|
    char   buf[128];
 | 
						|
 | 
						|
    rc = WideCharToMultiByte(CP_ACP, 0, e.Description(), -1,
 | 
						|
                             buf, sizeof(buf), NULL, NULL);
 | 
						|
 | 
						|
    if (rc)
 | 
						|
      snprintf(g->Message, sizeof(g->Message), "%s: %s", MSG(COM_ERROR), buf);
 | 
						|
    else
 | 
						|
      snprintf(g->Message, sizeof(g->Message), "%s hr=%x", MSG(COM_ERROR), e.Error());
 | 
						|
 | 
						|
    goto error;
 | 
						|
#endif   // _WIN32
 | 
						|
#if !defined(UNIX)
 | 
						|
  } catch(...) {
 | 
						|
    // Other errors
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(XMLTAB_INIT_ERR));
 | 
						|
    goto error;
 | 
						|
#endif
 | 
						|
  } // end of try-catches
 | 
						|
 | 
						|
  if (Root && Columns && (Multiple || !Nodedone)) {
 | 
						|
    // Allocate class nodes to avoid dynamic allocation
 | 
						|
    for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext())
 | 
						|
      if (!colp->IsSpecial())            // Not a pseudo column
 | 
						|
        colp->AllocNodes(g, Docp);
 | 
						|
 | 
						|
    Nodedone = true;
 | 
						|
    } // endif Nodedone
 | 
						|
 | 
						|
  if (Nrow < 0)
 | 
						|
    Nrow = (Nlist) ? Nlist->GetLength() : 0;
 | 
						|
 
 | 
						|
  // Init is Ok
 | 
						|
  return false;
 | 
						|
 | 
						|
error:
 | 
						|
  if (Docp)
 | 
						|
    Docp->CloseDoc(g, To_Xb);
 | 
						|
 | 
						|
  return !Void;
 | 
						|
} // end of Initialize
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Set TabNode attributes or header.                                  */
 | 
						|
/***********************************************************************/
 | 
						|
bool TDBXML::SetTabNode(PGLOBAL g)
 | 
						|
  {
 | 
						|
  assert(Mode == MODE_INSERT);
 | 
						|
 | 
						|
  if (Attrib)
 | 
						|
    SetNodeAttr(g, Attrib, TabNode);
 | 
						|
 | 
						|
  if (Header) {
 | 
						|
    PCOLDEF cdp;
 | 
						|
    PXNODE  rn, cn;
 | 
						|
 | 
						|
    if (Rowname) {
 | 
						|
      TabNode->AddText(g, "\n\t");
 | 
						|
      rn = TabNode->AddChildNode(g, Rowname, NULL);
 | 
						|
    } else {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(NO_ROW_NODE));
 | 
						|
      return true;
 | 
						|
    } // endif Rowname
 | 
						|
 | 
						|
    if (Hdattr)
 | 
						|
      SetNodeAttr(g, Hdattr, rn);
 | 
						|
 | 
						|
    for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
 | 
						|
      rn->AddText(g, "\n\t\t");
 | 
						|
      cn = rn->AddChildNode(g, "TH", NULL);
 | 
						|
      cn->SetContent(g, (char *)cdp->GetName(), 
 | 
						|
                         strlen(cdp->GetName()) + 1);
 | 
						|
      } // endfor cdp
 | 
						|
 | 
						|
    rn->AddText(g, "\n\t");
 | 
						|
    } // endif ColType
 | 
						|
 | 
						|
  return false;
 | 
						|
  } // end of SetTabNode
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Set attributes of a table or header node.                          */
 | 
						|
/***********************************************************************/
 | 
						|
void TDBXML::SetNodeAttr(PGLOBAL g, char *attr, PXNODE node)
 | 
						|
  {
 | 
						|
  char  *p, *pa, *pn = attr;
 | 
						|
  PXATTR an;
 | 
						|
 | 
						|
  do {
 | 
						|
    if ((p = strchr(pn, '='))) {
 | 
						|
      pa = pn;
 | 
						|
      *p++ = 0;
 | 
						|
  
 | 
						|
      if ((pn =   strchr(p, ';')))
 | 
						|
        *pn++ = 0;
 | 
						|
  
 | 
						|
      an = node->AddProperty(g, pa, NULL);
 | 
						|
      an->SetText(g, p, strlen(p) + 1);
 | 
						|
    } else
 | 
						|
      break;
 | 
						|
 | 
						|
    } while (pn);
 | 
						|
 | 
						|
  } // end of SetNodeAttr
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  XML Cardinality: returns table cardinality in number of rows.      */
 | 
						|
/*  This function can be called with a null argument to test the       */
 | 
						|
/*  availability of Cardinality implementation (1 yes, 0 no).          */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::Cardinality(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (!g)
 | 
						|
    return (Multiple || Xpand || Coltype == 2) ? 0 : 1;
 | 
						|
 | 
						|
  if (Multiple)
 | 
						|
    return 10;
 | 
						|
 | 
						|
  if (Nrow < 0)
 | 
						|
    if (Initialize(g))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  return (Void) ? 0 : Nrow - Header;
 | 
						|
  } // end of Cardinality
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  XML GetMaxSize: returns the number of tables in the database.      */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::GetMaxSize(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (MaxSize < 0) {
 | 
						|
    if (!Multiple)
 | 
						|
      MaxSize = Cardinality(g) * ((Xpand) ? Limit : 1);
 | 
						|
    else
 | 
						|
      MaxSize = 10;
 | 
						|
 | 
						|
    } // endif MaxSize
 | 
						|
 | 
						|
  return MaxSize;
 | 
						|
  } // end of GetMaxSize
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Return the position in the table.                                  */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::GetRecpos(void)
 | 
						|
  {
 | 
						|
  union {
 | 
						|
    uint Rpos;
 | 
						|
    BYTE Spos[4];
 | 
						|
    };
 | 
						|
 | 
						|
  Rpos = htonl(Irow);
 | 
						|
  Spos[0] = (BYTE)Nsub;
 | 
						|
  return Rpos;
 | 
						|
  } // end of GetRecpos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  RowNumber: return the ordinal number of the current row.           */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::RowNumber(PGLOBAL g, bool b)
 | 
						|
  {
 | 
						|
  if (To_Kindex && (Xpand || Coltype == 2) && !b) {
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Don't know how to retrieve RowID for expanded XML tables.      */
 | 
						|
    /*******************************************************************/
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(NO_ROWID_FOR_AM),
 | 
						|
                        GetAmName(g, GetAmType()));
 | 
						|
    return 0;          // Means error
 | 
						|
  } else
 | 
						|
    return (b || !(Xpand || Coltype == 2)) ? Irow - Header + 1 : N;
 | 
						|
 | 
						|
  } // end of RowNumber
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  XML Access Method opening routine.                                 */
 | 
						|
/***********************************************************************/
 | 
						|
bool TDBXML::OpenDB(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (Use == USE_OPEN) {
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Table already open replace it at its beginning.                */
 | 
						|
    /*******************************************************************/
 | 
						|
    if (!To_Kindex) {
 | 
						|
      Irow = Header - 1;
 | 
						|
      Nsub = 0;
 | 
						|
    } else
 | 
						|
      /*****************************************************************/
 | 
						|
      /*  Table is to be accessed through a sorted index table.        */
 | 
						|
      /*****************************************************************/
 | 
						|
      To_Kindex->Reset();
 | 
						|
 | 
						|
    return false;
 | 
						|
    } // endif use
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  OpenDB: initialize the XML file processing.                      */
 | 
						|
  /*********************************************************************/
 | 
						|
  Write = (Mode == MODE_INSERT || Mode == MODE_UPDATE);
 | 
						|
 | 
						|
  if (Initialize(g))
 | 
						|
    return true;
 | 
						|
 | 
						|
  NewRow = (Mode == MODE_INSERT);
 | 
						|
  Nsub = 0;
 | 
						|
  Use = USE_OPEN;       // Do it now in case we are recursively called
 | 
						|
  return false;
 | 
						|
  } // end of OpenDB
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Data Base read routine for XML access method.                      */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::ReadDB(PGLOBAL g)
 | 
						|
  {
 | 
						|
  bool same;
 | 
						|
 | 
						|
  if (Void)
 | 
						|
    return RC_EF;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Now start the pseudo reading process.                            */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (To_Kindex) {
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Reading is by an index table.                                  */
 | 
						|
    /*******************************************************************/
 | 
						|
    union {
 | 
						|
      uint Rpos;
 | 
						|
      BYTE Spos[4];
 | 
						|
      };
 | 
						|
 | 
						|
    int recpos = To_Kindex->Fetch(g);
 | 
						|
 | 
						|
    switch (recpos) {
 | 
						|
      case -1:           // End of file reached
 | 
						|
        return RC_EF;
 | 
						|
      case -2:           // No match for join
 | 
						|
        return RC_NF;
 | 
						|
      case -3:           // Same record as last non null one
 | 
						|
        same = true;
 | 
						|
        return RC_OK;
 | 
						|
      default:
 | 
						|
        Rpos = recpos;
 | 
						|
        Nsub = Spos[0];
 | 
						|
        Spos[0] = 0;
 | 
						|
 | 
						|
        if (Irow != (signed)ntohl(Rpos)) {
 | 
						|
          Irow = ntohl(Rpos);
 | 
						|
          same = false;
 | 
						|
        } else
 | 
						|
          same = true;
 | 
						|
 | 
						|
      } // endswitch recpos
 | 
						|
 | 
						|
  } else {
 | 
						|
    if (trace(1))
 | 
						|
      htrc("TDBXML ReadDB: Irow=%d Nrow=%d\n", Irow, Nrow);
 | 
						|
 | 
						|
    // This is to force the table to be expanded when constructing
 | 
						|
    // an index for which the expand column is not specified.
 | 
						|
    if (Colp && Irow >= Header) {
 | 
						|
      Colp->Eval(g);
 | 
						|
      Colp->Reset();
 | 
						|
      } // endif Colp
 | 
						|
 | 
						|
    if (!NextSame) {
 | 
						|
      if (++Irow == Nrow)
 | 
						|
        return RC_EF;
 | 
						|
 | 
						|
      same = false;
 | 
						|
      Nsub = 0;
 | 
						|
    } else {
 | 
						|
      // Not sure the multiple column read will be called
 | 
						|
      NextSame = false;
 | 
						|
      same = true;
 | 
						|
      Nsub++;
 | 
						|
    } // endif NextSame
 | 
						|
 | 
						|
    N++;                          // RowID
 | 
						|
  } // endif To_Kindex
 | 
						|
 | 
						|
  if (!same) {
 | 
						|
    if (trace(2))
 | 
						|
      htrc("TDBXML ReadDB: Irow=%d RowNode=%p\n", Irow, RowNode);
 | 
						|
 | 
						|
    // Get the new row node
 | 
						|
		if (Nlist) {
 | 
						|
			if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
 | 
						|
				snprintf(g->Message, sizeof(g->Message), MSG(MISSING_ROWNODE), Irow);
 | 
						|
				return RC_FX;
 | 
						|
			} // endif RowNode
 | 
						|
 | 
						|
		} else
 | 
						|
			RowNode = TabNode;
 | 
						|
 | 
						|
    if (Colname && Coltype == 2)
 | 
						|
      Clist = RowNode->SelectNodes(g, Colname, Clist);
 | 
						|
 | 
						|
    } // endif same
 | 
						|
 | 
						|
  return RC_OK;
 | 
						|
  } // end of ReadDB
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  CheckRow: called On Insert and Update. Must create the Row node    */
 | 
						|
/*  if it does not exist (Insert) and update the Clist if called by    */
 | 
						|
/*  a column having an Xpath because it can use an existing node that  */
 | 
						|
/*  was added while inserting or Updating this row.                    */
 | 
						|
/***********************************************************************/
 | 
						|
bool TDBXML::CheckRow(PGLOBAL g, bool b)
 | 
						|
  {
 | 
						|
  if (NewRow && Mode == MODE_INSERT)
 | 
						|
  {
 | 
						|
    if (Rowname) {
 | 
						|
      TabNode->AddText(g, "\n\t");
 | 
						|
      RowNode = TabNode->AddChildNode(g, Rowname, RowNode);
 | 
						|
    } else {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(NO_ROW_NODE));
 | 
						|
      return true;
 | 
						|
    } // endif Rowname
 | 
						|
  }
 | 
						|
 | 
						|
  if (Colname && (NewRow || b))
 | 
						|
    Clist = RowNode->SelectNodes(g, Colname, Clist);
 | 
						|
 | 
						|
  return NewRow = false;
 | 
						|
  } // end of CheckRow
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  WriteDB: Data Base write routine for XDB access methods.           */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::WriteDB(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (Mode == MODE_INSERT) {
 | 
						|
    if (Hasnod)
 | 
						|
      RowNode->AddText(g, "\n\t");
 | 
						|
 | 
						|
    NewRow = true;
 | 
						|
    } // endif Mode
 | 
						|
 | 
						|
  // Something was changed in the document
 | 
						|
  Changed = true;
 | 
						|
  return RC_OK;
 | 
						|
  } // end of WriteDB
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Data Base delete line routine for XDB access methods.              */
 | 
						|
/***********************************************************************/
 | 
						|
int TDBXML::DeleteDB(PGLOBAL g, int irc)
 | 
						|
  {
 | 
						|
	// TODO: Handle null Nlist
 | 
						|
  if (irc == RC_FX) {
 | 
						|
    // Delete all rows
 | 
						|
    for (Irow = 0; Irow < Nrow; Irow++)
 | 
						|
      if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(MISSING_ROWNODE), Irow);
 | 
						|
        return RC_FX;
 | 
						|
      } else {
 | 
						|
        TabNode->DeleteChild(g, RowNode);
 | 
						|
 | 
						|
        if (Nlist->DropItem(g, Irow))
 | 
						|
          return RC_FX;
 | 
						|
 | 
						|
      } // endif RowNode
 | 
						|
 | 
						|
    Changed = true;
 | 
						|
  } else if (irc != RC_EF) {
 | 
						|
    TabNode->DeleteChild(g, RowNode);
 | 
						|
 | 
						|
    if (Nlist->DropItem(g, Irow))
 | 
						|
      return RC_FX;
 | 
						|
 | 
						|
    Changed = true;
 | 
						|
  } // endif's irc
 | 
						|
 | 
						|
  return RC_OK;
 | 
						|
  } // end of DeleteDB
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Data Base close routine for XDB access methods.                    */
 | 
						|
/***********************************************************************/
 | 
						|
void TDBXML::CloseDB(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (Docp) {
 | 
						|
    if (Changed) {
 | 
						|
      char filename[_MAX_PATH];
 | 
						|
 | 
						|
      // We used the file name relative to recorded datapath
 | 
						|
      PlugSetPath(filename, Xfile, GetPath());
 | 
						|
 | 
						|
      if (Mode == MODE_INSERT)
 | 
						|
        TabNode->AddText(g, "\n");
 | 
						|
 | 
						|
      // Save the modified document
 | 
						|
      if (Docp->DumpDoc(g, filename)) {
 | 
						|
        PushWarning(g, this);
 | 
						|
        Docp->CloseDoc(g, To_Xb);
 | 
						|
 | 
						|
        // This causes a crash in Diagnostics_area::set_error_status
 | 
						|
//				throw (int)TYPE_AM_XML;
 | 
						|
			} // endif DumpDoc
 | 
						|
      
 | 
						|
      } // endif Changed
 | 
						|
 | 
						|
    // Free the document and terminate XML processing
 | 
						|
    Docp->CloseDoc(g, To_Xb);
 | 
						|
    } // endif docp
 | 
						|
 | 
						|
  if (Multiple) {
 | 
						|
    // Reset all constants to start a new parse
 | 
						|
    Docp = NULL;
 | 
						|
    Root = NULL;
 | 
						|
    Curp = NULL;
 | 
						|
    DBnode = NULL;
 | 
						|
    TabNode = NULL;
 | 
						|
    RowNode = NULL;
 | 
						|
    ColNode = NULL;
 | 
						|
    Nlist = NULL;
 | 
						|
    Clist = NULL;
 | 
						|
    To_Xb = NULL;
 | 
						|
    Colp = NULL;
 | 
						|
    Changed = false;
 | 
						|
    Checked = false;
 | 
						|
    NextSame = false;
 | 
						|
    NewRow = false;
 | 
						|
    Hasnod = false;
 | 
						|
    Write = false;
 | 
						|
    Nodedone = false;
 | 
						|
    Void = false;
 | 
						|
    Nrow = -1;
 | 
						|
    Irow = Header - 1;
 | 
						|
    Nsub = 0;
 | 
						|
    N = 0;
 | 
						|
    } // endif Multiple
 | 
						|
 | 
						|
  } // end of CloseDB
 | 
						|
 | 
						|
// ------------------------ XMLCOL functions ----------------------------
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  XMLCOL public constructor.                                        */
 | 
						|
/***********************************************************************/
 | 
						|
XMLCOL::XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
 | 
						|
      : COLBLK(cdp, tdbp, i)
 | 
						|
  {
 | 
						|
  if (cprec) {
 | 
						|
    Next = cprec->GetNext();
 | 
						|
    cprec->SetNext(this);
 | 
						|
  } else {
 | 
						|
    Next = tdbp->GetColumns();
 | 
						|
    tdbp->SetColumns(this);
 | 
						|
  } // endif cprec
 | 
						|
 | 
						|
  // Set additional XML access method information for column.
 | 
						|
  Tdbp = (PTDBXML)tdbp;
 | 
						|
  Nl = NULL;
 | 
						|
  Nlx = NULL;
 | 
						|
  ColNode = NULL;
 | 
						|
  ValNode = NULL;
 | 
						|
  Cxnp = NULL;
 | 
						|
  Vxnp = NULL;
 | 
						|
  Vxap = NULL;
 | 
						|
  AttNode = NULL;
 | 
						|
  Nodes = NULL;
 | 
						|
  Nod = 0;
 | 
						|
  Inod = -1;
 | 
						|
  Mul = false;
 | 
						|
  Checked = false;
 | 
						|
  Xname = cdp->GetFmt();
 | 
						|
  Long = cdp->GetLong();
 | 
						|
  Rank = cdp->GetOffset();
 | 
						|
  Type = Tdbp->Coltype;
 | 
						|
  Nx = -1;
 | 
						|
  Sx = -1;
 | 
						|
  N = 0;
 | 
						|
  Valbuf = NULL;
 | 
						|
  To_Val = NULL;
 | 
						|
  } // end of XMLCOL constructor
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  XMLCOL constructor used for copying columns.                       */
 | 
						|
/*  tdbp is the pointer to the new table descriptor.                   */
 | 
						|
/***********************************************************************/
 | 
						|
XMLCOL::XMLCOL(XMLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
 | 
						|
  {
 | 
						|
  Tdbp = col1->Tdbp;
 | 
						|
  Nl = col1->Nl;
 | 
						|
  Nlx = col1->Nlx;
 | 
						|
  ColNode = col1->ColNode;
 | 
						|
  ValNode = col1->ValNode;
 | 
						|
  Cxnp = col1->Cxnp;
 | 
						|
  Vxnp = col1->Vxnp;
 | 
						|
  Vxap = col1->Vxap;
 | 
						|
  AttNode = col1->AttNode;
 | 
						|
  Nodes = col1->Nodes;
 | 
						|
  Nod = col1->Nod;
 | 
						|
  Inod = col1->Inod;
 | 
						|
  Mul = col1->Mul;
 | 
						|
  Checked = col1->Checked;
 | 
						|
  Xname = col1->Xname;
 | 
						|
  Valbuf = col1->Valbuf;
 | 
						|
  Long = col1->Long;
 | 
						|
  Rank = col1->Rank;
 | 
						|
  Nx = col1->Nx;
 | 
						|
  Sx = col1->Sx;
 | 
						|
  N = col1->N;
 | 
						|
  Type = col1->Type;
 | 
						|
  To_Val = col1->To_Val;
 | 
						|
  } // end of XMLCOL copy constructor
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Allocate a buffer of the proper size.                              */
 | 
						|
/***********************************************************************/
 | 
						|
bool XMLCOL::AllocBuf(PGLOBAL g, bool mode)
 | 
						|
  {
 | 
						|
  if (Valbuf)
 | 
						|
    return false;                       // Already done
 | 
						|
 | 
						|
  return ParseXpath(g, mode);
 | 
						|
  } // end of AllocBuf
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Parse the eventual passed Xpath information.                       */
 | 
						|
/*  This information can be specified in the Xpath (or Fieldfmt)       */
 | 
						|
/*  column option when creating the table. It permits to indicate the  */
 | 
						|
/*  position of the node corresponding to that column in a Xpath-like  */
 | 
						|
/*  language (but not a truly compliant one).                          */
 | 
						|
/***********************************************************************/
 | 
						|
bool XMLCOL::ParseXpath(PGLOBAL g, bool mode)
 | 
						|
  {
 | 
						|
  char *p, *p2, *pbuf = NULL;
 | 
						|
  int   i, n = 1, len = strlen(Name);
 | 
						|
 | 
						|
  len += ((Tdbp->Colname) ? strlen(Tdbp->Colname) : 0);
 | 
						|
  len += ((Xname) ? strlen(Xname) : 0);
 | 
						|
  pbuf = (char*)PlugSubAlloc(g, NULL, len + 3);
 | 
						|
  *pbuf = '\0';
 | 
						|
 | 
						|
  if (!mode)
 | 
						|
    // Take care of an eventual extra column node a la html
 | 
						|
    if (Tdbp->Colname) {
 | 
						|
      char *p = strstr(Tdbp->Colname, "%d");
 | 
						|
      if (p)
 | 
						|
        snprintf(pbuf, len + 3, "%.*s%d%s/", (int) (p - Tdbp->Colname), Tdbp->Colname,
 | 
						|
            Rank + (Tdbp->Usedom ? 0 : 1), p + 2);
 | 
						|
      else
 | 
						|
        snprintf(pbuf, len + 3, "%s/", Tdbp->Colname);
 | 
						|
    } // endif Colname
 | 
						|
 | 
						|
  if (Xname) {
 | 
						|
    if (Type == 2) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_COL_XPATH), Name, Tdbp->Name);
 | 
						|
      return true;
 | 
						|
    } else
 | 
						|
      strcat(pbuf, Xname);
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      htrc("XMLCOL: pbuf=%s\n", pbuf);
 | 
						|
 | 
						|
    // For Update or Insert the Xpath must be analyzed
 | 
						|
    if (mode) {
 | 
						|
      for (i = 0, p = pbuf; (p = strchr(p, '/')); i++, p++)
 | 
						|
        Nod++;                       // One path node found
 | 
						|
 | 
						|
      if (Nod)
 | 
						|
        Nodes = (char**)PlugSubAlloc(g, NULL, Nod * sizeof(char*));
 | 
						|
 | 
						|
      } // endif mode
 | 
						|
 | 
						|
    // Analyze the Xpath for this column
 | 
						|
    for (i = 0, p = pbuf; (p2 = strchr(p, '/')); i++, p = p2 + 1) {
 | 
						|
      if (Tdbp->Mulnode && !strncmp(p, Tdbp->Mulnode, p2 - p))
 | 
						|
      {
 | 
						|
        if (!Tdbp->Xpand && mode) {
 | 
						|
          snprintf(g->Message, sizeof(g->Message), MSG(CONCAT_SUBNODE));
 | 
						|
          return true;
 | 
						|
        } else
 | 
						|
          Inod = i;                  // Index of multiple node
 | 
						|
      }
 | 
						|
 | 
						|
      if (mode) {
 | 
						|
        // For Update or Insert the Xpath must be explicit
 | 
						|
        if (strchr("@/.*", *p)) {
 | 
						|
          snprintf(g->Message, sizeof(g->Message), MSG(XPATH_NOT_SUPP), Name);
 | 
						|
          return true;
 | 
						|
        } else
 | 
						|
          Nodes[i] = p;
 | 
						|
 | 
						|
        *p2 = '\0';
 | 
						|
        } // endif mode
 | 
						|
 | 
						|
      } // endfor i, p
 | 
						|
 | 
						|
    if (*p == '/' || *p == '.') {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(XPATH_NOT_SUPP), Name);
 | 
						|
      return true;
 | 
						|
    } else if (*p == '@') {
 | 
						|
      p++;                           // Remove the @ if mode
 | 
						|
      Type = 0;                      // Column is an attribute
 | 
						|
    } else
 | 
						|
      Type = 1;                      // Column is a node
 | 
						|
 | 
						|
    if (!*p)
 | 
						|
      strcpy(p, Name);               // Xname is column name
 | 
						|
 | 
						|
    if (Type && Tdbp->Mulnode && !strcmp(p, Tdbp->Mulnode))
 | 
						|
      Inod = Nod;                    // Index of multiple node
 | 
						|
 | 
						|
    if (mode)                        // Prepare Xname
 | 
						|
      pbuf = p;
 | 
						|
 | 
						|
  } else if (Type == 2) {
 | 
						|
    // HTML like table, columns are retrieved by position
 | 
						|
    new(this) XPOSCOL(Value);        // Change the class of this column
 | 
						|
    Inod = -1;
 | 
						|
  } else if (Type == 0 && !mode) {
 | 
						|
    strcat(strcat(pbuf, "@"), Name);
 | 
						|
  } else {                           // Type == 1
 | 
						|
    if (Tdbp->Mulnode && !strcmp(Name, Tdbp->Mulnode))
 | 
						|
      Inod = 0;                      // Nod
 | 
						|
 | 
						|
    strcat(pbuf, Name);
 | 
						|
  } // endif,s
 | 
						|
 | 
						|
  if (Inod >= 0) {
 | 
						|
    Tdbp->Colp = this;               // To force expand
 | 
						|
 | 
						|
    if (Tdbp->Xpand)
 | 
						|
      n = Tdbp->Limit;
 | 
						|
 | 
						|
    new(this) XMULCOL(Value);        // Change the class of this column
 | 
						|
    } // endif Inod
 | 
						|
 | 
						|
  Valbuf = (char*)PlugSubAlloc(g, NULL, n * (Long + 1));
 | 
						|
 | 
						|
  for (i = 0; i < n; i++)
 | 
						|
    Valbuf[Long + (i * (Long + 1))] = '\0';
 | 
						|
 | 
						|
  if (Type || Nod)
 | 
						|
    Tdbp->Hasnod = true;
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    htrc("XMLCOL: Xname=%s\n", pbuf);
 | 
						|
 | 
						|
  // Save the calculated Xpath
 | 
						|
  Xname = pbuf;
 | 
						|
  return false;
 | 
						|
  } // end of ParseXpath
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  SetBuffer: prepare a column block for write operation.             */
 | 
						|
/***********************************************************************/
 | 
						|
bool XMLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
 | 
						|
  {
 | 
						|
  if (!(To_Val = value)) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(VALUE_ERROR), Name);
 | 
						|
    return true;
 | 
						|
  } else if (Buf_Type == value->GetType()) {
 | 
						|
    // Values are of the (good) column type
 | 
						|
    if (Buf_Type == TYPE_DATE) {
 | 
						|
      // If any of the date values is formatted
 | 
						|
      // output format must be set for the receiving table
 | 
						|
      if (GetDomain() || ((DTVAL *)value)->IsFormatted())
 | 
						|
        goto newval;          // This will make a new value;
 | 
						|
 | 
						|
    } else if (Buf_Type == TYPE_DOUBLE)
 | 
						|
      // Float values must be written with the correct (column) precision
 | 
						|
      // Note: maybe this should be forced by ShowValue instead of this ?
 | 
						|
      value->SetPrec(GetScale());
 | 
						|
 | 
						|
    Value = value;            // Directly access the external value
 | 
						|
  } else {
 | 
						|
    // Values are not of the (good) column type
 | 
						|
    if (check) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(TYPE_VALUE_ERR), Name,
 | 
						|
              GetTypeName(Buf_Type), GetTypeName(value->GetType()));
 | 
						|
      return true;
 | 
						|
      } // endif check
 | 
						|
 | 
						|
 newval:
 | 
						|
    if (InitValue(g))         // Allocate the matching value block
 | 
						|
      return true;
 | 
						|
 | 
						|
  } // endif's Value, Buf_Type
 | 
						|
 | 
						|
  // Because Colblk's have been made from a copy of the original TDB in
 | 
						|
  // case of Update, we must reset them to point to the original one.
 | 
						|
  if (To_Tdb->GetOrig()) {
 | 
						|
    To_Tdb = (PTDB)To_Tdb->GetOrig();
 | 
						|
    Tdbp = (PTDBXML)To_Tdb;   // Specific of XMLCOL
 | 
						|
 | 
						|
    // Allocate the XML buffer
 | 
						|
    if (AllocBuf(g, true))      // In Write mode
 | 
						|
      return true;
 | 
						|
 | 
						|
    } // endif GetOrig
 | 
						|
 | 
						|
  // Set the Column
 | 
						|
  Status = (ok) ? BUF_EMPTY : BUF_NO;
 | 
						|
  return false;
 | 
						|
  } // end of SetBuffer
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Alloc the nodes that will be used during the whole process.        */
 | 
						|
/***********************************************************************/
 | 
						|
void XMLCOL::AllocNodes(PGLOBAL g, PXDOC dp)
 | 
						|
{
 | 
						|
  Cxnp = dp->NewPnode(g);
 | 
						|
  Vxnp = dp->NewPnode(g);
 | 
						|
  Vxap = dp->NewPattr(g);
 | 
						|
} // end of AllocNodes
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  ReadColumn: what this routine does is to access the column node    */
 | 
						|
/*  from the corresponding table, extract from it the node text and    */
 | 
						|
/*  convert it to the column type.                                     */
 | 
						|
/***********************************************************************/
 | 
						|
void XMLCOL::ReadColumn(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (Nx == Tdbp->Irow)
 | 
						|
    return;                         // Same row than the last read
 | 
						|
 | 
						|
  ValNode = Tdbp->RowNode->SelectSingleNode(g, Xname, Vxnp);
 | 
						|
 | 
						|
  if (ValNode) {
 | 
						|
    if (ValNode->GetType() != XML_ELEMENT_NODE &&
 | 
						|
        ValNode->GetType() != XML_ATTRIBUTE_NODE) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_VALNODE), ValNode->GetType(), Name);
 | 
						|
			throw (int)TYPE_AM_XML;
 | 
						|
		} // endif type
 | 
						|
 | 
						|
    // Get the Xname value from the XML file
 | 
						|
    switch (ValNode->GetContent(g, Valbuf, Long + 1)) {
 | 
						|
      case RC_OK:
 | 
						|
        break;
 | 
						|
      case RC_INFO:
 | 
						|
        PushWarning(g, Tdbp);
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
				throw (int)TYPE_AM_XML;
 | 
						|
		} // endswitch
 | 
						|
 | 
						|
    Value->SetValue_psz(Valbuf);
 | 
						|
  } else {
 | 
						|
    if (Nullable)
 | 
						|
      Value->SetNull(true);
 | 
						|
 | 
						|
    Value->Reset();              // Null value
 | 
						|
  } // endif ValNode
 | 
						|
 | 
						|
  Nx = Tdbp->Irow;
 | 
						|
  } // end of ReadColumn
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  WriteColumn: what this routine does is to access the last row of   */
 | 
						|
/*  the corresponding table, and rewrite the content corresponding     */
 | 
						|
/*  to this column node from the column buffer and type.               */
 | 
						|
/***********************************************************************/
 | 
						|
void XMLCOL::WriteColumn(PGLOBAL g)
 | 
						|
  {
 | 
						|
  char  *p, buf[16];
 | 
						|
  int    done = 0;
 | 
						|
  int   i, n, k = 0;
 | 
						|
  PXNODE TopNode = NULL;
 | 
						|
 | 
						|
  if (trace(2))
 | 
						|
    htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
 | 
						|
          Name, Tdbp->GetTdb_No(), ColUse, Status);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Check whether this node must be written.                         */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Value != To_Val)
 | 
						|
    Value->SetValue_pval(To_Val, false);    // Convert the updated value
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  If a check pass was done while updating, all node contruction    */
 | 
						|
  /*  has been already one.                                            */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Status && Tdbp->Checked && !Value->IsNull()) {
 | 
						|
    assert (ColNode != NULL);
 | 
						|
    assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL);
 | 
						|
    goto fin;
 | 
						|
    } // endif Checked
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  On Insert, a Row node must be created for each row;              */
 | 
						|
  /*  For columns having an Xpath, the Clist must be updated.          */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Tdbp->CheckRow(g, Nod || Tdbp->Colname))
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Null values are represented by no node.                          */
 | 
						|
  /*********************************************************************/
 | 
						|
	if (Value->IsNull())
 | 
						|
    return;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Find the column and value nodes to update or insert.             */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Tdbp->Clist) {
 | 
						|
    n =  Tdbp->Clist->GetLength();
 | 
						|
    ColNode = NULL;
 | 
						|
  } else {
 | 
						|
    n = 1;
 | 
						|
    ColNode = Tdbp->RowNode->Clone(g, ColNode);
 | 
						|
  } // endif Clist
 | 
						|
 | 
						|
  ValNode = NULL;
 | 
						|
 | 
						|
  for (i = 0; i < n; i++) {
 | 
						|
    if (Tdbp->Clist)
 | 
						|
      ColNode = Tdbp->Clist->GetItem(g, i, Cxnp);
 | 
						|
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Check whether an Xpath was provided to go to the column node.  */
 | 
						|
    /*******************************************************************/
 | 
						|
    for (k = 0; k < Nod; k++)
 | 
						|
      if ((ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp)))
 | 
						|
        TopNode = ColNode;
 | 
						|
      else
 | 
						|
        break;
 | 
						|
 | 
						|
    if (ColNode)
 | 
						|
    {
 | 
						|
      if (Type)
 | 
						|
        ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp);
 | 
						|
      else
 | 
						|
        AttNode = ColNode->GetAttribute(g, Xname, Vxap);
 | 
						|
    }
 | 
						|
 | 
						|
    if (TopNode || ValNode || AttNode)
 | 
						|
      break;                      // We found the good column
 | 
						|
    else if (Tdbp->Clist)
 | 
						|
      ColNode = NULL;
 | 
						|
 | 
						|
    // refresh CList in case its Listp was freed in SelectSingleNode above
 | 
						|
    if (Tdbp->Clist)
 | 
						|
      Tdbp->RowNode->SelectNodes(g, Tdbp->Colname, Tdbp->Clist);
 | 
						|
    } // endfor i
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Create missing nodes.                                            */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (ColNode == NULL) {
 | 
						|
    if (TopNode == NULL)
 | 
						|
    {
 | 
						|
      if (Tdbp->Clist) {
 | 
						|
        Tdbp->RowNode->AddText(g, "\n\t\t");
 | 
						|
        ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname);
 | 
						|
        done = 2;
 | 
						|
        TopNode = ColNode;
 | 
						|
      } else
 | 
						|
        TopNode = Tdbp->RowNode;
 | 
						|
    }
 | 
						|
    for (; k < Nod && TopNode; k++) {
 | 
						|
      if (!done) {
 | 
						|
        TopNode->AddText(g, "\n\t\t");
 | 
						|
        done = 1;
 | 
						|
        } // endif done
 | 
						|
 | 
						|
      ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp);
 | 
						|
      TopNode = ColNode;
 | 
						|
      } // endfor k
 | 
						|
 | 
						|
    if (ColNode == NULL) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(COL_ALLOC_ERR));
 | 
						|
			throw (int)TYPE_AM_XML;
 | 
						|
		} // endif ColNode
 | 
						|
 | 
						|
    } // endif ColNode
 | 
						|
 | 
						|
  if (Type == 1) {
 | 
						|
    if (ValNode == NULL) {
 | 
						|
      if (done < 2)
 | 
						|
        ColNode->AddText(g, "\n\t\t");
 | 
						|
 | 
						|
      ValNode = ColNode->AddChildNode(g, Xname, Vxnp);
 | 
						|
      } // endif ValNode
 | 
						|
 | 
						|
  } else // (Type == 0)
 | 
						|
    if (AttNode == NULL)
 | 
						|
      AttNode = ColNode->AddProperty(g, Xname, Vxap);
 | 
						|
 | 
						|
  if (ValNode == NULL && AttNode == NULL) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(VAL_ALLOC_ERR));
 | 
						|
    throw (int)TYPE_AM_XML;
 | 
						|
    } // endif ValNode
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Get the string representation of Value according to column type. */
 | 
						|
  /*********************************************************************/
 | 
						|
  p = Value->GetCharString(buf);
 | 
						|
 | 
						|
  if (strlen(p) > (unsigned)Long) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, Long);
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
	} else
 | 
						|
    strcpy(Valbuf, p);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Updating must be done only when not in checking pass.            */
 | 
						|
  /*********************************************************************/
 | 
						|
 fin:
 | 
						|
  if (Status) {
 | 
						|
    if (Type) {
 | 
						|
      ValNode->SetContent(g, Valbuf, Long);
 | 
						|
    } else
 | 
						|
      AttNode->SetText(g, Valbuf, Long);
 | 
						|
 | 
						|
    } // endif Status
 | 
						|
 | 
						|
  } // end of WriteColumn
 | 
						|
 | 
						|
// ------------------------ XMULCOL functions ---------------------------
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  ReadColumn: what this routine does is to access the column node    */
 | 
						|
/*  from the corresponding table, extract from it the node text and    */
 | 
						|
/*  convert it to the column type.                                     */
 | 
						|
/***********************************************************************/
 | 
						|
void XMULCOL::ReadColumn(PGLOBAL g)
 | 
						|
  {
 | 
						|
  char *p;
 | 
						|
  int   i, len;
 | 
						|
  bool  b = Tdbp->Xpand;
 | 
						|
 | 
						|
  if (Nx != Tdbp->Irow) {                     // New row
 | 
						|
    Nl = Tdbp->RowNode->SelectNodes(g, Xname, Nl);
 | 
						|
 | 
						|
    if ((N = Nl->GetLength())) {
 | 
						|
      *(p = Valbuf) = '\0';
 | 
						|
      len = Long;
 | 
						|
 | 
						|
      if (N > Tdbp->Limit) {
 | 
						|
        N = Tdbp->Limit;
 | 
						|
        snprintf(g->Message, sizeof(g->Message), "Multiple values limited to %d", Tdbp->Limit);
 | 
						|
        PushWarning(g, Tdbp);
 | 
						|
        } // endif N
 | 
						|
 | 
						|
      for (i = 0; i < N; i++) {
 | 
						|
        ValNode = Nl->GetItem(g, i, Vxnp);
 | 
						|
 | 
						|
        if (ValNode->GetType() != XML_ELEMENT_NODE &&
 | 
						|
            ValNode->GetType() != XML_ATTRIBUTE_NODE) {
 | 
						|
          snprintf(g->Message, sizeof(g->Message), MSG(BAD_VALNODE), ValNode->GetType(), Name);
 | 
						|
					throw (int)TYPE_AM_XML;
 | 
						|
				} // endif type
 | 
						|
 | 
						|
        // Get the Xname value from the XML file
 | 
						|
        switch (ValNode->GetContent(g, p, (b ? Long : len))) {
 | 
						|
          case RC_OK:
 | 
						|
            break;
 | 
						|
          case RC_INFO:
 | 
						|
            PushWarning(g, Tdbp);
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            throw (int)TYPE_AM_XML;
 | 
						|
          } // endswitch
 | 
						|
 | 
						|
        if (!b) {
 | 
						|
          // Concatenate all values
 | 
						|
          if (N - i > 1)
 | 
						|
            strncat(Valbuf, ", ", len - strlen(p));
 | 
						|
 | 
						|
          if ((len -= strlen(p)) <= 0)
 | 
						|
            break;
 | 
						|
 | 
						|
          p += strlen(p);
 | 
						|
        } else            // Xpand
 | 
						|
          p += (Long + 1);
 | 
						|
 | 
						|
        } // endfor i
 | 
						|
 | 
						|
      Value->SetValue_psz(Valbuf);
 | 
						|
    } else {
 | 
						|
      if (Nullable)
 | 
						|
        Value->SetNull(true);
 | 
						|
 | 
						|
      Value->Reset();              // Null value
 | 
						|
    } // endif ValNode
 | 
						|
 | 
						|
  } else if (Sx == Tdbp->Nsub)
 | 
						|
    return;                        // Same row
 | 
						|
  else                             // Expanded value
 | 
						|
    Value->SetValue_psz(Valbuf + (Tdbp->Nsub * (Long + 1)));
 | 
						|
 | 
						|
  Nx = Tdbp->Irow;
 | 
						|
  Sx = Tdbp->Nsub;
 | 
						|
  Tdbp->NextSame = (Tdbp->Xpand && N - Sx > 1);
 | 
						|
  } // end of ReadColumn
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  WriteColumn: what this routine does is to access the last line     */
 | 
						|
/*  read from the corresponding table, and rewrite the field           */
 | 
						|
/*  corresponding to this column from the column buffer and type.      */
 | 
						|
/***********************************************************************/
 | 
						|
void XMULCOL::WriteColumn(PGLOBAL g)
 | 
						|
  {
 | 
						|
  char  *p, buf[16];
 | 
						|
  int    done = 0;
 | 
						|
  int   i, n, len, k = 0;
 | 
						|
  PXNODE TopNode = NULL;
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
 | 
						|
          Name, Tdbp->GetTdb_No(), ColUse, Status);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Check whether this node must be written.                         */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Value != To_Val)
 | 
						|
    Value->SetValue_pval(To_Val, false);    // Convert the updated value
 | 
						|
 | 
						|
  if (Value->IsNull())
 | 
						|
    return;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  If a check pass was done while updating, all node contruction    */
 | 
						|
  /*  has been already one.                                            */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Status && Tdbp->Checked) {
 | 
						|
    assert (ColNode);
 | 
						|
    assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL);
 | 
						|
    goto fin;
 | 
						|
    } // endif Checked
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  On Insert, a Row node must be created for each row;              */
 | 
						|
  /*  For columns having an Xpath, the Clist must be updated.          */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Tdbp->CheckRow(g, Nod))
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Find the column and value nodes to update or insert.             */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Tdbp->Clist) {
 | 
						|
    n =  Tdbp->Clist->GetLength();
 | 
						|
    ColNode = NULL;
 | 
						|
  } else {
 | 
						|
    n = 1;
 | 
						|
    ColNode = Tdbp->RowNode->Clone(g, ColNode);
 | 
						|
  } // endif Clist
 | 
						|
 | 
						|
  ValNode = NULL;
 | 
						|
 | 
						|
  for (i = 0; i < n; i++) {
 | 
						|
    if (Tdbp->Clist)
 | 
						|
      ColNode = Tdbp->Clist->GetItem(g, i, Cxnp);
 | 
						|
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Check whether an Xpath was provided to go to the column node.  */
 | 
						|
    /*******************************************************************/
 | 
						|
    for (k = 0; k < Nod; k++) {
 | 
						|
      if (k == Inod) {
 | 
						|
        // This is the multiple node
 | 
						|
        Nlx = ColNode->SelectNodes(g, Nodes[k], Nlx);
 | 
						|
        ColNode = Nlx->GetItem(g, Tdbp->Nsub, Cxnp);
 | 
						|
      } else
 | 
						|
        ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp);
 | 
						|
 | 
						|
      if (ColNode == NULL)
 | 
						|
        break;
 | 
						|
 | 
						|
      TopNode = ColNode;
 | 
						|
      } // endfor k
 | 
						|
 | 
						|
    if (ColNode)
 | 
						|
    {
 | 
						|
      if (Inod == Nod) {
 | 
						|
        /***************************************************************/
 | 
						|
        /*  The node value can be multiple.                            */
 | 
						|
        /***************************************************************/
 | 
						|
        assert (Type);
 | 
						|
 | 
						|
        // Get the value Node from the XML list
 | 
						|
        Nlx = ColNode->SelectNodes(g, Xname, Nlx);
 | 
						|
        len = Nlx->GetLength();
 | 
						|
 | 
						|
        if (len > 1 && !Tdbp->Xpand) {
 | 
						|
          snprintf(g->Message, sizeof(g->Message), MSG(BAD_VAL_UPDATE), Name);
 | 
						|
					throw (int)TYPE_AM_XML;
 | 
						|
				} else
 | 
						|
          ValNode = Nlx->GetItem(g, Tdbp->Nsub, Vxnp);
 | 
						|
 | 
						|
      } else  // Inod != Nod
 | 
						|
      {
 | 
						|
        if (Type)
 | 
						|
          ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp);
 | 
						|
        else
 | 
						|
          AttNode = ColNode->GetAttribute(g, Xname, Vxap);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (TopNode || ValNode || AttNode)
 | 
						|
      break;                     // We found the good column
 | 
						|
    else if (Tdbp->Clist)
 | 
						|
      ColNode = NULL;
 | 
						|
 | 
						|
    } // endfor i
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Create missing nodes.                                            */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (ColNode == NULL) {
 | 
						|
    if (TopNode == NULL)
 | 
						|
    {
 | 
						|
      if (Tdbp->Clist) {
 | 
						|
        Tdbp->RowNode->AddText(g, "\n\t\t");
 | 
						|
        ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname);
 | 
						|
        done = 2;
 | 
						|
        TopNode = ColNode;
 | 
						|
      } else
 | 
						|
        TopNode = Tdbp->RowNode;
 | 
						|
    }
 | 
						|
 | 
						|
    for (; k < Nod && TopNode; k++) {
 | 
						|
      if (!done) {
 | 
						|
        TopNode->AddText(g, "\n\t\t");
 | 
						|
        done = 1;
 | 
						|
        } // endif done
 | 
						|
 | 
						|
      ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp);
 | 
						|
      TopNode = ColNode;
 | 
						|
      } // endfor k
 | 
						|
 | 
						|
    if (ColNode == NULL) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(COL_ALLOC_ERR));
 | 
						|
			throw (int)TYPE_AM_XML;
 | 
						|
		} // endif ColNode
 | 
						|
 | 
						|
    } // endif ColNode
 | 
						|
 | 
						|
  if (Type == 1) {
 | 
						|
    if (ValNode == NULL) {
 | 
						|
      if (done < 2)
 | 
						|
        ColNode->AddText(g, "\n\t\t");
 | 
						|
 | 
						|
      ValNode = ColNode->AddChildNode(g, Xname, Vxnp);
 | 
						|
      } // endif ValNode
 | 
						|
 | 
						|
  } else // (Type == 0)
 | 
						|
    if (AttNode == NULL)
 | 
						|
      AttNode = ColNode->AddProperty(g, Xname, Vxap);
 | 
						|
 | 
						|
  if (ValNode == NULL && AttNode == NULL) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(VAL_ALLOC_ERR));
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
	} // endif ValNode
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Get the string representation of Value according to column type. */
 | 
						|
  /*********************************************************************/
 | 
						|
  p = Value->GetCharString(buf);
 | 
						|
 | 
						|
  if (strlen(p) > (unsigned)Long) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, Long);
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
	} else
 | 
						|
    strcpy(Valbuf, p);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Updating must be done only when not in checking pass.            */
 | 
						|
  /*********************************************************************/
 | 
						|
 fin:
 | 
						|
  if (Status) {
 | 
						|
    if (Type) {
 | 
						|
      ValNode->SetContent(g, Valbuf, Long);
 | 
						|
    } else
 | 
						|
      AttNode->SetText(g, Valbuf, Long);
 | 
						|
 | 
						|
    } // endif Status
 | 
						|
 | 
						|
  } // end of WriteColumn
 | 
						|
 | 
						|
/* ------------------------ XPOSCOL functions ------------------------ */
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  ReadColumn: what this routine does is to access the column node    */
 | 
						|
/*  from the corresponding table, extract from it the node text and    */
 | 
						|
/*  convert it to the column type.                                     */
 | 
						|
/***********************************************************************/
 | 
						|
void XPOSCOL::ReadColumn(PGLOBAL g)
 | 
						|
  {
 | 
						|
  if (Nx == Tdbp->Irow)
 | 
						|
    return;                         // Same row than the last read
 | 
						|
 | 
						|
  if (Tdbp->Clist == NULL) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(MIS_TAG_LIST));
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
	} // endif Clist
 | 
						|
 | 
						|
  if ((ValNode = Tdbp->Clist->GetItem(g, Rank, Vxnp))) {
 | 
						|
    // Get the column value from the XML file
 | 
						|
    switch (ValNode->GetContent(g, Valbuf, Long + 1)) {
 | 
						|
      case RC_OK:
 | 
						|
        break;
 | 
						|
      case RC_INFO:
 | 
						|
        PushWarning(g, Tdbp);
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
				throw (int)TYPE_AM_XML;
 | 
						|
		} // endswitch
 | 
						|
 | 
						|
    Value->SetValue_psz(Valbuf);
 | 
						|
  } else {
 | 
						|
    if (Nullable)
 | 
						|
      Value->SetNull(true);
 | 
						|
 | 
						|
    Value->Reset();              // Null value
 | 
						|
  } // endif ValNode
 | 
						|
 | 
						|
  Nx = Tdbp->Irow;
 | 
						|
  } // end of ReadColumn
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  WriteColumn: what this routine does is to access the last line     */
 | 
						|
/*  read from the corresponding table, and rewrite the field           */
 | 
						|
/*  corresponding to this column from the column buffer and type.      */
 | 
						|
/***********************************************************************/
 | 
						|
void XPOSCOL::WriteColumn(PGLOBAL g)
 | 
						|
  {
 | 
						|
  char          *p, buf[16];
 | 
						|
  int           i, k, n;
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
 | 
						|
          Name, Tdbp->GetTdb_No(), ColUse, Status);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Check whether this node must be written.                         */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Value != To_Val)
 | 
						|
    Value->SetValue_pval(To_Val, false);    // Convert the updated value
 | 
						|
 | 
						|
  if (Value->IsNull())
 | 
						|
    return;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  If a check pass was done while updating, all node contruction    */
 | 
						|
  /*  has been already one.                                            */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Status && Tdbp->Checked) {
 | 
						|
    assert (ValNode);
 | 
						|
    goto fin;
 | 
						|
    } // endif Checked
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  On Insert, a Row node must be created for each row;              */
 | 
						|
  /*  For all columns the Clist must be updated.                       */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Tdbp->CheckRow(g, true))
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Find the column and value nodes to update or insert.             */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (Tdbp->Clist == NULL) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(MIS_TAG_LIST));
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
	} // endif Clist
 | 
						|
 | 
						|
  n =  Tdbp->Clist->GetLength();
 | 
						|
  k = Rank;
 | 
						|
 | 
						|
  if (!(ValNode = Tdbp->Clist->GetItem(g, k, Vxnp))) {
 | 
						|
    /*******************************************************************/
 | 
						|
    /*  Create missing column nodes.                                   */
 | 
						|
    /*******************************************************************/
 | 
						|
    Tdbp->RowNode->AddText(g, "\n\t\t");
 | 
						|
 | 
						|
    for (i = n; i <= k; i++)
 | 
						|
      ValNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname, Vxnp);
 | 
						|
 | 
						|
    assert (ValNode);
 | 
						|
    } // endif ValNode
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Get the string representation of Value according to column type. */
 | 
						|
  /*********************************************************************/
 | 
						|
  p = Value->GetCharString(buf);
 | 
						|
 | 
						|
  if (strlen(p) > (unsigned)Long) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, Long);
 | 
						|
		throw (int)TYPE_AM_XML;
 | 
						|
	} else
 | 
						|
    strcpy(Valbuf, p);
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Updating must be done only when not in checking pass.            */
 | 
						|
  /*********************************************************************/
 | 
						|
 fin:
 | 
						|
  if (Status)
 | 
						|
    ValNode->SetContent(g, Valbuf, Long);
 | 
						|
 | 
						|
  } // end of WriteColumn
 | 
						|
 | 
						|
/* ---------------------------TDBXCT class --------------------------- */
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  TDBXCT class constructor.                                          */
 | 
						|
/***********************************************************************/
 | 
						|
TDBXCT::TDBXCT(PXMLDEF tdp) : TDBCAT(tdp)
 | 
						|
  {
 | 
						|
  Topt = tdp->GetTopt();
 | 
						|
  //Db = (char*)tdp->GetDB();
 | 
						|
	Db = (char*)tdp->Schema;
 | 
						|
	Tabn = tdp->Tabname;
 | 
						|
  } // end of TDBXCT constructor
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  GetResult: Get the list the JSON file columns.                     */
 | 
						|
/***********************************************************************/
 | 
						|
PQRYRES TDBXCT::GetResult(PGLOBAL g)
 | 
						|
  {
 | 
						|
  return XMLColumns(g, Db, Tabn, Topt, false);
 | 
						|
  } // end of GetResult
 | 
						|
 | 
						|
/* ------------------------ End of Tabxml ---------------------------- */
 |