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

Diff of /trunk/pgest.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 19 by dpavlin, Thu May 26 17:56:53 2005 UTC revision 73 by dpavlin, Mon Aug 7 22:47:41 2006 UTC
# Line 14  Line 14 
14   *   *
15   * Based on:   * Based on:
16   * - C example from PostgreSQL documentation (BSD licence)   * - C example from PostgreSQL documentation (BSD licence)
17   * - example002.c from Hyper Estraier (GPL)   * - coreexample002.c and nodeexample002.c from Hyper Estraier (GPL)
18   * - _textin/_textout from pgcurl.c (LGPL)   * - _textin/_textout from pgcurl.c (LGPL)
19   *   *
20   * This code is licenced under GPL   * This code is licenced under GPL
# Line 25  Line 25 
25  #include "funcapi.h"  #include "funcapi.h"
26  #include "utils/builtins.h"  #include "utils/builtins.h"
27  #include "utils/array.h"  #include "utils/array.h"
28    #include "utils/lsyscache.h"
29  #include "miscadmin.h"  #include "miscadmin.h"
30    #include "commands/trigger.h"
31    #include "executor/spi.h"
32    
33  #include <estraier.h>  #include <estraier.h>
34  #include <cabin.h>  #include <cabin.h>
35    #include <estnode.h>
36    
37  #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))  #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
38  #define _textout(str) DatumGetPointer(DirectFunctionCall1(textout, PointerGetDatum(str)))  #define _textout(str) DatumGetPointer(DirectFunctionCall1(textout, PointerGetDatum(str)))
39  #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))  #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
40  #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))  #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 */  /* prototype */
51  char *attr2text(ESTDOC *doc, char *attr);  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  ESTDB *db;  
56  ESTCOND *cond;  /* work in progress */
57  ESTDOC *doc;  PG_FUNCTION_INFO_V1(pgest_attr);
58  const CBLIST *texts;  Datum pgest_attr(PG_FUNCTION_ARGS)
59  int ecode, *est_result, resnum, i, j;  {
60  int limit = 0;          ArrayType       *attr_arr = PG_GETARG_ARRAYTYPE_P(6);
61  int offset = 0;          Oid             attr_element_type = ARR_ELEMTYPE(attr_arr);
62            int             attr_ndims = ARR_NDIM(attr_arr);
63  /* define PostgreSQL v1 function */          int             *attr_dim_counts = ARR_DIMS(attr_arr);
64  PG_FUNCTION_INFO_V1(pgest);          int             *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
65  Datum pgest(PG_FUNCTION_ARGS) {          int             ncols = 0;
66            int             nrows = 0;
67          FuncCallContext *funcctx;          int             indx[MAXDIM];
68          int             call_cntr;          int16           attr_len;
69          int             max_calls;          bool            attr_byval;
70          TupleDesc       tupdesc;          char            attr_align;
71          TupleTableSlot  *slot;          ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
72          AttInMetadata   *attinmeta;          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;          char            *index_path;
91          char            *query;          char            *query;
92          char            *attr;          char            *attr;
93            char            *order;
94    
         /* stuff done only on the first call of the function */  
         if (SRF_IS_FIRSTCALL()) {  
                 MemoryContext   oldcontext;  
   
                 /* create a function context for cross-call persistence */  
                 funcctx = SRF_FIRSTCALL_INIT();  
   
                 /* switch to memory context appropriate for multiple function calls */  
                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);  
                 /* take arguments from function */  
   
                 /* index path */  
                 if (PG_ARGISNULL(0)) {  
                         elog(ERROR, "index path can't be null");  
                         SRF_RETURN_DONE(funcctx);  
                 }  
                 index_path = _textout(PG_GETARG_TEXT_P(0));  
95    
96                  /* query string */          /* only allow 1D input array */
97                  if (PG_ARGISNULL(0)) {          if (attr_ndims == 1)
98                          query = "";          {
99                  } else {                  ncols = attr_dim_counts[0];
100                          query = _textout(PG_GETARG_TEXT_P(1));          }
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                  /* atribute filter */          /* get info about element type needed to construct the array */
115                  if (PG_ARGISNULL(2)) {          get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
                         attr = "";  
                 } else {  
                         attr = _textout(PG_GETARG_TEXT_P(2));  
                 }  
116    
117                  /* limit */          /* get the requested return tuple description */
118                  if (PG_ARGISNULL(3)) {          tupdesc = rsinfo->expectedDesc;
119                          limit = 0;          rsinfo_ncols = tupdesc->natts;
                 } else {  
                         limit = PG_GETARG_INT32(3);  
                 }  
120    
121                  /* offset */          /*
122                  if (PG_ARGISNULL(4)) {           * The requested tuple description better match up with the array
123                          offset = 0;           * we were given.
124                  } else {           */
125                          offset = PG_GETARG_INT32(4);          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                  /* open the database */          /* Now go to work */
135                  elog(DEBUG1, "pgest: est_db_open(%s)", index_path);          rsinfo->returnMode = SFRM_Materialize;
                   
                 if(!(db = est_db_open(index_path, ESTDBREADER, &ecode))){  
                         elog(ERROR, "est_db_open: can't open %s [%d]: %s", index_path, ecode, est_err_msg(ecode));  
                         SRF_RETURN_DONE(funcctx);  
                 }  
                   
                 elog(INFO, "pgest: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);  
                   
                 /* create a search condition object */  
                 if (!(cond = est_cond_new())) {  
                         elog(INFO, "pgest: est_cond_new failed");  
                         SRF_RETURN_DONE(funcctx);  
                 }  
                   
                 /* set the search phrase to the search condition object */  
                 if (! PG_ARGISNULL(1) && strlen(query) > 0)  
                         est_cond_set_phrase(cond, query);  
   
                 /* minimum valid attribute length is 10: @a STREQ a */  
                 if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {  
                         elog(INFO,"est_cond_add_attr(%s)", attr);  
                         est_cond_add_attr(cond, attr);  
                 }  
136    
137                  /* get the result of search */          per_query_ctx = fcinfo->flinfo->fn_mcxt;
138                  est_result = est_db_search(db, cond, &resnum, NULL);          oldcontext = MemoryContextSwitchTo(per_query_ctx);
                   
                 /* total number of tuples to be returned */  
                 if (limit && limit < resnum) {  
                         funcctx->max_calls = limit - offset;  
                 } else {  
                         funcctx->max_calls = resnum - offset;  
                 }  
139    
140                  /* check if results exists */          /* initialize our tuplestore */
141                  if ( 0 == funcctx->max_calls )          tupstore = tuplestore_begin_heap(true, false, SortMem);
                         elog(INFO, "pgest: no results for: %s", query );  
142    
                 elog(DEBUG1, "pgest: found %d hits for %s", resnum, query);  
143    
144                  /* Build a tuple description for a __pgest tuple */          /* take rest of arguments from function */
                 tupdesc = RelationNameGetTupleDesc("__pgest");  
145    
146                  /* allocate a slot for a tuple with this tupdesc */          /* index path */
147                  slot = TupleDescGetSlot(tupdesc);          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                  /* assign slot to function context */          /* query string */
156                  funcctx->slot = slot;          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    
                 /*  
                  * generate attribute metadata needed later to produce tuples from raw  
                  * C strings  
                  */  
                 attinmeta = TupleDescGetAttInMetadata(tupdesc);  
                 funcctx->attinmeta = attinmeta;  
176    
177                  MemoryContextSwitchTo(oldcontext);          /* limit */
178            if (PG_ARGISNULL(4)) {
179                    limit = 0;
180            } else {
181                    limit = PG_GETARG_INT32(4);
182            }
183    
184                  elog(DEBUG1, "SRF_IS_FIRSTCALL done");          /* offset */
185            if (PG_ARGISNULL(5)) {
186                    offset = 0;
187            } else {
188                    offset = PG_GETARG_INT32(5);
189          }          }
190    
         /* stuff done on every call of the function */  
         funcctx = SRF_PERCALL_SETUP();  
191    
192          call_cntr = funcctx->call_cntr;          /* open the database */
193          max_calls = funcctx->max_calls;          elog(DEBUG1, "pgest_attr: est_db_open(%s)", index_path);
194          slot = funcctx->slot;                  
195          attinmeta = funcctx->attinmeta;          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          if (limit && call_cntr > limit - 1) {          /* minimum valid attribute length is 10: @a STREQ a */
214                  elog(INFO, "call_cntr: %d limit: %d", call_cntr, limit);          if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {
215                  SRF_RETURN_DONE(funcctx);                  elog(DEBUG1,"attributes: %s", attr);
216                    cond_add_attr(cond, attr);
217          }          }
218    
219          if (call_cntr < max_calls) {          /* set the search phrase to the search condition object */
220                  char            **values;          if (! PG_ARGISNULL(3) && strlen(order) > 0) {
221                  HeapTuple       tuple;                  elog(DEBUG1,"est_cond_set_order(%s)", order);
222                  Datum           result;                  est_cond_set_order(cond, order);
223            }
224    
225                  elog(DEBUG1, "pgest: loop count %d", call_cntr);          if (limit) {
226                    elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
227                    est_cond_set_max(cond, limit + offset);
228            }
229    
230                  if (! est_result) {          /* get the result of search */
231                          elog(ERROR, "pgest: no estraier results");          est_result = est_db_search(db, cond, &resnum, NULL);
                         SRF_RETURN_DONE(funcctx);  
                 }  
                   
                 /*  
                  * Prepare a values array for storage in our slot.  
                  * This should be an array of C strings which will  
                  * be processed later by the type input functions.  
                  */  
232    
233                  if (doc = est_db_get_doc(db, est_result[call_cntr + offset], 0)) {          /* 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",                          elog(DEBUG1, "URI: %s\n Title: %s\n",
258                                  est_doc_attr(doc, "@uri"),                                  est_doc_attr(doc, "@uri"),
259                                  est_doc_attr(doc, "@title")                                  est_doc_attr(doc, "@title")
260                          );                          );
261                    }
262    
263                          values = (char **) palloc(4 * sizeof(char *));                  /* iterate over results */
264                    for (j = 0; j < ncols; j++)
265                    {
266                            bool    isnull;
267    
268  //                      values[0] = (char *) palloc(strlen(_estval) * sizeof(char));                          /* array value of this position */
269                            indx[0] = j + attr_dim_lower_bounds[0];
270    
271                          values[0] = (char *) attr2text(doc,"@id");                          dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
272                          values[1] = (char *) attr2text(doc,"@uri");  
273                          values[2] = (char *) attr2text(doc,"@title");                          if (!isnull && doc)
274                          values[3] = (char *) attr2text(doc,"@size");                                  values[j] = DatumGetCString(
275                                            attr2text(doc,
276                          /* destloy the document object */                                                  (char *)DirectFunctionCall1(textout, dvalue)
277                          elog(DEBUG2, "est_doc_delete");                                          ));
278                          est_doc_delete(doc);                          else
279                  } else {                                  values[j] = NULL;
                         elog(INFO, "no result from estraier");  
                         values[0] = DatumGetCString( "" );  
                         values[1] = DatumGetCString( "" );  
                         values[2] = DatumGetCString( "" );  
                         values[3] = DatumGetCString( "" );  
280                  }                  }
281                    /* construct the tuple */
282                    tuple = BuildTupleFromCStrings(attinmeta, values);
283    
284                    /* now store it */
285                    tuplestore_puttuple(tupstore, tuple);
286    
287                  elog(DEBUG2, "build tuple");                  /* delete estraier document object */
288                  /* build a tuple */                  if (doc) est_doc_delete(doc);
289                  tuple = BuildTupleFromCStrings(attinmeta, values);          }
290    
291                  elog(DEBUG2, "make tuple into datum");          tuplestore_donestoring(tupstore);
292                  /* make the tuple into a datum */          rsinfo->setResult = tupstore;
                 result = TupleGetDatum(slot, tuple);  
293    
294                  elog(DEBUG2, "cleanup");          /*
295                  /* clean up ? */           * SFRM_Materialize mode expects us to return a NULL Datum. The actual
296  /*           * tuples are in our tuplestore and passed back through
297                  pfree(values[0]);           * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
298                  pfree(values[1]);           * that we actually used to build our tuples with, so the caller can
299                  pfree(values[2]);           * verify we did what it was expecting.
300                  pfree(values[3]);           */
301                  pfree(values);          rsinfo->setDesc = tupdesc;
302  */          MemoryContextSwitchTo(oldcontext);
                   
                 elog(DEBUG2, "cleanup over");  
           
                 SRF_RETURN_NEXT(funcctx, result);  
         } else {  
                 elog(DEBUG1, "loop over");  
303    
304                  if(!est_db_close(db, &ecode)){          est_cond_delete(cond);
                         elog(INFO, "est_db_close error: %s", est_err_msg(ecode));  
                 }  
305    
306                  /* do when there is no more left */          if(!est_db_close(db, &ecode)){
307                  SRF_RETURN_DONE(funcctx);                  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  /* work in progress */  
316  PG_FUNCTION_INFO_V1(pgest_attr);  /* make text var from attr */
317  Datum pgest_attr(PG_FUNCTION_ARGS)  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(5);          ArrayType       *attr_arr = PG_GETARG_ARRAYTYPE_P(_arg_attr_array);
375          Oid             element_type = ARR_ELEMTYPE(attr_arr);          Oid             attr_element_type = ARR_ELEMTYPE(attr_arr);
376          int             ndims = ARR_NDIM(attr_arr);          int             attr_ndims = ARR_NDIM(attr_arr);
377          int             *dim_counts = ARR_DIMS(attr_arr);          int             *attr_dim_counts = ARR_DIMS(attr_arr);
378          int             *dim_lower_bounds = ARR_LBOUND(attr_arr);          int             *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
379          int             ncols = 0;          int             ncols = 0;
380          int             nrows = 0;          int             nrows = 0;
381          int             indx[MAXDIM];          int             indx[MAXDIM];
382          int16           typlen;          int16           attr_len;
383          bool            typbyval;          bool            attr_byval;
384          char            typalign;          char            attr_align;
385          ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;          ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
386          AttInMetadata   *attinmeta;          AttInMetadata   *attinmeta;
387          TupleDesc       tupdesc;          TupleDesc       tupdesc;
# Line 288  Datum pgest_attr(PG_FUNCTION_ARGS) Line 394  Datum pgest_attr(PG_FUNCTION_ARGS)
394          int             rsinfo_ncols;          int             rsinfo_ncols;
395          int             i, j;          int             i, j;
396          /* estvars */          /* estvars */
397          char            *index_path;          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;          char            *query;
410          char            *attr;          char            *attr;
411            char            *order;
412    
413    
414          /* only allow 1D input array */          /* only allow 1D input array */
415          if (ndims == 1)          if (attr_ndims == 1)
416          {          {
417                  ncols = dim_counts[0];                  ncols = attr_dim_counts[0];
418          }          }
419          else          else
420                  ereport(ERROR,                  ereport(ERROR,
# Line 312  Datum pgest_attr(PG_FUNCTION_ARGS) Line 430  Datum pgest_attr(PG_FUNCTION_ARGS)
430                                                  "allowed in this context")));                                                  "allowed in this context")));
431    
432          /* get info about element type needed to construct the array */          /* get info about element type needed to construct the array */
433          get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);          get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
434    
435          /* get the requested return tuple description */          /* get the requested return tuple description */
436          tupdesc = rsinfo->expectedDesc;          tupdesc = rsinfo->expectedDesc;
# Line 343  Datum pgest_attr(PG_FUNCTION_ARGS) Line 461  Datum pgest_attr(PG_FUNCTION_ARGS)
461    
462          /* take rest of arguments from function */          /* take rest of arguments from function */
463    
464          /* index path */          /* node URL */
465          if (PG_ARGISNULL(0)) {          if (PG_ARGISNULL(_arg_node_uri)) {
466                  ereport(ERROR,                  ereport(ERROR,
467                                  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),                                  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
468                                   errmsg("index path can't be null"),                                   errmsg("node URL can't be null"),
469                                   errdetail("Index path must be valid full path to HyperEstraier index")));                                   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          }          }
         index_path = _textout(PG_GETARG_TEXT_P(0));  
489    
490          /* query string */          /* query string */
491          if (PG_ARGISNULL(0)) {          if (PG_ARGISNULL(_arg_query)) {
492                  query = "";                  query = "";
493          } else {          } else {
494                  query = _textout(PG_GETARG_TEXT_P(1));                  query = _textout(PG_GETARG_TEXT_P(_arg_query));
495          }          }
496    
497          /* atribute filter */          /* atribute filter */
498          if (PG_ARGISNULL(2)) {          if (PG_ARGISNULL(_arg_attr)) {
499                  attr = "";                  attr = "";
500          } else {          } else {
501                  attr = _textout(PG_GETARG_TEXT_P(2));                  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 */          /* limit */
513          if (PG_ARGISNULL(3)) {          if (PG_ARGISNULL(_arg_limit)) {
514                  limit = 0;                  limit = 0;
515          } else {          } else {
516                  limit = PG_GETARG_INT32(3);                  limit = PG_GETARG_INT32(_arg_limit);
517          }          }
518    
519          /* offset */          /* offset */
520          if (PG_ARGISNULL(4)) {          if (PG_ARGISNULL(_arg_offset)) {
521                  offset = 0;                  offset = 0;
522          } else {          } else {
523                  offset = PG_GETARG_INT32(4);                  offset = PG_GETARG_INT32(_arg_offset);
524          }          }
525    
526            /* initialize the network environment */
527          /* open the database */          if(!est_init_net_env()){
528          elog(DEBUG1, "pgest_attr: est_db_open(%s)", index_path);                  ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
529                                            errmsg("pgest_node: can't create network enviroment")));
         if(!(db = est_db_open(index_path, ESTDBREADER, &ecode))){  
                 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),  
                         errmsg("est_db_open: can't open %s: %d", index_path, ecode),  
                         errdetail(est_err_msg(ecode))));  
530          }          }
531                    
532          elog(INFO, "pgest_attr: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);          /* 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 */          /* create a search condition object */
540          if (!(cond = est_cond_new())) {          if (!(cond = est_cond_new())) {
541                  ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),                  ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
542                          errmsg("pgest_attr: est_cond_new failed")));                          errmsg("pgest_node: est_cond_new failed")));
543          }          }
544                    
545          /* set the search phrase to the search condition object */          /* set the search phrase to the search condition object */
546          if (! PG_ARGISNULL(1) && strlen(query) > 0)          if (! PG_ARGISNULL(_arg_query) && strlen(query) > 0)
547                  est_cond_set_phrase(cond, query);                  est_cond_set_phrase(cond, query);
548    
549          /* minimum valid attribute length is 10: @a STREQ a */          /* minimum valid attribute length is 10: @a STREQ a */
550          if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {          if (! PG_ARGISNULL(_arg_attr) && strlen(attr) >= 10) {
551                  elog(INFO,"est_cond_add_attr(%s)", attr);                  elog(DEBUG1,"attributes: %s", attr);
552                  est_cond_add_attr(cond, attr);                  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 */          /* get the result of search */
572          est_result = est_db_search(db, cond, &resnum, NULL);          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 */          /* check if results exists */
587          if ( 0 == resnum ) {          if ( 0 == resnum ) {
588                  elog(INFO, "pgest_attr: no results for: %s", query );                  elog(INFO, "pgest_node: no results for: %s", query );
589          }          }
590    
591          /* total number of tuples to be returned */          /* total number of tuples to be returned */
592          if (limit && limit < resnum) {          if (limit && limit < resnum) {
593                  nrows = limit - offset;                  nrows = limit;
594          } else {          } else {
595                  nrows = resnum - offset;                  nrows = resnum - offset;
596          }          }
597    
598            /* get hints */
599            hints = est_noderes_hints(nres);
600    
601          elog(DEBUG1, "pgest_attr: found %d hits for %s", resnum, query);          elog(DEBUG1, "pgest_node: found %d hits for %s", resnum, query);
602    
603    
604          values = (char **) palloc(ncols * sizeof(char *));          values = (char **) palloc(ncols * sizeof(char *));
# Line 433  Datum pgest_attr(PG_FUNCTION_ARGS) Line 607  Datum pgest_attr(PG_FUNCTION_ARGS)
607          {          {
608    
609                  /* get result from estraier */                  /* get result from estraier */
610                  if (! ( doc = est_db_get_doc(db, est_result[i + offset], 0)) ) {                  if (! ( rdoc = est_noderes_get_doc(nres, i) )) {
611                          elog(INFO, "can't find result %d", i + offset);                          elog(INFO, "pgest_node: can't find result %d", i + offset);
612                  } else {                  } else {
613                          elog(DEBUG1, "URI: %s\n Title: %s\n",                          elog(DEBUG1, "URI: %s\n Title: %s\n",
614                                  est_doc_attr(doc, "@uri"),                                  est_resdoc_attr(rdoc, "@uri"),
615                                  est_doc_attr(doc, "@title")                                  est_resdoc_attr(rdoc, "@title")
616                          );                          );
617                  }                  }
618    
# Line 446  Datum pgest_attr(PG_FUNCTION_ARGS) Line 620  Datum pgest_attr(PG_FUNCTION_ARGS)
620                  for (j = 0; j < ncols; j++)                  for (j = 0; j < ncols; j++)
621                  {                  {
622                          bool    isnull;                          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 */                          /* array value of this position */
628                          indx[0] = j + dim_lower_bounds[0];                          indx[0] = j + attr_dim_lower_bounds[0];
629    
630                          dvalue = array_ref(attr_arr, ndims, indx, -1, typlen, typbyval, typalign, &isnull);                          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 && doc)                          if (!isnull && (hint = strstr(attr, HINTS_PREFIX)) != NULL) {
634                                  values[j] = DatumGetCString(                                  /* skip HINTS. prefix */
635                                          attr2text(doc,                                  hint += strlen(HINTS_PREFIX);
636                                                  DirectFunctionCall1(textout, dvalue)  
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                          else
649                                  values[j] = NULL;                                  values[j] = NULL;
650                  }                  }
# Line 466  Datum pgest_attr(PG_FUNCTION_ARGS) Line 654  Datum pgest_attr(PG_FUNCTION_ARGS)
654                  /* now store it */                  /* now store it */
655                  tuplestore_puttuple(tupstore, tuple);                  tuplestore_puttuple(tupstore, tuple);
656    
   
                 /* delete estraier document object */  
                 est_doc_delete(doc);  
657          }          }
658    
659          tuplestore_donestoring(tupstore);          tuplestore_donestoring(tupstore);
# Line 484  Datum pgest_attr(PG_FUNCTION_ARGS) Line 669  Datum pgest_attr(PG_FUNCTION_ARGS)
669          rsinfo->setDesc = tupdesc;          rsinfo->setDesc = tupdesc;
670          MemoryContextSwitchTo(oldcontext);          MemoryContextSwitchTo(oldcontext);
671    
672          if(!est_db_close(db, &ecode)){          /* delete the node result object */
673                  ereport(ERROR, (errcode(ERRCODE_IO_ERROR),          est_noderes_delete(nres);
674                          errmsg("est_db_close: %d", ecode),  
675                          errdetail(est_err_msg(ecode))));          /* 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;          return (Datum) 0;
685  }  }
686    
687    /* make text var from node attr */
688  /* make text var from attr */  char *node_attr2text(ESTRESDOC *rdoc, char *attr) {
 char *attr2text(ESTDOC *doc, char *attr) {  
689          char *val;          char *val;
690          const char *attrval;          const char *attrval;
691          int len;          int len;
692          int attrlen;          int attrlen;
693    
694          elog(DEBUG1, "doc: %08x, attr: %s", doc, attr);          if (! rdoc) return (Datum) NULL;
695    
696          if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {          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));                  val = (char *) palloc(attrlen * sizeof(char));
700          } else {          } else {
701                  return (Datum) NULL;                  return (Datum) NULL;
702          }          }
703    
704          len = strlen(attrval);          len = strlen(attrval);
705          elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);          elog(DEBUG1, "node_attr2text(%s) = '%s' %d bytes", attr, attrval, len);
706    
707          len++;          len++;
708          len *= sizeof(char);          len *= sizeof(char);
# Line 527  char *attr2text(ESTDOC *doc, char *attr) Line 719  char *attr2text(ESTDOC *doc, char *attr)
719          return val;          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    
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    

Legend:
Removed from v.19  
changed lines
  Added in v.73

  ViewVC Help
Powered by ViewVC 1.1.26