/[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 12 by dpavlin, Wed May 25 23:38:37 2005 UTC revision 85 by dpavlin, Sat Feb 3 17:13:07 2007 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    #ifdef PG_MODULE_MAGIC
48    PG_MODULE_MAGIC;
49    #endif
50    
51    #define ATTR_DELIMITER "{{!}}"
52    #define HINTS_PREFIX "HINTS."
53    
54  /* prototype */  /* prototype */
55  char *attr2text(ESTDOC *doc, char *attr);  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  ESTDB *db;  
60  ESTCOND *cond;  /* work in progress */
61  ESTDOC *doc;  PG_FUNCTION_INFO_V1(pgest_attr);
62  const CBLIST *texts;  Datum pgest_attr(PG_FUNCTION_ARGS)
63  int ecode, *est_result, resnum, i, j;  {
64  int limit = 0;          ArrayType       *attr_arr = PG_GETARG_ARRAYTYPE_P(6);
65  int offset = 0;          Oid             attr_element_type = ARR_ELEMTYPE(attr_arr);
66            int             attr_ndims = ARR_NDIM(attr_arr);
67  /* define PostgreSQL v1 function */          int             *attr_dim_counts = ARR_DIMS(attr_arr);
68  PG_FUNCTION_INFO_V1(pgest);          int             *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
69  Datum pgest(PG_FUNCTION_ARGS) {          int             ncols = 0;
70            int             nrows = 0;
71          FuncCallContext *funcctx;          int             indx[MAXDIM];
72          int             call_cntr;          int16           attr_len;
73          int             max_calls;          bool            attr_byval;
74          TupleDesc       tupdesc;          char            attr_align;
75          TupleTableSlot  *slot;          ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
76          AttInMetadata   *attinmeta;          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;          char            *index_path;
95          char            *query;          char            *query;
96          char            *attr;          char            *attr;
97            char            *order;
98    
         /* 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));  
99    
100                  /* query string */          /* only allow 1D input array */
101                  if (PG_ARGISNULL(0)) {          if (attr_ndims == 1)
102                          query = "";          {
103                  } else {                  ncols = attr_dim_counts[0];
104                          query = _textout(PG_GETARG_TEXT_P(1));          }
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                  /* atribute filter */          /* get info about element type needed to construct the array */
119                  if (PG_ARGISNULL(2)) {          get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
                         attr = "";  
                 } else {  
                         attr = _textout(PG_GETARG_TEXT_P(2));  
                 }  
120    
121                  /* limit */          /* get the requested return tuple description */
122                  if (! PG_ARGISNULL(3)) limit = PG_GETARG_INT32(3);          tupdesc = rsinfo->expectedDesc;
123            rsinfo_ncols = tupdesc->natts;
124    
125                  /* offset */          /*
126                  if (! PG_ARGISNULL(4)) offset = PG_GETARG_INT32(4);           * 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                  /* open the database */          /* Now go to work */
139                  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);  
                 }  
140    
141                  /* get the result of search */          per_query_ctx = fcinfo->flinfo->fn_mcxt;
142                  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;  
                 }  
143    
144                  /* check if results exists */          /* initialize our tuplestore */
145                  if ( 0 == funcctx->max_calls )          tupstore = tuplestore_begin_heap(true, false, SortMem);
                         elog(INFO, "pgest: no results for: %s", query );  
146    
                 elog(DEBUG1, "pgest: found %d hits for %s", resnum, query);  
147    
148                  /* Build a tuple description for a __pgest tuple */          /* take rest of arguments from function */
                 tupdesc = RelationNameGetTupleDesc("__pgest");  
149    
150                  /* allocate a slot for a tuple with this tupdesc */          /* index path */
151                  slot = TupleDescGetSlot(tupdesc);          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    
                 /* assign slot to function context */  
                 funcctx->slot = slot;  
180    
181                  /*          /* limit */
182                   * generate attribute metadata needed later to produce tuples from raw          if (PG_ARGISNULL(4)) {
183                   * C strings                  limit = 0;
184                   */          } else {
185                  attinmeta = TupleDescGetAttInMetadata(tupdesc);                  limit = PG_GETARG_INT32(4);
186                  funcctx->attinmeta = attinmeta;          }
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                  MemoryContextSwitchTo(oldcontext);          /* 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                  elog(DEBUG1, "SRF_IS_FIRSTCALL done");          if (limit) {
230                    elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
231                    est_cond_set_max(cond, limit + offset);
232          }          }
233    
234          /* stuff done on every call of the function */          /* get the result of search */
235          funcctx = SRF_PERCALL_SETUP();          est_result = est_db_search(db, cond, &resnum, NULL);
236    
237          call_cntr = funcctx->call_cntr;          /* check if results exists */
238          max_calls = funcctx->max_calls;          if ( 0 == resnum ) {
239          slot = funcctx->slot;                  elog(INFO, "pgest_attr: no results for: %s", query );
240          attinmeta = funcctx->attinmeta;          }
241    
242          if (limit && call_cntr > limit - 1) {          /* total number of tuples to be returned */
243                  elog(INFO, "call_cntr: %d limit: %d", call_cntr, limit);          if (limit && limit < resnum) {
244                  SRF_RETURN_DONE(funcctx);                  nrows = limit;
245            } else {
246                    nrows = resnum - offset;
247          }          }
248    
         if (call_cntr < max_calls) {  
                 char            **values;  
                 HeapTuple       tuple;  
                 Datum           result;  
249    
250                  elog(DEBUG1, "pgest: loop count %d", call_cntr);          elog(DEBUG1, "pgest_attr: found %d hits for %s", resnum, query);
251    
252                  if (! est_result) {          values = (char **) palloc(ncols * sizeof(char *));
                         elog(ERROR, "pgest: no estraier results");  
                         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.  
                  */  
253    
254                  if (doc = est_db_get_doc(db, est_result[call_cntr + offset], 0)) {          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",                          elog(DEBUG1, "URI: %s\n Title: %s\n",
262                                  est_doc_attr(doc, "@uri"),                                  est_doc_attr(doc, "@uri"),
263                                  est_doc_attr(doc, "@title")                                  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                          values = (char **) palloc(4 * sizeof(char *));                          /* array value of this position */
273                            indx[0] = j + attr_dim_lower_bounds[0];
274    
275  //                      values[0] = (char *) palloc(strlen(_estval) * sizeof(char));                          dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
276    
277                          values[0] = (char *) attr2text(doc,"@id");                          if (!isnull && doc)
278                          values[1] = (char *) attr2text(doc,"@uri");                                  values[j] = DatumGetCString(
279                          values[2] = (char *) attr2text(doc,"@title");                                          attr2text(doc,
280                          values[3] = (char *) attr2text(doc,"@type");                                                  (char *)DirectFunctionCall1(textout, dvalue)
281                                            ));
282                          /* destloy the document object */                          else
283                          elog(DEBUG2, "est_doc_delete");                                  values[j] = NULL;
                         est_doc_delete(doc);  
                 } else {  
                         elog(INFO, "no result from estraier");  
                         values[0] = DatumGetCString( "" );  
                         values[1] = DatumGetCString( "" );  
                         values[2] = DatumGetCString( "" );  
                         values[3] = DatumGetCString( "" );  
284                  }                  }
285                    /* construct the tuple */
286                    tuple = BuildTupleFromCStrings(attinmeta, values);
287    
288                    /* now store it */
289                    tuplestore_puttuple(tupstore, tuple);
290    
291                  elog(DEBUG2, "build tuple");                  /* delete estraier document object */
292                  /* build a tuple */                  if (doc) est_doc_delete(doc);
293                  tuple = BuildTupleFromCStrings(attinmeta, values);          }
294    
295                  elog(DEBUG2, "make tuple into datum");          tuplestore_donestoring(tupstore);
296                  /* make the tuple into a datum */          rsinfo->setResult = tupstore;
                 result = TupleGetDatum(slot, tuple);  
297    
298                  elog(DEBUG2, "cleanup");          /*
299                  /* clean up ? */           * SFRM_Materialize mode expects us to return a NULL Datum. The actual
300  /*           * tuples are in our tuplestore and passed back through
301                  pfree(values[0]);           * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
302                  pfree(values[1]);           * that we actually used to build our tuples with, so the caller can
303                  pfree(values[2]);           * verify we did what it was expecting.
304                  pfree(values[3]);           */
305                  pfree(values);          rsinfo->setDesc = tupdesc;
306  */          MemoryContextSwitchTo(oldcontext);
                   
                 elog(DEBUG2, "cleanup over");  
           
                 SRF_RETURN_NEXT(funcctx, result);  
         } else {  
                 elog(DEBUG1, "loop over");  
307    
308                  if(!est_db_close(db, &ecode)){          est_cond_delete(cond);
                         elog(INFO, "est_db_close error: %s", est_err_msg(ecode));  
                 }  
309    
310                  /* do when there is no more left */          if(!est_db_close(db, &ecode)){
311                  SRF_RETURN_DONE(funcctx);                  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  /* work in progress */  
320  PG_FUNCTION_INFO_V1(pgest2);  /* make text var from attr */
321  Datum pgest2(PG_FUNCTION_ARGS)  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          int             nrows = 3;          ArrayType       *attr_arr = PG_GETARG_ARRAYTYPE_P(_arg_attr_array);
379          int16           typlen;          Oid             attr_element_type = ARR_ELEMTYPE(attr_arr);
380          bool            typbyval;          int             attr_ndims = ARR_NDIM(attr_arr);
381          char            typalign;          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;          ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
390          AttInMetadata   *attinmeta;          AttInMetadata   *attinmeta;
391          TupleDesc       tupdesc;          TupleDesc       tupdesc;
392          Tuplestorestate *tupstore = NULL;          Tuplestorestate *tupstore = NULL;
393          HeapTuple       tuple;          HeapTuple       tuple;
394          MemoryContext   per_query_ctx;          MemoryContext   per_query_ctx;
395          MemoryContext   oldcontext;          MemoryContext   oldcontext;
396          Datum           dvalue;          Datum           dvalue;
397          char            **values;          char            **values;
398          int             ncols;          int             rsinfo_ncols;
399          int             i, j;          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 */          /* check to see if caller supports us returning a tuplestore */
430          if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))          if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
431                  ereport(ERROR,                  ereport(ERROR,
# Line 280  Datum pgest2(PG_FUNCTION_ARGS) Line 433  Datum pgest2(PG_FUNCTION_ARGS)
433                                   errmsg("materialize mode required, but it is not " \                                   errmsg("materialize mode required, but it is not " \
434                                                  "allowed in this context")));                                                  "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 */          /* get the requested return tuple description */
440          tupdesc = rsinfo->expectedDesc;          tupdesc = rsinfo->expectedDesc;
441          ncols = tupdesc->natts;          rsinfo_ncols = tupdesc->natts;
442    
443          /*          /*
444           * The requested tuple description better match up with the array           * The requested tuple description better match up with the array
445           * we were given.           * 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 */          /* OK, use it */
454          attinmeta = TupleDescGetAttInMetadata(tupdesc);          attinmeta = TupleDescGetAttInMetadata(tupdesc);
455    
# Line 300  Datum pgest2(PG_FUNCTION_ARGS) Line 462  Datum pgest2(PG_FUNCTION_ARGS)
462          /* initialize our tuplestore */          /* initialize our tuplestore */
463          tupstore = tuplestore_begin_heap(true, false, SortMem);          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 *));          values = (char **) palloc(ncols * sizeof(char *));
609    
610          for (i = 0; i < nrows; i++)          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++)                  for (j = 0; j < ncols; j++)
625                  {                  {
626                          values[j] = DatumGetCString( "foo" );                          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 */                  /* construct the tuple */
656                  tuple = BuildTupleFromCStrings(attinmeta, values);                  tuple = BuildTupleFromCStrings(attinmeta, values);
657    
658                  /* now store it */                  /* now store it */
659                  tuplestore_puttuple(tupstore, tuple);                  tuplestore_puttuple(tupstore, tuple);
660    
661          }          }
662    
663          tuplestore_donestoring(tupstore);          tuplestore_donestoring(tupstore);
# Line 328  Datum pgest2(PG_FUNCTION_ARGS) Line 673  Datum pgest2(PG_FUNCTION_ARGS)
673          rsinfo->setDesc = tupdesc;          rsinfo->setDesc = tupdesc;
674          MemoryContextSwitchTo(oldcontext);          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;          return (Datum) 0;
689  }  }
690    
691    /* make text var from node attr */
692  /* make text var from attr */  char *node_attr2text(ESTRESDOC *rdoc, char *attr) {
 char *attr2text(ESTDOC *doc, char *attr) {  
693          char *val;          char *val;
694          const char *attrval;          const char *attrval;
695          int len;          int len;
696          int attrlen;          int attrlen;
697    
698          elog(DEBUG1, "doc: %08x, attr: %s", doc, attr);          if (! rdoc) return (Datum) NULL;
699    
700          if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {          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));                  val = (char *) palloc(attrlen * sizeof(char));
704          } else {          } else {
705                  return (Datum) NULL;                  return (Datum) NULL;
706          }          }
707    
708          len = strlen(attrval);          len = strlen(attrval);
709          elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);          elog(DEBUG1, "node_attr2text(%s) = '%s' %d bytes", attr, attrval, len);
710    
711          len++;          len++;
712          len *= sizeof(char);          len *= sizeof(char);
# Line 365  char *attr2text(ESTDOC *doc, char *attr) Line 723  char *attr2text(ESTDOC *doc, char *attr)
723          return val;          return val;
724  }  }
725    
726  /* make integer variable from property */  /* 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  char *prop2int(SW_RESULT sw_res, char *propname) {   * UPDATE, INSERT and DELETE triggers are like this:
752          char *val;  
753          unsigned long prop;  CREATE TRIGGER pgest_trigger_update AFTER UPDATE
754          int len;          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          elog(DEBUG2, "prop2int(%s)", propname);  */
760    
761          prop = estResultPropertyULong( sw_res, propname );  PG_FUNCTION_INFO_V1(pgest_trigger);
762          if (error_or_abort( est_handle )) return NULL;  Datum pgest_trigger(PG_FUNCTION_ARGS) {
763    
764          elog(DEBUG1, "prop2int(%s) = %lu", propname, prop);          TriggerData *data;
765            TupleDesc   tupdesc;
766            HeapTuple   ret;
767    
768          len = 128 * sizeof(char);          char **args;
769          elog(DEBUG2, "palloc(%d)", len);          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          val = palloc(len);          ESTNODE *node;
778          memset(val, 0, len);          ESTDOC *doc;
779    
         snprintf(val, len, "%lu", prop);  
780    
         elog(DEBUG2, "val=%s", val);  
781    
782          return val;          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  */  

Legend:
Removed from v.12  
changed lines
  Added in v.85

  ViewVC Help
Powered by ViewVC 1.1.26