/[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

Contents of /trunk/pgest.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 80 - (show annotations)
Tue Aug 8 12:15:55 2006 UTC (17 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 22089 byte(s)
bugfix: offset implementation didn't return all records
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 * - coreexample002.c and nodeexample002.c from Hyper Estraier (GPL)
18 * - _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 #include "utils/lsyscache.h"
29 #include "miscadmin.h"
30 #include "commands/trigger.h"
31 #include "executor/spi.h"
32
33 #include <estraier.h>
34 #include <cabin.h>
35 #include <estnode.h>
36
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 /* SortMem got renamed in PostgreSQL 8.0 */
43 #ifndef SortMem
44 #define SortMem 16 * 1024
45 #endif
46
47 #define ATTR_DELIMITER "{{!}}"
48 #define HINTS_PREFIX "HINTS."
49
50 /* prototype */
51 char *attr2text(ESTDOC *doc, char *attr);
52 char *node_attr2text(ESTRESDOC *rdoc, char *attr);
53 void cond_add_attr(ESTCOND *cond, char *attr);
54
55
56 /* work in progress */
57 PG_FUNCTION_INFO_V1(pgest_attr);
58 Datum pgest_attr(PG_FUNCTION_ARGS)
59 {
60 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(6);
61 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 int ncols = 0;
66 int nrows = 0;
67 int indx[MAXDIM];
68 int16 attr_len;
69 bool attr_byval;
70 char attr_align;
71 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
72 AttInMetadata *attinmeta;
73 TupleDesc tupdesc;
74 Tuplestorestate *tupstore = NULL;
75 HeapTuple tuple;
76 MemoryContext per_query_ctx;
77 MemoryContext oldcontext;
78 Datum dvalue;
79 char **values;
80 int rsinfo_ncols;
81 int i, j;
82 /* estvars */
83 ESTDB *db;
84 ESTCOND *cond;
85 ESTDOC *doc;
86 int ecode, *est_result, resnum;
87 int limit = 0;
88 int offset = 0;
89
90 char *index_path;
91 char *query;
92 char *attr;
93 char *order;
94
95
96 /* only allow 1D input array */
97 if (attr_ndims == 1)
98 {
99 ncols = attr_dim_counts[0];
100 }
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 /* 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 /* get info about element type needed to construct the array */
115 get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
116
117 /* get the requested return tuple description */
118 tupdesc = rsinfo->expectedDesc;
119 rsinfo_ncols = tupdesc->natts;
120
121 /*
122 * The requested tuple description better match up with the array
123 * we were given.
124 */
125 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 /* 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
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 if (PG_ARGISNULL(1)) {
157 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
169 /* sort order */
170 if (PG_ARGISNULL(3)) {
171 order = "";
172 } else {
173 order = _textout(PG_GETARG_TEXT_P(3));
174 }
175
176
177 /* limit */
178 if (PG_ARGISNULL(4)) {
179 limit = 0;
180 } else {
181 limit = PG_GETARG_INT32(4);
182 }
183
184 /* offset */
185 if (PG_ARGISNULL(5)) {
186 offset = 0;
187 } else {
188 offset = PG_GETARG_INT32(5);
189 }
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 elog(DEBUG1, "pgest_attr: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);
202
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 elog(DEBUG1,"attributes: %s", attr);
216 cond_add_attr(cond, attr);
217 }
218
219 /* 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 if (limit) {
226 elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
227 est_cond_set_max(cond, limit + offset);
228 }
229
230 /* 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 nrows = limit;
241 } else {
242 nrows = resnum - offset;
243 }
244
245
246 elog(DEBUG1, "pgest_attr: found %d hits for %s", resnum, query);
247
248 values = (char **) palloc(ncols * sizeof(char *));
249
250 for (i = 0; i < nrows; i++)
251 {
252
253 /* get result from estraier */
254 if (! ( doc = est_db_get_doc(db, est_result[i + offset], 0)) ) {
255 elog(INFO, "pgest_attr: can't find result %d", i + offset);
256 } 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 for (j = 0; j < ncols; j++)
265 {
266 bool isnull;
267
268 /* array value of this position */
269 indx[0] = j + attr_dim_lower_bounds[0];
270
271 dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
272
273 if (!isnull && doc)
274 values[j] = DatumGetCString(
275 attr2text(doc,
276 (char *)DirectFunctionCall1(textout, dvalue)
277 ));
278 else
279 values[j] = NULL;
280 }
281 /* construct the tuple */
282 tuple = BuildTupleFromCStrings(attinmeta, values);
283
284 /* now store it */
285 tuplestore_puttuple(tupstore, tuple);
286
287 /* delete estraier document object */
288 if (doc) est_doc_delete(doc);
289 }
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 est_cond_delete(cond);
305
306 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 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 int attrlen;
322
323 if (! doc) return (Datum) NULL;
324
325 elog(DEBUG1, "doc: %p, attr: %s", doc, attr);
326
327 if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {
328 val = (char *) palloc(attrlen * sizeof(char));
329 } else {
330 return (Datum) NULL;
331 }
332
333 len = strlen(attrval);
334 elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);
335
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 /*
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 /* select * from pgest( */
358 #define _arg_node_uri 0
359 #define _arg_login 1
360 #define _arg_passwd 2
361 #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 /* as (foo text, ... ); */
369
370
371 PG_FUNCTION_INFO_V1(pgest_node);
372 Datum pgest_node(PG_FUNCTION_ARGS)
373 {
374 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(_arg_attr_array);
375 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 CBMAP *hints;
402 int resnum = 0;
403 int limit = 0;
404 int offset = 0;
405 int depth = 0;
406
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 if (PG_ARGISNULL(_arg_node_uri)) {
466 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 node_url = _textout(PG_GETARG_TEXT_P(_arg_node_uri));
472
473 /* login and password */
474 if (PG_ARGISNULL(_arg_login) || PG_ARGISNULL(_arg_passwd)) {
475 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 user = _textout(PG_GETARG_TEXT_P(_arg_login));
481 passwd = _textout(PG_GETARG_TEXT_P(_arg_passwd));
482
483 /* depth of search */
484 if (PG_ARGISNULL(_arg_depth)) {
485 depth = 0;
486 } else {
487 depth = PG_GETARG_INT32(_arg_depth);
488 }
489
490 /* query string */
491 if (PG_ARGISNULL(_arg_query)) {
492 query = "";
493 } else {
494 query = _textout(PG_GETARG_TEXT_P(_arg_query));
495 }
496
497 /* atribute filter */
498 if (PG_ARGISNULL(_arg_attr)) {
499 attr = "";
500 } else {
501 attr = _textout(PG_GETARG_TEXT_P(_arg_attr));
502 }
503
504 /* sort order */
505 if (PG_ARGISNULL(_arg_order)) {
506 order = "";
507 } else {
508 order = _textout(PG_GETARG_TEXT_P(_arg_order));
509 }
510
511
512 /* limit */
513 if (PG_ARGISNULL(_arg_limit)) {
514 limit = 0;
515 } else {
516 limit = PG_GETARG_INT32(_arg_limit);
517 }
518
519 /* offset */
520 if (PG_ARGISNULL(_arg_offset)) {
521 offset = 0;
522 } else {
523 offset = PG_GETARG_INT32(_arg_offset);
524 }
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 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
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 if (! PG_ARGISNULL(_arg_query) && strlen(query) > 0)
547 est_cond_set_phrase(cond, query);
548
549 /* minimum valid attribute length is 10: @a STREQ a */
550 if (! PG_ARGISNULL(_arg_attr) && strlen(attr) >= 10) {
551 elog(DEBUG1,"attributes: %s", attr);
552 cond_add_attr(cond, attr);
553 }
554
555 /* set the search phrase to the search condition object */
556 if (! PG_ARGISNULL(_arg_order) && strlen(order) > 0) {
557 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 if (offset) {
567 elog(DEBUG1,"est_cond_set_skip(%d)", offset);
568 est_cond_set_skip(cond, offset);
569 }
570
571 /* get the result of search */
572 nres = est_node_search(node, cond, depth);
573
574 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 /* 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;
596 }
597
598 /* get hints */
599 hints = est_noderes_hints(nres);
600
601 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 if (! ( rdoc = est_noderes_get_doc(nres, i) )) {
611 elog(INFO, "pgest_node: can't find result %d", i + offset);
612 } 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 char *attr; /* current attribute name */
624 char *hint; /* position of current hint in attribute */
625 char *hint_val;
626
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 attr = (char *)DirectFunctionCall1(textout, dvalue);
632
633 if (!isnull && (hint = strstr(attr, HINTS_PREFIX)) != NULL) {
634 /* skip HINTS. prefix */
635 hint += strlen(HINTS_PREFIX);
636
637 hint_val = (char *)cbmapget(hints, hint, -1, NULL);
638 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 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 if (! rdoc) return (Datum) NULL;
695
696 elog(DEBUG1, "doc: %p, attr: %s", rdoc, attr);
697
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 /* 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] = %zd\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
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 int edit_doc = 0;
772
773 ESTNODE *node;
774 ESTDOC *doc;
775
776
777
778 if (! CALLED_AS_TRIGGER(fcinfo)) {
779 elog(ERROR, "pgest_trigger() must be called as a trigger");
780 }
781
782 data = (TriggerData *) fcinfo->context;
783
784 if (data->tg_trigger->tgnargs < 5)
785 elog(ERROR, "pgest_trigger() requires at least 5 parameters ('http://localhost:1978/node/trivia', 'user', 'passwd', 'pk_column', 'column', ... )");
786
787 args = data->tg_trigger->tgargs;
788 keycol = args[3];
789
790 tupdesc = data->tg_relation->rd_att;
791
792 knumber = SPI_fnumber(tupdesc, keycol);
793 key = SPI_getvalue(data->tg_trigtuple, tupdesc, knumber);
794
795
796 /* initialize the network environment */
797 if( ! est_init_net_env() )
798 elog(ERROR, "pgest_trigger: network is unavailable\n");
799
800 /* create and configure the node connection object */
801 node = est_node_new( args[0] );
802 est_node_set_auth(node, args[1], args[2]);
803
804
805 if (TRIGGER_FIRED_BY_INSERT(data->tg_event)) {
806 /* There is no old data */
807 ret = data->tg_trigtuple;
808
809 create_doc++;
810
811 } else if (TRIGGER_FIRED_BY_UPDATE(data->tg_event)) {
812 ret = data->tg_newtuple;
813
814 edit_doc++;
815
816 } else if (TRIGGER_FIRED_BY_DELETE(data->tg_event)) {
817 /* There is no new data */
818 ret = data->tg_trigtuple;
819
820 if (! est_node_out_doc_by_uri(node, key) )
821 elog(ERROR, "est_node_doc_by_uri(%s): %d\n", key, est_node_status(node));
822
823 } else {
824 elog(ERROR, "pgest_trigger() not called from INSERT/UPDATE/DELETE");
825 }
826
827 if ( create_doc || edit_doc ) {
828
829 if ( create_doc ) {
830 /* create a document object */
831 doc = est_doc_new();
832 est_doc_add_attr(doc, "@uri", key);
833
834 elog(DEBUG1, "est_doc_new @uri=%s", key);
835 } else {
836 /* edit existing document */
837 doc = est_node_get_doc_by_uri(node, key);
838 if (doc == NULL)
839 elog(ERROR, "est_node_get_doc_by_uri(%s): %d\n", key, est_node_status(node));
840 elog(DEBUG1, "est_node_get_doc_by_uri(%s)", key);
841 }
842
843 for( i = 4; i < data->tg_trigger->tgnargs; i++ ) {
844
845 col_data = SPI_getvalue(ret, tupdesc, SPI_fnumber(tupdesc, args[i]));
846
847 if (data) {
848 elog(DEBUG1, " + %s = %s", args[i], col_data);
849 est_doc_add_attr(doc, args[i], col_data);
850 est_doc_add_text(doc, col_data);
851 }
852
853 }
854
855 if ( edit_doc ) {
856 /* update existing document */
857 if( ! est_node_edit_doc(node, doc) )
858 elog(ERROR, "est_node_edit_doc: %d\n", est_node_status(node));
859 } else {
860 /* register the document object to the node */
861 if( ! est_node_put_doc(node, doc) )
862 elog(ERROR, "est_node_put_doc: %d\n", est_node_status(node));
863 }
864
865 /* destroy the document object */
866 est_doc_delete(doc);
867
868 }
869
870 /* destroy the node object */
871 est_node_delete(node);
872 /* free the networking environment */
873 est_free_net_env();
874
875
876 return PointerGetDatum(ret);
877 }
878

  ViewVC Help
Powered by ViewVC 1.1.26