* Copyright (C) 1995-2005, Index Data ApS
* See the file LICENSE for details.
*
- * $Id: zoom-c.c,v 1.57 2005-12-19 20:19:29 adam Exp $
+ * $Id: zoom-c.c,v 1.81 2006-07-06 10:17:54 adam Exp $
*/
/**
* \file zoom-c.c
#include <assert.h>
#include <string.h>
+#include <errno.h>
#include "zoom-p.h"
#include <yaz/yaz-util.h>
#include <yaz/charneg.h>
#include <yaz/ill.h>
#include <yaz/srw.h>
+#include <yaz/cql.h>
+#include <yaz/ccl.h>
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
static zoom_ret ZOOM_connection_send_init (ZOOM_connection c);
static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out);
+static char *cql2pqf(ZOOM_connection c, const char *cql);
static void initlog()
{
event->kind = kind;
event->next = 0;
event->prev = 0;
+ yaz_log(log_details, "ZOOM_event_create(%d)", kind);
return event;
}
addinfo2 ? addinfo2 : "");
}
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
static void set_HTTP_error (ZOOM_connection c, int error,
const char *addinfo, const char *addinfo2)
{
static void clear_error (ZOOM_connection c)
{
-
+ /*
+ * If an error is tied to an operation then it's ok to clear: for
+ * example, a diagnostic returned from a search is cleared by a
+ * subsequent search. However, problems such as Connection Lost
+ * or Init Refused are not cleared, because they are not
+ * recoverable: doing another search doesn't help.
+ */
switch (c->error)
{
case ZOOM_ERROR_CONNECT:
assert (0);
}
xfree (task);
+
+ if (!c->tasks)
+ {
+ ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
+ ZOOM_connection_put_event(c, event);
+ }
}
}
char **databaseNames;
const char *cp = ZOOM_options_get (options, "databaseName");
- if (!cp || !*cp)
+ if ((!cp || !*cp) && con->host_port)
{
if (strncmp (con->host_port, "unix:", 5) == 0)
cp = strchr(con->host_port+5, ':');
return c;
}
+static zoom_sru_mode get_sru_mode_from_string(const char *s)
+{
+ if (!s || !*s)
+ return zoom_sru_soap;
+ if (!yaz_matchstr(s, "soap"))
+ return zoom_sru_soap;
+ else if (!yaz_matchstr(s, "get"))
+ return zoom_sru_get;
+ else if (!yaz_matchstr(s, "post"))
+ return zoom_sru_post;
+ return zoom_sru_error;
+}
+
ZOOM_API(void)
ZOOM_connection_connect(ZOOM_connection c,
const char *host, int portnum)
else
c->lang = 0;
+ val = ZOOM_options_get (c->options, "sru");
+ c->sru_mode = get_sru_mode_from_string(val);
+
xfree (c->host_port);
if (portnum)
{
return 0;
}
+/*
+ * Translate the CQL string client-side into RPN which is passed to
+ * the server. This is useful for server's that don't themselves
+ * support CQL, for which ZOOM_query_cql() is useless. `conn' is used
+ * only as a place to stash diagnostics if compilation fails; if this
+ * information is not needed, a null pointer may be used.
+ */
+ZOOM_API(int)
+ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
+{
+ char *rpn;
+ int ret;
+ ZOOM_connection freeme = 0;
+
+ yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
+ if (conn == 0)
+ conn = freeme = ZOOM_connection_create(0);
+
+ rpn = cql2pqf(conn, str);
+ if (freeme != 0)
+ ZOOM_connection_destroy(freeme);
+ if (rpn == 0)
+ return -1;
+
+ ret = ZOOM_query_prefix(s, rpn);
+ xfree(rpn);
+ return ret;
+}
+
+/*
+ * Analogous in every way to ZOOM_query_cql2rpn(), except that there
+ * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
+ * to the server, as the YAZ GFS doesn't know how to handle this.
+ */
+ZOOM_API(int)
+ ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
+ int *ccl_error, const char **error_string,
+ int *error_pos)
+{
+ int ret;
+ struct ccl_rpn_node *rpn;
+ CCL_bibset bibset = ccl_qual_mk();
+
+ if (config)
+ ccl_qual_buf(bibset, config);
+
+ rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
+ if (!rpn)
+ {
+ *error_string = ccl_err_msg(*ccl_error);
+ ret = -1;
+ }
+ else
+ {
+ WRBUF wr = wrbuf_alloc();
+ ccl_pquery(wr, rpn);
+ ccl_rpn_delete(rpn);
+ ret = ZOOM_query_prefix(s, wrbuf_buf(wr));
+ wrbuf_free(wr, 1);
+ }
+ ccl_qual_rm(&bibset);
+ return ret;
+}
+
ZOOM_API(int)
ZOOM_query_sortby(ZOOM_query s, const char *criteria)
{
ZOOM_resultset ZOOM_resultset_create ()
{
+ int i;
ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
initlog();
r->schema = 0;
r->count = 0;
r->step = 0;
- r->record_cache = 0;
+ for (i = 0; i<RECORD_HASH_SIZE; i++)
+ r->record_hash[i] = 0;
r->r_sort_spec = 0;
r->query = 0;
r->connection = 0;
ZOOM_API(void)
ZOOM_resultset_cache_reset(ZOOM_resultset r)
{
- ZOOM_record_cache rc;
-
- for (rc = r->record_cache; rc; rc = rc->next)
+ int i;
+ for (i = 0; i<RECORD_HASH_SIZE; i++)
{
- if (rc->rec.wrbuf_marc)
- wrbuf_free (rc->rec.wrbuf_marc, 1);
- if (rc->rec.wrbuf_iconv)
- wrbuf_free (rc->rec.wrbuf_iconv, 1);
- if (rc->rec.wrbuf_opac)
- wrbuf_free (rc->rec.wrbuf_opac, 1);
+ ZOOM_record_cache rc;
+ for (rc = r->record_hash[i]; rc; rc = rc->next)
+ {
+ if (rc->rec.wrbuf_marc)
+ wrbuf_free (rc->rec.wrbuf_marc, 1);
+ if (rc->rec.wrbuf_iconv)
+ wrbuf_free (rc->rec.wrbuf_iconv, 1);
+ if (rc->rec.wrbuf_opac)
+ wrbuf_free (rc->rec.wrbuf_opac, 1);
+ }
+ r->record_hash[i] = 0;
}
- r->record_cache = 0;
}
ZOOM_API(void)
if (!r)
return ;
- yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%d count=%d",
- r, r, start, count);
+ yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
+ r, r, (long) start, (long) count);
if (count && recs)
force_present = 1;
ZOOM_resultset_retrieve (r, force_present, start, count);
if (c->cs && c->cs->protocol == PROTO_HTTP)
{
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
const char *path = 0;
c->proto = PROTO_HTTP;
ZOOM_options_get(c->options, "implementationName"),
odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
- version = odr_strdup(c->odr_out, "$Revision: 1.57 $");
+ version = odr_strdup(c->odr_out, "$Revision: 1.81 $");
if (strlen(version) > 10) /* check for unexpanded CVS strings */
version[strlen(version)-2] = '\0';
ireq->implementationVersion = odr_prepend(c->odr_out,
if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
{
- char **charsets_addresses = 0;
- char **langs_addresses = 0;
- int charsets_count = 0;
- int langs_count = 0;
-
- if (c->charset)
- nmem_strsplit_blank(c->odr_out->mem, c->charset,
- &charsets_addresses, &charsets_count);
- if (c->lang)
- nmem_strsplit_blank(c->odr_out->mem, c->lang,
- &langs_addresses, &langs_count);
ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
oi_unit->information.externallyDefinedInfo =
- yaz_set_proposal_charneg(c->odr_out,
- (const char **) charsets_addresses,
- charsets_count,
- (const char **) langs_addresses,
- langs_count,
- 1);
+ yaz_set_proposal_charneg_list(c->odr_out, " ",
+ c->charset, c->lang, 1);
}
}
assert (apdu);
return send_APDU (c, apdu);
}
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
{
- char ctype[50];
- Z_SOAP_Handler h[2] = {
- {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec},
- {0, 0, 0}
- };
- ODR o = odr_createmem(ODR_ENCODE);
- int ret;
- Z_SOAP *p = (Z_SOAP*) odr_malloc(o, sizeof(*p));
Z_GDU *gdu;
ZOOM_Event event;
- gdu = z_get_HTTP_Request(c->odr_out);
- gdu->u.HTTP_Request->path = c->path;
+ gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port, c->path);
- if (c->host_port)
+ if (c->sru_mode == zoom_sru_get)
{
- const char *cp0 = strstr(c->host_port, "://");
- const char *cp1 = 0;
- if (cp0)
- cp0 = cp0+3;
- else
- cp0 = c->host_port;
-
- cp1 = strchr(cp0, '/');
- if (!cp1)
- cp1 = cp0+strlen(cp0);
-
- if (cp0 && cp1)
- {
- char *h = (char*) odr_malloc(c->odr_out, cp1 - cp0 + 1);
- memcpy (h, cp0, cp1 - cp0);
- h[cp1-cp0] = '\0';
- z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
- "Host", h);
- }
+ yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
}
-
- strcpy(ctype, "text/xml");
- if (c->charset && strlen(c->charset) < 20)
+ else if (c->sru_mode == zoom_sru_post)
{
- strcat(ctype, "; charset=");
- strcat(ctype, c->charset);
+ yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+ }
+ else if (c->sru_mode == zoom_sru_soap)
+ {
+ yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
}
- z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
- "Content-Type", ctype);
- z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
- "SOAPAction", "\"\"");
- p->which = Z_SOAP_generic;
- p->u.generic = (Z_SOAP_Generic *) odr_malloc(o, sizeof(*p->u.generic));
- p->u.generic->no = 0;
- p->u.generic->ns = 0;
- p->u.generic->p = sr;
- p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
-
- ret = z_soap_codec_enc(o, &p,
- &gdu->u.HTTP_Request->content_buf,
- &gdu->u.HTTP_Request->content_len, h,
- c->charset);
-
if (!z_GDU(c->odr_out, &gdu, 0, 0))
return zoom_complete;
c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
-
- odr_destroy(o);
-
+
event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
ZOOM_connection_put_event (c, event);
odr_reset(c->odr_out);
}
#endif
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
{
int i;
record_cache_lookup (resultset, i + resultset->start);
if (!rec)
break;
+ else
+ {
+ ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+ ZOOM_connection_put_event(c, event);
+ }
}
- if (i == resultset->count)
+ resultset->start += i;
+ resultset->count -= i;
+
+ if (resultset->count == 0)
return zoom_complete;
}
assert(resultset->query);
if (!rec)
{
- ZOOM_resultset_retrieve (r, 1, pos, 1);
+ /*
+ * MIKE: I think force_sync should always be zero, but I don't
+ * want to make this change until I get the go-ahead from
+ * Adam, in case something depends on the old synchronous
+ * behaviour.
+ */
+ int force_sync = 1;
+ if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
+ ZOOM_resultset_retrieve (r, force_sync, pos, 1);
rec = ZOOM_resultset_record_immediate (r, pos);
}
return rec;
}
return 0;
}
- else if (!strcmp (type, "xml") || !strcmp(type, "oai"))
+ else if (!strcmp (type, "xml"))
{
Z_External *r = (Z_External *) npr->u.databaseRecord;
oident *ent = oid_getentbyoid(r->direct_reference);
const char *ret_buf;
int marc_decode_type = YAZ_MARC_MARCXML;
- if (!strcmp(type, "oai"))
- marc_decode_type = YAZ_MARC_OAIMARC;
switch (ent->value)
{
case VAL_SOIF:
return strcmp(v1, v2);
}
+static size_t record_hash(int pos)
+{
+ if (pos < 0)
+ pos = 0;
+ return pos % RECORD_HASH_SIZE;
+}
+
static void record_cache_add (ZOOM_resultset r, Z_NamePlusRecord *npr,
int pos)
{
ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
ZOOM_connection_put_event(r->connection, event);
- for (rc = r->record_cache; rc; rc = rc->next)
+ for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
{
if (pos == rc->pos)
{
rc->schema = 0;
rc->pos = pos;
- rc->next = r->record_cache;
- r->record_cache = rc;
+ rc->next = r->record_hash[record_hash(pos)];
+ r->record_hash[record_hash(pos)] = rc;
}
static ZOOM_record record_cache_lookup (ZOOM_resultset r, int pos)
const char *syntax =
ZOOM_resultset_option_get (r, "preferredRecordSyntax");
- for (rc = r->record_cache; rc; rc = rc->next)
+ for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
{
if (pos == rc->pos)
{
record_cache_add (resultset, p->records[i],
i+ resultset->start);
}
+ resultset->count -= i;
+ resultset->start += i;
/* transfer our response to search_nmem .. we need it later */
nmem_transfer (resultset->odr->mem, nmem);
nmem_destroy (nmem);
yaz_log(log_details, "%p send_present no tasks", c);
return zoom_complete;
}
+
switch (c->tasks->which)
{
case ZOOM_TASK_SEARCH:
record_cache_lookup (resultset, i + resultset->start);
if (!rec)
break;
+ else
+ {
+ ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+ ZOOM_connection_put_event(c, event);
+ }
}
- if (i == resultset->count)
+ resultset->start += i;
+ resultset->count -= i;
+
+ if (resultset->count == 0)
{
yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
return zoom_complete;
apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
req = apdu->u.presentRequest;
- resultset->start += i;
- resultset->count -= i;
-
if (i)
yaz_log(log_details, "%p send_present skip=%d", c, i);
*req->resultSetStartPoint = resultset->start + 1;
*req->numberOfRecordsRequested = resultset->step>0 ?
resultset->step : resultset->count;
+ if (*req->numberOfRecordsRequested + resultset->start > resultset->size)
+ *req->numberOfRecordsRequested = resultset->size - resultset->start;
assert (*req->numberOfRecordsRequested > 0);
-
if (syntax && *syntax)
req->preferredRecordSyntax =
yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
{
ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
char *start;
+ char *freeme = 0;
scan->connection = c;
scan->odr = odr_createmem (ODR_DECODE);
/*
* We need to check the query-type, so we can recognise CQL and
- * compile it into a form that we can use here. The ZOOM_query
- * structure has no explicit `type' member, but inspection of the
- * ZOOM_query_prefix() and ZOOM_query_cql() functions shows how
- * the structure is set up in each case.
+ * CCL and compile them into a form that we can use here. The
+ * ZOOM_query structure has no explicit `type' member, but
+ * inspection of the ZOOM_query_prefix() and ZOOM_query_cql()
+ * functions shows how the structure is set up in each case.
*/
-
if (q->z_query->which == Z_Query_type_1) {
yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'",
c, q, q->query_string);
} else if (q->z_query->which == Z_Query_type_104) {
yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
c, q, q->query_string);
- /* Not yet supported */
- abort();
+ start = freeme = cql2pqf(c, q->query_string);
+ if (start == 0)
+ return 0;
} else {
yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
c, q, q->query_string);
abort();
}
- if ((scan->termListAndStartPoint =
- p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
- start)) != 0)
+ scan->termListAndStartPoint =
+ p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
+ xfree (freeme);
+ if (scan->termListAndStartPoint != 0)
{
ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
task->u.scan.scan = scan;
event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
ZOOM_connection_put_event (c, event);
- return do_write_ex (c, c->tasks->u.package->buf_out,
- c->tasks->u.package->len_out);
+ c->buf_out = c->tasks->u.package->buf_out;
+ c->len_out = c->tasks->u.package->len_out;
+
+ return do_write(c);
}
static zoom_ret send_scan (ZOOM_connection c)
r->descriptor = 0;
r->which = Z_External_single;
- r->u.single_ASN1_type = (Odr_oct *)
- odr_malloc (out, sizeof(*r->u.single_ASN1_type));
- r->u.single_ASN1_type->buf = (unsigned char*)
- odr_malloc (out, illRequest_size);
- r->u.single_ASN1_type->len = illRequest_size;
- r->u.single_ASN1_type->size = illRequest_size;
- memcpy (r->u.single_ASN1_type->buf, illRequest_buf, illRequest_size);
+ r->u.single_ASN1_type =
+ odr_create_Odr_oct(out,
+ (unsigned char *)illRequest_buf,
+ illRequest_size);
}
return r;
}
Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
const char *doc = ZOOM_options_get(p->options, "doc");
+ if (!doc)
+ doc = "";
+
req->taskSpecificParameters = ext;
ext->direct_reference = req->packageType;
ext->descriptor = 0;
ext->indirect_reference = 0;
ext->which = Z_External_octet;
- ext->u.single_ASN1_type = (Odr_oct *)
- odr_malloc (p->odr_out, sizeof(Odr_oct));
-
- if (!doc)
- doc = "";
- ext->u.single_ASN1_type->buf = (unsigned char*)
- odr_strdup(p->odr_out, doc);
- ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len = strlen(doc);
+ ext->u.single_ASN1_type =
+ odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc,
+ strlen(doc));
return apdu;
}
notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
if (recordIdOpaque)
{
- notToKeep->elements[0]->u.opaque = (Odr_oct *)
- odr_malloc (p->odr_out, sizeof(Odr_oct));
- notToKeep->elements[0]->u.opaque->size =
- notToKeep->elements[0]->u.opaque->len = strlen(recordIdOpaque);
- notToKeep->elements[0]->u.opaque->buf = (unsigned char*)
- odr_strdup(p->odr_out, recordIdOpaque);
+ notToKeep->elements[0]->u.opaque =
+ odr_create_Odr_oct(p->odr_out,
+ (const unsigned char *) recordIdOpaque,
+ strlen(recordIdOpaque));
}
else if (recordIdNumber)
{
}
}
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
static void handle_srw_response(ZOOM_connection c,
Z_SRW_searchRetrieveResponse *res)
{
npr->u.databaseRecord->direct_reference =
yaz_oidval_to_z3950oid(c->odr_in, CLASS_RECSYN, VAL_TEXT_XML);
npr->u.databaseRecord->which = Z_External_octet;
+
npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
odr_malloc(c->odr_in, sizeof(Odr_oct));
npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
}
#endif
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
{
int ret = -1;
recv_apdu (c, gdu->u.z3950);
else if (gdu->which == Z_GDU_HTTP_Response)
{
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
handle_http (c, gdu->u.HTTP_Response);
#else
set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
return "Unsupported query type";
case ZOOM_ERROR_INVALID_QUERY:
return "Invalid query";
+ case ZOOM_ERROR_CQL_PARSE:
+ return "CQL parsing error";
+ case ZOOM_ERROR_CQL_TRANSFORM:
+ return "CQL transformation error";
+ case ZOOM_ERROR_CCL_CONFIG:
+ return "CCL configuration error";
+ case ZOOM_ERROR_CCL_PARSE:
+ return "CCL parsing error";
default:
return diagbib1_str (error);
}
int i, r, nfds;
int max_fd = 0;
+ yaz_log(log_details, "ZOOM_event no=%d", no);
for (i = 0; i<no; i++)
{
ZOOM_connection c = cs[i];
for (i = 0; i<no; i++)
{
ZOOM_connection c = cs[i];
- ZOOM_Event event;
- if (c && ZOOM_connection_exec_task (c))
+ if (c)
{
+ ZOOM_Event event;
+ ZOOM_connection_exec_task (c);
if ((event = ZOOM_connection_get_event(c)))
{
ZOOM_Event_destroy (event);
}
return 0;
}
+
+
+/*
+ * Returns an xmalloc()d string containing RPN that corresponds to the
+ * CQL passed in. On error, sets the Connection object's error state
+ * and returns a null pointer.
+ * ### We could cache CQL parser and/or transformer in Connection.
+ */
+static char *cql2pqf(ZOOM_connection c, const char *cql)
+{
+ CQL_parser parser;
+ int error;
+ struct cql_node *node;
+ const char *cqlfile;
+ static cql_transform_t trans;
+ char pqfbuf[512];
+
+ parser = cql_parser_create();
+ if ((error = cql_parser_string(parser, cql)) != 0) {
+ cql_parser_destroy(parser);
+ set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
+ return 0;
+ }
+
+ node = cql_parser_result(parser);
+ /* ### Do not call cql_parser_destroy() yet: it destroys `node'! */
+
+ cqlfile = ZOOM_connection_option_get(c, "cqlfile");
+ if (cqlfile == 0) {
+ cql_parser_destroy(parser);
+ cql_node_destroy(node);
+ set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
+ return 0;
+ }
+
+ if ((trans = cql_transform_open_fname(cqlfile)) == 0) {
+ char buf[512];
+ cql_parser_destroy(parser);
+ cql_node_destroy(node);
+ sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
+ cqlfile, strerror(errno));
+ set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+ return 0;
+ }
+
+ error = cql_transform_buf(trans, node, pqfbuf, sizeof pqfbuf);
+ cql_parser_destroy(parser);
+ cql_node_destroy(node);
+ if (error != 0) {
+ char buf[512];
+ const char *addinfo;
+ error = cql_transform_error(trans, &addinfo);
+ cql_transform_close(trans);
+ sprintf(buf, "%.200s (addinfo=%.200s)", cql_strerror(error), addinfo);
+ set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+ return 0;
+ }
+
+ cql_transform_close(trans);
+ return xstrdup(pqfbuf);
+}
+
/*
* Local variables:
* c-basic-offset: 4