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