/[pgswish]/trunk/pgswish.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/pgswish.c

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

revision 12 by dpavlin, Sat Feb 19 15:09:05 2005 UTC revision 22 by dpavlin, Sun May 29 22:41:20 2005 UTC
# Line 6  Line 6 
6   * TODO:   * TODO:
7   * - check null input using PG_ARGISNULL before using PG_GETARG_xxxx   * - check null input using PG_ARGISNULL before using PG_GETARG_xxxx
8   * - support composite type arguments   * - support composite type arguments
9     * - split error_or_abort
10     * - use getResultPropValue not SwishResultPropertyStr
11     * - fix everything about pgswish_arr which is broken
12   *   *
13   * NOTES:   * NOTES:
14   * - clear structures with memset to support hash indexes (who whould like   * - clear structures with memset to support hash indexes (who whould like
# Line 25  Line 28 
28  #include "fmgr.h"  #include "fmgr.h"
29  #include "funcapi.h"  #include "funcapi.h"
30  #include "utils/builtins.h"  #include "utils/builtins.h"
31    #include "utils/array.h"
32    #include "miscadmin.h"
33  #include <swish-e.h>  #include <swish-e.h>
34    
35  #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))  #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
# Line 32  Line 37 
37  #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))  #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
38  #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))  #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
39    
40    /* Globals */
41  SW_HANDLE   swish_handle = NULL;/* Database handle */  static SW_HANDLE   swish_handle = NULL; /* Database handle */
42  SW_SEARCH   search = NULL;      /* search handle -- holds search parameters */  static SW_SEARCH   search = NULL;       /* search handle -- search parameters */
43  SW_RESULTS  swish_results = NULL;       /* results handle -- holds list of results */  static SW_RESULTS  swish_results = NULL; /* results handle -- list of results */
44    static SW_RESULT   *sw_res = NULL;      /* one row from swish-e results */
45    
46  /* define PostgreSQL v1 function */  /* define PostgreSQL v1 function */
47  PG_FUNCTION_INFO_V1(pgswish);  PG_FUNCTION_INFO_V1(pgswish);
# Line 49  Datum pgswish(PG_FUNCTION_ARGS) { Line 55  Datum pgswish(PG_FUNCTION_ARGS) {
55          AttInMetadata   *attinmeta;          AttInMetadata   *attinmeta;
56          char            *index_path;          char            *index_path;
57          char            *query;          char            *query;
58            FILE            *logfh;
59    
60          /* stuff done only on the first call of the function */          /* stuff done only on the first call of the function */
61          if (SRF_IS_FIRSTCALL()) {          if (SRF_IS_FIRSTCALL()) {
# Line 66  Datum pgswish(PG_FUNCTION_ARGS) { Line 73  Datum pgswish(PG_FUNCTION_ARGS) {
73                  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);                  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
74    
75                                    
76                  /* Send any errors or warnings to stderr (default is stdout) */                  /* Send any errors or warnings to log, as well as
77                  SwishErrorsToStderr();                   * STDOUT and STDERR (just to be sure) */
78                    if ( logfh = fopen("/tmp/pgswish.log", "a") ) {
79                            set_error_handle( logfh );
80                            elog(INFO, "loggin swish-e errors to /tmp/pgswish.log");
81                            /* redirect STDOUT and STDERR to log */
82                            dup2(1, logfh);
83                            dup2(2, logfh);
84                    } else {
85                            elog(INFO, "can't open /tmp/pgswish.log -- errors from swish-e won't be cought and may result in back-end crashes!");
86                    }
87    
88                  elog(INFO, "pgswish: SwishInit(%s)", index_path);                  elog(INFO, "pgswish: SwishInit(%s)", index_path);
89                    
90                  swish_handle = SwishInit( index_path );                  swish_handle = SwishInit( index_path );
91    
92                    if ( SwishError( swish_handle ) )
93                            elog(INFO, "pgswish: SwishInit(%s) failed: %s", index_path, SwishErrorString( swish_handle ));
94                    
95                    elog(INFO, "handle: %08x", swish_handle);
96    
97                  if (! swish_handle) {                  if (! swish_handle) {
98                          elog(ERROR, "pgswish: can't open %s", index_path);                          elog(ERROR, "pgswish: can't open %s", index_path);
99                          SRF_RETURN_DONE(funcctx);                          SRF_RETURN_DONE(funcctx);
100                  }                  }
101                                    
102                  error_or_abort( swish_handle );                  if (error_or_abort( swish_handle )) SRF_RETURN_DONE(funcctx);
103                  /* set ranking scheme. default is 0 */                  /* set ranking scheme. default is 0 */
104                  SwishRankScheme( swish_handle, 0 );                  SwishRankScheme( swish_handle, 0 );
105                  error_or_abort( swish_handle );                  if (error_or_abort( swish_handle )) SRF_RETURN_DONE(funcctx);
106    
107                  elog(INFO, "pgswish: SwishQuery(%s)", query);                  elog(INFO, "pgswish: SwishQuery(%s)", query);
108                  /* Here's a short-cut to searching that creates a search object and searches at the same time */                  /* Here's a short-cut to searching that creates a search object and searches at the same time */
109                  swish_results = SwishQuery( swish_handle, query);                  swish_results = SwishQuery( swish_handle, query);
110                  error_or_abort( swish_handle );                  if (error_or_abort( swish_handle )) SRF_RETURN_DONE(funcctx);
111    
112                  /* total number of tuples to be returned */                  /* total number of tuples to be returned */
113                  funcctx->max_calls = SwishHits( swish_results );                  funcctx->max_calls = SwishHits( swish_results );
# Line 114  Datum pgswish(PG_FUNCTION_ARGS) { Line 135  Datum pgswish(PG_FUNCTION_ARGS) {
135                  funcctx->attinmeta = attinmeta;                  funcctx->attinmeta = attinmeta;
136    
137                  MemoryContextSwitchTo(oldcontext);                  MemoryContextSwitchTo(oldcontext);
138    
139                    elog(INFO, "SRF_IS_FIRSTCALL done");
140          }          }
141    
142          /* stuff done on every call of the function */          /* stuff done on every call of the function */
# Line 128  Datum pgswish(PG_FUNCTION_ARGS) { Line 151  Datum pgswish(PG_FUNCTION_ARGS) {
151                  char            **values;                  char            **values;
152                  HeapTuple       tuple;                  HeapTuple       tuple;
153                  Datum           result;                  Datum           result;
                 SW_RESULT       *sw_res;        /* one row from swish-e results */  
154    
155                  elog(INFO, "pgswish: in loop %d", call_cntr);                  elog(INFO, "pgswish: loop count %d", call_cntr);
156    
157                  if (! swish_results) {                  if (! swish_results) {
158                          elog(ERROR, "pgswish: no swish-e results");                          elog(ERROR, "pgswish: no swish-e results");
159                          SRF_RETURN_DONE(funcctx);                          SRF_RETURN_DONE(funcctx);
160                  }                  }
161                                    
162                  elog(INFO, "pgswish: check for swish-e error");                  elog(DEBUG1, "pgswish: check for swish-e error");
163                  error_or_abort( swish_handle );                  if (error_or_abort( swish_handle )) SRF_RETURN_DONE(funcctx);
164    
165                  /*                  /*
166                   * Prepare a values array for storage in our slot.                   * Prepare a values array for storage in our slot.
167                   * This should be an array of C strings which will                   * This should be an array of C strings which will
168                   * be processed later by the type input functions.                   * be processed later by the type input functions.
169                   */                   */
                 values = (char **) palloc(4 * sizeof(char *));  
170    
171                  sw_res = SwishNextResult( swish_results );                  sw_res = SwishNextResult( swish_results );
172                  if (! sw_res) {                  if (! sw_res) {
173                          elog(ERROR, "pgswish: swish-e sort result list: %d rows expected %d", call_cntr, max_calls - 1);                          elog(ERROR, "pgswish: swish-e sort result list: %d rows expected %d", call_cntr, max_calls - 1);
174                            Free_Results_Object( swish_results );
175                            Free_Search_Object( search );
176                          SRF_RETURN_DONE(funcctx);                          SRF_RETURN_DONE(funcctx);
177                  }                  }
178                                    
# Line 164  Datum pgswish(PG_FUNCTION_ARGS) { Line 187  Datum pgswish(PG_FUNCTION_ARGS) {
187                          SwishResultPropertyULong ( sw_res, "swishfilenum" )                          SwishResultPropertyULong ( sw_res, "swishfilenum" )
188                  );                  );
189    
190                  elog(INFO, "swishdocpath: %s", prop2text( sw_res, "swishdocpath" ) );                  values = (char **) palloc(4 * sizeof(char *));
191    
192                  values[0] = prop2int( sw_res, "swishrank" );                  values[0] = prop2int( sw_res, "swishrank" );
193                  values[1] = prop2text( sw_res, "swishdocpath" );                  values[1] = prop2text( sw_res, "swishdocpath" );
194                  values[2] = prop2text( sw_res, "swishtitle" );                  values[2] = prop2text( sw_res, "swishtitle" );
195                  values[3] = prop2int( sw_res, "swishdocsize" );                  values[3] = prop2int( sw_res, "swishdocsize" );
196                    
197    /*
198                    values[0] = (char *) palloc(16 * sizeof(char));
199                    snprintf(values[0], 16, "%d", 1);
200                    values[1] = (char *) palloc(16 * sizeof(char));
201                    snprintf(values[1], 16, "%d", 2);
202                    values[2] = (char *) palloc(16 * sizeof(char));
203                    snprintf(values[2], 16, "%d", 3);
204                    values[3] = (char *) palloc(16 * sizeof(char));
205                    snprintf(values[3], 16, "%d", 4);
206    */
207    
208                  /* build a tuple */                  /* build a tuple */
209                  tuple = BuildTupleFromCStrings(attinmeta, values);                  tuple = BuildTupleFromCStrings(attinmeta, values);
210    
# Line 177  Datum pgswish(PG_FUNCTION_ARGS) { Line 212  Datum pgswish(PG_FUNCTION_ARGS) {
212                  result = TupleGetDatum(slot, tuple);                  result = TupleGetDatum(slot, tuple);
213    
214                  /* clean up ? */                  /* clean up ? */
215                    pfree(values[0]);
216                  elog(INFO, "row: %s|%s|%s|%s",values[0],values[1],values[2],values[3]);                  pfree(values[1]);
217                    pfree(values[2]);
218                    pfree(values[3]);
219                    pfree(values);
220                    
221                    elog(DEBUG1, "row: %s|%s|%s|%s",values[0],values[1],values[2],values[3]);
222                    
223                  SRF_RETURN_NEXT(funcctx, result);                  SRF_RETURN_NEXT(funcctx, result);
224          } else {          } else {
225                    elog(INFO, "loop over");
226    
227                  /* free swish object and close */                  /* free swish object and close */
228                  Free_Search_Object( search );                  Free_Search_Object( search );
229                  SwishClose( swish_handle );                  SwishClose( swish_handle );
# Line 191  Datum pgswish(PG_FUNCTION_ARGS) { Line 233  Datum pgswish(PG_FUNCTION_ARGS) {
233          }          }
234  }  }
235    
236    
237  /*  /*
238   * elog errors   * new function with support for property selection
  *  
239   */   */
240    
241    PG_FUNCTION_INFO_V1(pgswish_arr);
242    Datum pgswish_arr(PG_FUNCTION_ARGS)
243    {
244            ArrayType       *prop_arr = PG_GETARG_ARRAYTYPE_P(5);
245            Oid             prop_element_type = ARR_ELEMTYPE(prop_arr);
246            int             prop_ndims = ARR_NDIM(prop_arr);
247            int             *prop_dim_counts = ARR_DIMS(prop_arr);
248            int             *prop_dim_lower_bounds = ARR_LBOUND(prop_arr);
249            int             ncols = 0;
250            int             nrows = 0;
251            int             indx[MAXDIM];
252            int16           prop_len;
253            bool            prop_byval;
254            char            prop_align;
255            ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
256            AttInMetadata   *attinmeta;
257            TupleDesc       tupdesc;
258            Tuplestorestate *tupstore = NULL;
259            HeapTuple       tuple;
260            MemoryContext   per_query_ctx;
261            MemoryContext   oldcontext;
262            Datum           dvalue;
263            char            **values;
264            int             rsinfo_ncols;
265            int             i, j;
266            /* swish-e */
267            FILE            *logfh;
268            int             resnum;
269            int             limit = 0;
270            int             offset = 0;
271    
272            char            *index_path;
273            char            *query;
274            char            *attr;
275    
276    
277            /* only allow 1D input array */
278            if (prop_ndims == 1)
279            {
280                    ncols = prop_dim_counts[0];
281            }
282            else
283                    ereport(ERROR,
284                                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
285                                     errmsg("invalid input array"),
286                                     errdetail("Input array must have 1 dimension")));
287                    
288            /* check to see if caller supports us returning a tuplestore */
289            if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
290                    ereport(ERROR,
291                                    (errcode(ERRCODE_SYNTAX_ERROR),
292                                     errmsg("materialize mode required, but it is not " \
293                                                    "allowed in this context")));
294    
295            /* get info about element type needed to construct the array */
296            get_typlenbyvalalign(prop_element_type, &prop_len, &prop_byval, &prop_align);
297    
298            /* get the requested return tuple description */
299            tupdesc = rsinfo->expectedDesc;
300            rsinfo_ncols = tupdesc->natts;
301    
302            /*
303             * The requested tuple description better match up with the array
304             * we were given.
305             */
306            if (rsinfo_ncols != ncols)
307                    ereport(ERROR,
308                                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
309                                     errmsg("invalid input array"),
310                                     errdetail("Number of elements in array must match number of query specified columns.")));
311    
312            /* OK, use it */
313            attinmeta = TupleDescGetAttInMetadata(tupdesc);
314    
315            /* Now go to work */
316            rsinfo->returnMode = SFRM_Materialize;
317    
318            per_query_ctx = fcinfo->flinfo->fn_mcxt;
319            oldcontext = MemoryContextSwitchTo(per_query_ctx);
320    
321            /* initialize our tuplestore */
322            tupstore = tuplestore_begin_heap(true, false, SortMem);
323    
324    
325            /* take rest of arguments from function */
326    
327            /* index path */
328            if (PG_ARGISNULL(0)) {
329                    ereport(ERROR,
330                                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
331                                     errmsg("index path can't be null"),
332                                     errdetail("Index path must be valid full path to swish-e index")));
333            }
334            index_path = _textout(PG_GETARG_TEXT_P(0));
335    
336            /* query string */
337            if (PG_ARGISNULL(0)) {
338                    query = "";
339            } else {
340                    query = _textout(PG_GETARG_TEXT_P(1));
341            }
342    
343            /* atribute filter */
344            if (PG_ARGISNULL(2)) {
345                    attr = "";
346            } else {
347                    attr = _textout(PG_GETARG_TEXT_P(2));
348            }
349    
350            /* limit */
351            if (PG_ARGISNULL(3)) {
352                    limit = 0;
353            } else {
354                    limit = PG_GETARG_INT32(3);
355            }
356    
357            /* offset */
358            if (PG_ARGISNULL(4)) {
359                    offset = 0;
360            } else {
361                    offset = PG_GETARG_INT32(4);
362            }
363    
364    
365            /* Send any errors or warnings to log, as well as
366             * STDOUT and STDERR (just to be sure) */
367            if ( logfh = fopen("/tmp/pgswish.log", "a") ) {
368                    set_error_handle( logfh );
369                    elog(INFO, "loggin swish-e errors to /tmp/pgswish.log");
370                    /* redirect STDOUT and STDERR to log */
371                    dup2(1, logfh);
372                    dup2(2, logfh);
373            } else {
374                    elog(INFO, "can't open /tmp/pgswish.log -- errors from swish-e won't be cought and may result in back-end crashes!");
375            }
376    
377            elog(INFO, "pgswish: SwishInit(%s)", index_path);
378    
379            swish_handle = SwishInit( index_path );
380    
381            if ( SwishError( swish_handle ) || ! swish_handle )
382                    ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
383                            errmsg("pgswish: SwishInit(%s) failed", index_path ),
384                            errdetail( SwishErrorString( swish_handle ) )
385                    ));
386    
387            elog(DEBUG1, "pgswish: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);
388    
389    
390            /* set ranking scheme. default is 0 */
391            SwishRankScheme( swish_handle, 0 );
392            error_or_abort( swish_handle );
393    
394            elog(INFO, "pgswish: SwishQuery(%s)", query);
395            /* Here's a short-cut to searching that creates a search object
396             * and searches at the same time */
397            
398            /* set the search phrase to the search condition object */
399            if (! PG_ARGISNULL(1) && strlen(query) > 0)
400                    swish_results = SwishQuery( swish_handle, query);
401            error_or_abort( swish_handle );
402    
403            /* total number of tuples to be returned */
404            resnum = SwishHits( swish_results );
405    
406            /* FIXME */
407            if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {
408                    elog(DEBUG1,"ignored: %s", attr);
409            }
410    
411            /* check if results exists */
412            if ( 0 == resnum ) {
413                    elog(INFO, "pgswish: no results for: %s", query );
414            }
415    
416            /* total number of tuples to be returned */
417            if (limit && limit < resnum) {
418                    nrows = limit - offset;
419            } else {
420                    nrows = resnum - offset;
421            }
422    
423    
424            elog(DEBUG1, "pgswish: found %d hits for %s", resnum, query);
425    
426    
427            values = (char **) palloc(ncols * sizeof(char *));
428    
429            for (i = 0; i < nrows; i++)
430            {
431                    SwishSeekResult( swish_results, i + offset );
432                    sw_res = SwishNextResult( swish_results );
433    
434                    /* get result from swish-e */
435                    if (! ( SwishErrorString( swish_handle ) ) ) {
436                            elog(INFO, "can't find result %d", i + offset);
437                    } else {
438                            elog(INFO, "Path: %s\n  Rank: %lu\n  Size: %lu\n  Title: %s\n  Index: %s\n  Modified: %s\n  Record #: %lu\n  File   #: %lu\n\n",
439                                    SwishResultPropertyStr   ( sw_res, "swishdocpath" ),
440                                    SwishResultPropertyULong ( sw_res, "swishrank" ),
441                                    SwishResultPropertyULong ( sw_res, "swishdocsize" ),
442                                    SwishResultPropertyStr   ( sw_res, "swishtitle"),
443                                    SwishResultPropertyStr   ( sw_res, "swishdbfile" ),
444                                    SwishResultPropertyStr   ( sw_res, "swishlastmodified" ),
445                                    SwishResultPropertyULong ( sw_res, "swishreccount" ),  /* can figure this out in loop, of course */
446                                    SwishResultPropertyULong ( sw_res, "swishfilenum" )
447                            );
448                    }
449    
450                    /* iterate over results */
451                    for (j = 0; j < ncols; j++)
452                    {
453                            bool    isnull;
454    
455                            /* array value of this position */
456                            indx[0] = j + prop_dim_lower_bounds[0];
457    
458                            dvalue = array_ref(prop_arr, prop_ndims, indx, -1, prop_len, prop_byval, prop_align, &isnull);
459    
460                            if (!isnull && sw_res)
461                                    values[j] = DatumGetCString(
462                                            prop2text( sw_res,
463                                                    (char *)DirectFunctionCall1(textout, dvalue)
464                                            ));
465                            else
466                                    values[j] = NULL;
467                    }
468                    /* construct the tuple */
469                    tuple = BuildTupleFromCStrings(attinmeta, values);
470    
471                    /* now store it */
472                    tuplestore_puttuple(tupstore, tuple);
473    
474            }
475    
476            tuplestore_donestoring(tupstore);
477            rsinfo->setResult = tupstore;
478    
479            /*
480             * SFRM_Materialize mode expects us to return a NULL Datum. The actual
481             * tuples are in our tuplestore and passed back through
482             * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
483             * that we actually used to build our tuples with, so the caller can
484             * verify we did what it was expecting.
485             */
486            rsinfo->setDesc = tupdesc;
487            MemoryContextSwitchTo(oldcontext);
488    
489            /* free swish object and close */
490            Free_Search_Object( search );
491            SwishClose( swish_handle );
492    
493            return (Datum) 0;
494    }
495    
496    
497    
498    
499    /* make text var from property */
500  char *prop2text(SW_RESULT sw_res, char *propname) {  char *prop2text(SW_RESULT sw_res, char *propname) {
501          char *val;          char *val;
502          char *prop;          char *prop;
503          int len;          int len;
504    
505          elog(INFO, "prop2text(%s)", propname);          elog(DEBUG2, "prop2text(%s)", propname);
506    
507          prop = SwishResultPropertyStr( sw_res, propname );          prop = SwishResultPropertyStr( sw_res, propname );
508          error_or_abort( swish_handle );          if (error_or_abort( swish_handle )) return NULL;
509    
510          len = strlen(prop);          len = strlen(prop);
511          elog(INFO, "prop2text(%s) = '%s' %d bytes", propname, prop, len);          elog(DEBUG1, "prop2text(%s) = '%s' %d bytes", propname, prop, len);
512    
513          len++;          len++;
514          len *= sizeof(char);          len *= sizeof(char);
515    
516          elog(INFO, "palloc(%d)", len);          elog(DEBUG2, "palloc(%d)", len);
517    
518          val = palloc(len);          val = palloc(len);
519    
520          memset(val, 0, len);          memset(val, 0, len);
521          strncpy(val, prop, len);          strncpy(val, prop, len);
522    
523          elog(INFO, "val=%s", val);          elog(DEBUG2, "val=%s", val);
524    
525          return val;          return val;
526  }  }
527    
528    /* make integer variable from property */
529  char *prop2int(SW_RESULT sw_res, char *propname) {  char *prop2int(SW_RESULT sw_res, char *propname) {
530          char *val;          char *val;
531          unsigned long prop;          unsigned long prop;
532          int len;          int len;
533    
534          elog(INFO, "prop2int(%s)", propname);          elog(DEBUG2, "prop2int(%s)", propname);
535    
536          prop = SwishResultPropertyULong( sw_res, propname );          prop = SwishResultPropertyULong( sw_res, propname );
537          error_or_abort( swish_handle );          if (error_or_abort( swish_handle )) return NULL;
538    
539          elog(INFO, "prop2int(%s) = %lu", propname, prop);          elog(DEBUG1, "prop2int(%s) = %lu", propname, prop);
540    
541          len = 128 * sizeof(char);          len = 128 * sizeof(char);
542          elog(INFO, "palloc(%d)", len);          elog(DEBUG2, "palloc(%d)", len);
543    
544          val = palloc(len);          val = palloc(len);
545          memset(val, 0, len);          memset(val, 0, len);
546    
547          snprintf(val, len, "%lu", prop);          snprintf(val, len, "%lu", prop);
548    
549          elog(INFO, "val=%s", val);          elog(DEBUG2, "val=%s", val);
550    
551          return val;          return val;
552  }  }
553    
554    
555  static void error_or_abort( SW_HANDLE swish_handle ) {  /*
556     * check if swish has returned error, and elog it.
557     */
558    static int error_or_abort( SW_HANDLE swish_handle ) {
559          if ( !SwishError( swish_handle ) )          if ( !SwishError( swish_handle ) )
560                  return;                  return 0;
561    
562          /* print a message */          /* print a message */
563          elog(ERROR,          elog(ERROR,
# Line 261  static void error_or_abort( SW_HANDLE sw Line 566  static void error_or_abort( SW_HANDLE sw
566                          SwishErrorString( swish_handle ),                          SwishErrorString( swish_handle ),
567                          SwishLastErrorMsg( swish_handle )                          SwishLastErrorMsg( swish_handle )
568          );          );
569            if ( swish_results ) Free_Results_Object( swish_results );
570          if ( search ) Free_Search_Object( search );          if ( search ) Free_Search_Object( search );
571          SwishClose( swish_handle );          SwishClose( swish_handle );
572    
573          /* do when there is no more left */          return 1;
574  }  }
575    

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

  ViewVC Help
Powered by ViewVC 1.1.26