--- trunk/pgest.c 2005/10/29 18:54:40 49 +++ trunk/pgest.c 2006/08/07 22:44:21 72 @@ -25,7 +25,11 @@ #include "funcapi.h" #include "utils/builtins.h" #include "utils/array.h" +#include "utils/lsyscache.h" #include "miscadmin.h" +#include "commands/trigger.h" +#include "executor/spi.h" + #include #include #include @@ -41,10 +45,12 @@ #endif #define ATTR_DELIMITER "{{!}}" +#define HINTS_PREFIX "HINTS." /* prototype */ char *attr2text(ESTDOC *doc, char *attr); char *node_attr2text(ESTRESDOC *rdoc, char *attr); +void cond_add_attr(ESTCOND *cond, char *attr); /* work in progress */ @@ -77,7 +83,6 @@ ESTDB *db; ESTCOND *cond; ESTDOC *doc; - const CBLIST *texts; int ecode, *est_result, resnum; int limit = 0; int offset = 0; @@ -208,13 +213,7 @@ /* minimum valid attribute length is 10: @a STREQ a */ if (! PG_ARGISNULL(2) && strlen(attr) >= 10) { elog(DEBUG1,"attributes: %s", attr); - char *curr_attr; - curr_attr = strtok(attr, ATTR_DELIMITER); - while (curr_attr) { - elog(DEBUG1,"est_cond_add_attr(%s)", curr_attr); - est_cond_add_attr(cond, curr_attr); - curr_attr = strtok(NULL, ATTR_DELIMITER); - } + cond_add_attr(cond, attr); } /* set the search phrase to the search condition object */ @@ -323,7 +322,7 @@ if (! doc) return (Datum) NULL; - elog(DEBUG1, "doc: %08x, attr: %s", doc, attr); + elog(DEBUG1, "doc: %p, attr: %s", doc, attr); if ( (attrval = est_doc_attr(doc, attr)) && (attrlen = strlen(attrval)) ) { val = (char *) palloc(attrlen * sizeof(char)); @@ -399,7 +398,7 @@ ESTCOND *cond; ESTNODERES *nres; ESTRESDOC *rdoc; - const CBLIST *texts; + CBMAP *hints; int resnum = 0; int limit = 0; int offset = 0; @@ -550,13 +549,7 @@ /* minimum valid attribute length is 10: @a STREQ a */ if (! PG_ARGISNULL(_arg_attr) && strlen(attr) >= 10) { elog(DEBUG1,"attributes: %s", attr); - char *curr_attr; - curr_attr = strtok(attr, ATTR_DELIMITER); - while (curr_attr) { - elog(DEBUG1,"est_cond_add_attr(%s)", curr_attr); - est_cond_add_attr(cond, curr_attr); - curr_attr = strtok(NULL, ATTR_DELIMITER); - } + cond_add_attr(cond, attr); } /* set the search phrase to the search condition object */ @@ -570,6 +563,11 @@ est_cond_set_max(cond, limit + offset); } + if (offset) { + elog(DEBUG1,"est_cond_set_skip(%d)", offset); + est_cond_set_skip(cond, offset); + } + /* get the result of search */ nres = est_node_search(node, cond, depth); @@ -597,6 +595,8 @@ nrows = resnum - offset; } + /* get hints */ + hints = est_noderes_hints(nres); elog(DEBUG1, "pgest_node: found %d hits for %s", resnum, query); @@ -607,7 +607,7 @@ { /* get result from estraier */ - if (! ( rdoc = est_noderes_get_doc(nres, i + offset) )) { + if (! ( rdoc = est_noderes_get_doc(nres, i) )) { elog(INFO, "pgest_node: can't find result %d", i + offset); } else { elog(DEBUG1, "URI: %s\n Title: %s\n", @@ -620,17 +620,31 @@ for (j = 0; j < ncols; j++) { bool isnull; + char *attr; /* current attribute name */ + char *hint; /* position of current hint in attribute */ + char *hint_val; /* array value of this position */ indx[0] = j + attr_dim_lower_bounds[0]; dvalue = array_ref(attr_arr, attr_ndims, indx, -1, attr_len, attr_byval, attr_align, &isnull); + attr = (char *)DirectFunctionCall1(textout, dvalue); - if (!isnull && rdoc) - values[j] = DatumGetCString( - node_attr2text(rdoc, - (char *)DirectFunctionCall1(textout, dvalue) - )); + if (!isnull && (hint = strstr(attr, HINTS_PREFIX)) != NULL) { + /* skip HINTS. prefix */ + hint += strlen(HINTS_PREFIX); + + hint_val = (char *)cbmapget(hints, hint, -1, NULL); + elog(DEBUG2, "hint %s = %s", hint, hint_val); + + if (hint_val != NULL) { + values[j] = DatumGetCString( hint_val ); + } else { + elog(INFO, "can't get hint in results: %s", hint); + values[j] = NULL; + } + } else if (!isnull && rdoc) + values[j] = DatumGetCString( node_attr2text(rdoc, attr) ); else values[j] = NULL; } @@ -679,7 +693,7 @@ if (! rdoc) return (Datum) NULL; - elog(DEBUG1, "doc: %08x, attr: %s", rdoc, attr); + elog(DEBUG1, "doc: %p, attr: %s", rdoc, attr); if ( (attrval = est_resdoc_attr(rdoc, attr)) && (attrlen = strlen(attrval)) ) { val = (char *) palloc(attrlen * sizeof(char)); @@ -705,3 +719,145 @@ return val; } +/* parse attributes and add them to confition */ +void cond_add_attr(ESTCOND *cond, char *attr) { + char *next; + char *curr_attr; + while ( strlen(attr) > 0 ) { + printf("len [%s] = %d\n", attr, strlen(attr)); + if ((next = strstr(attr, ATTR_DELIMITER)) != NULL) { + curr_attr = palloc( next - attr + 1 ); + memcpy(curr_attr, attr, next-attr); + curr_attr[next-attr] = '\0'; + next += strlen(ATTR_DELIMITER); + } else { + next = ""; + curr_attr = attr; + } + elog(DEBUG1, "est_cond_add_attr(%s)", curr_attr); + est_cond_add_attr(cond, curr_attr); + attr = next; + } +} + +/* trigger to keep data in Hyper Estraier index up-to-date */ +/* CREATE FUNCTION pgest_trigger() RETURNS TRIGGER AS ... */ + +/* + * UPDATE, INSERT and DELETE triggers are like this: + +CREATE TRIGGER pgest_trigger_update AFTER UPDATE + ON table_name FOR EACH ROW + EXECUTE PROCEDURE pgest_trigger('http://localhost:1978/node/trivia','admin','admin', + 'name_of_pk', 'column', 'another_column', 'and_so_on' + ) + +*/ + +PG_FUNCTION_INFO_V1(pgest_trigger); +Datum pgest_trigger(PG_FUNCTION_ARGS) { + + TriggerData *data; + TupleDesc tupdesc; + HeapTuple ret; + + char **args; + char *keycol = NULL; + char *key = NULL; + char *col_data = NULL; + int knumber; + int i; + int create_doc = 0; + + ESTNODE *node; + ESTDOC *doc; + + + + if (! CALLED_AS_TRIGGER(fcinfo)) { + elog(ERROR, "pgest_trigger() must be called as a trigger"); + } + + data = (TriggerData *) fcinfo->context; + + if (data->tg_trigger->tgnargs < 5) + elog(ERROR, "pgest_trigger() requires at least 5 parameters ('http://localhost:1978/node/trivia', 'user', 'passwd', 'pk_column', 'column', ... )"); + + args = data->tg_trigger->tgargs; + keycol = args[3]; + + tupdesc = data->tg_relation->rd_att; + + knumber = SPI_fnumber(tupdesc, keycol); + key = SPI_getvalue(data->tg_trigtuple, tupdesc, knumber); + + + /* initialize the network environment */ + if( ! est_init_net_env() ) + elog(ERROR, "pgest_trigger: network is unavailable\n"); + + /* create and configure the node connection object */ + node = est_node_new( args[0] ); + est_node_set_auth(node, args[1], args[2]); + + + if (TRIGGER_FIRED_BY_INSERT(data->tg_event)) { + /* There is no old data */ + ret = data->tg_trigtuple; + + create_doc++; + + } else if (TRIGGER_FIRED_BY_UPDATE(data->tg_event)) { + ret = data->tg_newtuple; + + create_doc++; + + } else if (TRIGGER_FIRED_BY_DELETE(data->tg_event)) { + /* There is no new data */ + ret = data->tg_trigtuple; + + if (! est_node_out_doc_by_uri(node, key) ) + elog(ERROR, "est_node_doc_by_uri(%s): %d\n", key, est_node_status(node)); + + } else { + elog(ERROR, "pgest_trigger() not called from INSERT/UPDATE/DELETE"); + } + + if ( create_doc ) { + + /* create a document object */ + doc = est_doc_new(); + est_doc_add_attr(doc, "@uri", key); + + elog(DEBUG1, "est_doc_new @uri=%s", key); + + for( i = 4; i < data->tg_trigger->tgnargs; i++ ) { + + col_data = SPI_getvalue(ret, tupdesc, SPI_fnumber(tupdesc, args[i])); + + if (data) { + elog(DEBUG1, " + %s = %s", args[i], col_data); + est_doc_add_attr(doc, args[i], col_data); + est_doc_add_text(doc, col_data); + } + + } + + /* register the document object to the node */ + if( ! est_node_put_doc(node, doc) ) + elog(ERROR, "est_node_put_doc: %d\n", est_node_status(node)); + + /* destroy the document object */ + est_doc_delete(doc); + + } + + /* destroy the node object */ + est_node_delete(node); + /* free the networking environment */ + est_free_net_env(); + + + return PointerGetDatum(ret); +} +