1 /************* Tabxml C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: TABXML                                                */
3 /* -------------                                                       */
4 /*  Version 3.0                                                        */
5 /*                                                                     */
6 /*  Author Olivier BERTRAND          2007 - 2020                       */
7 /*                                                                     */
8 /*  This program are the XML tables classes using MS-DOM or libxml2.   */
9 /***********************************************************************/
10 
11 /***********************************************************************/
12 /*  Include required compiler header files.                            */
13 /***********************************************************************/
14 #include "my_global.h"
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #if defined(_WIN32)
19 #include <io.h>
20 #include <winsock2.h>
21 //#include <windows.h>
22 #include <comdef.h>
23 #else   // !_WIN32
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <unistd.h>
28 //#include <ctype.h>
29 #include "osutil.h"
30 #define _O_RDONLY O_RDONLY
31 #endif  // !_WIN32
32 #include "resource.h"                        // for IDS_COLUMNS
33 
34 #define INCLUDE_TDBXML
35 #define NODE_TYPE_LIST
36 
37 /***********************************************************************/
38 /*  Include application header files:                                  */
39 /*  global.h    is header containing all global declarations.          */
40 /*  plgdbsem.h  is header containing the DB application declarations.  */
41 /*  tabdos.h    is header containing the TABDOS class declarations.    */
42 /***********************************************************************/
43 #include "global.h"
44 #include "plgdbsem.h"
45 //#include "reldef.h"
46 #include "xtable.h"
47 #include "colblk.h"
48 #include "mycat.h"
49 #include "xindex.h"
50 #include "plgxml.h"
51 #include "tabxml.h"
52 #include "tabmul.h"
53 
54 extern "C" char version[];
55 
56 #if defined(_WIN32) && defined(DOMDOC_SUPPORT)
57 #define XMLSUP "MS-DOM"
58 #else   // !_WIN32
59 #define XMLSUP "libxml2"
60 #endif  // !_WIN32
61 
62 #define TYPE_UNKNOWN     12        /* Must be greater than other types */
63 #define XLEN(M)  sizeof(M) - strlen(M) - 1	       /* To avoid overflow*/
64 
65 int GetDefaultDepth(void);
66 
67 /***********************************************************************/
68 /* Class and structure used by XMLColumns.                             */
69 /***********************************************************************/
70 typedef class XMCOL *PXCL;
71 
72 class XMCOL : public BLOCK {
73  public:
74   // Constructors
XMCOL(void)75   XMCOL(void) {Next = NULL;
76                Name[0] = 0;
77                Fmt = NULL;
78                Type = 1;
79                Len = Scale = 0;
80                Cbn = false;
81                Found = true;}
XMCOL(PGLOBAL g,PXCL xp,char * fmt,int i)82   XMCOL(PGLOBAL g, PXCL xp, char *fmt, int i) {
83                Next = NULL;
84                strcpy(Name, xp->Name);
85                Fmt = (*fmt) ? PlugDup(g, fmt) : NULL;
86                Type = xp->Type;
87                Len = xp->Len;
88                Scale = xp->Scale;
89                Cbn = (xp->Cbn || i > 1);
90                Found = true;}
91 
92   // Members
93   PXCL  Next;
94   char  Name[64];
95   char *Fmt;
96   int   Type;
97   int   Len;
98   int   Scale;
99   bool  Cbn;
100   bool  Found;
101 }; // end of class XMCOL
102 
103 typedef struct LVL {
104   PXNODE  pn;
105   PXLIST  nl;
106   PXATTR  atp;
107   bool    b;
108   long    k;
109   int     m, n;
110 } *PLVL;
111 
112 /***********************************************************************/
113 /* XMLColumns: construct the result blocks containing the description  */
114 /* of all the columns of a table contained inside an XML file.         */
115 /***********************************************************************/
XMLColumns(PGLOBAL g,char * db,char * tab,PTOS topt,bool info)116 PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info)
117 {
118   static int  buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
119                           TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
120   static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
121                           FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
122   static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
123   char    colname[65], fmt[129], buf[512];
124   int     i, j, lvl, n = 0;
125   int     ncol = sizeof(buftyp) / sizeof(int);
126   bool    ok = true;
127 	PCSZ    fn, op;
128   PXCL    xcol, xcp, fxcp = NULL, pxcp = NULL;
129   PLVL   *lvlp, vp;
130   PXNODE  node = NULL;
131   PXMLDEF tdp;
132   PTDBXML txmp;
133   PQRYRES qrp;
134   PCOLRES crp;
135 
136   if (info) {
137     length[0] = 128;
138     length[7] = 256;
139     goto skipit;
140     } // endif info
141 
142 	if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
143 		strcpy(g->Message, "Cannot find column definition for multiple table");
144 		return NULL;
145 	}	// endif Multiple
146 
147 	/*********************************************************************/
148   /*  Open the input file.                                             */
149   /*********************************************************************/
150   if (!(fn = GetStringTableOption(g, topt, "Filename", NULL))) {
151     if (topt->http) // REST table can have default filename
152       fn = GetStringTableOption(g, topt, "Subtype", NULL);
153 
154     if (!fn) {
155       strcpy(g->Message, MSG(MISSING_FNAME));
156       return NULL;
157     } else
158       topt->subtype = NULL;
159 
160   } // endif fn
161 
162   lvl = GetIntegerTableOption(g, topt, "Level", GetDefaultDepth());
163   lvl = GetIntegerTableOption(g, topt, "Depth", lvl);
164   lvl = (lvl < 0) ? 0 : (lvl > 16) ? 16 : lvl;
165 
166   if (trace(1))
167     htrc("File %s lvl=%d\n", topt->filename, lvl);
168 
169   tdp = new(g) XMLDEF;
170   tdp->Fn = fn;
171 
172 	if (!(tdp->Database = SetPath(g, db)))
173 		return NULL;
174 
175   tdp->Tabname = tab;
176 	tdp->Tabname = (char*)GetStringTableOption(g, topt, "Tabname", tab);
177 	tdp->Rowname = (char*)GetStringTableOption(g, topt, "Rownode", NULL);
178 	tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
179 	tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
180 	tdp->Skip = GetBooleanTableOption(g, topt, "Skipnull", false);
181 
182   if (!(op = GetStringTableOption(g, topt, "Xmlsup", NULL)))
183 #if defined(_WIN32)
184     tdp->Usedom = true;
185 #else   // !_WIN32
186     tdp->Usedom = false;
187 #endif  // !_WIN32
188   else
189     tdp->Usedom = (toupper(*op) == 'M' || toupper(*op) == 'D');
190 
191   txmp = new(g) TDBXML(tdp);
192 
193   if (txmp->Initialize(g))
194     goto err;
195 
196   xcol = new(g) XMCOL;
197   colname[64] = 0;
198   fmt[128] = 0;
199   lvlp = (PLVL*)PlugSubAlloc(g, NULL, sizeof(PLVL) * (lvl + 1));
200 
201   for (j = 0; j <= lvl; j++)
202     lvlp[j] = (PLVL)PlugSubAlloc(g, NULL, sizeof(LVL));
203 
204   /*********************************************************************/
205   /*  Analyse the XML tree and define columns.                         */
206   /*********************************************************************/
207   for (i = 1; ; i++) {
208     // Get next row
209     switch (txmp->ReadDB(g)) {
210       case RC_EF:
211         vp = NULL;
212         break;
213       case RC_FX:
214         goto err;
215       default:
216         vp = lvlp[0];
217         vp->pn = txmp->RowNode;
218         vp->atp = vp->pn->GetAttribute(g, NULL);
219         vp->nl = vp->pn->GetChildElements(g);
220         vp->b = true;
221         vp->k = 0;
222         vp->m = vp->n = 0;
223         j = 0;
224       } // endswitch ReadDB
225 
226     if (!vp)
227       break;
228 
229     while (true) {
230       if (!vp->atp &&
231           !(node = (vp->nl) ? vp->nl->GetItem(g, vp->k++, tdp->Usedom ?
232                                               node : NULL)
233             : NULL))
234       {
235         if (j) {
236           vp = lvlp[--j];
237 
238           if (!tdp->Usedom)    // nl was destroyed
239             vp->nl = vp->pn->GetChildElements(g);
240 
241           if (!lvlp[j+1]->b) {
242             vp->k--;
243             ok = false;
244             } // endif b
245 
246           continue;
247         } else
248           break;
249       }
250       xcol->Name[vp->n] = 0;
251       fmt[vp->m] = 0;
252 
253      more:
254       if (vp->atp) {
255 				size_t z = sizeof(colname) - 1;
256         strncpy(colname, vp->atp->GetName(g), z);
257 				colname[z] = 0;
258 				strncat(xcol->Name, colname, XLEN(xcol->Name));
259 
260         switch (vp->atp->GetText(g, buf, sizeof(buf))) {
261           case RC_INFO:
262             PushWarning(g, txmp);
263             /* falls through */
264           case RC_OK:
265             strncat(fmt, "@", XLEN(fmt));
266             break;
267           default:
268             goto err;
269           } // enswitch rc
270 
271         if (j)
272           strncat(fmt, colname, XLEN(fmt));
273 
274       } else {
275         if (tdp->Usedom && node->GetType() != 1)
276           continue;
277 
278         strncpy(colname, node->GetName(g), sizeof(colname));
279 				strncat(xcol->Name, colname, XLEN(xcol->Name));
280 
281         if (j)
282           strncat(fmt, colname, XLEN(fmt));
283 
284         if (j < lvl && ok) {
285           vp = lvlp[j+1];
286           vp->k = 0;
287 					vp->pn = node;
288 					vp->atp = node->GetAttribute(g, NULL);
289           vp->nl = node->GetChildElements(g);
290 
291           if (tdp->Usedom && vp->nl->GetLength() == 1) {
292             node = vp->nl->GetItem(g, 0, node);
293             vp->b = (node->GetType() == 1);   // Must be ab element
294           } else
295             vp->b = (vp->nl && vp->nl->GetLength());
296 
297           if (vp->atp || vp->b) {
298             if (!vp->atp)
299 							node = vp->nl->GetItem(g, vp->k++, tdp->Usedom ? node : NULL);
300 
301 						if (!j)
302 							strncat(fmt, colname, XLEN(fmt));
303 
304 						strncat(fmt, "/", XLEN(fmt));
305 						strncat(xcol->Name, "_", XLEN(xcol->Name));
306 						j++;
307             vp->n = (int)strlen(xcol->Name);
308             vp->m = (int)strlen(fmt);
309             goto more;
310           } else {
311             vp = lvlp[j];
312 
313             if (!tdp->Usedom)    // nl was destroyed
314               vp->nl = vp->pn->GetChildElements(g);
315 
316           } // endif vp
317 
318         } else
319           ok = true;
320 
321         switch (node->GetContent(g, buf, sizeof(buf))) {
322           case RC_INFO:
323             PushWarning(g, txmp);
324             /* falls through */
325           case RC_OK:
326 						xcol->Cbn = !strlen(buf);
327             break;
328           default:
329             goto err;
330           } // enswitch rc
331 
332       } // endif atp;
333 
334       xcol->Len = strlen(buf);
335 
336       // Check whether this column was already found
337       for (xcp = fxcp; xcp; xcp = xcp->Next)
338         if (!strcmp(xcol->Name, xcp->Name))
339           break;
340 
341       if (xcp) {
342         if (xcp->Type != xcol->Type)
343           xcp->Type = TYPE_STRING;
344 
345         if (*fmt && (!xcp->Fmt || strlen(xcp->Fmt) < strlen(fmt))) {
346           xcp->Fmt = PlugDup(g, fmt);
347           length[7] = MY_MAX(length[7], strlen(fmt));
348           } // endif *fmt
349 
350         xcp->Len = MY_MAX(xcp->Len, xcol->Len);
351         xcp->Scale = MY_MAX(xcp->Scale, xcol->Scale);
352         xcp->Cbn |= (xcol->Cbn || !xcol->Len);
353         xcp->Found = true;
354       } else if(xcol->Len || !tdp->Skip) {
355         // New column
356         xcp = new(g) XMCOL(g, xcol, fmt, i);
357         length[0] = MY_MAX(length[0], strlen(xcol->Name));
358         length[7] = MY_MAX(length[7], strlen(fmt));
359 
360         if (pxcp) {
361           xcp->Next = pxcp->Next;
362           pxcp->Next = xcp;
363         } else
364           fxcp = xcp;
365 
366         n++;
367       } // endif xcp
368 
369 			if (xcp)
370 				pxcp = xcp;
371 
372       if (vp->atp)
373         vp->atp = vp->atp->GetNext(g);
374 
375       } // endwhile
376 
377     // Missing column can be null
378     for (xcp = fxcp; xcp; xcp = xcp->Next) {
379       xcp->Cbn |= !xcp->Found;
380       xcp->Found = false;
381       } // endfor xcp
382 
383     } // endor i
384 
385   txmp->CloseDB(g);
386 
387  skipit:
388   if (trace(1))
389     htrc("XMLColumns: n=%d len=%d\n", n, length[0]);
390 
391   /*********************************************************************/
392   /*  Allocate the structures used to refer to the result set.         */
393   /*********************************************************************/
394   qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
395                           buftyp, fldtyp, length, false, false);
396 
397   crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
398   crp->Name = "Nullable";
399   crp->Next->Name = "Xpath";
400 
401   if (info || !qrp)
402     return qrp;
403 
404   qrp->Nblin = n;
405 
406   /*********************************************************************/
407   /*  Now get the results into blocks.                                 */
408   /*********************************************************************/
409   for (i = 0, xcp = fxcp; xcp; i++, xcp = xcp->Next) {
410     if (xcp->Type == TYPE_UNKNOWN)            // Void column
411       xcp->Type = TYPE_STRING;
412 
413     crp = qrp->Colresp;                    // Column Name
414     crp->Kdata->SetValue(xcp->Name, i);
415     crp = crp->Next;                       // Data Type
416     crp->Kdata->SetValue(xcp->Type, i);
417     crp = crp->Next;                       // Type Name
418     crp->Kdata->SetValue(GetTypeName(xcp->Type), i);
419     crp = crp->Next;                       // Precision
420     crp->Kdata->SetValue(xcp->Len, i);
421     crp = crp->Next;                       // Length
422     crp->Kdata->SetValue(xcp->Len, i);
423     crp = crp->Next;                       // Scale (precision)
424     crp->Kdata->SetValue(xcp->Scale, i);
425     crp = crp->Next;                       // Nullable
426     crp->Kdata->SetValue(xcp->Cbn ? 1 : 0, i);
427     crp = crp->Next;                       // Field format
428 
429     if (crp->Kdata)
430       crp->Kdata->SetValue(xcp->Fmt, i);
431 
432     } // endfor i
433 
434   /*********************************************************************/
435   /*  Return the result pointer.                                       */
436   /*********************************************************************/
437   return qrp;
438 
439 err:
440   txmp->CloseDB(g);
441   return NULL;
442   } // end of XMLColumns
443 
444 /* -------------- Implementation of the XMLDEF class  ---------------- */
445 
446 /***********************************************************************/
447 /*  Constructor.                                                       */
448 /***********************************************************************/
XMLDEF(void)449 XMLDEF::XMLDEF(void)
450   {
451   Pseudo = 3;
452   Fn = NULL;
453   Encoding = NULL;
454   Tabname = NULL;
455   Rowname = NULL;
456   Colname = NULL;
457   Mulnode = NULL;
458   XmlDB = NULL;
459   Nslist = NULL;
460   DefNs = NULL;
461   Attrib = NULL;
462   Hdattr = NULL;
463 	Entry = NULL;
464   Coltype = 1;
465   Limit = 0;
466   Header = 0;
467   Xpand = false;
468   Usedom = false;
469 	Zipped = false;
470 	Mulentries = false;
471 	Skip = false;
472   } // end of XMLDEF constructor
473 
474 /***********************************************************************/
475 /*  DefineAM: define specific AM block values from XDB file.           */
476 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int poff)477 bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
478   {
479 	PCSZ defrow, defcol;
480 	char buf[10];
481 
482   Fn = GetStringCatInfo(g, "Filename", NULL);
483   Encoding = GetStringCatInfo(g, "Encoding", "UTF-8");
484 
485   if (*Fn == '?') {
486     strcpy(g->Message, MSG(MISSING_FNAME));
487     return true;
488     } // endif fn
489 
490   if ((signed)GetIntCatInfo("Flag", -1) != -1) {
491     strcpy(g->Message, MSG(DEPREC_FLAG));
492     return true;
493     } // endif flag
494 
495   defrow = defcol = NULL;
496   GetCharCatInfo("Coltype", "", buf, sizeof(buf));
497 
498   switch (toupper(*buf)) {
499     case 'A':                          // Attribute
500     case '@':
501     case '0':
502       Coltype = 0;
503       break;
504     case '\0':                         // Default
505     case 'T':                          // Tag
506     case 'N':                          // Node
507     case '1':
508       Coltype = 1;
509       break;
510     case 'C':                          // Column
511     case 'P':                          // Position
512     case 'H':                          // HTML
513     case '2':
514       Coltype = 2;
515       defrow = "TR";
516       defcol = "TD";
517       break;
518     default:
519       sprintf(g->Message, MSG(INV_COL_TYPE), buf);
520       return true;
521     } // endswitch typname
522 
523   Tabname = GetStringCatInfo(g, "Name", Name);  // Deprecated
524   Tabname = GetStringCatInfo(g, "Table_name", Tabname); // Deprecated
525   Tabname = GetStringCatInfo(g, "Tabname", Tabname);
526   Rowname = GetStringCatInfo(g, "Rownode", defrow);
527   Colname = GetStringCatInfo(g, "Colnode", defcol);
528   Mulnode = GetStringCatInfo(g, "Mulnode", NULL);
529   XmlDB = GetStringCatInfo(g, "XmlDB", NULL);
530   Nslist = GetStringCatInfo(g, "Nslist", NULL);
531   DefNs = GetStringCatInfo(g, "DefNs", NULL);
532   Limit = GetIntCatInfo("Limit", 50);
533   Xpand = GetBoolCatInfo("Expand", false);
534   Header = GetIntCatInfo("Header", 0);
535   GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf));
536 
537   // Note that if no support is specified, the default is MS-DOM
538   // on Windows and libxml2 otherwise
539   if (*buf == '*')
540 #if defined(_WIN32)
541     Usedom = true;
542 #else   // !_WIN32
543     Usedom = false;
544 #endif  // !_WIN32
545   else
546     Usedom = (toupper(*buf) == 'M' || toupper(*buf) == 'D');
547 
548   // Get eventual table node attribute
549   Attrib = GetStringCatInfo(g, "Attribute", NULL);
550   Hdattr = GetStringCatInfo(g, "HeadAttr", NULL);
551 
552 	// Specific for zipped files
553 	if ((Zipped = GetBoolCatInfo("Zipped", false)))
554 		Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL)))
555 		? strchr(Entry, '*') || strchr(Entry, '?')
556 		: GetBoolCatInfo("Mulentries", false);
557 
558 	return false;
559   } // end of DefineAM
560 
561 /***********************************************************************/
562 /*  GetTable: makes a new TDB of the proper type.                      */
563 /***********************************************************************/
GetTable(PGLOBAL g,MODE m)564 PTDB XMLDEF::GetTable(PGLOBAL g, MODE m)
565   {
566   if (Catfunc == FNC_COL)
567     return new(g) TDBXCT(this);
568 
569 	if (Zipped && !(m == MODE_READ || m == MODE_ANY)) {
570 		strcpy(g->Message, "ZIpped XML tables are read only");
571 		return NULL;
572 	}	// endif Zipped
573 
574   PTDBASE tdbp = new(g) TDBXML(this);
575 
576   if (Multiple)
577     tdbp = new(g) TDBMUL(tdbp);
578 
579   return tdbp;
580   } // end of GetTable
581 
582 /* ------------------------- TDBXML Class ---------------------------- */
583 
584 /***********************************************************************/
585 /*  Implementation of the TDBXML constuctor.                           */
586 /***********************************************************************/
TDBXML(PXMLDEF tdp)587 TDBXML::TDBXML(PXMLDEF tdp) : TDBASE(tdp)
588   {
589   Docp = NULL;
590   Root = NULL;
591   Curp = NULL;
592   DBnode = NULL;
593   TabNode = NULL;
594   RowNode = NULL;
595   ColNode = NULL;
596   Nlist = NULL;
597   Clist = NULL;
598   To_Xb = NULL;
599   Colp = NULL;
600   Xfile = tdp->Fn;
601   Enc = tdp->Encoding;
602   Tabname = tdp->Tabname;
603 #if 0 // why all these?
604   Rowname = (tdp->Rowname) ? tdp->Rowname : NULL;
605   Colname = (tdp->Colname) ? tdp->Colname : NULL;
606   Mulnode = (tdp->Mulnode) ? tdp->Mulnode : NULL;
607   XmlDB = (tdp->XmlDB) ? tdp->XmlDB : NULL;
608   Nslist = (tdp->Nslist) ? tdp->Nslist : NULL;
609   DefNs = (tdp->DefNs) ? tdp->DefNs : NULL;
610   Attrib = (tdp->Attrib) ? tdp->Attrib : NULL;
611   Hdattr = (tdp->Hdattr) ? tdp->Hdattr : NULL;
612 #endif // 0
613 	Rowname = tdp->Rowname;
614 	Colname = tdp->Colname;
615 	Mulnode = tdp->Mulnode;
616 	XmlDB = tdp->XmlDB;
617 	Nslist = tdp->Nslist;
618 	DefNs = tdp->DefNs;
619 	Attrib = tdp->Attrib;
620 	Hdattr = tdp->Hdattr;
621 	Entry = tdp->Entry;
622 	Coltype = tdp->Coltype;
623   Limit = tdp->Limit;
624   Xpand = tdp->Xpand;
625 	Zipped = tdp->Zipped;
626 	Mulentries = tdp->Mulentries;
627 	Changed = false;
628   Checked = false;
629   NextSame = false;
630   NewRow = false;
631   Hasnod = false;
632   Write = false;
633   Bufdone = false;
634   Nodedone = false;
635   Void = false;
636   Usedom = tdp->Usedom;
637   Header = tdp->Header;
638   Multiple = tdp->Multiple;
639   Nrow = -1;
640   Irow = Header - 1;
641   Nsub = 0;
642   N = 0;
643   } // end of TDBXML constructor
644 
TDBXML(PTDBXML tdbp)645 TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp)
646   {
647   Docp = tdbp->Docp;
648   Root = tdbp->Root;
649   Curp = tdbp->Curp;
650   DBnode = tdbp->DBnode;
651   TabNode = tdbp->TabNode;
652   RowNode = tdbp->RowNode;
653   ColNode = tdbp->ColNode;
654   Nlist = tdbp->Nlist;
655   Clist = tdbp->Clist;
656   To_Xb = tdbp->To_Xb;
657   Colp = tdbp->Colp;
658   Xfile = tdbp->Xfile;
659   Enc = tdbp->Enc;
660   Tabname = tdbp->Tabname;
661   Rowname = tdbp->Rowname;
662   Colname = tdbp->Colname;
663   Mulnode = tdbp->Mulnode;
664   XmlDB = tdbp->XmlDB;
665   Nslist = tdbp->Nslist;
666   DefNs = tdbp->DefNs;
667   Attrib = tdbp->Attrib;
668   Hdattr = tdbp->Hdattr;
669 	Entry = tdbp->Entry;
670 	Coltype = tdbp->Coltype;
671   Limit = tdbp->Limit;
672   Xpand = tdbp->Xpand;
673 	Zipped = tdbp->Zipped;
674 	Mulentries = tdbp->Mulentries;
675 	Changed = tdbp->Changed;
676   Checked = tdbp->Checked;
677   NextSame = tdbp->NextSame;
678   NewRow = tdbp->NewRow;
679   Hasnod = tdbp->Hasnod;
680   Write = tdbp->Write;
681   Void = tdbp->Void;
682   Usedom = tdbp->Usedom;
683   Header = tdbp->Header;
684   Multiple = tdbp->Multiple;
685   Nrow = tdbp->Nrow;
686   Irow = tdbp->Irow;
687   Nsub = tdbp->Nsub;
688   N = tdbp->N;
689   } // end of TDBXML copy constructor
690 
691 // Used for update
Clone(PTABS t)692 PTDB TDBXML::Clone(PTABS t)
693   {
694   PTDB    tp;
695   PXMLCOL cp1, cp2;
696   PGLOBAL g = t->G;
697 
698   tp = new(g) TDBXML(this);
699 
700   for (cp1 = (PXMLCOL)Columns; cp1; cp1 = (PXMLCOL)cp1->GetNext()) {
701     cp2 = new(g) XMLCOL(cp1, tp);  // Make a copy
702     NewPointer(t, cp1, cp2);
703     } // endfor cp1
704 
705   return tp;
706   } // end of Clone
707 
708 /***********************************************************************/
709 /*  Must not be in tabxml.h because of OEM tables                      */
710 /***********************************************************************/
data_charset()711 const CHARSET_INFO *TDBXML::data_charset()
712 {
713 	return &my_charset_utf8_general_ci;
714 }	// end of data_charset
715 
716 /***********************************************************************/
717 /*  Allocate XML column description block.                             */
718 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)719 PCOL TDBXML::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
720   {
721   if (trace(1))
722     htrc("TDBXML: MakeCol %s n=%d\n", (cdp) ? cdp->GetName() : "<null>", n);
723 
724   return new(g) XMLCOL(cdp, this, cprec, n);
725   } // end of MakeCol
726 
727 /***********************************************************************/
728 /*  InsertSpecialColumn: Put a special column ahead of the column list.*/
729 /***********************************************************************/
InsertSpecialColumn(PCOL colp)730 PCOL TDBXML::InsertSpecialColumn(PCOL colp)
731   {
732   if (!colp->IsSpecial())
733     return NULL;
734 
735 //if (Xpand && ((SPCBLK*)colp)->GetRnm())
736 //  colp->SetKey(0);               // Rownum is no more a key
737 
738   colp->SetNext(Columns);
739   Columns = colp;
740   return colp;
741   } // end of InsertSpecialColumn
742 
743 /***********************************************************************/
744 /*  LoadTableFile: Load and parse an XML file.                         */
745 /***********************************************************************/
LoadTableFile(PGLOBAL g,char * filename)746 int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
747   {
748   int     rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2;
749   PFBLOCK fp = NULL;
750   PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
751 
752   if (Docp)
753     return rc;               // Already done
754 
755   if (trace(1))
756     htrc("TDBXML: loading %s\n", filename);
757 
758   /*********************************************************************/
759   /*  Firstly we check whether this file have been already loaded.     */
760   /*********************************************************************/
761   if ((Mode == MODE_READ || Mode == MODE_ANY) && !Zipped)
762     for (fp = dup->Openlist; fp; fp = fp->Next)
763       if (fp->Type == type && fp->Length && fp->Count)
764         if (!stricmp(fp->Fname, filename))
765           break;
766 
767   if (fp) {
768     /*******************************************************************/
769     /*  File already loaded. Just increment use count and get pointer. */
770     /*******************************************************************/
771     fp->Count++;
772     Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc, fp)
773                     : GetLibxmlDoc(g, Nslist, DefNs, Enc, fp);
774   } else {
775     /*******************************************************************/
776     /*  Parse the XML file.                                            */
777     /*******************************************************************/
778     if (!(Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc)
779                           : GetLibxmlDoc(g, Nslist, DefNs, Enc)))
780       return RC_FX;
781 
782     // Initialize the implementation
783     if (Docp->Initialize(g, Entry, Zipped)) {
784       sprintf(g->Message, MSG(INIT_FAILED), (Usedom) ? "DOM" : "libxml2");
785       return RC_FX;
786       } // endif init
787 
788     if (trace(1))
789       htrc("TDBXML: parsing %s rc=%d\n", filename, rc);
790 
791     // Parse the XML file
792     if (Docp->ParseFile(g, filename)) {
793       // Does the file exist?
794       int h= global_open(g, MSGID_NONE, filename, _O_RDONLY);
795 
796       if (h != -1) {
797         rc = (!_filelength(h)) ? RC_EF : RC_INFO;
798         close(h);
799       } else
800         rc = (errno == ENOENT) ? RC_NF : RC_INFO;
801 
802       // Cannot make a Xblock until document is made
803       return rc;
804       } // endif Docp
805 
806     /*******************************************************************/
807     /*  Link a Xblock. This make possible to reuse already opened docs */
808     /*  and also to automatically close them in case of error g->jump. */
809     /*******************************************************************/
810     fp = Docp->LinkXblock(g, Mode, rc, filename);
811   } // endif xp
812 
813   To_Xb = fp;                                  // Useful when closing
814   return rc;
815   } // end of LoadTableFile
816 
817 /***********************************************************************/
818 /*  Initialize the processing of the XML file.                         */
819 /*  Note: this function can be called several times, eventally before  */
820 /*  the columns are known (from TBL for instance)                      */
821 /***********************************************************************/
Initialize(PGLOBAL g)822 bool TDBXML::Initialize(PGLOBAL g)
823   {
824   int     rc;
825   PXMLCOL colp;
826 
827   if (Void)
828     return false;
829 
830   if (Columns) {
831     // Allocate the buffers that will contain node values
832     for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext())
833 			if (!colp->IsSpecial()) {            // Not a pseudo column
834 				if (!Bufdone && colp->AllocBuf(g, Mode == MODE_INSERT))
835 					return true;
836 
837 				colp->Nx = colp->Sx = -1;
838 			} // endif Special
839 
840     Bufdone = true;
841     } // endif Bufdone
842 
843 #if !defined(UNIX)
844 	if (!Root) try {
845 #else
846 	if (!Root) {
847 #endif
848 		char tabpath[64], filename[_MAX_PATH];
849 
850 		//  We used the file name relative to recorded datapath
851 		PlugSetPath(filename, Xfile, GetPath());
852 
853 		// Load or re-use the table file
854 		rc = LoadTableFile(g, filename);
855 
856 		if (rc == RC_OK) {
857 			// Get root node
858 			if (!(Root = Docp->GetRoot(g))) {
859 				// This should never happen as load should have failed
860 				strcpy(g->Message, MSG(EMPTY_DOC));
861 				goto error;
862 			} // endif Root
863 
864 		// If tabname is not an Xpath,
865 		// construct one that will find it anywhere
866 			if (!strchr(Tabname, '/'))
867 				strcat(strcpy(tabpath, "//"), Tabname);
868 			else
869 				strcpy(tabpath, Tabname);
870 
871 			// Evaluate table xpath
872 			if ((TabNode = Root->SelectSingleNode(g, tabpath))) {
873 				if (TabNode->GetType() != XML_ELEMENT_NODE) {
874 					sprintf(g->Message, MSG(BAD_NODE_TYPE), TabNode->GetType());
875 					goto error;
876 				} // endif Type
877 
878 			} else if (Mode == MODE_INSERT && XmlDB) {
879 				// We are adding a new table to a multi-table file
880 
881 				// If XmlDB is not an Xpath,
882 				// construct one that will find it anywhere
883 				if (!strchr(XmlDB, '/'))
884 					strcat(strcpy(tabpath, "//"), XmlDB);
885 				else
886 					strcpy(tabpath, XmlDB);
887 
888 				if (!(DBnode = Root->SelectSingleNode(g, tabpath))) {
889 					// DB node does not exist yet; we cannot create it
890 					// because we don't know where it should be placed
891 					sprintf(g->Message, MSG(MISSING_NODE), XmlDB, Xfile);
892 					goto error;
893 				} // endif DBnode
894 
895 				if (!(TabNode = DBnode->AddChildNode(g, Tabname))) {
896 					sprintf(g->Message, MSG(FAIL_ADD_NODE), Tabname);
897 					goto error;
898 				} // endif TabNode
899 
900 				DBnode->AddText(g, "\n");
901 			} else {
902 				TabNode = Root;              // Try this ?
903 				Tabname = TabNode->GetName(g);
904 			} // endif's
905 
906 		} else if (rc == RC_NF || rc == RC_EF) {
907 			// The XML file does not exist or is void
908 			if (Mode == MODE_INSERT) {
909 				// New Document
910 				char buf[64];
911 
912 				// Create the XML node
913 				if (Docp->NewDoc(g, "1.0")) {
914 					strcpy(g->Message, MSG(NEW_DOC_FAILED));
915 					goto error;
916 				} // endif NewDoc
917 
918 			//  Now we can link the Xblock
919 				To_Xb = Docp->LinkXblock(g, Mode, rc, filename);
920 
921 				// Add a CONNECT comment node
922 				strcpy(buf, " Created by the MariaDB CONNECT Storage Engine");
923 				Docp->AddComment(g, buf);
924 
925 				if (XmlDB) {
926 					// This is a multi-table file
927 					DBnode = Root = Docp->NewRoot(g, XmlDB);
928 					DBnode->AddText(g, "\n");
929 					TabNode = DBnode->AddChildNode(g, Tabname);
930 					DBnode->AddText(g, "\n");
931 				} else
932 					TabNode = Root = Docp->NewRoot(g, Tabname);
933 
934 				if (TabNode == NULL || Root == NULL) {
935 					strcpy(g->Message, MSG(XML_INIT_ERROR));
936 					goto error;
937 				} else if (SetTabNode(g))
938 					goto error;
939 
940 			} else {
941 				sprintf(g->Message, MSG(FILE_UNFOUND), Xfile);
942 
943 				if (Mode == MODE_READ) {
944 					PushWarning(g, this);
945 					Void = true;
946 				} // endif Mode
947 
948 				goto error;
949 			} // endif Mode
950 
951 		} else if (rc == RC_INFO) {
952 			// Loading failed
953 			sprintf(g->Message, MSG(LOADING_FAILED), Xfile);
954 			goto error;
955 		} else // (rc == RC_FX)
956 			goto error;
957 
958 		if (!Rowname) {
959 			for (PXNODE n = TabNode->GetChild(g); n; n = n->GetNext(g))
960 				if (n->GetType() == XML_ELEMENT_NODE) {
961 					Rowname = n->GetName(g);
962 					break;
963 				} // endif Type
964 
965 			if (!Rowname)
966 				Rowname = TabNode->GetName(g);
967 		} // endif Rowname
968 
969 			// Get row node list
970 		if (strcmp(Rowname, Tabname))
971 			Nlist = TabNode->SelectNodes(g, Rowname);
972 		else
973 			Nrow = 1;
974 
975 
976 		Docp->SetNofree(true);       // For libxml2
977 #if defined(_WIN32)
978 	} catch (_com_error e) {
979     // We come here if a DOM command threw an error
980     char   buf[128];
981 
982     rc = WideCharToMultiByte(CP_ACP, 0, e.Description(), -1,
983                              buf, sizeof(buf), NULL, NULL);
984 
985     if (rc)
986       sprintf(g->Message, "%s: %s", MSG(COM_ERROR), buf);
987     else
988       sprintf(g->Message, "%s hr=%x", MSG(COM_ERROR), e.Error());
989 
990     goto error;
991 #endif   // _WIN32
992 #if !defined(UNIX)
993   } catch(...) {
994     // Other errors
995     strcpy(g->Message, MSG(XMLTAB_INIT_ERR));
996     goto error;
997 #endif
998   } // end of try-catches
999 
1000   if (Root && Columns && (Multiple || !Nodedone)) {
1001     // Allocate class nodes to avoid dynamic allocation
1002     for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext())
1003       if (!colp->IsSpecial())            // Not a pseudo column
1004         colp->AllocNodes(g, Docp);
1005 
1006     Nodedone = true;
1007     } // endif Nodedone
1008 
1009   if (Nrow < 0)
1010     Nrow = (Nlist) ? Nlist->GetLength() : 0;
1011 
1012   // Init is Ok
1013   return false;
1014 
1015 error:
1016   if (Docp)
1017     Docp->CloseDoc(g, To_Xb);
1018 
1019   return !Void;
1020 } // end of Initialize
1021 
1022 /***********************************************************************/
1023 /*  Set TabNode attributes or header.                                  */
1024 /***********************************************************************/
1025 bool TDBXML::SetTabNode(PGLOBAL g)
1026   {
1027   assert(Mode == MODE_INSERT);
1028 
1029   if (Attrib)
1030     SetNodeAttr(g, Attrib, TabNode);
1031 
1032   if (Header) {
1033     PCOLDEF cdp;
1034     PXNODE  rn, cn;
1035 
1036     if (Rowname) {
1037       TabNode->AddText(g, "\n\t");
1038       rn = TabNode->AddChildNode(g, Rowname, NULL);
1039     } else {
1040       strcpy(g->Message, MSG(NO_ROW_NODE));
1041       return true;
1042     } // endif Rowname
1043 
1044     if (Hdattr)
1045       SetNodeAttr(g, Hdattr, rn);
1046 
1047     for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) {
1048       rn->AddText(g, "\n\t\t");
1049       cn = rn->AddChildNode(g, "TH", NULL);
1050       cn->SetContent(g, (char *)cdp->GetName(),
1051                          strlen(cdp->GetName()) + 1);
1052       } // endfor cdp
1053 
1054     rn->AddText(g, "\n\t");
1055     } // endif ColType
1056 
1057   return false;
1058   } // end of SetTabNode
1059 
1060 /***********************************************************************/
1061 /*  Set attributes of a table or header node.                          */
1062 /***********************************************************************/
1063 void TDBXML::SetNodeAttr(PGLOBAL g, char *attr, PXNODE node)
1064   {
1065   char  *p, *pa, *pn = attr;
1066   PXATTR an;
1067 
1068   do {
1069     if ((p = strchr(pn, '='))) {
1070       pa = pn;
1071       *p++ = 0;
1072 
1073       if ((pn =   strchr(p, ';')))
1074         *pn++ = 0;
1075 
1076       an = node->AddProperty(g, pa, NULL);
1077       an->SetText(g, p, strlen(p) + 1);
1078     } else
1079       break;
1080 
1081     } while (pn);
1082 
1083   } // end of SetNodeAttr
1084 
1085 /***********************************************************************/
1086 /*  XML Cardinality: returns table cardinality in number of rows.      */
1087 /*  This function can be called with a null argument to test the       */
1088 /*  availability of Cardinality implementation (1 yes, 0 no).          */
1089 /***********************************************************************/
1090 int TDBXML::Cardinality(PGLOBAL g)
1091   {
1092   if (!g)
1093     return (Multiple || Xpand || Coltype == 2) ? 0 : 1;
1094 
1095   if (Multiple)
1096     return 10;
1097 
1098   if (Nrow < 0)
1099     if (Initialize(g))
1100       return -1;
1101 
1102   return (Void) ? 0 : Nrow - Header;
1103   } // end of Cardinality
1104 
1105 /***********************************************************************/
1106 /*  XML GetMaxSize: returns the number of tables in the database.      */
1107 /***********************************************************************/
1108 int TDBXML::GetMaxSize(PGLOBAL g)
1109   {
1110   if (MaxSize < 0) {
1111     if (!Multiple)
1112       MaxSize = Cardinality(g) * ((Xpand) ? Limit : 1);
1113     else
1114       MaxSize = 10;
1115 
1116     } // endif MaxSize
1117 
1118   return MaxSize;
1119   } // end of GetMaxSize
1120 
1121 /***********************************************************************/
1122 /*  Return the position in the table.                                  */
1123 /***********************************************************************/
1124 int TDBXML::GetRecpos(void)
1125   {
1126   union {
1127     uint Rpos;
1128     BYTE Spos[4];
1129     };
1130 
1131   Rpos = htonl(Irow);
1132   Spos[0] = (BYTE)Nsub;
1133   return Rpos;
1134   } // end of GetRecpos
1135 
1136 /***********************************************************************/
1137 /*  RowNumber: return the ordinal number of the current row.           */
1138 /***********************************************************************/
1139 int TDBXML::RowNumber(PGLOBAL g, bool b)
1140   {
1141   if (To_Kindex && (Xpand || Coltype == 2) && !b) {
1142     /*******************************************************************/
1143     /*  Don't know how to retrieve RowID for expanded XML tables.      */
1144     /*******************************************************************/
1145     sprintf(g->Message, MSG(NO_ROWID_FOR_AM),
1146                         GetAmName(g, GetAmType()));
1147     return 0;          // Means error
1148   } else
1149     return (b || !(Xpand || Coltype == 2)) ? Irow - Header + 1 : N;
1150 
1151   } // end of RowNumber
1152 
1153 /***********************************************************************/
1154 /*  XML Access Method opening routine.                                 */
1155 /***********************************************************************/
1156 bool TDBXML::OpenDB(PGLOBAL g)
1157   {
1158   if (Use == USE_OPEN) {
1159     /*******************************************************************/
1160     /*  Table already open replace it at its beginning.                */
1161     /*******************************************************************/
1162     if (!To_Kindex) {
1163       Irow = Header - 1;
1164       Nsub = 0;
1165     } else
1166       /*****************************************************************/
1167       /*  Table is to be accessed through a sorted index table.        */
1168       /*****************************************************************/
1169       To_Kindex->Reset();
1170 
1171     return false;
1172     } // endif use
1173 
1174   /*********************************************************************/
1175   /*  OpenDB: initialize the XML file processing.                      */
1176   /*********************************************************************/
1177   Write = (Mode == MODE_INSERT || Mode == MODE_UPDATE);
1178 
1179   if (Initialize(g))
1180     return true;
1181 
1182   NewRow = (Mode == MODE_INSERT);
1183   Nsub = 0;
1184   Use = USE_OPEN;       // Do it now in case we are recursively called
1185   return false;
1186   } // end of OpenDB
1187 
1188 /***********************************************************************/
1189 /*  Data Base read routine for XML access method.                      */
1190 /***********************************************************************/
1191 int TDBXML::ReadDB(PGLOBAL g)
1192   {
1193   bool same;
1194 
1195   if (Void)
1196     return RC_EF;
1197 
1198   /*********************************************************************/
1199   /*  Now start the pseudo reading process.                            */
1200   /*********************************************************************/
1201   if (To_Kindex) {
1202     /*******************************************************************/
1203     /*  Reading is by an index table.                                  */
1204     /*******************************************************************/
1205     union {
1206       uint Rpos;
1207       BYTE Spos[4];
1208       };
1209 
1210     int recpos = To_Kindex->Fetch(g);
1211 
1212     switch (recpos) {
1213       case -1:           // End of file reached
1214         return RC_EF;
1215       case -2:           // No match for join
1216         return RC_NF;
1217       case -3:           // Same record as last non null one
1218         same = true;
1219         return RC_OK;
1220       default:
1221         Rpos = recpos;
1222         Nsub = Spos[0];
1223         Spos[0] = 0;
1224 
1225         if (Irow != (signed)ntohl(Rpos)) {
1226           Irow = ntohl(Rpos);
1227           same = false;
1228         } else
1229           same = true;
1230 
1231       } // endswitch recpos
1232 
1233   } else {
1234     if (trace(1))
1235       htrc("TDBXML ReadDB: Irow=%d Nrow=%d\n", Irow, Nrow);
1236 
1237     // This is to force the table to be expanded when constructing
1238     // an index for which the expand column is not specified.
1239     if (Colp && Irow >= Header) {
1240       Colp->Eval(g);
1241       Colp->Reset();
1242       } // endif Colp
1243 
1244     if (!NextSame) {
1245       if (++Irow == Nrow)
1246         return RC_EF;
1247 
1248       same = false;
1249       Nsub = 0;
1250     } else {
1251       // Not sure the multiple column read will be called
1252       NextSame = false;
1253       same = true;
1254       Nsub++;
1255     } // endif NextSame
1256 
1257     N++;                          // RowID
1258   } // endif To_Kindex
1259 
1260   if (!same) {
1261     if (trace(2))
1262       htrc("TDBXML ReadDB: Irow=%d RowNode=%p\n", Irow, RowNode);
1263 
1264     // Get the new row node
1265 		if (Nlist) {
1266 			if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
1267 				sprintf(g->Message, MSG(MISSING_ROWNODE), Irow);
1268 				return RC_FX;
1269 			} // endif RowNode
1270 
1271 		} else
1272 			RowNode = TabNode;
1273 
1274     if (Colname && Coltype == 2)
1275       Clist = RowNode->SelectNodes(g, Colname, Clist);
1276 
1277     } // endif same
1278 
1279   return RC_OK;
1280   } // end of ReadDB
1281 
1282 /***********************************************************************/
1283 /*  CheckRow: called On Insert and Update. Must create the Row node    */
1284 /*  if it does not exist (Insert) and update the Clist if called by    */
1285 /*  a column having an Xpath because it can use an existing node that  */
1286 /*  was added while inserting or Updating this row.                    */
1287 /***********************************************************************/
1288 bool TDBXML::CheckRow(PGLOBAL g, bool b)
1289   {
1290   if (NewRow && Mode == MODE_INSERT)
1291   {
1292     if (Rowname) {
1293       TabNode->AddText(g, "\n\t");
1294       RowNode = TabNode->AddChildNode(g, Rowname, RowNode);
1295     } else {
1296       strcpy(g->Message, MSG(NO_ROW_NODE));
1297       return true;
1298     } // endif Rowname
1299   }
1300 
1301   if (Colname && (NewRow || b))
1302     Clist = RowNode->SelectNodes(g, Colname, Clist);
1303 
1304   return NewRow = false;
1305   } // end of CheckRow
1306 
1307 /***********************************************************************/
1308 /*  WriteDB: Data Base write routine for XDB access methods.           */
1309 /***********************************************************************/
1310 int TDBXML::WriteDB(PGLOBAL g)
1311   {
1312   if (Mode == MODE_INSERT) {
1313     if (Hasnod)
1314       RowNode->AddText(g, "\n\t");
1315 
1316     NewRow = true;
1317     } // endif Mode
1318 
1319   // Something was changed in the document
1320   Changed = true;
1321   return RC_OK;
1322   } // end of WriteDB
1323 
1324 /***********************************************************************/
1325 /*  Data Base delete line routine for XDB access methods.              */
1326 /***********************************************************************/
1327 int TDBXML::DeleteDB(PGLOBAL g, int irc)
1328   {
1329 	// TODO: Handle null Nlist
1330   if (irc == RC_FX) {
1331     // Delete all rows
1332     for (Irow = 0; Irow < Nrow; Irow++)
1333       if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) {
1334         sprintf(g->Message, MSG(MISSING_ROWNODE), Irow);
1335         return RC_FX;
1336       } else {
1337         TabNode->DeleteChild(g, RowNode);
1338 
1339         if (Nlist->DropItem(g, Irow))
1340           return RC_FX;
1341 
1342       } // endif RowNode
1343 
1344     Changed = true;
1345   } else if (irc != RC_EF) {
1346     TabNode->DeleteChild(g, RowNode);
1347 
1348     if (Nlist->DropItem(g, Irow))
1349       return RC_FX;
1350 
1351     Changed = true;
1352   } // endif's irc
1353 
1354   return RC_OK;
1355   } // end of DeleteDB
1356 
1357 /***********************************************************************/
1358 /*  Data Base close routine for XDB access methods.                    */
1359 /***********************************************************************/
1360 void TDBXML::CloseDB(PGLOBAL g)
1361   {
1362   if (Docp) {
1363     if (Changed) {
1364       char filename[_MAX_PATH];
1365 
1366       // We used the file name relative to recorded datapath
1367       PlugSetPath(filename, Xfile, GetPath());
1368 
1369       if (Mode == MODE_INSERT)
1370         TabNode->AddText(g, "\n");
1371 
1372       // Save the modified document
1373       if (Docp->DumpDoc(g, filename)) {
1374         PushWarning(g, this);
1375         Docp->CloseDoc(g, To_Xb);
1376 
1377         // This causes a crash in Diagnostics_area::set_error_status
1378 //				throw (int)TYPE_AM_XML;
1379 			} // endif DumpDoc
1380 
1381       } // endif Changed
1382 
1383     // Free the document and terminate XML processing
1384     Docp->CloseDoc(g, To_Xb);
1385     } // endif docp
1386 
1387   if (Multiple) {
1388     // Reset all constants to start a new parse
1389     Docp = NULL;
1390     Root = NULL;
1391     Curp = NULL;
1392     DBnode = NULL;
1393     TabNode = NULL;
1394     RowNode = NULL;
1395     ColNode = NULL;
1396     Nlist = NULL;
1397     Clist = NULL;
1398     To_Xb = NULL;
1399     Colp = NULL;
1400     Changed = false;
1401     Checked = false;
1402     NextSame = false;
1403     NewRow = false;
1404     Hasnod = false;
1405     Write = false;
1406     Nodedone = false;
1407     Void = false;
1408     Nrow = -1;
1409     Irow = Header - 1;
1410     Nsub = 0;
1411     N = 0;
1412     } // endif Multiple
1413 
1414   } // end of CloseDB
1415 
1416 // ------------------------ XMLCOL functions ----------------------------
1417 
1418 /***********************************************************************/
1419 /*  XMLCOL public constructor.                                        */
1420 /***********************************************************************/
1421 XMLCOL::XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1422       : COLBLK(cdp, tdbp, i)
1423   {
1424   if (cprec) {
1425     Next = cprec->GetNext();
1426     cprec->SetNext(this);
1427   } else {
1428     Next = tdbp->GetColumns();
1429     tdbp->SetColumns(this);
1430   } // endif cprec
1431 
1432   // Set additional XML access method information for column.
1433   Tdbp = (PTDBXML)tdbp;
1434   Nl = NULL;
1435   Nlx = NULL;
1436   ColNode = NULL;
1437   ValNode = NULL;
1438   Cxnp = NULL;
1439   Vxnp = NULL;
1440   Vxap = NULL;
1441   AttNode = NULL;
1442   Nodes = NULL;
1443   Nod = 0;
1444   Inod = -1;
1445   Mul = false;
1446   Checked = false;
1447   Xname = cdp->GetFmt();
1448   Long = cdp->GetLong();
1449   Rank = cdp->GetOffset();
1450   Type = Tdbp->Coltype;
1451   Nx = -1;
1452   Sx = -1;
1453   N = 0;
1454   Valbuf = NULL;
1455   To_Val = NULL;
1456   } // end of XMLCOL constructor
1457 
1458 /***********************************************************************/
1459 /*  XMLCOL constructor used for copying columns.                       */
1460 /*  tdbp is the pointer to the new table descriptor.                   */
1461 /***********************************************************************/
1462 XMLCOL::XMLCOL(XMLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
1463   {
1464   Tdbp = col1->Tdbp;
1465   Nl = col1->Nl;
1466   Nlx = col1->Nlx;
1467   ColNode = col1->ColNode;
1468   ValNode = col1->ValNode;
1469   Cxnp = col1->Cxnp;
1470   Vxnp = col1->Vxnp;
1471   Vxap = col1->Vxap;
1472   AttNode = col1->AttNode;
1473   Nodes = col1->Nodes;
1474   Nod = col1->Nod;
1475   Inod = col1->Inod;
1476   Mul = col1->Mul;
1477   Checked = col1->Checked;
1478   Xname = col1->Xname;
1479   Valbuf = col1->Valbuf;
1480   Long = col1->Long;
1481   Rank = col1->Rank;
1482   Nx = col1->Nx;
1483   Sx = col1->Sx;
1484   N = col1->N;
1485   Type = col1->Type;
1486   To_Val = col1->To_Val;
1487   } // end of XMLCOL copy constructor
1488 
1489 /***********************************************************************/
1490 /*  Allocate a buffer of the proper size.                              */
1491 /***********************************************************************/
1492 bool XMLCOL::AllocBuf(PGLOBAL g, bool mode)
1493   {
1494   if (Valbuf)
1495     return false;                       // Already done
1496 
1497   return ParseXpath(g, mode);
1498   } // end of AllocBuf
1499 
1500 /***********************************************************************/
1501 /*  Parse the eventual passed Xpath information.                       */
1502 /*  This information can be specified in the Xpath (or Fieldfmt)       */
1503 /*  column option when creating the table. It permits to indicate the  */
1504 /*  position of the node corresponding to that column in a Xpath-like  */
1505 /*  language (but not a truly compliant one).                          */
1506 /***********************************************************************/
1507 bool XMLCOL::ParseXpath(PGLOBAL g, bool mode)
1508   {
1509   char *p, *p2, *pbuf = NULL;
1510   int   i, n = 1, len = strlen(Name);
1511 
1512   len += ((Tdbp->Colname) ? strlen(Tdbp->Colname) : 0);
1513   len += ((Xname) ? strlen(Xname) : 0);
1514   pbuf = (char*)PlugSubAlloc(g, NULL, len + 3);
1515   *pbuf = '\0';
1516 
1517   if (!mode)
1518     // Take care of an eventual extra column node a la html
1519     if (Tdbp->Colname) {
1520       char *p = strstr(Tdbp->Colname, "%d");
1521       if (p)
1522         snprintf(pbuf, len + 3, "%.*s%d%s/", (int) (p - Tdbp->Colname), Tdbp->Colname,
1523             Rank + (Tdbp->Usedom ? 0 : 1), p + 2);
1524       else
1525         snprintf(pbuf, len + 3, "%s/", Tdbp->Colname);
1526     } // endif Colname
1527 
1528   if (Xname) {
1529     if (Type == 2) {
1530       sprintf(g->Message, MSG(BAD_COL_XPATH), Name, Tdbp->Name);
1531       return true;
1532     } else
1533       strcat(pbuf, Xname);
1534 
1535     if (trace(1))
1536       htrc("XMLCOL: pbuf=%s\n", pbuf);
1537 
1538     // For Update or Insert the Xpath must be analyzed
1539     if (mode) {
1540       for (i = 0, p = pbuf; (p = strchr(p, '/')); i++, p++)
1541         Nod++;                       // One path node found
1542 
1543       if (Nod)
1544         Nodes = (char**)PlugSubAlloc(g, NULL, Nod * sizeof(char*));
1545 
1546       } // endif mode
1547 
1548     // Analyze the Xpath for this column
1549     for (i = 0, p = pbuf; (p2 = strchr(p, '/')); i++, p = p2 + 1) {
1550       if (Tdbp->Mulnode && !strncmp(p, Tdbp->Mulnode, p2 - p))
1551       {
1552         if (!Tdbp->Xpand && mode) {
1553           strcpy(g->Message, MSG(CONCAT_SUBNODE));
1554           return true;
1555         } else
1556           Inod = i;                  // Index of multiple node
1557       }
1558 
1559       if (mode) {
1560         // For Update or Insert the Xpath must be explicit
1561         if (strchr("@/.*", *p)) {
1562           sprintf(g->Message, MSG(XPATH_NOT_SUPP), Name);
1563           return true;
1564         } else
1565           Nodes[i] = p;
1566 
1567         *p2 = '\0';
1568         } // endif mode
1569 
1570       } // endfor i, p
1571 
1572     if (*p == '/' || *p == '.') {
1573       sprintf(g->Message, MSG(XPATH_NOT_SUPP), Name);
1574       return true;
1575     } else if (*p == '@') {
1576       p++;                           // Remove the @ if mode
1577       Type = 0;                      // Column is an attribute
1578     } else
1579       Type = 1;                      // Column is a node
1580 
1581     if (!*p)
1582       strcpy(p, Name);               // Xname is column name
1583 
1584     if (Type && Tdbp->Mulnode && !strcmp(p, Tdbp->Mulnode))
1585       Inod = Nod;                    // Index of multiple node
1586 
1587     if (mode)                        // Prepare Xname
1588       pbuf = p;
1589 
1590   } else if (Type == 2) {
1591     // HTML like table, columns are retrieved by position
1592     new(this) XPOSCOL(Value);        // Change the class of this column
1593     Inod = -1;
1594   } else if (Type == 0 && !mode) {
1595     strcat(strcat(pbuf, "@"), Name);
1596   } else {                           // Type == 1
1597     if (Tdbp->Mulnode && !strcmp(Name, Tdbp->Mulnode))
1598       Inod = 0;                      // Nod
1599 
1600     strcat(pbuf, Name);
1601   } // endif,s
1602 
1603   if (Inod >= 0) {
1604     Tdbp->Colp = this;               // To force expand
1605 
1606     if (Tdbp->Xpand)
1607       n = Tdbp->Limit;
1608 
1609     new(this) XMULCOL(Value);        // Change the class of this column
1610     } // endif Inod
1611 
1612   Valbuf = (char*)PlugSubAlloc(g, NULL, n * (Long + 1));
1613 
1614   for (i = 0; i < n; i++)
1615     Valbuf[Long + (i * (Long + 1))] = '\0';
1616 
1617   if (Type || Nod)
1618     Tdbp->Hasnod = true;
1619 
1620   if (trace(1))
1621     htrc("XMLCOL: Xname=%s\n", pbuf);
1622 
1623   // Save the calculated Xpath
1624   Xname = pbuf;
1625   return false;
1626   } // end of ParseXpath
1627 
1628 /***********************************************************************/
1629 /*  SetBuffer: prepare a column block for write operation.             */
1630 /***********************************************************************/
1631 bool XMLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
1632   {
1633   if (!(To_Val = value)) {
1634     sprintf(g->Message, MSG(VALUE_ERROR), Name);
1635     return true;
1636   } else if (Buf_Type == value->GetType()) {
1637     // Values are of the (good) column type
1638     if (Buf_Type == TYPE_DATE) {
1639       // If any of the date values is formatted
1640       // output format must be set for the receiving table
1641       if (GetDomain() || ((DTVAL *)value)->IsFormatted())
1642         goto newval;          // This will make a new value;
1643 
1644     } else if (Buf_Type == TYPE_DOUBLE)
1645       // Float values must be written with the correct (column) precision
1646       // Note: maybe this should be forced by ShowValue instead of this ?
1647       value->SetPrec(GetScale());
1648 
1649     Value = value;            // Directly access the external value
1650   } else {
1651     // Values are not of the (good) column type
1652     if (check) {
1653       sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
1654               GetTypeName(Buf_Type), GetTypeName(value->GetType()));
1655       return true;
1656       } // endif check
1657 
1658  newval:
1659     if (InitValue(g))         // Allocate the matching value block
1660       return true;
1661 
1662   } // endif's Value, Buf_Type
1663 
1664   // Because Colblk's have been made from a copy of the original TDB in
1665   // case of Update, we must reset them to point to the original one.
1666   if (To_Tdb->GetOrig()) {
1667     To_Tdb = (PTDB)To_Tdb->GetOrig();
1668     Tdbp = (PTDBXML)To_Tdb;   // Specific of XMLCOL
1669 
1670     // Allocate the XML buffer
1671     if (AllocBuf(g, true))      // In Write mode
1672       return true;
1673 
1674     } // endif GetOrig
1675 
1676   // Set the Column
1677   Status = (ok) ? BUF_EMPTY : BUF_NO;
1678   return false;
1679   } // end of SetBuffer
1680 
1681 /***********************************************************************/
1682 /*  Alloc the nodes that will be used during the whole process.        */
1683 /***********************************************************************/
1684 void XMLCOL::AllocNodes(PGLOBAL g, PXDOC dp)
1685 {
1686   Cxnp = dp->NewPnode(g);
1687   Vxnp = dp->NewPnode(g);
1688   Vxap = dp->NewPattr(g);
1689 } // end of AllocNodes
1690 
1691 /***********************************************************************/
1692 /*  ReadColumn: what this routine does is to access the column node    */
1693 /*  from the corresponding table, extract from it the node text and    */
1694 /*  convert it to the column type.                                     */
1695 /***********************************************************************/
1696 void XMLCOL::ReadColumn(PGLOBAL g)
1697   {
1698   if (Nx == Tdbp->Irow)
1699     return;                         // Same row than the last read
1700 
1701   ValNode = Tdbp->RowNode->SelectSingleNode(g, Xname, Vxnp);
1702 
1703   if (ValNode) {
1704     if (ValNode->GetType() != XML_ELEMENT_NODE &&
1705         ValNode->GetType() != XML_ATTRIBUTE_NODE) {
1706       sprintf(g->Message, MSG(BAD_VALNODE), ValNode->GetType(), Name);
1707 			throw (int)TYPE_AM_XML;
1708 		} // endif type
1709 
1710     // Get the Xname value from the XML file
1711     switch (ValNode->GetContent(g, Valbuf, Long + 1)) {
1712       case RC_OK:
1713         break;
1714       case RC_INFO:
1715         PushWarning(g, Tdbp);
1716         break;
1717       default:
1718 				throw (int)TYPE_AM_XML;
1719 		} // endswitch
1720 
1721     Value->SetValue_psz(Valbuf);
1722   } else {
1723     if (Nullable)
1724       Value->SetNull(true);
1725 
1726     Value->Reset();              // Null value
1727   } // endif ValNode
1728 
1729   Nx = Tdbp->Irow;
1730   } // end of ReadColumn
1731 
1732 /***********************************************************************/
1733 /*  WriteColumn: what this routine does is to access the last row of   */
1734 /*  the corresponding table, and rewrite the content corresponding     */
1735 /*  to this column node from the column buffer and type.               */
1736 /***********************************************************************/
1737 void XMLCOL::WriteColumn(PGLOBAL g)
1738   {
1739   char  *p, buf[16];
1740   int    done = 0;
1741   int   i, n, k = 0;
1742   PXNODE TopNode = NULL;
1743 
1744   if (trace(2))
1745     htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
1746           Name, Tdbp->GetTdb_No(), ColUse, Status);
1747 
1748   /*********************************************************************/
1749   /*  Check whether this node must be written.                         */
1750   /*********************************************************************/
1751   if (Value != To_Val)
1752     Value->SetValue_pval(To_Val, false);    // Convert the updated value
1753 
1754   /*********************************************************************/
1755   /*  If a check pass was done while updating, all node contruction    */
1756   /*  has been already one.                                            */
1757   /*********************************************************************/
1758   if (Status && Tdbp->Checked && !Value->IsNull()) {
1759     assert (ColNode != NULL);
1760     assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL);
1761     goto fin;
1762     } // endif Checked
1763 
1764   /*********************************************************************/
1765   /*  On Insert, a Row node must be created for each row;              */
1766   /*  For columns having an Xpath, the Clist must be updated.          */
1767   /*********************************************************************/
1768   if (Tdbp->CheckRow(g, Nod || Tdbp->Colname))
1769 		throw (int)TYPE_AM_XML;
1770 
1771   /*********************************************************************/
1772   /*  Null values are represented by no node.                          */
1773   /*********************************************************************/
1774 	if (Value->IsNull())
1775     return;
1776 
1777   /*********************************************************************/
1778   /*  Find the column and value nodes to update or insert.             */
1779   /*********************************************************************/
1780   if (Tdbp->Clist) {
1781     n =  Tdbp->Clist->GetLength();
1782     ColNode = NULL;
1783   } else {
1784     n = 1;
1785     ColNode = Tdbp->RowNode->Clone(g, ColNode);
1786   } // endif Clist
1787 
1788   ValNode = NULL;
1789 
1790   for (i = 0; i < n; i++) {
1791     if (Tdbp->Clist)
1792       ColNode = Tdbp->Clist->GetItem(g, i, Cxnp);
1793 
1794     /*******************************************************************/
1795     /*  Check whether an Xpath was provided to go to the column node.  */
1796     /*******************************************************************/
1797     for (k = 0; k < Nod; k++)
1798       if ((ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp)))
1799         TopNode = ColNode;
1800       else
1801         break;
1802 
1803     if (ColNode)
1804     {
1805       if (Type)
1806         ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp);
1807       else
1808         AttNode = ColNode->GetAttribute(g, Xname, Vxap);
1809     }
1810 
1811     if (TopNode || ValNode || AttNode)
1812       break;                      // We found the good column
1813     else if (Tdbp->Clist)
1814       ColNode = NULL;
1815 
1816     // refresh CList in case its Listp was freed in SelectSingleNode above
1817     if (Tdbp->Clist)
1818       Tdbp->RowNode->SelectNodes(g, Tdbp->Colname, Tdbp->Clist);
1819     } // endfor i
1820 
1821   /*********************************************************************/
1822   /*  Create missing nodes.                                            */
1823   /*********************************************************************/
1824   if (ColNode == NULL) {
1825     if (TopNode == NULL)
1826     {
1827       if (Tdbp->Clist) {
1828         Tdbp->RowNode->AddText(g, "\n\t\t");
1829         ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname);
1830         done = 2;
1831         TopNode = ColNode;
1832       } else
1833         TopNode = Tdbp->RowNode;
1834     }
1835     for (; k < Nod && TopNode; k++) {
1836       if (!done) {
1837         TopNode->AddText(g, "\n\t\t");
1838         done = 1;
1839         } // endif done
1840 
1841       ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp);
1842       TopNode = ColNode;
1843       } // endfor k
1844 
1845     if (ColNode == NULL) {
1846       strcpy(g->Message, MSG(COL_ALLOC_ERR));
1847 			throw (int)TYPE_AM_XML;
1848 		} // endif ColNode
1849 
1850     } // endif ColNode
1851 
1852   if (Type == 1) {
1853     if (ValNode == NULL) {
1854       if (done < 2)
1855         ColNode->AddText(g, "\n\t\t");
1856 
1857       ValNode = ColNode->AddChildNode(g, Xname, Vxnp);
1858       } // endif ValNode
1859 
1860   } else // (Type == 0)
1861     if (AttNode == NULL)
1862       AttNode = ColNode->AddProperty(g, Xname, Vxap);
1863 
1864   if (ValNode == NULL && AttNode == NULL) {
1865     strcpy(g->Message, MSG(VAL_ALLOC_ERR));
1866     throw (int)TYPE_AM_XML;
1867     } // endif ValNode
1868 
1869   /*********************************************************************/
1870   /*  Get the string representation of Value according to column type. */
1871   /*********************************************************************/
1872   p = Value->GetCharString(buf);
1873 
1874   if (strlen(p) > (unsigned)Long) {
1875     sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long);
1876 		throw (int)TYPE_AM_XML;
1877 	} else
1878     strcpy(Valbuf, p);
1879 
1880   /*********************************************************************/
1881   /*  Updating must be done only when not in checking pass.            */
1882   /*********************************************************************/
1883  fin:
1884   if (Status) {
1885     if (Type) {
1886       ValNode->SetContent(g, Valbuf, Long);
1887     } else
1888       AttNode->SetText(g, Valbuf, Long);
1889 
1890     } // endif Status
1891 
1892   } // end of WriteColumn
1893 
1894 // ------------------------ XMULCOL functions ---------------------------
1895 
1896 /***********************************************************************/
1897 /*  ReadColumn: what this routine does is to access the column node    */
1898 /*  from the corresponding table, extract from it the node text and    */
1899 /*  convert it to the column type.                                     */
1900 /***********************************************************************/
1901 void XMULCOL::ReadColumn(PGLOBAL g)
1902   {
1903   char *p;
1904   int   i, len;
1905   bool  b = Tdbp->Xpand;
1906 
1907   if (Nx != Tdbp->Irow) {                     // New row
1908     Nl = Tdbp->RowNode->SelectNodes(g, Xname, Nl);
1909 
1910     if ((N = Nl->GetLength())) {
1911       *(p = Valbuf) = '\0';
1912       len = Long;
1913 
1914       if (N > Tdbp->Limit) {
1915         N = Tdbp->Limit;
1916         sprintf(g->Message, "Multiple values limited to %d", Tdbp->Limit);
1917         PushWarning(g, Tdbp);
1918         } // endif N
1919 
1920       for (i = 0; i < N; i++) {
1921         ValNode = Nl->GetItem(g, i, Vxnp);
1922 
1923         if (ValNode->GetType() != XML_ELEMENT_NODE &&
1924             ValNode->GetType() != XML_ATTRIBUTE_NODE) {
1925           sprintf(g->Message, MSG(BAD_VALNODE), ValNode->GetType(), Name);
1926 					throw (int)TYPE_AM_XML;
1927 				} // endif type
1928 
1929         // Get the Xname value from the XML file
1930         switch (ValNode->GetContent(g, p, (b ? Long : len))) {
1931           case RC_OK:
1932             break;
1933           case RC_INFO:
1934             PushWarning(g, Tdbp);
1935             break;
1936           default:
1937             throw (int)TYPE_AM_XML;
1938           } // endswitch
1939 
1940         if (!b) {
1941           // Concatenate all values
1942           if (N - i > 1)
1943             strncat(Valbuf, ", ", len - strlen(p));
1944 
1945           if ((len -= strlen(p)) <= 0)
1946             break;
1947 
1948           p += strlen(p);
1949         } else            // Xpand
1950           p += (Long + 1);
1951 
1952         } // endfor i
1953 
1954       Value->SetValue_psz(Valbuf);
1955     } else {
1956       if (Nullable)
1957         Value->SetNull(true);
1958 
1959       Value->Reset();              // Null value
1960     } // endif ValNode
1961 
1962   } else if (Sx == Tdbp->Nsub)
1963     return;                        // Same row
1964   else                             // Expanded value
1965     Value->SetValue_psz(Valbuf + (Tdbp->Nsub * (Long + 1)));
1966 
1967   Nx = Tdbp->Irow;
1968   Sx = Tdbp->Nsub;
1969   Tdbp->NextSame = (Tdbp->Xpand && N - Sx > 1);
1970   } // end of ReadColumn
1971 
1972 /***********************************************************************/
1973 /*  WriteColumn: what this routine does is to access the last line     */
1974 /*  read from the corresponding table, and rewrite the field           */
1975 /*  corresponding to this column from the column buffer and type.      */
1976 /***********************************************************************/
1977 void XMULCOL::WriteColumn(PGLOBAL g)
1978   {
1979   char  *p, buf[16];
1980   int    done = 0;
1981   int   i, n, len, k = 0;
1982   PXNODE TopNode = NULL;
1983 
1984   if (trace(1))
1985     htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
1986           Name, Tdbp->GetTdb_No(), ColUse, Status);
1987 
1988   /*********************************************************************/
1989   /*  Check whether this node must be written.                         */
1990   /*********************************************************************/
1991   if (Value != To_Val)
1992     Value->SetValue_pval(To_Val, false);    // Convert the updated value
1993 
1994   if (Value->IsNull())
1995     return;
1996 
1997   /*********************************************************************/
1998   /*  If a check pass was done while updating, all node contruction    */
1999   /*  has been already one.                                            */
2000   /*********************************************************************/
2001   if (Status && Tdbp->Checked) {
2002     assert (ColNode);
2003     assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL);
2004     goto fin;
2005     } // endif Checked
2006 
2007   /*********************************************************************/
2008   /*  On Insert, a Row node must be created for each row;              */
2009   /*  For columns having an Xpath, the Clist must be updated.          */
2010   /*********************************************************************/
2011   if (Tdbp->CheckRow(g, Nod))
2012 		throw (int)TYPE_AM_XML;
2013 
2014   /*********************************************************************/
2015   /*  Find the column and value nodes to update or insert.             */
2016   /*********************************************************************/
2017   if (Tdbp->Clist) {
2018     n =  Tdbp->Clist->GetLength();
2019     ColNode = NULL;
2020   } else {
2021     n = 1;
2022     ColNode = Tdbp->RowNode->Clone(g, ColNode);
2023   } // endif Clist
2024 
2025   ValNode = NULL;
2026 
2027   for (i = 0; i < n; i++) {
2028     if (Tdbp->Clist)
2029       ColNode = Tdbp->Clist->GetItem(g, i, Cxnp);
2030 
2031     /*******************************************************************/
2032     /*  Check whether an Xpath was provided to go to the column node.  */
2033     /*******************************************************************/
2034     for (k = 0; k < Nod; k++) {
2035       if (k == Inod) {
2036         // This is the multiple node
2037         Nlx = ColNode->SelectNodes(g, Nodes[k], Nlx);
2038         ColNode = Nlx->GetItem(g, Tdbp->Nsub, Cxnp);
2039       } else
2040         ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp);
2041 
2042       if (ColNode == NULL)
2043         break;
2044 
2045       TopNode = ColNode;
2046       } // endfor k
2047 
2048     if (ColNode)
2049     {
2050       if (Inod == Nod) {
2051         /***************************************************************/
2052         /*  The node value can be multiple.                            */
2053         /***************************************************************/
2054         assert (Type);
2055 
2056         // Get the value Node from the XML list
2057         Nlx = ColNode->SelectNodes(g, Xname, Nlx);
2058         len = Nlx->GetLength();
2059 
2060         if (len > 1 && !Tdbp->Xpand) {
2061           sprintf(g->Message, MSG(BAD_VAL_UPDATE), Name);
2062 					throw (int)TYPE_AM_XML;
2063 				} else
2064           ValNode = Nlx->GetItem(g, Tdbp->Nsub, Vxnp);
2065 
2066       } else  // Inod != Nod
2067       {
2068         if (Type)
2069           ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp);
2070         else
2071           AttNode = ColNode->GetAttribute(g, Xname, Vxap);
2072       }
2073     }
2074     if (TopNode || ValNode || AttNode)
2075       break;                     // We found the good column
2076     else if (Tdbp->Clist)
2077       ColNode = NULL;
2078 
2079     } // endfor i
2080 
2081   /*********************************************************************/
2082   /*  Create missing nodes.                                            */
2083   /*********************************************************************/
2084   if (ColNode == NULL) {
2085     if (TopNode == NULL)
2086     {
2087       if (Tdbp->Clist) {
2088         Tdbp->RowNode->AddText(g, "\n\t\t");
2089         ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname);
2090         done = 2;
2091         TopNode = ColNode;
2092       } else
2093         TopNode = Tdbp->RowNode;
2094     }
2095 
2096     for (; k < Nod && TopNode; k++) {
2097       if (!done) {
2098         TopNode->AddText(g, "\n\t\t");
2099         done = 1;
2100         } // endif done
2101 
2102       ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp);
2103       TopNode = ColNode;
2104       } // endfor k
2105 
2106     if (ColNode == NULL) {
2107       strcpy(g->Message, MSG(COL_ALLOC_ERR));
2108 			throw (int)TYPE_AM_XML;
2109 		} // endif ColNode
2110 
2111     } // endif ColNode
2112 
2113   if (Type == 1) {
2114     if (ValNode == NULL) {
2115       if (done < 2)
2116         ColNode->AddText(g, "\n\t\t");
2117 
2118       ValNode = ColNode->AddChildNode(g, Xname, Vxnp);
2119       } // endif ValNode
2120 
2121   } else // (Type == 0)
2122     if (AttNode == NULL)
2123       AttNode = ColNode->AddProperty(g, Xname, Vxap);
2124 
2125   if (ValNode == NULL && AttNode == NULL) {
2126     strcpy(g->Message, MSG(VAL_ALLOC_ERR));
2127 		throw (int)TYPE_AM_XML;
2128 	} // endif ValNode
2129 
2130   /*********************************************************************/
2131   /*  Get the string representation of Value according to column type. */
2132   /*********************************************************************/
2133   p = Value->GetCharString(buf);
2134 
2135   if (strlen(p) > (unsigned)Long) {
2136     sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long);
2137 		throw (int)TYPE_AM_XML;
2138 	} else
2139     strcpy(Valbuf, p);
2140 
2141   /*********************************************************************/
2142   /*  Updating must be done only when not in checking pass.            */
2143   /*********************************************************************/
2144  fin:
2145   if (Status) {
2146     if (Type) {
2147       ValNode->SetContent(g, Valbuf, Long);
2148     } else
2149       AttNode->SetText(g, Valbuf, Long);
2150 
2151     } // endif Status
2152 
2153   } // end of WriteColumn
2154 
2155 /* ------------------------ XPOSCOL functions ------------------------ */
2156 
2157 /***********************************************************************/
2158 /*  ReadColumn: what this routine does is to access the column node    */
2159 /*  from the corresponding table, extract from it the node text and    */
2160 /*  convert it to the column type.                                     */
2161 /***********************************************************************/
2162 void XPOSCOL::ReadColumn(PGLOBAL g)
2163   {
2164   if (Nx == Tdbp->Irow)
2165     return;                         // Same row than the last read
2166 
2167   if (Tdbp->Clist == NULL) {
2168     strcpy(g->Message, MSG(MIS_TAG_LIST));
2169 		throw (int)TYPE_AM_XML;
2170 	} // endif Clist
2171 
2172   if ((ValNode = Tdbp->Clist->GetItem(g, Rank, Vxnp))) {
2173     // Get the column value from the XML file
2174     switch (ValNode->GetContent(g, Valbuf, Long + 1)) {
2175       case RC_OK:
2176         break;
2177       case RC_INFO:
2178         PushWarning(g, Tdbp);
2179         break;
2180       default:
2181 				throw (int)TYPE_AM_XML;
2182 		} // endswitch
2183 
2184     Value->SetValue_psz(Valbuf);
2185   } else {
2186     if (Nullable)
2187       Value->SetNull(true);
2188 
2189     Value->Reset();              // Null value
2190   } // endif ValNode
2191 
2192   Nx = Tdbp->Irow;
2193   } // end of ReadColumn
2194 
2195 /***********************************************************************/
2196 /*  WriteColumn: what this routine does is to access the last line     */
2197 /*  read from the corresponding table, and rewrite the field           */
2198 /*  corresponding to this column from the column buffer and type.      */
2199 /***********************************************************************/
2200 void XPOSCOL::WriteColumn(PGLOBAL g)
2201   {
2202   char          *p, buf[16];
2203   int           i, k, n;
2204 
2205   if (trace(1))
2206     htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
2207           Name, Tdbp->GetTdb_No(), ColUse, Status);
2208 
2209   /*********************************************************************/
2210   /*  Check whether this node must be written.                         */
2211   /*********************************************************************/
2212   if (Value != To_Val)
2213     Value->SetValue_pval(To_Val, false);    // Convert the updated value
2214 
2215   if (Value->IsNull())
2216     return;
2217 
2218   /*********************************************************************/
2219   /*  If a check pass was done while updating, all node contruction    */
2220   /*  has been already one.                                            */
2221   /*********************************************************************/
2222   if (Status && Tdbp->Checked) {
2223     assert (ValNode);
2224     goto fin;
2225     } // endif Checked
2226 
2227   /*********************************************************************/
2228   /*  On Insert, a Row node must be created for each row;              */
2229   /*  For all columns the Clist must be updated.                       */
2230   /*********************************************************************/
2231   if (Tdbp->CheckRow(g, true))
2232 		throw (int)TYPE_AM_XML;
2233 
2234   /*********************************************************************/
2235   /*  Find the column and value nodes to update or insert.             */
2236   /*********************************************************************/
2237   if (Tdbp->Clist == NULL) {
2238     strcpy(g->Message, MSG(MIS_TAG_LIST));
2239 		throw (int)TYPE_AM_XML;
2240 	} // endif Clist
2241 
2242   n =  Tdbp->Clist->GetLength();
2243   k = Rank;
2244 
2245   if (!(ValNode = Tdbp->Clist->GetItem(g, k, Vxnp))) {
2246     /*******************************************************************/
2247     /*  Create missing column nodes.                                   */
2248     /*******************************************************************/
2249     Tdbp->RowNode->AddText(g, "\n\t\t");
2250 
2251     for (i = n; i <= k; i++)
2252       ValNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname, Vxnp);
2253 
2254     assert (ValNode);
2255     } // endif ValNode
2256 
2257   /*********************************************************************/
2258   /*  Get the string representation of Value according to column type. */
2259   /*********************************************************************/
2260   p = Value->GetCharString(buf);
2261 
2262   if (strlen(p) > (unsigned)Long) {
2263     sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long);
2264 		throw (int)TYPE_AM_XML;
2265 	} else
2266     strcpy(Valbuf, p);
2267 
2268   /*********************************************************************/
2269   /*  Updating must be done only when not in checking pass.            */
2270   /*********************************************************************/
2271  fin:
2272   if (Status)
2273     ValNode->SetContent(g, Valbuf, Long);
2274 
2275   } // end of WriteColumn
2276 
2277 /* ---------------------------TDBXCT class --------------------------- */
2278 
2279 /***********************************************************************/
2280 /*  TDBXCT class constructor.                                          */
2281 /***********************************************************************/
2282 TDBXCT::TDBXCT(PXMLDEF tdp) : TDBCAT(tdp)
2283   {
2284   Topt = tdp->GetTopt();
2285   //Db = (char*)tdp->GetDB();
2286 	Db = (char*)tdp->Schema;
2287 	Tabn = tdp->Tabname;
2288   } // end of TDBXCT constructor
2289 
2290 /***********************************************************************/
2291 /*  GetResult: Get the list the JSON file columns.                     */
2292 /***********************************************************************/
2293 PQRYRES TDBXCT::GetResult(PGLOBAL g)
2294   {
2295   return XMLColumns(g, Db, Tabn, Topt, false);
2296   } // end of GetResult
2297 
2298 /* ------------------------ End of Tabxml ---------------------------- */
2299