/[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 57 - (show annotations)
Thu May 11 16:19:38 2006 UTC (17 years, 11 months ago) by dpavlin
File MIME type: text/plain
File size: 18675 byte(s)
initial implementation of hints using (fake) atribute names in form HINTS.something
(e.g. HITS.HIT or HINTS.WORDNUM...)
1 /*
2 * integrate Hyper Estraier into PostgreSQL
3 *
4 * Dobrica Pavlinusic <dpavlin@rot13.org> 2005-05-19
5 *
6 * TODO:
7 * - all
8 *
9 * NOTES:
10 * - clear structures with memset to support hash indexes (who whould like
11 * to create hash index on table returned from function?)
12 * - number of returned rows is set by PostgreSQL evaluator, see:
13 * http://archives.postgresql.org/pgsql-hackers/2005-02/msg00546.php
14 *
15 * Based on:
16 * - C example from PostgreSQL documentation (BSD licence)
17 * - coreexample002.c and nodeexample002.c from Hyper Estraier (GPL)
18 * - _textin/_textout from pgcurl.c (LGPL)
19 *
20 * This code is licenced under GPL
21 */
22
23 #include "postgres.h"
24 #include "fmgr.h"
25 #include "funcapi.h"
26 #include "utils/builtins.h"
27 #include "utils/array.h"
28 #include "utils/lsyscache.h"
29 #include "miscadmin.h"
30 #include <estraier.h>
31 #include <cabin.h>
32 #include <estnode.h>
33
34 #define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
35 #define _textout(str) DatumGetPointer(DirectFunctionCall1(textout, PointerGetDatum(str)))
36 #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
37 #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
38
39 /* SortMem got renamed in PostgreSQL 8.0 */
40 #ifndef SortMem
41 #define SortMem 16 * 1024
42 #endif
43
44 #define ATTR_DELIMITER "{{!}}"
45 #define HINTS_PREFIX "HINTS."
46
47 /* prototype */
48 char *attr2text(ESTDOC *doc, char *attr);
49 char *node_attr2text(ESTRESDOC *rdoc, char *attr);
50 void cond_add_attr(ESTCOND *cond, char *attr);
51
52
53 /* work in progress */
54 PG_FUNCTION_INFO_V1(pgest_attr);
55 Datum pgest_attr(PG_FUNCTION_ARGS)
56 {
57 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(6);
58 Oid attr_element_type = ARR_ELEMTYPE(attr_arr);
59 int attr_ndims = ARR_NDIM(attr_arr);
60 int *attr_dim_counts = ARR_DIMS(attr_arr);
61 int *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
62 int ncols = 0;
63 int nrows = 0;
64 int indx[MAXDIM];
65 int16 attr_len;
66 bool attr_byval;
67 char attr_align;
68 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
69 AttInMetadata *attinmeta;
70 TupleDesc tupdesc;
71 Tuplestorestate *tupstore = NULL;
72 HeapTuple tuple;
73 MemoryContext per_query_ctx;
74 MemoryContext oldcontext;
75 Datum dvalue;
76 char **values;
77 int rsinfo_ncols;
78 int i, j;
79 /* estvars */
80 ESTDB *db;
81 ESTCOND *cond;
82 ESTDOC *doc;
83 int ecode, *est_result, resnum;
84 int limit = 0;
85 int offset = 0;
86
87 char *index_path;
88 char *query;
89 char *attr;
90 char *order;
91
92
93 /* only allow 1D input array */
94 if (attr_ndims == 1)
95 {
96 ncols = attr_dim_counts[0];
97 }
98 else
99 ereport(ERROR,
100 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
101 errmsg("invalid input array"),
102 errdetail("Input array must have 1 dimension")));
103
104 /* check to see if caller supports us returning a tuplestore */
105 if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
106 ereport(ERROR,
107 (errcode(ERRCODE_SYNTAX_ERROR),
108 errmsg("materialize mode required, but it is not " \
109 "allowed in this context")));
110
111 /* get info about element type needed to construct the array */
112 get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
113
114 /* get the requested return tuple description */
115 tupdesc = rsinfo->expectedDesc;
116 rsinfo_ncols = tupdesc->natts;
117
118 /*
119 * The requested tuple description better match up with the array
120 * we were given.
121 */
122 if (rsinfo_ncols != ncols)
123 ereport(ERROR,
124 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
125 errmsg("invalid input array"),
126 errdetail("Number of elements in array must match number of query specified columns.")));
127
128 /* OK, use it */
129 attinmeta = TupleDescGetAttInMetadata(tupdesc);
130
131 /* Now go to work */
132 rsinfo->returnMode = SFRM_Materialize;
133
134 per_query_ctx = fcinfo->flinfo->fn_mcxt;
135 oldcontext = MemoryContextSwitchTo(per_query_ctx);
136
137 /* initialize our tuplestore */
138 tupstore = tuplestore_begin_heap(true, false, SortMem);
139
140
141 /* take rest of arguments from function */
142
143 /* index path */
144 if (PG_ARGISNULL(0)) {
145 ereport(ERROR,
146 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
147 errmsg("index path can't be null"),
148 errdetail("Index path must be valid full path to HyperEstraier index")));
149 }
150 index_path = _textout(PG_GETARG_TEXT_P(0));
151
152 /* query string */
153 if (PG_ARGISNULL(1)) {
154 query = "";
155 } else {
156 query = _textout(PG_GETARG_TEXT_P(1));
157 }
158
159 /* atribute filter */
160 if (PG_ARGISNULL(2)) {
161 attr = "";
162 } else {
163 attr = _textout(PG_GETARG_TEXT_P(2));
164 }
165
166 /* sort order */
167 if (PG_ARGISNULL(3)) {
168 order = "";
169 } else {
170 order = _textout(PG_GETARG_TEXT_P(3));
171 }
172
173
174 /* limit */
175 if (PG_ARGISNULL(4)) {
176 limit = 0;
177 } else {
178 limit = PG_GETARG_INT32(4);
179 }
180
181 /* offset */
182 if (PG_ARGISNULL(5)) {
183 offset = 0;
184 } else {
185 offset = PG_GETARG_INT32(5);
186 }
187
188
189 /* open the database */
190 elog(DEBUG1, "pgest_attr: est_db_open(%s)", index_path);
191
192 if(!(db = est_db_open(index_path, ESTDBREADER, &ecode))){
193 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
194 errmsg("est_db_open: can't open %s: %d", index_path, ecode),
195 errdetail(est_err_msg(ecode))));
196 }
197
198 elog(DEBUG1, "pgest_attr: query[%s] attr[%s] limit %d offset %d", query, (PG_ARGISNULL(2) ? "NULL" : attr), limit, offset);
199
200 /* create a search condition object */
201 if (!(cond = est_cond_new())) {
202 ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
203 errmsg("pgest_attr: est_cond_new failed")));
204 }
205
206 /* set the search phrase to the search condition object */
207 if (! PG_ARGISNULL(1) && strlen(query) > 0)
208 est_cond_set_phrase(cond, query);
209
210 /* minimum valid attribute length is 10: @a STREQ a */
211 if (! PG_ARGISNULL(2) && strlen(attr) >= 10) {
212 elog(DEBUG1,"attributes: %s", attr);
213 cond_add_attr(cond, attr);
214 }
215
216 /* set the search phrase to the search condition object */
217 if (! PG_ARGISNULL(3) && strlen(order) > 0) {
218 elog(DEBUG1,"est_cond_set_order(%s)", order);
219 est_cond_set_order(cond, order);
220 }
221
222 if (limit) {
223 elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
224 est_cond_set_max(cond, limit + offset);
225 }
226
227 /* get the result of search */
228 est_result = est_db_search(db, cond, &resnum, NULL);
229
230 /* check if results exists */
231 if ( 0 == resnum ) {
232 elog(INFO, "pgest_attr: no results for: %s", query );
233 }
234
235 /* total number of tuples to be returned */
236 if (limit && limit < resnum) {
237 nrows = limit;
238 } else {
239 nrows = resnum - offset;
240 }
241
242
243 elog(DEBUG1, "pgest_attr: found %d hits for %s", resnum, query);
244
245 values = (char **) palloc(ncols * sizeof(char *));
246
247 for (i = 0; i < nrows; i++)
248 {
249
250 /* get result from estraier */
251 if (! ( doc = est_db_get_doc(db, est_result[i + offset], 0)) ) {
252 elog(INFO, "pgest_attr: can't find result %d", i + offset);
253 } else {
254 elog(DEBUG1, "URI: %s\n Title: %s\n",
255 est_doc_attr(doc, "@uri"),
256 est_doc_attr(doc, "@title")
257 );
258 }
259
260 /* iterate over results */
261 for (j = 0; j < ncols; j++)
262 {
263 bool isnull;
264
265 /* array value of this position */
266 indx[0] = j + attr_dim_lower_bounds[0];
267
268 dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
269
270 if (!isnull && doc)
271 values[j] = DatumGetCString(
272 attr2text(doc,
273 (char *)DirectFunctionCall1(textout, dvalue)
274 ));
275 else
276 values[j] = NULL;
277 }
278 /* construct the tuple */
279 tuple = BuildTupleFromCStrings(attinmeta, values);
280
281 /* now store it */
282 tuplestore_puttuple(tupstore, tuple);
283
284 /* delete estraier document object */
285 if (doc) est_doc_delete(doc);
286 }
287
288 tuplestore_donestoring(tupstore);
289 rsinfo->setResult = tupstore;
290
291 /*
292 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
293 * tuples are in our tuplestore and passed back through
294 * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
295 * that we actually used to build our tuples with, so the caller can
296 * verify we did what it was expecting.
297 */
298 rsinfo->setDesc = tupdesc;
299 MemoryContextSwitchTo(oldcontext);
300
301 est_cond_delete(cond);
302
303 if(!est_db_close(db, &ecode)){
304 ereport(ERROR, (errcode(ERRCODE_IO_ERROR),
305 errmsg("est_db_close: %d", ecode),
306 errdetail(est_err_msg(ecode))));
307 }
308
309 return (Datum) 0;
310 }
311
312
313 /* make text var from attr */
314 char *attr2text(ESTDOC *doc, char *attr) {
315 char *val;
316 const char *attrval;
317 int len;
318 int attrlen;
319
320 if (! doc) return (Datum) NULL;
321
322 elog(DEBUG1, "doc: %p, attr: %s", doc, attr);
323
324 if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) {
325 val = (char *) palloc(attrlen * sizeof(char));
326 } else {
327 return (Datum) NULL;
328 }
329
330 len = strlen(attrval);
331 elog(DEBUG1, "attr2text(%s) = '%s' %d bytes", attr, attrval, len);
332
333 len++;
334 len *= sizeof(char);
335
336 elog(DEBUG2, "palloc(%d)", len);
337
338 val = palloc(len);
339
340 memset(val, 0, len);
341 strncpy(val, attrval, len);
342
343 elog(DEBUG2, "val=%s", val);
344
345 return val;
346 }
347
348 /*
349 * variation on theme: use node API which doesn't open index on
350 * every query which is much faster for large indexes
351 *
352 */
353
354 /* select * from pgest( */
355 #define _arg_node_uri 0
356 #define _arg_login 1
357 #define _arg_passwd 2
358 #define _arg_depth 3
359 #define _arg_query 4
360 #define _arg_attr 5
361 #define _arg_order 6
362 #define _arg_limit 7
363 #define _arg_offset 8
364 #define _arg_attr_array 9
365 /* as (foo text, ... ); */
366
367
368 PG_FUNCTION_INFO_V1(pgest_node);
369 Datum pgest_node(PG_FUNCTION_ARGS)
370 {
371 ArrayType *attr_arr = PG_GETARG_ARRAYTYPE_P(_arg_attr_array);
372 Oid attr_element_type = ARR_ELEMTYPE(attr_arr);
373 int attr_ndims = ARR_NDIM(attr_arr);
374 int *attr_dim_counts = ARR_DIMS(attr_arr);
375 int *attr_dim_lower_bounds = ARR_LBOUND(attr_arr);
376 int ncols = 0;
377 int nrows = 0;
378 int indx[MAXDIM];
379 int16 attr_len;
380 bool attr_byval;
381 char attr_align;
382 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
383 AttInMetadata *attinmeta;
384 TupleDesc tupdesc;
385 Tuplestorestate *tupstore = NULL;
386 HeapTuple tuple;
387 MemoryContext per_query_ctx;
388 MemoryContext oldcontext;
389 Datum dvalue;
390 char **values;
391 int rsinfo_ncols;
392 int i, j;
393 /* estvars */
394 ESTNODE *node;
395 ESTCOND *cond;
396 ESTNODERES *nres;
397 ESTRESDOC *rdoc;
398 CBMAP *hints;
399 int resnum = 0;
400 int limit = 0;
401 int offset = 0;
402 int depth = 0;
403
404 char *node_url;
405 char *user, *passwd;
406 char *query;
407 char *attr;
408 char *order;
409
410
411 /* only allow 1D input array */
412 if (attr_ndims == 1)
413 {
414 ncols = attr_dim_counts[0];
415 }
416 else
417 ereport(ERROR,
418 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
419 errmsg("invalid input array"),
420 errdetail("Input array must have 1 dimension")));
421
422 /* check to see if caller supports us returning a tuplestore */
423 if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
424 ereport(ERROR,
425 (errcode(ERRCODE_SYNTAX_ERROR),
426 errmsg("materialize mode required, but it is not " \
427 "allowed in this context")));
428
429 /* get info about element type needed to construct the array */
430 get_typlenbyvalalign(attr_element_type, &attr_len, &attr_byval, &attr_align);
431
432 /* get the requested return tuple description */
433 tupdesc = rsinfo->expectedDesc;
434 rsinfo_ncols = tupdesc->natts;
435
436 /*
437 * The requested tuple description better match up with the array
438 * we were given.
439 */
440 if (rsinfo_ncols != ncols)
441 ereport(ERROR,
442 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
443 errmsg("invalid input array"),
444 errdetail("Number of elements in array must match number of query specified columns.")));
445
446 /* OK, use it */
447 attinmeta = TupleDescGetAttInMetadata(tupdesc);
448
449 /* Now go to work */
450 rsinfo->returnMode = SFRM_Materialize;
451
452 per_query_ctx = fcinfo->flinfo->fn_mcxt;
453 oldcontext = MemoryContextSwitchTo(per_query_ctx);
454
455 /* initialize our tuplestore */
456 tupstore = tuplestore_begin_heap(true, false, SortMem);
457
458
459 /* take rest of arguments from function */
460
461 /* node URL */
462 if (PG_ARGISNULL(_arg_node_uri)) {
463 ereport(ERROR,
464 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
465 errmsg("node URL can't be null"),
466 errdetail("Node URL must be valid URL to HyperEstraier node")));
467 }
468 node_url = _textout(PG_GETARG_TEXT_P(_arg_node_uri));
469
470 /* login and password */
471 if (PG_ARGISNULL(_arg_login) || PG_ARGISNULL(_arg_passwd)) {
472 ereport(ERROR,
473 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
474 errmsg("username and password can't be NULL"),
475 errdetail("You must specify valid username and password to HyperEstraier node")));
476 }
477 user = _textout(PG_GETARG_TEXT_P(_arg_login));
478 passwd = _textout(PG_GETARG_TEXT_P(_arg_passwd));
479
480 /* depth of search */
481 if (PG_ARGISNULL(_arg_depth)) {
482 depth = 0;
483 } else {
484 depth = PG_GETARG_INT32(_arg_depth);
485 }
486
487 /* query string */
488 if (PG_ARGISNULL(_arg_query)) {
489 query = "";
490 } else {
491 query = _textout(PG_GETARG_TEXT_P(_arg_query));
492 }
493
494 /* atribute filter */
495 if (PG_ARGISNULL(_arg_attr)) {
496 attr = "";
497 } else {
498 attr = _textout(PG_GETARG_TEXT_P(_arg_attr));
499 }
500
501 /* sort order */
502 if (PG_ARGISNULL(_arg_order)) {
503 order = "";
504 } else {
505 order = _textout(PG_GETARG_TEXT_P(_arg_order));
506 }
507
508
509 /* limit */
510 if (PG_ARGISNULL(_arg_limit)) {
511 limit = 0;
512 } else {
513 limit = PG_GETARG_INT32(_arg_limit);
514 }
515
516 /* offset */
517 if (PG_ARGISNULL(_arg_offset)) {
518 offset = 0;
519 } else {
520 offset = PG_GETARG_INT32(_arg_offset);
521 }
522
523 /* initialize the network environment */
524 if(!est_init_net_env()){
525 ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
526 errmsg("pgest_node: can't create network enviroment")));
527 }
528
529 /* create the node connection object */
530 elog(DEBUG1, "pgest_node: est_node_new(%s) as %s", node_url, user);
531 node = est_node_new(node_url);
532 est_node_set_auth(node, user, passwd);
533
534 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);
535
536 /* create a search condition object */
537 if (!(cond = est_cond_new())) {
538 ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
539 errmsg("pgest_node: est_cond_new failed")));
540 }
541
542 /* set the search phrase to the search condition object */
543 if (! PG_ARGISNULL(_arg_query) && strlen(query) > 0)
544 est_cond_set_phrase(cond, query);
545
546 /* minimum valid attribute length is 10: @a STREQ a */
547 if (! PG_ARGISNULL(_arg_attr) && strlen(attr) >= 10) {
548 elog(DEBUG1,"attributes: %s", attr);
549 cond_add_attr(cond, attr);
550 }
551
552 /* set the search phrase to the search condition object */
553 if (! PG_ARGISNULL(_arg_order) && strlen(order) > 0) {
554 elog(DEBUG1,"est_cond_set_order(%s)", order);
555 est_cond_set_order(cond, order);
556 }
557
558 if (limit) {
559 elog(DEBUG1,"est_cond_set_max(%d)", limit + offset);
560 est_cond_set_max(cond, limit + offset);
561 }
562
563 if (offset) {
564 elog(DEBUG1,"est_cond_set_skip(%d)", offset);
565 est_cond_set_skip(cond, offset);
566 }
567
568 /* get the result of search */
569 nres = est_node_search(node, cond, depth);
570
571 if (! nres) {
572 int status = est_node_status(node);
573 est_cond_delete(cond);
574 est_node_delete(node);
575 est_free_net_env();
576 ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),
577 errmsg("pgest_node: search failed, node status %d", status)));
578 }
579
580 /* get number of results */
581 resnum = est_noderes_doc_num(nres);
582
583 /* check if results exists */
584 if ( 0 == resnum ) {
585 elog(INFO, "pgest_node: no results for: %s", query );
586 }
587
588 /* total number of tuples to be returned */
589 if (limit && limit < resnum) {
590 nrows = limit;
591 } else {
592 nrows = resnum - offset;
593 }
594
595 /* get hints */
596 hints = est_noderes_hints(nres);
597
598 elog(DEBUG1, "pgest_node: found %d hits for %s", resnum, query);
599
600
601 values = (char **) palloc(ncols * sizeof(char *));
602
603 for (i = 0; i < nrows; i++)
604 {
605
606 /* get result from estraier */
607 if (! ( rdoc = est_noderes_get_doc(nres, i) )) {
608 elog(INFO, "pgest_node: can't find result %d", i + offset);
609 } else {
610 elog(DEBUG1, "URI: %s\n Title: %s\n",
611 est_resdoc_attr(rdoc, "@uri"),
612 est_resdoc_attr(rdoc, "@title")
613 );
614 }
615
616 /* iterate over results */
617 for (j = 0; j < ncols; j++)
618 {
619 bool isnull;
620 char *attr; /* current attribute name */
621 char *hint; /* position of current hint in attribute */
622 char *hint_val;
623
624 /* array value of this position */
625 indx[0] = j + attr_dim_lower_bounds[0];
626
627 dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull);
628 attr = (char *)DirectFunctionCall1(textout, dvalue);
629
630 if (!isnull && (hint = strstr(attr, HINTS_PREFIX)) != NULL) {
631 /* skip HINTS. prefix */
632 hint += strlen(HINTS_PREFIX);
633
634 hint_val = cbmapget(hints, hint, -1, NULL);
635 elog(DEBUG2, "hint %s = %s", hint, hint_val);
636
637 if (hint_val != NULL) {
638 values[j] = DatumGetCString( hint_val );
639 } else {
640 elog(INFO, "can't get hint in results: %s", hint);
641 values[j] = NULL;
642 }
643 } else if (!isnull && rdoc)
644 values[j] = DatumGetCString( node_attr2text(rdoc, attr) );
645 else
646 values[j] = NULL;
647 }
648 /* construct the tuple */
649 tuple = BuildTupleFromCStrings(attinmeta, values);
650
651 /* now store it */
652 tuplestore_puttuple(tupstore, tuple);
653
654 }
655
656 tuplestore_donestoring(tupstore);
657 rsinfo->setResult = tupstore;
658
659 /*
660 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
661 * tuples are in our tuplestore and passed back through
662 * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
663 * that we actually used to build our tuples with, so the caller can
664 * verify we did what it was expecting.
665 */
666 rsinfo->setDesc = tupdesc;
667 MemoryContextSwitchTo(oldcontext);
668
669 /* delete the node result object */
670 est_noderes_delete(nres);
671
672 /* destroy the search condition object */
673 est_cond_delete(cond);
674
675 /* destroy the node object */
676 est_node_delete(node);
677
678 /* free the networking environment */
679 est_free_net_env();
680
681 return (Datum) 0;
682 }
683
684 /* make text var from node attr */
685 char *node_attr2text(ESTRESDOC *rdoc, char *attr) {
686 char *val;
687 const char *attrval;
688 int len;
689 int attrlen;
690
691 if (! rdoc) return (Datum) NULL;
692
693 elog(DEBUG1, "doc: %p, attr: %s", rdoc, attr);
694
695 if ( (attrval = est_resdoc_attr(rdoc, attr)) && (attrlen = strlen(attrval)) ) {
696 val = (char *) palloc(attrlen * sizeof(char));
697 } else {
698 return (Datum) NULL;
699 }
700
701 len = strlen(attrval);
702 elog(DEBUG1, "node_attr2text(%s) = '%s' %d bytes", attr, attrval, len);
703
704 len++;
705 len *= sizeof(char);
706
707 elog(DEBUG2, "palloc(%d)", len);
708
709 val = palloc(len);
710
711 memset(val, 0, len);
712 strncpy(val, attrval, len);
713
714 elog(DEBUG2, "val=%s", val);
715
716 return val;
717 }
718
719 /* parse attributes and add them to confition */
720 void cond_add_attr(ESTCOND *cond, char *attr) {
721 char *next;
722 char *curr_attr;
723 while ( strlen(attr) > 0 ) {
724 printf("len [%s] = %d\n", attr, strlen(attr));
725 if ((next = strstr(attr, ATTR_DELIMITER)) != NULL) {
726 curr_attr = palloc( next - attr + 1 );
727 memcpy(curr_attr, attr, next-attr);
728 curr_attr[next-attr] = '\0';
729 next += strlen(ATTR_DELIMITER);
730 } else {
731 next = "";
732 curr_attr = attr;
733 }
734 elog(DEBUG1, "est_cond_add_attr(%s)", curr_attr);
735 est_cond_add_attr(cond, curr_attr);
736 attr = next;
737 }
738 }

  ViewVC Help
Powered by ViewVC 1.1.26