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

  ViewVC Help
Powered by ViewVC 1.1.26