~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to contrib/xml2/xpath.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Parser interface for DOM-based parser (libxml) rather than
 
2
   stream-based SAX-type parser */
 
3
 
 
4
#include "postgres.h"
 
5
#include "fmgr.h"
 
6
#include "executor/spi.h"
 
7
#include "funcapi.h"
 
8
#include "miscadmin.h"
 
9
#include "lib/stringinfo.h"
 
10
 
 
11
/* libxml includes */
 
12
 
 
13
#include <libxml/xpath.h>
 
14
#include <libxml/tree.h>
 
15
#include <libxml/xmlmemory.h>
 
16
#include <libxml/xmlerror.h>
 
17
#include <libxml/parserInternals.h>
 
18
 
 
19
/* declarations */
 
20
 
 
21
static void *pgxml_palloc(size_t size);
 
22
static void *pgxml_repalloc(void *ptr, size_t size);
 
23
static void pgxml_pfree(void *ptr);
 
24
static char *pgxml_pstrdup(const char *string);
 
25
static void pgxml_errorHandler(void *ctxt, const char *msg,...);
 
26
 
 
27
void            elog_error(int level, char *explain, int force);
 
28
void            pgxml_parser_init(void);
 
29
 
 
30
static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 
31
                                   xmlChar * toptagname, xmlChar * septagname,
 
32
                                   xmlChar * plainsep);
 
33
 
 
34
text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar * toptag,
 
35
                                         xmlChar * septag, xmlChar * plainsep);
 
36
 
 
37
xmlChar    *pgxml_texttoxmlchar(text *textstring);
 
38
 
 
39
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar * xpath);
 
40
 
 
41
 
 
42
Datum           xml_valid(PG_FUNCTION_ARGS);
 
43
Datum           xml_encode_special_chars(PG_FUNCTION_ARGS);
 
44
Datum           xpath_nodeset(PG_FUNCTION_ARGS);
 
45
Datum           xpath_string(PG_FUNCTION_ARGS);
 
46
Datum           xpath_number(PG_FUNCTION_ARGS);
 
47
Datum           xpath_bool(PG_FUNCTION_ARGS);
 
48
Datum           xpath_list(PG_FUNCTION_ARGS);
 
49
Datum           xpath_table(PG_FUNCTION_ARGS);
 
50
 
 
51
/* Global variables */
 
52
char       *errbuf;                             /* per line error buffer */
 
53
char       *pgxml_errorMsg = NULL;              /* overall error message */
 
54
 
 
55
/* Convenience macros */
 
56
 
 
57
#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
 
58
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
 
59
 
 
60
#define ERRBUF_SIZE 200
 
61
 
 
62
/* memory handling passthrough functions (e.g. palloc, pstrdup are
 
63
   currently macros, and the others might become so...) */
 
64
 
 
65
static void *
 
66
pgxml_palloc(size_t size)
 
67
{
 
68
/*      elog(DEBUG1,"Alloc %d in CMC %x",size,CurrentMemoryContext); */
 
69
        return palloc(size);
 
70
}
 
71
 
 
72
static void *
 
73
pgxml_repalloc(void *ptr, size_t size)
 
74
{
 
75
/*      elog(DEBUG1,"ReAlloc in CMC %x",CurrentMemoryContext);*/
 
76
        return repalloc(ptr, size);
 
77
}
 
78
 
 
79
static void
 
80
pgxml_pfree(void *ptr)
 
81
{
 
82
/*      elog(DEBUG1,"Free in CMC %x",CurrentMemoryContext); */
 
83
        pfree(ptr);
 
84
}
 
85
 
 
86
static char *
 
87
pgxml_pstrdup(const char *string)
 
88
{
 
89
        return pstrdup(string);
 
90
}
 
91
 
 
92
/* The error handling function. This formats an error message and sets
 
93
 * a flag - an ereport will be issued prior to return
 
94
 */
 
95
 
 
96
static void
 
97
pgxml_errorHandler(void *ctxt, const char *msg,...)
 
98
{
 
99
        va_list         args;
 
100
 
 
101
        va_start(args, msg);
 
102
        vsnprintf(errbuf, ERRBUF_SIZE, msg, args);
 
103
        va_end(args);
 
104
        /* Now copy the argument across */
 
105
        if (pgxml_errorMsg == NULL)
 
106
                pgxml_errorMsg = pstrdup(errbuf);
 
107
        else
 
108
        {
 
109
                int32           xsize = strlen(pgxml_errorMsg);
 
110
 
 
111
                pgxml_errorMsg = repalloc(pgxml_errorMsg,
 
112
                                                                  (size_t) (xsize + strlen(errbuf) + 1));
 
113
                strncpy(&pgxml_errorMsg[xsize - 1], errbuf, strlen(errbuf));
 
114
                pgxml_errorMsg[xsize + strlen(errbuf) - 1] = '\0';
 
115
 
 
116
        }
 
117
        memset(errbuf, 0, ERRBUF_SIZE);
 
118
}
 
119
 
 
120
/* This function reports the current message at the level specified */
 
121
void
 
122
elog_error(int level, char *explain, int force)
 
123
{
 
124
        if (force || (pgxml_errorMsg != NULL))
 
125
        {
 
126
                if (pgxml_errorMsg == NULL)
 
127
                {
 
128
                        ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
 
129
                                                        errmsg(explain)));
 
130
                }
 
131
                else
 
132
                {
 
133
                        ereport(level, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
 
134
                                                        errmsg("%s:%s", explain, pgxml_errorMsg)));
 
135
                        pfree(pgxml_errorMsg);
 
136
                }
 
137
        }
 
138
}
 
139
 
 
140
void
 
141
pgxml_parser_init()
 
142
{
 
143
        /*
 
144
         * This code could also set parser settings from  user-supplied info.
 
145
         * Quite how these settings are made is another matter :)
 
146
         */
 
147
 
 
148
        xmlMemSetup(pgxml_pfree, pgxml_palloc, pgxml_repalloc, pgxml_pstrdup);
 
149
        xmlInitParser();
 
150
 
 
151
        xmlSetGenericErrorFunc(NULL, pgxml_errorHandler);
 
152
 
 
153
        xmlSubstituteEntitiesDefault(1);
 
154
        xmlLoadExtDtdDefaultValue = 1;
 
155
 
 
156
        pgxml_errorMsg = NULL;
 
157
 
 
158
        errbuf = palloc(200);
 
159
        memset(errbuf, 0, 200);
 
160
 
 
161
}
 
162
 
 
163
 
 
164
/* Returns true if document is well-formed */
 
165
 
 
166
PG_FUNCTION_INFO_V1(xml_valid);
 
167
 
 
168
Datum
 
169
xml_valid(PG_FUNCTION_ARGS)
 
170
{
 
171
        /* called as xml_valid(document) */
 
172
        xmlDocPtr       doctree;
 
173
        text       *t = PG_GETARG_TEXT_P(0);            /* document buffer */
 
174
        int32           docsize = VARSIZE(t) - VARHDRSZ;
 
175
 
 
176
        pgxml_parser_init();
 
177
 
 
178
        doctree = xmlParseMemory((char *) VARDATA(t), docsize);
 
179
        if (doctree == NULL)
 
180
        {
 
181
                xmlCleanupParser();
 
182
                PG_RETURN_BOOL(false);  /* i.e. not well-formed */
 
183
        }
 
184
        xmlCleanupParser();
 
185
        xmlFreeDoc(doctree);
 
186
        PG_RETURN_BOOL(true);
 
187
}
 
188
 
 
189
 
 
190
/* Encodes special characters (<, >, &, " and \r) as XML entities */
 
191
 
 
192
PG_FUNCTION_INFO_V1(xml_encode_special_chars);
 
193
 
 
194
Datum
 
195
xml_encode_special_chars(PG_FUNCTION_ARGS)
 
196
{
 
197
        text *tin = PG_GETARG_TEXT_P(0);
 
198
        text *tout;
 
199
        int32 ressize;
 
200
        xmlChar *ts, *tt;
 
201
 
 
202
        ts = pgxml_texttoxmlchar(tin);
 
203
 
 
204
        tt = xmlEncodeSpecialChars(NULL, ts);
 
205
 
 
206
        pfree(ts);
 
207
 
 
208
        ressize = strlen(tt);
 
209
        tout = (text *) palloc(ressize + VARHDRSZ);
 
210
        memcpy(VARDATA(tout), tt, ressize);
 
211
        VARATT_SIZEP(tout) = ressize + VARHDRSZ;
 
212
 
 
213
        xmlFree(tt);
 
214
 
 
215
        PG_RETURN_TEXT_P(tout);
 
216
}
 
217
 
 
218
static xmlChar
 
219
*
 
220
pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
 
221
                                   xmlChar * toptagname,
 
222
                                   xmlChar * septagname,
 
223
                                   xmlChar * plainsep)
 
224
{
 
225
        /* Function translates a nodeset into a text representation */
 
226
 
 
227
        /*
 
228
         * iterates over each node in the set and calls xmlNodeDump to write
 
229
         * it to an xmlBuffer -from which an xmlChar * string is returned.
 
230
         */
 
231
 
 
232
        /* each representation is surrounded by <tagname> ... </tagname> */
 
233
 
 
234
        /*
 
235
         * plainsep is an ordinary (not tag) seperator - if used, then nodes
 
236
         * are cast to string as output method
 
237
         */
 
238
 
 
239
 
 
240
        xmlBufferPtr buf;
 
241
        xmlChar    *result;
 
242
        int                     i;
 
243
 
 
244
        buf = xmlBufferCreate();
 
245
 
 
246
        if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
 
247
        {
 
248
                xmlBufferWriteChar(buf, "<");
 
249
                xmlBufferWriteCHAR(buf, toptagname);
 
250
                xmlBufferWriteChar(buf, ">");
 
251
        }
 
252
        if (nodeset != NULL)
 
253
        {
 
254
                for (i = 0; i < nodeset->nodeNr; i++)
 
255
                {
 
256
 
 
257
                        if (plainsep != NULL)
 
258
                        {
 
259
                                xmlBufferWriteCHAR(buf,
 
260
                                                  xmlXPathCastNodeToString(nodeset->nodeTab[i]));
 
261
 
 
262
                                /* If this isn't the last entry, write the plain sep. */
 
263
                                if (i < (nodeset->nodeNr) - 1)
 
264
                                        xmlBufferWriteChar(buf, plainsep);
 
265
                        }
 
266
                        else
 
267
                        {
 
268
 
 
269
 
 
270
                                if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
 
271
                                {
 
272
                                        xmlBufferWriteChar(buf, "<");
 
273
                                        xmlBufferWriteCHAR(buf, septagname);
 
274
                                        xmlBufferWriteChar(buf, ">");
 
275
                                }
 
276
                                xmlNodeDump(buf,
 
277
                                                        nodeset->nodeTab[i]->doc,
 
278
                                                        nodeset->nodeTab[i],
 
279
                                                        1, 0);
 
280
 
 
281
                                if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
 
282
                                {
 
283
                                        xmlBufferWriteChar(buf, "</");
 
284
                                        xmlBufferWriteCHAR(buf, septagname);
 
285
                                        xmlBufferWriteChar(buf, ">");
 
286
                                }
 
287
                        }
 
288
                }
 
289
        }
 
290
 
 
291
        if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
 
292
        {
 
293
                xmlBufferWriteChar(buf, "</");
 
294
                xmlBufferWriteCHAR(buf, toptagname);
 
295
                xmlBufferWriteChar(buf, ">");
 
296
        }
 
297
        result = xmlStrdup(buf->content);
 
298
        xmlBufferFree(buf);
 
299
        return result;
 
300
}
 
301
 
 
302
 
 
303
/* Translate a PostgreSQL "varlena" -i.e. a variable length parameter
 
304
 * into the libxml2 representation
 
305
 */
 
306
 
 
307
xmlChar *
 
308
pgxml_texttoxmlchar(text *textstring)
 
309
{
 
310
        xmlChar    *res;
 
311
        int32           txsize;
 
312
 
 
313
        txsize = VARSIZE(textstring) - VARHDRSZ;
 
314
        res = (xmlChar *) palloc(txsize + 1);
 
315
        memcpy((char *) res, VARDATA(textstring), txsize);
 
316
        res[txsize] = '\0';
 
317
        return res;
 
318
}
 
319
 
 
320
/* Public visible XPath functions */
 
321
 
 
322
/* This is a "raw" xpath function. Check that it returns child elements
 
323
 * properly
 
324
 */
 
325
 
 
326
PG_FUNCTION_INFO_V1(xpath_nodeset);
 
327
 
 
328
Datum
 
329
xpath_nodeset(PG_FUNCTION_ARGS)
 
330
{
 
331
        xmlChar    *xpath,
 
332
                           *toptag,
 
333
                           *septag;
 
334
        int32           pathsize;
 
335
        text
 
336
                           *xpathsupp,
 
337
                           *xpres;
 
338
 
 
339
        /* PG_GETARG_TEXT_P(0) is document buffer */
 
340
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
 
341
 
 
342
        toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
 
343
        septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
 
344
 
 
345
        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
 
346
 
 
347
        xpath = pgxml_texttoxmlchar(xpathsupp);
 
348
 
 
349
        xpres = pgxml_result_to_text(
 
350
                                                                 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
 
351
                                                                 toptag, septag, NULL);
 
352
 
 
353
        /* xmlCleanupParser(); done by result_to_text routine */
 
354
        pfree(xpath);
 
355
 
 
356
        if (xpres == NULL)
 
357
                PG_RETURN_NULL();
 
358
        PG_RETURN_TEXT_P(xpres);
 
359
}
 
360
 
 
361
/*      The following function is almost identical, but returns the elements in */
 
362
/*      a list. */
 
363
 
 
364
PG_FUNCTION_INFO_V1(xpath_list);
 
365
 
 
366
Datum
 
367
xpath_list(PG_FUNCTION_ARGS)
 
368
{
 
369
        xmlChar    *xpath,
 
370
                           *plainsep;
 
371
        int32           pathsize;
 
372
        text
 
373
                           *xpathsupp,
 
374
                           *xpres;
 
375
 
 
376
        /* PG_GETARG_TEXT_P(0) is document buffer */
 
377
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
 
378
 
 
379
        plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
 
380
 
 
381
        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
 
382
 
 
383
        xpath = pgxml_texttoxmlchar(xpathsupp);
 
384
 
 
385
        xpres = pgxml_result_to_text(
 
386
                                                                 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
 
387
                                                                 NULL, NULL, plainsep);
 
388
 
 
389
        /* xmlCleanupParser(); done by result_to_text routine */
 
390
        pfree(xpath);
 
391
 
 
392
        if (xpres == NULL)
 
393
                PG_RETURN_NULL();
 
394
        PG_RETURN_TEXT_P(xpres);
 
395
}
 
396
 
 
397
 
 
398
PG_FUNCTION_INFO_V1(xpath_string);
 
399
 
 
400
Datum
 
401
xpath_string(PG_FUNCTION_ARGS)
 
402
{
 
403
        xmlChar    *xpath;
 
404
        int32           pathsize;
 
405
        text
 
406
                           *xpathsupp,
 
407
                           *xpres;
 
408
 
 
409
        /* PG_GETARG_TEXT_P(0) is document buffer */
 
410
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
 
411
 
 
412
        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
 
413
 
 
414
        /*
 
415
         * We encapsulate the supplied path with "string()" = 8 chars + 1 for
 
416
         * NUL at end
 
417
         */
 
418
        /* We could try casting to string using the libxml function? */
 
419
 
 
420
        xpath = (xmlChar *) palloc(pathsize + 9);
 
421
        memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
 
422
        strncpy((char *) xpath, "string(", 7);
 
423
        xpath[pathsize + 7] = ')';
 
424
        xpath[pathsize + 8] = '\0';
 
425
 
 
426
        xpres = pgxml_result_to_text(
 
427
                                                                 pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
 
428
                                                                 NULL, NULL, NULL);
 
429
 
 
430
        xmlCleanupParser();
 
431
        pfree(xpath);
 
432
 
 
433
        if (xpres == NULL)
 
434
                PG_RETURN_NULL();
 
435
        PG_RETURN_TEXT_P(xpres);
 
436
}
 
437
 
 
438
 
 
439
PG_FUNCTION_INFO_V1(xpath_number);
 
440
 
 
441
Datum
 
442
xpath_number(PG_FUNCTION_ARGS)
 
443
{
 
444
        xmlChar    *xpath;
 
445
        int32           pathsize;
 
446
        text
 
447
                           *xpathsupp;
 
448
 
 
449
        float4          fRes;
 
450
 
 
451
        xmlXPathObjectPtr res;
 
452
 
 
453
        /* PG_GETARG_TEXT_P(0) is document buffer */
 
454
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
 
455
 
 
456
        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
 
457
 
 
458
        xpath = pgxml_texttoxmlchar(xpathsupp);
 
459
 
 
460
        res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
 
461
        pfree(xpath);
 
462
 
 
463
        if (res == NULL)
 
464
        {
 
465
                xmlCleanupParser();
 
466
                PG_RETURN_NULL();
 
467
        }
 
468
 
 
469
        fRes = xmlXPathCastToNumber(res);
 
470
        xmlCleanupParser();
 
471
        if (xmlXPathIsNaN(fRes))
 
472
                PG_RETURN_NULL();
 
473
 
 
474
        PG_RETURN_FLOAT4(fRes);
 
475
 
 
476
}
 
477
 
 
478
 
 
479
PG_FUNCTION_INFO_V1(xpath_bool);
 
480
 
 
481
Datum
 
482
xpath_bool(PG_FUNCTION_ARGS)
 
483
{
 
484
        xmlChar    *xpath;
 
485
        int32           pathsize;
 
486
        text
 
487
                           *xpathsupp;
 
488
 
 
489
        int                     bRes;
 
490
 
 
491
        xmlXPathObjectPtr res;
 
492
 
 
493
        /* PG_GETARG_TEXT_P(0) is document buffer */
 
494
        xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
 
495
 
 
496
        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
 
497
 
 
498
        xpath = pgxml_texttoxmlchar(xpathsupp);
 
499
 
 
500
        res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
 
501
        pfree(xpath);
 
502
 
 
503
        if (res == NULL)
 
504
        {
 
505
                xmlCleanupParser();
 
506
                PG_RETURN_BOOL(false);
 
507
        }
 
508
 
 
509
        bRes = xmlXPathCastToBoolean(res);
 
510
        xmlCleanupParser();
 
511
        PG_RETURN_BOOL(bRes);
 
512
 
 
513
}
 
514
 
 
515
 
 
516
 
 
517
/* Core function to evaluate XPath query */
 
518
 
 
519
xmlXPathObjectPtr
 
520
pgxml_xpath(text *document, xmlChar * xpath)
 
521
{
 
522
 
 
523
        xmlDocPtr       doctree;
 
524
        xmlXPathContextPtr ctxt;
 
525
        xmlXPathObjectPtr res;
 
526
 
 
527
        xmlXPathCompExprPtr comppath;
 
528
 
 
529
        int32           docsize;
 
530
 
 
531
 
 
532
        docsize = VARSIZE(document) - VARHDRSZ;
 
533
 
 
534
        pgxml_parser_init();
 
535
 
 
536
        doctree = xmlParseMemory((char *) VARDATA(document), docsize);
 
537
        if (doctree == NULL)
 
538
        {                                                       /* not well-formed */
 
539
                return NULL;
 
540
        }
 
541
 
 
542
        ctxt = xmlXPathNewContext(doctree);
 
543
        ctxt->node = xmlDocGetRootElement(doctree);
 
544
 
 
545
 
 
546
        /* compile the path */
 
547
        comppath = xmlXPathCompile(xpath);
 
548
        if (comppath == NULL)
 
549
        {
 
550
                xmlCleanupParser();
 
551
                xmlFreeDoc(doctree);
 
552
                elog_error(ERROR, "XPath Syntax Error", 1);
 
553
 
 
554
                return NULL;
 
555
        }
 
556
 
 
557
        /* Now evaluate the path expression. */
 
558
        res = xmlXPathCompiledEval(comppath, ctxt);
 
559
        xmlXPathFreeCompExpr(comppath);
 
560
 
 
561
        if (res == NULL)
 
562
        {
 
563
                xmlXPathFreeContext(ctxt);
 
564
                /* xmlCleanupParser(); */
 
565
                xmlFreeDoc(doctree);
 
566
 
 
567
                return NULL;
 
568
        }
 
569
        /* xmlFreeDoc(doctree); */
 
570
        return res;
 
571
}
 
572
 
 
573
text
 
574
                   *
 
575
pgxml_result_to_text(xmlXPathObjectPtr res,
 
576
                                         xmlChar * toptag,
 
577
                                         xmlChar * septag,
 
578
                                         xmlChar * plainsep)
 
579
{
 
580
        xmlChar    *xpresstr;
 
581
        int32           ressize;
 
582
        text       *xpres;
 
583
 
 
584
        if (res == NULL)
 
585
        {
 
586
                xmlCleanupParser();
 
587
                return NULL;
 
588
        }
 
589
        switch (res->type)
 
590
        {
 
591
                case XPATH_NODESET:
 
592
                        xpresstr = pgxmlNodeSetToText(res->nodesetval,
 
593
                                                                                  toptag,
 
594
                                                                                  septag, plainsep);
 
595
                        break;
 
596
 
 
597
                case XPATH_STRING:
 
598
                        xpresstr = xmlStrdup(res->stringval);
 
599
                        break;
 
600
 
 
601
                default:
 
602
                        elog(NOTICE, "Unsupported XQuery result: %d", res->type);
 
603
                        xpresstr = xmlStrdup("<unsupported/>");
 
604
        }
 
605
 
 
606
 
 
607
        /* Now convert this result back to text */
 
608
        ressize = strlen(xpresstr);
 
609
        xpres = (text *) palloc(ressize + VARHDRSZ);
 
610
        memcpy(VARDATA(xpres), xpresstr, ressize);
 
611
        VARATT_SIZEP(xpres) = ressize + VARHDRSZ;
 
612
 
 
613
        /* Free various storage */
 
614
        xmlCleanupParser();
 
615
        /* xmlFreeDoc(doctree);  -- will die at end of tuple anyway */
 
616
 
 
617
        xmlFree(xpresstr);
 
618
 
 
619
        elog_error(ERROR, "XPath error", 0);
 
620
 
 
621
 
 
622
        return xpres;
 
623
}
 
624
 
 
625
/* xpath_table is a table function. It needs some tidying (as do the
 
626
 * other functions here!
 
627
 */
 
628
 
 
629
PG_FUNCTION_INFO_V1(xpath_table);
 
630
 
 
631
Datum
 
632
xpath_table(PG_FUNCTION_ARGS)
 
633
{
 
634
/* SPI (input tuple) support */
 
635
        SPITupleTable *tuptable;
 
636
        HeapTuple       spi_tuple;
 
637
        TupleDesc       spi_tupdesc;
 
638
 
 
639
/* Output tuple (tuplestore) support */
 
640
        Tuplestorestate *tupstore = NULL;
 
641
        TupleDesc       ret_tupdesc;
 
642
        HeapTuple       ret_tuple;
 
643
 
 
644
        ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 
645
        AttInMetadata *attinmeta;
 
646
        MemoryContext per_query_ctx;
 
647
        MemoryContext oldcontext;
 
648
 
 
649
/* Function parameters */
 
650
        char       *pkeyfield = GET_STR(PG_GETARG_TEXT_P(0));
 
651
        char       *xmlfield = GET_STR(PG_GETARG_TEXT_P(1));
 
652
        char       *relname = GET_STR(PG_GETARG_TEXT_P(2));
 
653
        char       *xpathset = GET_STR(PG_GETARG_TEXT_P(3));
 
654
        char       *condition = GET_STR(PG_GETARG_TEXT_P(4));
 
655
 
 
656
        char      **values;
 
657
        xmlChar   **xpaths;
 
658
        xmlChar    *pos;
 
659
        xmlChar    *pathsep = "|";
 
660
 
 
661
        int                     numpaths;
 
662
        int                     ret;
 
663
        int                     proc;
 
664
        int                     i;
 
665
        int                     j;
 
666
        int                     rownr;                  /* For issuing multiple rows from one
 
667
                                                                 * original document */
 
668
        int                     had_values;             /* To determine end of nodeset results */
 
669
 
 
670
        StringInfo      querysql;
 
671
 
 
672
/* We only have a valid tuple description in table function mode */
 
673
        if (rsinfo->expectedDesc == NULL)
 
674
        {
 
675
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
 
676
                          errmsg("xpath_table must be called as a table function")));
 
677
        }
 
678
 
 
679
/* The tuplestore must exist in a higher context than
 
680
 * this function call (per_query_ctx is used) */
 
681
 
 
682
        per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
 
683
        oldcontext = MemoryContextSwitchTo(per_query_ctx);
 
684
 
 
685
/* Create the tuplestore - work_mem is the max in-memory size before a
 
686
 * file is created on disk to hold it.
 
687
 */
 
688
 
 
689
        tupstore = tuplestore_begin_heap(true, false, work_mem);
 
690
 
 
691
        MemoryContextSwitchTo(oldcontext);
 
692
 
 
693
        /* get the requested return tuple description */
 
694
        ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
 
695
 
 
696
        /*
 
697
         * At the moment we assume that the returned attributes make sense for
 
698
         * the XPath specififed (i.e. we trust the caller). It's not fatal if
 
699
         * they get it wrong - the input function for the column type will
 
700
         * raise an error if the path result can't be converted into the
 
701
         * correct binary representation.
 
702
         */
 
703
 
 
704
        attinmeta = TupleDescGetAttInMetadata(ret_tupdesc);
 
705
 
 
706
        /*
 
707
         * We want to materialise because it means that we don't have to carry
 
708
         * libxml2 parser state between invocations of this function
 
709
         */
 
710
 
 
711
        /* check to see if caller supports us returning a tuplestore */
 
712
        if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
 
713
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
 
714
                   errmsg("xpath_table requires Materialize mode, but it is not "
 
715
                                  "allowed in this context")));
 
716
 
 
717
        /* Set return mode and allocate value space. */
 
718
        rsinfo->returnMode = SFRM_Materialize;
 
719
        rsinfo->setDesc = ret_tupdesc;
 
720
 
 
721
        values = (char **) palloc(ret_tupdesc->natts * sizeof(char *));
 
722
 
 
723
        xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));
 
724
 
 
725
        /* Split XPaths. xpathset is a writable CString. */
 
726
 
 
727
        /* Note that we stop splitting once we've done all needed for tupdesc */
 
728
 
 
729
        numpaths = 0;
 
730
        pos = xpathset;
 
731
        do
 
732
        {
 
733
                xpaths[numpaths] = pos;
 
734
                pos = strstr(pos, pathsep);
 
735
                if (pos != NULL)
 
736
                {
 
737
                        *pos = '\0';
 
738
                        pos++;
 
739
                }
 
740
                numpaths++;
 
741
        } while ((pos != NULL) && (numpaths < (ret_tupdesc->natts - 1)));
 
742
 
 
743
        /* Now build query */
 
744
 
 
745
        querysql = makeStringInfo();
 
746
 
 
747
        /* Build initial sql statement */
 
748
        appendStringInfo(querysql, "SELECT %s, %s FROM %s WHERE %s",
 
749
                                         pkeyfield,
 
750
                                         xmlfield,
 
751
                                         relname,
 
752
                                         condition
 
753
                );
 
754
 
 
755
 
 
756
        if ((ret = SPI_connect()) < 0)
 
757
                elog(ERROR, "xpath_table: SPI_connect returned %d", ret);
 
758
 
 
759
        if ((ret = SPI_exec(querysql->data, 0)) != SPI_OK_SELECT)
 
760
                elog(ERROR, "xpath_table: SPI execution failed for query %s", querysql->data);
 
761
 
 
762
        proc = SPI_processed;
 
763
        /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */
 
764
        tuptable = SPI_tuptable;
 
765
        spi_tupdesc = tuptable->tupdesc;
 
766
 
 
767
/* Switch out of SPI context */
 
768
        MemoryContextSwitchTo(oldcontext);
 
769
 
 
770
 
 
771
/* Check that SPI returned correct result. If you put a comma into one of
 
772
 * the function parameters, this will catch it when the SPI query returns
 
773
 * e.g. 3 columns.
 
774
 */
 
775
 
 
776
        if (spi_tupdesc->natts != 2)
 
777
        {
 
778
                ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 
779
                                                errmsg("Expression returning multiple columns is not valid in parameter list"),
 
780
                                                errdetail("Expected two columns in SPI result, got %d", spi_tupdesc->natts)));
 
781
        }
 
782
 
 
783
/* Setup the parser. Beware that this must happen in the same context as the
 
784
 * cleanup - which means that any error from here on must do cleanup to
 
785
 * ensure that the entity table doesn't get freed by being out of context.
 
786
 */
 
787
        pgxml_parser_init();
 
788
 
 
789
        /* For each row i.e. document returned from SPI */
 
790
        for (i = 0; i < proc; i++)
 
791
        {
 
792
                char       *pkey;
 
793
                char       *xmldoc;
 
794
 
 
795
                xmlDocPtr       doctree;
 
796
                xmlXPathContextPtr ctxt;
 
797
                xmlXPathObjectPtr res;
 
798
                xmlChar    *resstr;
 
799
 
 
800
 
 
801
                xmlXPathCompExprPtr comppath;
 
802
 
 
803
                /* Extract the row data as C Strings */
 
804
 
 
805
                spi_tuple = tuptable->vals[i];
 
806
                pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
 
807
                xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
 
808
 
 
809
 
 
810
                /*
 
811
                 * Clear the values array, so that not-well-formed documents
 
812
                 * return NULL in all columns.
 
813
                 */
 
814
 
 
815
                /* Note that this also means that spare columns will be NULL. */
 
816
                for (j = 0; j < ret_tupdesc->natts; j++)
 
817
                        values[j] = NULL;
 
818
 
 
819
                /* Insert primary key */
 
820
                values[0] = pkey;
 
821
 
 
822
                /* Parse the document */
 
823
                doctree = xmlParseMemory(xmldoc, strlen(xmldoc));
 
824
 
 
825
                if (doctree == NULL)
 
826
                {                                               /* not well-formed, so output all-NULL
 
827
                                                                 * tuple */
 
828
 
 
829
                        ret_tuple = BuildTupleFromCStrings(attinmeta, values);
 
830
                        oldcontext = MemoryContextSwitchTo(per_query_ctx);
 
831
                        tuplestore_puttuple(tupstore, ret_tuple);
 
832
                        MemoryContextSwitchTo(oldcontext);
 
833
                        heap_freetuple(ret_tuple);
 
834
                }
 
835
                else
 
836
                {
 
837
                        /* New loop here - we have to deal with nodeset results */
 
838
                        rownr = 0;
 
839
 
 
840
                        do
 
841
                        {
 
842
                                /* Now evaluate the set of xpaths. */
 
843
                                had_values = 0;
 
844
                                for (j = 0; j < numpaths; j++)
 
845
                                {
 
846
 
 
847
                                        ctxt = xmlXPathNewContext(doctree);
 
848
                                        ctxt->node = xmlDocGetRootElement(doctree);
 
849
                                        xmlSetGenericErrorFunc(ctxt, pgxml_errorHandler);
 
850
 
 
851
                                        /* compile the path */
 
852
                                        comppath = xmlXPathCompile(xpaths[j]);
 
853
                                        if (comppath == NULL)
 
854
                                        {
 
855
                                                xmlCleanupParser();
 
856
                                                xmlFreeDoc(doctree);
 
857
 
 
858
                                                elog_error(ERROR, "XPath Syntax Error", 1);
 
859
 
 
860
                                                PG_RETURN_NULL();               /* Keep compiler happy */
 
861
                                        }
 
862
 
 
863
                                        /* Now evaluate the path expression. */
 
864
                                        res = xmlXPathCompiledEval(comppath, ctxt);
 
865
                                        xmlXPathFreeCompExpr(comppath);
 
866
 
 
867
                                        if (res != NULL)
 
868
                                        {
 
869
                                                switch (res->type)
 
870
                                                {
 
871
                                                        case XPATH_NODESET:
 
872
                                                                /* We see if this nodeset has enough nodes */
 
873
                                                                if ((res->nodesetval != NULL) && (rownr < res->nodesetval->nodeNr))
 
874
                                                                {
 
875
                                                                        resstr =
 
876
                                                                                xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]);
 
877
                                                                        had_values = 1;
 
878
                                                                }
 
879
                                                                else
 
880
                                                                        resstr = NULL;
 
881
 
 
882
                                                                break;
 
883
 
 
884
                                                        case XPATH_STRING:
 
885
                                                                resstr = xmlStrdup(res->stringval);
 
886
                                                                break;
 
887
 
 
888
                                                        default:
 
889
                                                                elog(NOTICE, "Unsupported XQuery result: %d", res->type);
 
890
                                                                resstr = xmlStrdup("<unsupported/>");
 
891
                                                }
 
892
 
 
893
 
 
894
                                                /*
 
895
                                                 * Insert this into the appropriate column in the
 
896
                                                 * result tuple.
 
897
                                                 */
 
898
                                                values[j + 1] = resstr;
 
899
                                        }
 
900
                                        xmlXPathFreeContext(ctxt);
 
901
                                }
 
902
                                /* Now add the tuple to the output, if there is one. */
 
903
                                if (had_values)
 
904
                                {
 
905
                                        ret_tuple = BuildTupleFromCStrings(attinmeta, values);
 
906
                                        oldcontext = MemoryContextSwitchTo(per_query_ctx);
 
907
                                        tuplestore_puttuple(tupstore, ret_tuple);
 
908
                                        MemoryContextSwitchTo(oldcontext);
 
909
                                        heap_freetuple(ret_tuple);
 
910
                                }
 
911
 
 
912
                                rownr++;
 
913
 
 
914
                        } while (had_values);
 
915
 
 
916
                }
 
917
 
 
918
                xmlFreeDoc(doctree);
 
919
 
 
920
                pfree(pkey);
 
921
                pfree(xmldoc);
 
922
        }
 
923
 
 
924
        xmlCleanupParser();
 
925
/* Needed to flag completeness in 7.3.1. 7.4 defines it as a no-op. */
 
926
        tuplestore_donestoring(tupstore);
 
927
 
 
928
        SPI_finish();
 
929
 
 
930
        rsinfo->setResult = tupstore;
 
931
 
 
932
        /*
 
933
         * SFRM_Materialize mode expects us to return a NULL Datum. The actual
 
934
         * tuples are in our tuplestore and passed back through
 
935
         * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
 
936
         * that we actually used to build our tuples with, so the caller can
 
937
         * verify we did what it was expecting.
 
938
         */
 
939
        return (Datum) 0;
 
940
 
 
941
}