/[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 85 - (show annotations)
Sat Feb 3 17:13:07 2007 UTC (17 years, 1 month ago) by dpavlin
File MIME type: text/plain
File size: 22137 byte(s)
add PG_MODULE_MAGIC for PostgreSQL 8.2
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 #ifdef PG_MODULE_MAGIC
48 PG_MODULE_MAGIC;
49 #endif
50
51 #define ATTR_DELIMITER "{{!}}"
52 #define HINTS_PREFIX "HINTS."
53
54 /* prototype */
55 char *attr2text(ESTDOC *doc, char *attr);
56 char *node_attr2text(ESTRESDOC *rdoc, char *attr);
57 void cond_add_attr(ESTCOND *cond, char *attr);
58
59
60 /* work in progress */
61 PG_FUNCTION_INFO_V1(pgest_attr);
62 Datum pgest_attr(PG_FUNCTION_ARGS)
63 {
64 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(6);
65 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 int ncols = 0;
70 int nrows = 0;
71 int indx[MAXDIM];
72 int16 attr_len;
73 bool attr_byval;
74 char attr_align;
75 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
76 AttInMetadata *attinmeta;
77 TupleDesc tupdesc;
78 Tuplestorestate *tupstore = NULL;
79 HeapTuple tuple;
80 MemoryContext per_query_ctx;
81 MemoryContext oldcontext;
82 Datum dvalue;
83 char **values;
84 int rsinfo_ncols;
85 int i, j;
86 /* estvars */
87 ESTDB *db;
88 ESTCOND *cond;
89 ESTDOC *doc;
90 int ecode, *est_result, resnum;
91 int limit = 0;
92 int offset = 0;
93
94 char *index_path;
95 char *query;
96 char *attr;
97 char *order;
98
99
100 /* only allow 1D input array */
101 if (attr_ndims == 1)
102 {
103 ncols = attr_dim_counts[0];
104 }
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 /* 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 /* get info about element type needed to construct the array */
119 get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
120
121 /* get the requested return tuple description */
122 tupdesc = rsinfo->expectedDesc;
123 rsinfo_ncols = tupdesc->natts;
124
125 /*
126 * The requested tuple description better match up with the array
127 * we were given.
128 */
129 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 /* 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
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 if (PG_ARGISNULL(1)) {
161 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
173 /* sort order */
174 if (PG_ARGISNULL(3)) {
175 order = "";
176 } else {
177 order = _textout(PG_GETARG_TEXT_P(3));
178 }
179
180
181 /* limit */
182 if (PG_ARGISNULL(4)) {
183 limit = 0;
184 } else {
185 limit = PG_GETARG_INT32(4);
186 }
187
188 /* offset */
189 if (PG_ARGISNULL(5)) {
190 offset = 0;
191 } else {
192 offset = PG_GETARG_INT32(5);
193 }
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 elog(DEBUG1, "pgest_attr: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);
206
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 elog(DEBUG1,"attributes: %s", attr);
220 cond_add_attr(cond, attr);
221 }
222
223 /* 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 if (limit) {
230 elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
231 est_cond_set_max(cond, limit + offset);
232 }
233
234 /* 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 nrows = limit;
245 } else {
246 nrows = resnum - offset;
247 }
248
249
250 elog(DEBUG1, "pgest_attr: found %d hits for %s", resnum, query);
251
252 values = (char **) palloc(ncols * sizeof(char *));
253
254 for (i = 0; i < nrows; i++)
255 {
256
257 /* get result from estraier */
258 if (! ( doc = est_db_get_doc(db, est_result[i + offset], 0)) ) {
259 elog(INFO, "pgest_attr: can't find result %d", i + offset);
260 } 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 for (j = 0; j < ncols; j++)
269 {
270 bool isnull;
271
272 /* array value of this position */
273 indx[0] = j + attr_dim_lower_bounds[0];
274
275 dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
276
277 if (!isnull && doc)
278 values[j] = DatumGetCString(
279 attr2text(doc,
280 (char *)DirectFunctionCall1(textout, dvalue)
281 ));
282 else
283 values[j] = NULL;
284 }
285 /* construct the tuple */
286 tuple = BuildTupleFromCStrings(attinmeta, values);
287
288 /* now store it */
289 tuplestore_puttuple(tupstore, tuple);
290
291 /* delete estraier document object */
292 if (doc) est_doc_delete(doc);
293 }
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 est_cond_delete(cond);
309
310 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 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 int attrlen;
326
327 if (! doc) return (Datum) NULL;
328
329 elog(DEBUG1, "doc: %p, attr: %s", doc, attr);
330
331 if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {
332 val = (char *) palloc(attrlen * sizeof(char));
333 } else {
334 return (Datum) NULL;
335 }
336
337 len = strlen(attrval);
338 elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);
339
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 /*
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 /* select * from pgest( */
362 #define _arg_node_uri 0
363 #define _arg_login 1
364 #define _arg_passwd 2
365 #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 /* as (foo text, ... ); */
373
374
375 PG_FUNCTION_INFO_V1(pgest_node);
376 Datum pgest_node(PG_FUNCTION_ARGS)
377 {
378 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(_arg_attr_array);
379 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 CBMAP *hints;
406 int resnum = 0;
407 int limit = 0;
408 int offset = 0;
409 int depth = 0;
410
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 if (PG_ARGISNULL(_arg_node_uri)) {
470 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 node_url = _textout(PG_GETARG_TEXT_P(_arg_node_uri));
476
477 /* login and password */
478 if (PG_ARGISNULL(_arg_login) || PG_ARGISNULL(_arg_passwd)) {
479 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 user = _textout(PG_GETARG_TEXT_P(_arg_login));
485 passwd = _textout(PG_GETARG_TEXT_P(_arg_passwd));
486
487 /* depth of search */
488 if (PG_ARGISNULL(_arg_depth)) {
489 depth = 0;
490 } else {
491 depth = PG_GETARG_INT32(_arg_depth);
492 }
493
494 /* query string */
495 if (PG_ARGISNULL(_arg_query)) {
496 query = "";
497 } else {
498 query = _textout(PG_GETARG_TEXT_P(_arg_query));
499 }
500
501 /* atribute filter */
502 if (PG_ARGISNULL(_arg_attr)) {
503 attr = "";
504 } else {
505 attr = _textout(PG_GETARG_TEXT_P(_arg_attr));
506 }
507
508 /* sort order */
509 if (PG_ARGISNULL(_arg_order)) {
510 order = "";
511 } else {
512 order = _textout(PG_GETARG_TEXT_P(_arg_order));
513 }
514
515
516 /* limit */
517 if (PG_ARGISNULL(_arg_limit)) {
518 limit = 0;
519 } else {
520 limit = PG_GETARG_INT32(_arg_limit);
521 }
522
523 /* offset */
524 if (PG_ARGISNULL(_arg_offset)) {
525 offset = 0;
526 } else {
527 offset = PG_GETARG_INT32(_arg_offset);
528 }
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 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
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 if (! PG_ARGISNULL(_arg_query) && strlen(query) > 0)
551 est_cond_set_phrase(cond, query);
552
553 /* minimum valid attribute length is 10: @a STREQ a */
554 if (! PG_ARGISNULL(_arg_attr) && strlen(attr) >= 10) {
555 elog(DEBUG1,"attributes: %s", attr);
556 cond_add_attr(cond, attr);
557 }
558
559 /* set the search phrase to the search condition object */
560 if (! PG_ARGISNULL(_arg_order) && strlen(order) > 0) {
561 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 if (offset) {
571 elog(DEBUG1,"est_cond_set_skip(%d)", offset);
572 est_cond_set_skip(cond, offset);
573 }
574
575 /* get the result of search */
576 nres = est_node_search(node, cond, depth);
577
578 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 /* 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 nrows = resnum;
600 }
601
602 /* get hints */
603 hints = est_noderes_hints(nres);
604
605 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 if (! ( rdoc = est_noderes_get_doc(nres, i) )) {
615 elog(INFO, "pgest_node: can't find result %d", i + offset);
616 } 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 char *attr; /* current attribute name */
628 char *hint; /* position of current hint in attribute */
629 char *hint_val;
630
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 attr = (char *)DirectFunctionCall1(textout, dvalue);
636
637 if (!isnull && (hint = strstr(attr, HINTS_PREFIX)) != NULL) {
638 /* skip HINTS. prefix */
639 hint += strlen(HINTS_PREFIX);
640
641 hint_val = (char *)cbmapget(hints, hint, -1, NULL);
642 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 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 if (! rdoc) return (Datum) NULL;
699
700 elog(DEBUG1, "doc: %p, attr: %s", rdoc, attr);
701
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 /* 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 printf("len [%s] = %zd\n", attr, strlen(attr));
732 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
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 int edit_doc = 0;
776
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 edit_doc++;
819
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 if ( create_doc || edit_doc ) {
832
833 if ( create_doc ) {
834 /* create a document object */
835 doc = est_doc_new();
836 est_doc_add_attr(doc, "@uri", key);
837
838 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
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 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
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