/[pgestraier]/trunk/pgest.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/pgest.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Wed May 25 23:38:37 2005 UTC (18 years, 11 months ago) by dpavlin
File MIME type: text/plain
File size: 9766 byte(s)
added NULL handling for optional arguments

1 /*
2 * integrate Hyper Estraier into PostgreSQL
3 *
4 * Dobrica Pavlinusic <dpavlin@rot13.org> 2005-05-19
5 *
6 * TODO:
7 * - all
8 *
9 * NOTES:
10 * - clear structures with memset to support hash indexes (who whould like
11 * to create hash index on table returned from function?)
12 * - number of returned rows is set by PostgreSQL evaluator, see:
13 * http://archives.postgresql.org/pgsql-hackers/2005-02/msg00546.php
14 *
15 * Based on:
16 * - C example from PostgreSQL documentation (BSD licence)
17 * - example002.c from Hyper Estraier (GPL)
18 * - _textin/_textout from pgcurl.c (LGPL)
19 *
20 * This code is licenced under GPL
21 */
22
23 #include "postgres.h"
24 #include "fmgr.h"
25 #include "funcapi.h"
26 #include "utils/builtins.h"
27 #include "utils/array.h"
28 #include "miscadmin.h"
29 #include <estraier.h>
30 #include <cabin.h>
31
32 #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
33 #define _textout(str) DatumGetPointer(DirectFunctionCall1(textout, PointerGetDatum(str)))
34 #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
35 #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
36
37 /* prototype */
38 char *attr2text(ESTDOC *doc, char *attr);
39
40 ESTDB *db;
41 ESTCOND *cond;
42 ESTDOC *doc;
43 const CBLIST *texts;
44 int ecode, *est_result, resnum, i, j;
45 int limit = 0;
46 int offset = 0;
47
48 /* define PostgreSQL v1 function */
49 PG_FUNCTION_INFO_V1(pgest);
50 Datum pgest(PG_FUNCTION_ARGS) {
51
52 FuncCallContext *funcctx;
53 int call_cntr;
54 int max_calls;
55 TupleDesc tupdesc;
56 TupleTableSlot *slot;
57 AttInMetadata *attinmeta;
58 char *index_path;
59 char *query;
60 char *attr;
61
62 /* stuff done only on the first call of the function */
63 if (SRF_IS_FIRSTCALL()) {
64 MemoryContext oldcontext;
65
66 /* create a function context for cross-call persistence */
67 funcctx = SRF_FIRSTCALL_INIT();
68
69 /* switch to memory context appropriate for multiple function calls */
70 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
71 /* take arguments from function */
72
73 /* index path */
74 if (PG_ARGISNULL(0)) {
75 elog(ERROR, "index path can't be null");
76 SRF_RETURN_DONE(funcctx);
77 }
78 index_path = _textout(PG_GETARG_TEXT_P(0));
79
80 /* query string */
81 if (PG_ARGISNULL(0)) {
82 query = "";
83 } else {
84 query = _textout(PG_GETARG_TEXT_P(1));
85 }
86
87 /* atribute filter */
88 if (PG_ARGISNULL(2)) {
89 attr = "";
90 } else {
91 attr = _textout(PG_GETARG_TEXT_P(2));
92 }
93
94 /* limit */
95 if (! PG_ARGISNULL(3)) limit = PG_GETARG_INT32(3);
96
97 /* offset */
98 if (! PG_ARGISNULL(4)) offset = PG_GETARG_INT32(4);
99
100
101 /* open the database */
102 elog(DEBUG1, "pgest: est_db_open(%s)", index_path);
103
104 if(!(db = est_db_open(index_path, ESTDBREADER, &ecode))){
105 elog(ERROR, "est_db_open: can't open %s [%d]: %s", index_path, ecode, est_err_msg(ecode));
106 SRF_RETURN_DONE(funcctx);
107 }
108
109 elog(INFO, "pgest: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);
110
111 /* create a search condition object */
112 if (!(cond = est_cond_new())) {
113 elog(INFO, "pgest: est_cond_new failed");
114 SRF_RETURN_DONE(funcctx);
115 }
116
117 /* set the search phrase to the search condition object */
118 if (! PG_ARGISNULL(1) && strlen(query) > 0)
119 est_cond_set_phrase(cond, query);
120
121 /* minimum valid attribute length is 10: @a STREQ a */
122 if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {
123 elog(INFO,"est_cond_add_attr(%s)", attr);
124 est_cond_add_attr(cond, attr);
125 }
126
127 /* get the result of search */
128 est_result = est_db_search(db, cond, &resnum, NULL);
129
130 /* total number of tuples to be returned */
131 if (limit && limit < resnum) {
132 funcctx->max_calls = limit - offset;
133 } else {
134 funcctx->max_calls = resnum - offset;
135 }
136
137 /* check if results exists */
138 if ( 0 == funcctx->max_calls )
139 elog(INFO, "pgest: no results for: %s", query );
140
141 elog(DEBUG1, "pgest: found %d hits for %s", resnum, query);
142
143 /* Build a tuple description for a __pgest tuple */
144 tupdesc = RelationNameGetTupleDesc("__pgest");
145
146 /* allocate a slot for a tuple with this tupdesc */
147 slot = TupleDescGetSlot(tupdesc);
148
149 /* assign slot to function context */
150 funcctx->slot = slot;
151
152 /*
153 * generate attribute metadata needed later to produce tuples from raw
154 * C strings
155 */
156 attinmeta = TupleDescGetAttInMetadata(tupdesc);
157 funcctx->attinmeta = attinmeta;
158
159 MemoryContextSwitchTo(oldcontext);
160
161 elog(DEBUG1, "SRF_IS_FIRSTCALL done");
162 }
163
164 /* stuff done on every call of the function */
165 funcctx = SRF_PERCALL_SETUP();
166
167 call_cntr = funcctx->call_cntr;
168 max_calls = funcctx->max_calls;
169 slot = funcctx->slot;
170 attinmeta = funcctx->attinmeta;
171
172 if (limit && call_cntr > limit - 1) {
173 elog(INFO, "call_cntr: %d limit: %d", call_cntr, limit);
174 SRF_RETURN_DONE(funcctx);
175 }
176
177 if (call_cntr < max_calls) {
178 char **values;
179 HeapTuple tuple;
180 Datum result;
181
182 elog(DEBUG1, "pgest: loop count %d", call_cntr);
183
184 if (! est_result) {
185 elog(ERROR, "pgest: no estraier results");
186 SRF_RETURN_DONE(funcctx);
187 }
188
189 /*
190 * Prepare a values array for storage in our slot.
191 * This should be an array of C strings which will
192 * be processed later by the type input functions.
193 */
194
195 if (doc = est_db_get_doc(db, est_result[call_cntr + offset], 0)) {
196
197 elog(DEBUG1, "URI: %s\n Title: %s\n",
198 est_doc_attr(doc, "@uri"),
199 est_doc_attr(doc, "@title")
200 );
201
202 values = (char **) palloc(4 * sizeof(char *));
203
204 // values[0] = (char *) palloc(strlen(_estval) * sizeof(char));
205
206 values[0] = (char *) attr2text(doc,"@id");
207 values[1] = (char *) attr2text(doc,"@uri");
208 values[2] = (char *) attr2text(doc,"@title");
209 values[3] = (char *) attr2text(doc,"@type");
210
211 /* destloy the document object */
212 elog(DEBUG2, "est_doc_delete");
213 est_doc_delete(doc);
214 } else {
215 elog(INFO, "no result from estraier");
216 values[0] = DatumGetCString( "" );
217 values[1] = DatumGetCString( "" );
218 values[2] = DatumGetCString( "" );
219 values[3] = DatumGetCString( "" );
220 }
221
222
223 elog(DEBUG2, "build tuple");
224 /* build a tuple */
225 tuple = BuildTupleFromCStrings(attinmeta, values);
226
227 elog(DEBUG2, "make tuple into datum");
228 /* make the tuple into a datum */
229 result = TupleGetDatum(slot, tuple);
230
231 elog(DEBUG2, "cleanup");
232 /* clean up ? */
233 /*
234 pfree(values[0]);
235 pfree(values[1]);
236 pfree(values[2]);
237 pfree(values[3]);
238 pfree(values);
239 */
240
241 elog(DEBUG2, "cleanup over");
242
243 SRF_RETURN_NEXT(funcctx, result);
244 } else {
245 elog(DEBUG1, "loop over");
246
247 if(!est_db_close(db, &ecode)){
248 elog(INFO, "est_db_close error: %s", est_err_msg(ecode));
249 }
250
251 /* do when there is no more left */
252 SRF_RETURN_DONE(funcctx);
253 }
254 }
255
256 /* work in progress */
257 PG_FUNCTION_INFO_V1(pgest2);
258 Datum pgest2(PG_FUNCTION_ARGS)
259 {
260 int nrows = 3;
261 int16 typlen;
262 bool typbyval;
263 char typalign;
264 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
265 AttInMetadata *attinmeta;
266 TupleDesc tupdesc;
267 Tuplestorestate *tupstore = NULL;
268 HeapTuple tuple;
269 MemoryContext per_query_ctx;
270 MemoryContext oldcontext;
271 Datum dvalue;
272 char **values;
273 int ncols;
274 int i, j;
275
276 /* check to see if caller supports us returning a tuplestore */
277 if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
278 ereport(ERROR,
279 (errcode(ERRCODE_SYNTAX_ERROR),
280 errmsg("materialize mode required, but it is not " \
281 "allowed in this context")));
282
283 /* get the requested return tuple description */
284 tupdesc = rsinfo->expectedDesc;
285 ncols = tupdesc->natts;
286
287 /*
288 * The requested tuple description better match up with the array
289 * we were given.
290 */
291 /* OK, use it */
292 attinmeta = TupleDescGetAttInMetadata(tupdesc);
293
294 /* Now go to work */
295 rsinfo->returnMode = SFRM_Materialize;
296
297 per_query_ctx = fcinfo->flinfo->fn_mcxt;
298 oldcontext = MemoryContextSwitchTo(per_query_ctx);
299
300 /* initialize our tuplestore */
301 tupstore = tuplestore_begin_heap(true, false, SortMem);
302
303 values = (char **) palloc(ncols * sizeof(char *));
304
305 for (i = 0; i < nrows; i++)
306 {
307 for (j = 0; j < ncols; j++)
308 {
309 values[j] = DatumGetCString( "foo" );
310 }
311 /* construct the tuple */
312 tuple = BuildTupleFromCStrings(attinmeta, values);
313
314 /* now store it */
315 tuplestore_puttuple(tupstore, tuple);
316 }
317
318 tuplestore_donestoring(tupstore);
319 rsinfo->setResult = tupstore;
320
321 /*
322 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
323 * tuples are in our tuplestore and passed back through
324 * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
325 * that we actually used to build our tuples with, so the caller can
326 * verify we did what it was expecting.
327 */
328 rsinfo->setDesc = tupdesc;
329 MemoryContextSwitchTo(oldcontext);
330
331 return (Datum) 0;
332 }
333
334
335 /* make text var from attr */
336 char *attr2text(ESTDOC *doc, char *attr) {
337 char *val;
338 const char *attrval;
339 int len;
340 int attrlen;
341
342 elog(DEBUG1, "doc: %08x, attr: %s", doc, attr);
343
344 if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {
345 val = (char *) palloc(attrlen * sizeof(char));
346 } else {
347 return (Datum) NULL;
348 }
349
350 len = strlen(attrval);
351 elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);
352
353 len++;
354 len *= sizeof(char);
355
356 elog(DEBUG2, "palloc(%d)", len);
357
358 val = palloc(len);
359
360 memset(val, 0, len);
361 strncpy(val, attrval, len);
362
363 elog(DEBUG2, "val=%s", val);
364
365 return val;
366 }
367
368 /* make integer variable from property */
369 /*
370 char *prop2int(SW_RESULT sw_res, char *propname) {
371 char *val;
372 unsigned long prop;
373 int len;
374
375 elog(DEBUG2, "prop2int(%s)", propname);
376
377 prop = estResultPropertyULong( sw_res, propname );
378 if (error_or_abort( est_handle )) return NULL;
379
380 elog(DEBUG1, "prop2int(%s) = %lu", propname, prop);
381
382 len = 128 * sizeof(char);
383 elog(DEBUG2, "palloc(%d)", len);
384
385 val = palloc(len);
386 memset(val, 0, len);
387
388 snprintf(val, len, "%lu", prop);
389
390 elog(DEBUG2, "val=%s", val);
391
392 return val;
393 }
394 */

  ViewVC Help
Powered by ViewVC 1.1.26