/[pgestraier]/trunk/pgest.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/pgest.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 85 - (hide annotations)
Sat Feb 3 17:13:07 2007 UTC (17 years, 2 months ago) by dpavlin
File MIME type: text/plain
File size: 22137 byte(s)
add PG_MODULE_MAGIC for PostgreSQL 8.2
1 dpavlin 1 /*
2     * integrate Hyper Estraier into PostgreSQL
3     *
4     * Dobrica Pavlinusic <dpavlin@rot13.org> 2005-05-19
5     *
6     * TODO:
7     * - all
8     *
9     * NOTES:
10     * - clear structures with memset to support hash indexes (who whould like
11     * to create hash index on table returned from function?)
12     * - number of returned rows is set by PostgreSQL evaluator, see:
13     * http://archives.postgresql.org/pgsql-hackers/2005-02/msg00546.php
14     *
15     * Based on:
16     * - C example from PostgreSQL documentation (BSD licence)
17 dpavlin 41 * - coreexample002.c and nodeexample002.c from Hyper Estraier (GPL)
18 dpavlin 1 * - _textin/_textout from pgcurl.c (LGPL)
19     *
20     * This code is licenced under GPL
21     */
22    
23     #include "postgres.h"
24     #include "fmgr.h"
25     #include "funcapi.h"
26     #include "utils/builtins.h"
27     #include "utils/array.h"
28 dpavlin 56 #include "utils/lsyscache.h"
29 dpavlin 1 #include "miscadmin.h"
30 dpavlin 61 #include "commands/trigger.h"
31     #include "executor/spi.h"
32    
33 dpavlin 1 #include <estraier.h>
34     #include <cabin.h>
35 dpavlin 41 #include <estnode.h>
36 dpavlin 1
37     #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
38     #define _textout(str) DatumGetPointer(DirectFunctionCall1(textout, PointerGetDatum(str)))
39     #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
40     #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
41    
42 dpavlin 27 /* SortMem got renamed in PostgreSQL 8.0 */
43     #ifndef SortMem
44     #define SortMem 16 * 1024
45     #endif
46    
47 dpavlin 85 #ifdef PG_MODULE_MAGIC
48     PG_MODULE_MAGIC;
49     #endif
50    
51 dpavlin 40 #define ATTR_DELIMITER "{{!}}"
52 dpavlin 57 #define HINTS_PREFIX "HINTS."
53 dpavlin 40
54 dpavlin 5 /* prototype */
55     char *attr2text(ESTDOC *doc, char *attr);
56 dpavlin 41 char *node_attr2text(ESTRESDOC *rdoc, char *attr);
57 dpavlin 56 void cond_add_attr(ESTCOND *cond, char *attr);
58 dpavlin 1
59    
60     /* work in progress */
61 dpavlin 19 PG_FUNCTION_INFO_V1(pgest_attr);
62     Datum pgest_attr(PG_FUNCTION_ARGS)
63 dpavlin 1 {
64 dpavlin 31 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(6);
65 dpavlin 25 Oid attr_element_type = ARR_ELEMTYPE(attr_arr);
66     int attr_ndims = ARR_NDIM(attr_arr);
67     int *attr_dim_counts = ARR_DIMS(attr_arr);
68     int *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
69 dpavlin 19 int ncols = 0;
70     int nrows = 0;
71     int indx[MAXDIM];
72 dpavlin 25 int16 attr_len;
73     bool attr_byval;
74     char attr_align;
75 dpavlin 1 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
76     AttInMetadata *attinmeta;
77     TupleDesc tupdesc;
78 dpavlin 19 Tuplestorestate *tupstore = NULL;
79 dpavlin 1 HeapTuple tuple;
80     MemoryContext per_query_ctx;
81     MemoryContext oldcontext;
82     Datum dvalue;
83     char **values;
84 dpavlin 19 int rsinfo_ncols;
85 dpavlin 1 int i, j;
86 dpavlin 19 /* estvars */
87 dpavlin 25 ESTDB *db;
88     ESTCOND *cond;
89     ESTDOC *doc;
90     int ecode, *est_result, resnum;
91     int limit = 0;
92     int offset = 0;
93    
94 dpavlin 19 char *index_path;
95     char *query;
96     char *attr;
97 dpavlin 31 char *order;
98 dpavlin 1
99 dpavlin 19
100     /* only allow 1D input array */
101 dpavlin 25 if (attr_ndims == 1)
102 dpavlin 19 {
103 dpavlin 25 ncols = attr_dim_counts[0];
104 dpavlin 19 }
105     else
106     ereport(ERROR,
107     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
108     errmsg("invalid input array"),
109     errdetail("Input array must have 1 dimension")));
110    
111 dpavlin 1 /* check to see if caller supports us returning a tuplestore */
112     if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
113     ereport(ERROR,
114     (errcode(ERRCODE_SYNTAX_ERROR),
115     errmsg("materialize mode required, but it is not " \
116     "allowed in this context")));
117    
118 dpavlin 19 /* get info about element type needed to construct the array */
119 dpavlin 25 get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
120 dpavlin 19
121 dpavlin 1 /* get the requested return tuple description */
122     tupdesc = rsinfo->expectedDesc;
123 dpavlin 19 rsinfo_ncols = tupdesc->natts;
124 dpavlin 1
125     /*
126     * The requested tuple description better match up with the array
127     * we were given.
128     */
129 dpavlin 19 if (rsinfo_ncols != ncols)
130     ereport(ERROR,
131     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
132     errmsg("invalid input array"),
133     errdetail("Number of elements in array must match number of query specified columns.")));
134    
135 dpavlin 1 /* OK, use it */
136     attinmeta = TupleDescGetAttInMetadata(tupdesc);
137    
138     /* Now go to work */
139     rsinfo->returnMode = SFRM_Materialize;
140    
141     per_query_ctx = fcinfo->flinfo->fn_mcxt;
142     oldcontext = MemoryContextSwitchTo(per_query_ctx);
143    
144     /* initialize our tuplestore */
145     tupstore = tuplestore_begin_heap(true, false, SortMem);
146    
147 dpavlin 19
148     /* take rest of arguments from function */
149    
150     /* index path */
151     if (PG_ARGISNULL(0)) {
152     ereport(ERROR,
153     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
154     errmsg("index path can't be null"),
155     errdetail("Index path must be valid full path to HyperEstraier index")));
156     }
157     index_path = _textout(PG_GETARG_TEXT_P(0));
158    
159     /* query string */
160 dpavlin 31 if (PG_ARGISNULL(1)) {
161 dpavlin 19 query = "";
162     } else {
163     query = _textout(PG_GETARG_TEXT_P(1));
164     }
165    
166     /* atribute filter */
167     if (PG_ARGISNULL(2)) {
168     attr = "";
169     } else {
170     attr = _textout(PG_GETARG_TEXT_P(2));
171     }
172 dpavlin 31
173     /* sort order */
174     if (PG_ARGISNULL(3)) {
175     order = "";
176     } else {
177     order = _textout(PG_GETARG_TEXT_P(3));
178     }
179 dpavlin 19
180 dpavlin 31
181 dpavlin 19 /* limit */
182 dpavlin 31 if (PG_ARGISNULL(4)) {
183 dpavlin 19 limit = 0;
184     } else {
185 dpavlin 31 limit = PG_GETARG_INT32(4);
186 dpavlin 19 }
187    
188     /* offset */
189 dpavlin 31 if (PG_ARGISNULL(5)) {
190 dpavlin 19 offset = 0;
191     } else {
192 dpavlin 31 offset = PG_GETARG_INT32(5);
193 dpavlin 19 }
194    
195    
196     /* open the database */
197     elog(DEBUG1, "pgest_attr: est_db_open(%s)", index_path);
198    
199     if(!(db = est_db_open(index_path, ESTDBREADER, &ecode))){
200     ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
201     errmsg("est_db_open: can't open %s: %d", index_path, ecode),
202     errdetail(est_err_msg(ecode))));
203     }
204    
205 dpavlin 20 elog(DEBUG1, "pgest_attr: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);
206 dpavlin 19
207     /* create a search condition object */
208     if (!(cond = est_cond_new())) {
209     ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
210     errmsg("pgest_attr: est_cond_new failed")));
211     }
212    
213     /* set the search phrase to the search condition object */
214     if (! PG_ARGISNULL(1) && strlen(query) > 0)
215     est_cond_set_phrase(cond, query);
216    
217     /* minimum valid attribute length is 10: @a STREQ a */
218     if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {
219 dpavlin 40 elog(DEBUG1,"attributes: %s", attr);
220 dpavlin 56 cond_add_attr(cond, attr);
221 dpavlin 19 }
222    
223 dpavlin 31 /* set the search phrase to the search condition object */
224     if (! PG_ARGISNULL(3) && strlen(order) > 0) {
225     elog(DEBUG1,"est_cond_set_order(%s)", order);
226     est_cond_set_order(cond, order);
227     }
228    
229 dpavlin 38 if (limit) {
230     elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
231     est_cond_set_max(cond, limit + offset);
232     }
233    
234 dpavlin 19 /* get the result of search */
235     est_result = est_db_search(db, cond, &resnum, NULL);
236    
237     /* check if results exists */
238     if ( 0 == resnum ) {
239     elog(INFO, "pgest_attr: no results for: %s", query );
240     }
241    
242     /* total number of tuples to be returned */
243     if (limit && limit < resnum) {
244 dpavlin 31 nrows = limit;
245 dpavlin 19 } else {
246     nrows = resnum - offset;
247     }
248    
249    
250     elog(DEBUG1, "pgest_attr: found %d hits for %s", resnum, query);
251    
252 dpavlin 1 values = (char **) palloc(ncols * sizeof(char *));
253    
254     for (i = 0; i < nrows; i++)
255     {
256 dpavlin 19
257     /* get result from estraier */
258     if (! ( doc = est_db_get_doc(db, est_result[i + offset], 0)) ) {
259 dpavlin 47 elog(INFO, "pgest_attr: can't find result %d", i + offset);
260 dpavlin 19 } else {
261     elog(DEBUG1, "URI: %s\n Title: %s\n",
262     est_doc_attr(doc, "@uri"),
263     est_doc_attr(doc, "@title")
264     );
265     }
266    
267     /* iterate over results */
268 dpavlin 1 for (j = 0; j < ncols; j++)
269     {
270 dpavlin 19 bool isnull;
271    
272     /* array value of this position */
273 dpavlin 25 indx[0] = j + attr_dim_lower_bounds[0];
274 dpavlin 19
275 dpavlin 25 dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
276 dpavlin 19
277     if (!isnull && doc)
278     values[j] = DatumGetCString(
279     attr2text(doc,
280 dpavlin 20 (char *)DirectFunctionCall1(textout, dvalue)
281 dpavlin 19 ));
282     else
283     values[j] = NULL;
284 dpavlin 1 }
285     /* construct the tuple */
286     tuple = BuildTupleFromCStrings(attinmeta, values);
287    
288     /* now store it */
289     tuplestore_puttuple(tupstore, tuple);
290 dpavlin 19
291     /* delete estraier document object */
292 dpavlin 47 if (doc) est_doc_delete(doc);
293 dpavlin 1 }
294    
295     tuplestore_donestoring(tupstore);
296     rsinfo->setResult = tupstore;
297    
298     /*
299     * SFRM_Materialize mode expects us to return a NULL Datum. The actual
300     * tuples are in our tuplestore and passed back through
301     * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
302     * that we actually used to build our tuples with, so the caller can
303     * verify we did what it was expecting.
304     */
305     rsinfo->setDesc = tupdesc;
306     MemoryContextSwitchTo(oldcontext);
307    
308 dpavlin 31 est_cond_delete(cond);
309    
310 dpavlin 19 if(!est_db_close(db, &ecode)){
311     ereport(ERROR, (errcode(ERRCODE_IO_ERROR),
312     errmsg("est_db_close: %d", ecode),
313     errdetail(est_err_msg(ecode))));
314     }
315    
316 dpavlin 1 return (Datum) 0;
317     }
318    
319    
320     /* make text var from attr */
321     char *attr2text(ESTDOC *doc, char *attr) {
322     char *val;
323     const char *attrval;
324     int len;
325 dpavlin 4 int attrlen;
326 dpavlin 1
327 dpavlin 47 if (! doc) return (Datum) NULL;
328    
329 dpavlin 56 elog(DEBUG1, "doc: %p, attr: %s", doc, attr);
330 dpavlin 1
331 dpavlin 4 if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {
332     val = (char *) palloc(attrlen * sizeof(char));
333 dpavlin 1 } else {
334     return (Datum) NULL;
335     }
336    
337     len = strlen(attrval);
338 dpavlin 2 elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);
339 dpavlin 1
340     len++;
341     len *= sizeof(char);
342    
343     elog(DEBUG2, "palloc(%d)", len);
344    
345     val = palloc(len);
346    
347     memset(val, 0, len);
348     strncpy(val, attrval, len);
349    
350     elog(DEBUG2, "val=%s", val);
351    
352     return val;
353     }
354    
355 dpavlin 41 /*
356     * variation on theme: use node API which doesn't open index on
357     * every query which is much faster for large indexes
358     *
359     */
360    
361 dpavlin 48 /* select * from pgest( */
362     #define _arg_node_uri 0
363     #define _arg_login 1
364     #define _arg_passwd 2
365 dpavlin 49 #define _arg_depth 3
366     #define _arg_query 4
367     #define _arg_attr 5
368     #define _arg_order 6
369     #define _arg_limit 7
370     #define _arg_offset 8
371     #define _arg_attr_array 9
372 dpavlin 48 /* as (foo text, ... ); */
373    
374    
375 dpavlin 41 PG_FUNCTION_INFO_V1(pgest_node);
376     Datum pgest_node(PG_FUNCTION_ARGS)
377     {
378 dpavlin 48 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(_arg_attr_array);
379 dpavlin 41 Oid attr_element_type = ARR_ELEMTYPE(attr_arr);
380     int attr_ndims = ARR_NDIM(attr_arr);
381     int *attr_dim_counts = ARR_DIMS(attr_arr);
382     int *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
383     int ncols = 0;
384     int nrows = 0;
385     int indx[MAXDIM];
386     int16 attr_len;
387     bool attr_byval;
388     char attr_align;
389     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
390     AttInMetadata *attinmeta;
391     TupleDesc tupdesc;
392     Tuplestorestate *tupstore = NULL;
393     HeapTuple tuple;
394     MemoryContext per_query_ctx;
395     MemoryContext oldcontext;
396     Datum dvalue;
397     char **values;
398     int rsinfo_ncols;
399     int i, j;
400     /* estvars */
401     ESTNODE *node;
402     ESTCOND *cond;
403     ESTNODERES *nres;
404     ESTRESDOC *rdoc;
405 dpavlin 57 CBMAP *hints;
406 dpavlin 41 int resnum = 0;
407     int limit = 0;
408     int offset = 0;
409 dpavlin 49 int depth = 0;
410 dpavlin 41
411     char *node_url;
412     char *user, *passwd;
413     char *query;
414     char *attr;
415     char *order;
416    
417    
418     /* only allow 1D input array */
419     if (attr_ndims == 1)
420     {
421     ncols = attr_dim_counts[0];
422     }
423     else
424     ereport(ERROR,
425     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
426     errmsg("invalid input array"),
427     errdetail("Input array must have 1 dimension")));
428    
429     /* check to see if caller supports us returning a tuplestore */
430     if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
431     ereport(ERROR,
432     (errcode(ERRCODE_SYNTAX_ERROR),
433     errmsg("materialize mode required, but it is not " \
434     "allowed in this context")));
435    
436     /* get info about element type needed to construct the array */
437     get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
438    
439     /* get the requested return tuple description */
440     tupdesc = rsinfo->expectedDesc;
441     rsinfo_ncols = tupdesc->natts;
442    
443     /*
444     * The requested tuple description better match up with the array
445     * we were given.
446     */
447     if (rsinfo_ncols != ncols)
448     ereport(ERROR,
449     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
450     errmsg("invalid input array"),
451     errdetail("Number of elements in array must match number of query specified columns.")));
452    
453     /* OK, use it */
454     attinmeta = TupleDescGetAttInMetadata(tupdesc);
455    
456     /* Now go to work */
457     rsinfo->returnMode = SFRM_Materialize;
458    
459     per_query_ctx = fcinfo->flinfo->fn_mcxt;
460     oldcontext = MemoryContextSwitchTo(per_query_ctx);
461    
462     /* initialize our tuplestore */
463     tupstore = tuplestore_begin_heap(true, false, SortMem);
464    
465    
466     /* take rest of arguments from function */
467    
468     /* node URL */
469 dpavlin 48 if (PG_ARGISNULL(_arg_node_uri)) {
470 dpavlin 41 ereport(ERROR,
471     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
472     errmsg("node URL can't be null"),
473     errdetail("Node URL must be valid URL to HyperEstraier node")));
474     }
475 dpavlin 48 node_url = _textout(PG_GETARG_TEXT_P(_arg_node_uri));
476 dpavlin 41
477     /* login and password */
478 dpavlin 48 if (PG_ARGISNULL(_arg_login) || PG_ARGISNULL(_arg_passwd)) {
479 dpavlin 41 ereport(ERROR,
480     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
481     errmsg("username and password can't be NULL"),
482     errdetail("You must specify valid username and password to HyperEstraier node")));
483     }
484 dpavlin 48 user = _textout(PG_GETARG_TEXT_P(_arg_login));
485     passwd = _textout(PG_GETARG_TEXT_P(_arg_passwd));
486 dpavlin 41
487 dpavlin 49 /* depth of search */
488     if (PG_ARGISNULL(_arg_depth)) {
489     depth = 0;
490     } else {
491     depth = PG_GETARG_INT32(_arg_depth);
492     }
493    
494 dpavlin 41 /* query string */
495 dpavlin 48 if (PG_ARGISNULL(_arg_query)) {
496 dpavlin 41 query = "";
497     } else {
498 dpavlin 48 query = _textout(PG_GETARG_TEXT_P(_arg_query));
499 dpavlin 41 }
500    
501     /* atribute filter */
502 dpavlin 48 if (PG_ARGISNULL(_arg_attr)) {
503 dpavlin 41 attr = "";
504     } else {
505 dpavlin 48 attr = _textout(PG_GETARG_TEXT_P(_arg_attr));
506 dpavlin 41 }
507    
508     /* sort order */
509 dpavlin 48 if (PG_ARGISNULL(_arg_order)) {
510 dpavlin 41 order = "";
511     } else {
512 dpavlin 48 order = _textout(PG_GETARG_TEXT_P(_arg_order));
513 dpavlin 41 }
514    
515    
516     /* limit */
517 dpavlin 48 if (PG_ARGISNULL(_arg_limit)) {
518 dpavlin 41 limit = 0;
519     } else {
520 dpavlin 48 limit = PG_GETARG_INT32(_arg_limit);
521 dpavlin 41 }
522    
523     /* offset */
524 dpavlin 48 if (PG_ARGISNULL(_arg_offset)) {
525 dpavlin 41 offset = 0;
526     } else {
527 dpavlin 48 offset = PG_GETARG_INT32(_arg_offset);
528 dpavlin 41 }
529    
530     /* initialize the network environment */
531     if(!est_init_net_env()){
532     ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
533     errmsg("pgest_node: can't create network enviroment")));
534     }
535    
536     /* create the node connection object */
537     elog(DEBUG1, "pgest_node: est_node_new(%s) as %s", node_url, user);
538     node = est_node_new(node_url);
539     est_node_set_auth(node, user, passwd);
540    
541 dpavlin 49 elog(DEBUG1, "pgest_node: node: %s (d:%d) query[%s] attr[%s] limit %d offset %d", node_url, depth, query, (PG_ARGISNULL(_arg_attr) ? "NULL" : attr), limit, offset);
542 dpavlin 41
543     /* create a search condition object */
544     if (!(cond = est_cond_new())) {
545     ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
546     errmsg("pgest_node: est_cond_new failed")));
547     }
548    
549     /* set the search phrase to the search condition object */
550 dpavlin 48 if (! PG_ARGISNULL(_arg_query) && strlen(query) > 0)
551 dpavlin 41 est_cond_set_phrase(cond, query);
552    
553     /* minimum valid attribute length is 10: @a STREQ a */
554 dpavlin 48 if (! PG_ARGISNULL(_arg_attr) && strlen(attr) >= 10) {
555 dpavlin 41 elog(DEBUG1,"attributes: %s", attr);
556 dpavlin 56 cond_add_attr(cond, attr);
557 dpavlin 41 }
558    
559     /* set the search phrase to the search condition object */
560 dpavlin 48 if (! PG_ARGISNULL(_arg_order) && strlen(order) > 0) {
561 dpavlin 41 elog(DEBUG1,"est_cond_set_order(%s)", order);
562     est_cond_set_order(cond, order);
563     }
564    
565     if (limit) {
566     elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
567     est_cond_set_max(cond, limit + offset);
568     }
569    
570 dpavlin 51 if (offset) {
571     elog(DEBUG1,"est_cond_set_skip(%d)", offset);
572     est_cond_set_skip(cond, offset);
573     }
574    
575 dpavlin 41 /* get the result of search */
576 dpavlin 49 nres = est_node_search(node, cond, depth);
577 dpavlin 41
578 dpavlin 44 if (! nres) {
579     int status = est_node_status(node);
580     est_cond_delete(cond);
581     est_node_delete(node);
582     est_free_net_env();
583     ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
584     errmsg("pgest_node: search failed, node status %d", status)));
585     }
586    
587 dpavlin 41 /* get number of results */
588     resnum = est_noderes_doc_num(nres);
589    
590     /* check if results exists */
591     if ( 0 == resnum ) {
592     elog(INFO, "pgest_node: no results for: %s", query );
593     }
594    
595     /* total number of tuples to be returned */
596     if (limit && limit < resnum) {
597     nrows = limit;
598     } else {
599 dpavlin 80 nrows = resnum;
600 dpavlin 41 }
601    
602 dpavlin 57 /* get hints */
603     hints = est_noderes_hints(nres);
604    
605 dpavlin 41 elog(DEBUG1, "pgest_node: found %d hits for %s", resnum, query);
606    
607    
608     values = (char **) palloc(ncols * sizeof(char *));
609    
610     for (i = 0; i < nrows; i++)
611     {
612    
613     /* get result from estraier */
614 dpavlin 51 if (! ( rdoc = est_noderes_get_doc(nres, i) )) {
615 dpavlin 47 elog(INFO, "pgest_node: can't find result %d", i + offset);
616 dpavlin 41 } else {
617     elog(DEBUG1, "URI: %s\n Title: %s\n",
618     est_resdoc_attr(rdoc, "@uri"),
619     est_resdoc_attr(rdoc, "@title")
620     );
621     }
622    
623     /* iterate over results */
624     for (j = 0; j < ncols; j++)
625     {
626     bool isnull;
627 dpavlin 57 char *attr; /* current attribute name */
628     char *hint; /* position of current hint in attribute */
629     char *hint_val;
630 dpavlin 41
631     /* array value of this position */
632     indx[0] = j + attr_dim_lower_bounds[0];
633    
634     dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
635 dpavlin 57 attr = (char *)DirectFunctionCall1(textout, dvalue);
636 dpavlin 41
637 dpavlin 57 if (!isnull && (hint = strstr(attr, HINTS_PREFIX)) != NULL) {
638     /* skip HINTS. prefix */
639     hint += strlen(HINTS_PREFIX);
640    
641 dpavlin 72 hint_val = (char *)cbmapget(hints, hint, -1, NULL);
642 dpavlin 57 elog(DEBUG2, "hint %s = %s", hint, hint_val);
643    
644     if (hint_val != NULL) {
645     values[j] = DatumGetCString( hint_val );
646     } else {
647     elog(INFO, "can't get hint in results: %s", hint);
648     values[j] = NULL;
649     }
650     } else if (!isnull && rdoc)
651     values[j] = DatumGetCString( node_attr2text(rdoc, attr) );
652 dpavlin 41 else
653     values[j] = NULL;
654     }
655     /* construct the tuple */
656     tuple = BuildTupleFromCStrings(attinmeta, values);
657    
658     /* now store it */
659     tuplestore_puttuple(tupstore, tuple);
660    
661     }
662    
663     tuplestore_donestoring(tupstore);
664     rsinfo->setResult = tupstore;
665    
666     /*
667     * SFRM_Materialize mode expects us to return a NULL Datum. The actual
668     * tuples are in our tuplestore and passed back through
669     * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
670     * that we actually used to build our tuples with, so the caller can
671     * verify we did what it was expecting.
672     */
673     rsinfo->setDesc = tupdesc;
674     MemoryContextSwitchTo(oldcontext);
675    
676     /* delete the node result object */
677     est_noderes_delete(nres);
678    
679     /* destroy the search condition object */
680     est_cond_delete(cond);
681    
682     /* destroy the node object */
683     est_node_delete(node);
684    
685     /* free the networking environment */
686     est_free_net_env();
687    
688     return (Datum) 0;
689     }
690    
691     /* make text var from node attr */
692     char *node_attr2text(ESTRESDOC *rdoc, char *attr) {
693     char *val;
694     const char *attrval;
695     int len;
696     int attrlen;
697    
698 dpavlin 47 if (! rdoc) return (Datum) NULL;
699    
700 dpavlin 56 elog(DEBUG1, "doc: %p, attr: %s", rdoc, attr);
701 dpavlin 41
702     if ( (attrval = est_resdoc_attr(rdoc, attr)) && (attrlen = strlen(attrval)) ) {
703     val = (char *) palloc(attrlen * sizeof(char));
704     } else {
705     return (Datum) NULL;
706     }
707    
708     len = strlen(attrval);
709     elog(DEBUG1, "node_attr2text(%s) = '%s' %d bytes", attr, attrval, len);
710    
711     len++;
712     len *= sizeof(char);
713    
714     elog(DEBUG2, "palloc(%d)", len);
715    
716     val = palloc(len);
717    
718     memset(val, 0, len);
719     strncpy(val, attrval, len);
720    
721     elog(DEBUG2, "val=%s", val);
722    
723     return val;
724     }
725    
726 dpavlin 56 /* parse attributes and add them to confition */
727     void cond_add_attr(ESTCOND *cond, char *attr) {
728     char *next;
729     char *curr_attr;
730     while ( strlen(attr) > 0 ) {
731 dpavlin 73 printf("len [%s] = %zd\n", attr, strlen(attr));
732 dpavlin 56 if ((next = strstr(attr, ATTR_DELIMITER)) != NULL) {
733     curr_attr = palloc( next - attr + 1 );
734     memcpy(curr_attr, attr, next-attr);
735     curr_attr[next-attr] = '\0';
736     next += strlen(ATTR_DELIMITER);
737     } else {
738     next = "";
739     curr_attr = attr;
740     }
741     elog(DEBUG1, "est_cond_add_attr(%s)", curr_attr);
742     est_cond_add_attr(cond, curr_attr);
743     attr = next;
744     }
745     }
746 dpavlin 61
747     /* trigger to keep data in Hyper Estraier index up-to-date */
748     /* CREATE FUNCTION pgest_trigger() RETURNS TRIGGER AS ... */
749    
750     /*
751     * UPDATE, INSERT and DELETE triggers are like this:
752    
753     CREATE TRIGGER pgest_trigger_update AFTER UPDATE
754     ON table_name FOR EACH ROW
755     EXECUTE PROCEDURE pgest_trigger('http://localhost:1978/node/trivia','admin','admin',
756     'name_of_pk', 'column', 'another_column', 'and_so_on'
757     )
758    
759     */
760    
761     PG_FUNCTION_INFO_V1(pgest_trigger);
762     Datum pgest_trigger(PG_FUNCTION_ARGS) {
763    
764     TriggerData *data;
765     TupleDesc tupdesc;
766     HeapTuple ret;
767    
768     char **args;
769     char *keycol = NULL;
770     char *key = NULL;
771     char *col_data = NULL;
772     int knumber;
773     int i;
774     int create_doc = 0;
775 dpavlin 79 int edit_doc = 0;
776 dpavlin 61
777     ESTNODE *node;
778     ESTDOC *doc;
779    
780    
781    
782     if (! CALLED_AS_TRIGGER(fcinfo)) {
783     elog(ERROR, "pgest_trigger() must be called as a trigger");
784     }
785    
786     data = (TriggerData *) fcinfo->context;
787    
788     if (data->tg_trigger->tgnargs < 5)
789     elog(ERROR, "pgest_trigger() requires at least 5 parameters ('http://localhost:1978/node/trivia', 'user', 'passwd', 'pk_column', 'column', ... )");
790    
791     args = data->tg_trigger->tgargs;
792     keycol = args[3];
793    
794     tupdesc = data->tg_relation->rd_att;
795    
796     knumber = SPI_fnumber(tupdesc, keycol);
797     key = SPI_getvalue(data->tg_trigtuple, tupdesc, knumber);
798    
799    
800     /* initialize the network environment */
801     if( ! est_init_net_env() )
802     elog(ERROR, "pgest_trigger: network is unavailable\n");
803    
804     /* create and configure the node connection object */
805     node = est_node_new( args[0] );
806     est_node_set_auth(node, args[1], args[2]);
807    
808    
809     if (TRIGGER_FIRED_BY_INSERT(data->tg_event)) {
810     /* There is no old data */
811     ret = data->tg_trigtuple;
812    
813     create_doc++;
814    
815     } else if (TRIGGER_FIRED_BY_UPDATE(data->tg_event)) {
816     ret = data->tg_newtuple;
817    
818 dpavlin 79 edit_doc++;
819 dpavlin 61
820     } else if (TRIGGER_FIRED_BY_DELETE(data->tg_event)) {
821     /* There is no new data */
822     ret = data->tg_trigtuple;
823    
824     if (! est_node_out_doc_by_uri(node, key) )
825     elog(ERROR, "est_node_doc_by_uri(%s): %d\n", key, est_node_status(node));
826    
827     } else {
828     elog(ERROR, "pgest_trigger() not called from INSERT/UPDATE/DELETE");
829     }
830    
831 dpavlin 79 if ( create_doc || edit_doc ) {
832 dpavlin 61
833 dpavlin 79 if ( create_doc ) {
834     /* create a document object */
835     doc = est_doc_new();
836     est_doc_add_attr(doc, "@uri", key);
837 dpavlin 61
838 dpavlin 79 elog(DEBUG1, "est_doc_new @uri=%s", key);
839     } else {
840     /* edit existing document */
841     doc = est_node_get_doc_by_uri(node, key);
842     if (doc == NULL)
843     elog(ERROR, "est_node_get_doc_by_uri(%s): %d\n", key, est_node_status(node));
844     elog(DEBUG1, "est_node_get_doc_by_uri(%s)", key);
845     }
846 dpavlin 61
847     for( i = 4; i < data->tg_trigger->tgnargs; i++ ) {
848    
849     col_data = SPI_getvalue(ret, tupdesc, SPI_fnumber(tupdesc, args[i]));
850    
851     if (data) {
852     elog(DEBUG1, " + %s = %s", args[i], col_data);
853     est_doc_add_attr(doc, args[i], col_data);
854     est_doc_add_text(doc, col_data);
855     }
856    
857     }
858    
859 dpavlin 79 if ( edit_doc ) {
860     /* update existing document */
861     if( ! est_node_edit_doc(node, doc) )
862     elog(ERROR, "est_node_edit_doc: %d\n", est_node_status(node));
863     } else {
864     /* register the document object to the node */
865     if( ! est_node_put_doc(node, doc) )
866     elog(ERROR, "est_node_put_doc: %d\n", est_node_status(node));
867     }
868 dpavlin 61
869     /* destroy the document object */
870     est_doc_delete(doc);
871    
872     }
873    
874     /* destroy the node object */
875     est_node_delete(node);
876     /* free the networking environment */
877     est_free_net_env();
878    
879    
880     return PointerGetDatum(ret);
881     }
882    

  ViewVC Help
Powered by ViewVC 1.1.26