+ return 0;
+}
+
+const char *client_get_query(struct client *cl, const char **type, NMEM nmem)
+{
+ if (cl->pquery)
+ {
+ *type = "pqf";
+ return nmem_strdup(nmem, cl->pquery);
+ }
+ if (cl->cqlquery)
+ {
+ *type = "cql";
+ return nmem_strdup(nmem, cl->cqlquery);
+ }
+ *type = 0;
+ return 0;
+}
+
+int client_start_search(struct client *cl)
+{
+ struct session_database *sdb = client_get_database(cl);
+ struct connection *co = 0;
+ ZOOM_connection link = 0;
+ struct session *se = client_get_session(cl);
+ ZOOM_resultset rs;
+ const char *opt_piggyback = session_setting_oneval(sdb, PZ_PIGGYBACK);
+ const char *opt_queryenc = session_setting_oneval(sdb, PZ_QUERYENCODING);
+ const char *opt_elements = session_setting_oneval(sdb, PZ_ELEMENTS);
+ const char *opt_requestsyn = session_setting_oneval(sdb, PZ_REQUESTSYNTAX);
+ const char *opt_maxrecs = session_setting_oneval(sdb, PZ_MAXRECS);
+ const char *opt_sru = session_setting_oneval(sdb, PZ_SRU);
+ const char *opt_sort = session_setting_oneval(sdb, PZ_SORT);
+ const char *opt_preferred = session_setting_oneval(sdb, PZ_PREFERRED);
+ const char *extra_args = session_setting_oneval(sdb, PZ_EXTRA_ARGS);
+ const char *opt_present_chunk = session_setting_oneval(sdb, PZ_PRESENT_CHUNK);
+ const char *opt_timeout = session_setting_oneval(sdb, PZ_TIMEOUT);
+ ZOOM_query query;
+ char maxrecs_str[24], startrecs_str[24], present_chunk_str[24];
+ struct timeval tval;
+ int present_chunk = 20; // Default chunk size
+ int rc_prep_connection;
+ int operation_timeout = se->service->z3950_operation_timeout;
+
+ cl->diagnostic = 0;
+ cl->record_failures = cl->ingest_failures = cl->filtered = 0;
+
+ yaz_gettimeofday(&tval);
+ tval.tv_sec += 5;
+
+ if (opt_timeout && *opt_timeout)
+ operation_timeout = atoi(opt_timeout);
+
+ if (opt_present_chunk && strcmp(opt_present_chunk,"")) {
+ present_chunk = atoi(opt_present_chunk);
+ yaz_log(YLOG_DEBUG, "Present chunk set to %d", present_chunk);
+ }
+ rc_prep_connection =
+ client_prep_connection(cl, operation_timeout,
+ se->service->z3950_session_timeout,
+ se->service->server->iochan_man,
+ &tval);
+ /* Nothing has changed and we already have a result */
+ if (cl->same_search == 1 && rc_prep_connection == 2)
+ {
+ session_log(se, YLOG_LOG, "%s: reuse result", client_get_id(cl));
+ client_report_facets(cl, cl->resultset);
+ return client_reingest(cl);
+ }
+ else if (!rc_prep_connection)
+ {
+ client_set_diagnostic(cl, 2,
+ ZOOM_diag_str(2),
+ "Cannot create connection");
+ client_set_state_nb(cl, Client_Error);
+ return -1;
+ }
+ co = client_get_connection(cl);
+ assert(cl);
+ link = connection_get_link(co);
+ assert(link);
+
+ session_log(se, YLOG_LOG, "%s: new search", client_get_id(cl));
+
+ client_destroy_xdoc(cl);
+ client_init_xdoc(cl);
+
+ if (extra_args && *extra_args)
+ ZOOM_connection_option_set(link, "extraArgs", extra_args);
+
+ if (opt_preferred) {
+ cl->preferred = atoi(opt_preferred);
+ if (cl->preferred)
+ yaz_log(YLOG_LOG, "Target %s has preferred status: %d",
+ client_get_id(cl), cl->preferred);
+ }
+
+ if (*opt_piggyback)
+ ZOOM_connection_option_set(link, "piggyback", opt_piggyback);
+ else
+ ZOOM_connection_option_set(link, "piggyback", "1");
+ if (*opt_queryenc)
+ ZOOM_connection_option_set(link, "rpnCharset", opt_queryenc);
+ if (*opt_sru && *opt_elements)
+ ZOOM_connection_option_set(link, "schema", opt_elements);
+ else if (*opt_elements)
+ ZOOM_connection_option_set(link, "elementSetName", opt_elements);
+ if (*opt_requestsyn)
+ ZOOM_connection_option_set(link, "preferredRecordSyntax", opt_requestsyn);
+
+ if (opt_maxrecs && *opt_maxrecs)
+ {
+ cl->maxrecs = atoi(opt_maxrecs);
+ }
+
+ /* convert back to string representation used in ZOOM API */
+ sprintf(maxrecs_str, "%d", cl->maxrecs);
+ ZOOM_connection_option_set(link, "count", maxrecs_str);
+
+ /* A present_chunk less than 1 will disable chunking. */
+ if (present_chunk > 0 && cl->maxrecs > present_chunk) {
+ sprintf(present_chunk_str, "%d", present_chunk);
+ ZOOM_connection_option_set(link, "presentChunk", present_chunk_str);
+ yaz_log(YLOG_DEBUG, "Present chunk set to %s", present_chunk_str);
+ }
+ else {
+ ZOOM_connection_option_set(link, "presentChunk", maxrecs_str);
+ yaz_log(YLOG_DEBUG, "Present chunk set to %s (maxrecs)", maxrecs_str);
+ }
+ sprintf(startrecs_str, "%d", cl->startrecs);
+ ZOOM_connection_option_set(link, "start", startrecs_str);
+
+ /* TODO Verify does it break something for CQL targets(non-SOLR) ? */
+ /* facets definition is in PQF */
+ client_set_facets_request(cl, link);
+
+ query = ZOOM_query_create();
+ if (cl->cqlquery)
+ {
+ session_log(se, YLOG_LOG, "%s: Search CQL: %s", client_get_id(cl),
+ cl->cqlquery);
+ ZOOM_query_cql(query, cl->cqlquery);
+ if (*opt_sort)
+ ZOOM_query_sortby(query, opt_sort);
+ }
+ else
+ {
+ session_log(se, YLOG_LOG, "%s: Search PQF: %s", client_get_id(cl),
+ cl->pquery);
+ ZOOM_query_prefix(query, cl->pquery);
+ }
+ if (cl->sort_strategy && cl->sort_criteria) {
+ yaz_log(YLOG_LOG, "Client %s: "
+ "Set ZOOM sort strategy and criteria: %s %s",
+ client_get_id(cl), cl->sort_strategy, cl->sort_criteria);
+ ZOOM_query_sortby2(query, cl->sort_strategy, cl->sort_criteria);
+ }
+
+ yaz_log(YLOG_DEBUG,"Client %s: Starting search", client_get_id(cl));
+ client_set_state(cl, Client_Working);
+ cl->hits = 0;
+ cl->record_offset = 0;
+ rs = ZOOM_connection_search(link, query);
+ ZOOM_query_destroy(query);
+ ZOOM_resultset_destroy(cl->resultset);
+ cl->resultset = rs;
+ connection_continue(co);
+ return 0;
+}
+
+struct client *client_create(const char *id)
+{
+ struct client *cl = xmalloc(sizeof(*cl));
+ cl->maxrecs = 100;
+ cl->startrecs = 0;
+ cl->pquery = 0;
+ cl->cqlquery = 0;
+ cl->addinfo = 0;
+ cl->message = 0;
+ cl->database = 0;
+ cl->connection = 0;
+ cl->session = 0;
+ cl->hits = 0;
+ cl->record_offset = 0;
+ cl->filtered = 0;
+ cl->diagnostic = 0;
+ cl->state = Client_Disconnected;
+ cl->show_raw = 0;
+ cl->resultset = 0;
+ cl->suggestions = 0;
+ cl->mutex = 0;
+ pazpar2_mutex_create(&cl->mutex, "client");
+ cl->preferred = 0;
+ cl->ref_count = 1;
+ cl->facet_limits = 0;
+ cl->sort_strategy = 0;
+ cl->sort_criteria = 0;
+ assert(id);
+ cl->id = xstrdup(id);
+ client_init_xdoc(cl);
+ client_use(1);
+
+ yaz_log(YLOG_DEBUG, "client_create c=%p %s", cl, id);
+ return cl;
+}
+
+void client_lock(struct client *c)
+{
+ yaz_mutex_enter(c->mutex);
+}
+
+void client_unlock(struct client *c)
+{
+ yaz_mutex_leave(c->mutex);
+}
+
+void client_incref(struct client *c)
+{
+ pazpar2_incref(&c->ref_count, c->mutex);
+ yaz_log(YLOG_DEBUG, "client_incref c=%p %s cnt=%d",
+ c, client_get_id(c), c->ref_count);
+}
+
+int client_destroy(struct client *c)
+{
+ if (c)
+ {
+ yaz_log(YLOG_DEBUG, "client_destroy c=%p %s cnt=%d",
+ c, client_get_id(c), c->ref_count);
+ if (!pazpar2_decref(&c->ref_count, c->mutex))
+ {
+ xfree(c->pquery);
+ c->pquery = 0;
+ xfree(c->cqlquery);
+ c->cqlquery = 0;
+ xfree(c->addinfo);
+ c->addinfo = 0;
+ xfree(c->message);
+ c->message = 0;
+ xfree(c->id);
+ xfree(c->sort_strategy);
+ xfree(c->sort_criteria);
+ assert(!c->connection);
+ facet_limits_destroy(c->facet_limits);
+
+ client_destroy_xdoc(c);
+ if (c->resultset)
+ {
+ ZOOM_resultset_destroy(c->resultset);
+ }
+ yaz_mutex_destroy(&c->mutex);
+ xfree(c);
+ client_use(-1);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void client_set_connection(struct client *cl, struct connection *con)
+{
+ if (cl->resultset)
+ ZOOM_resultset_release(cl->resultset);
+ if (con)
+ {
+ assert(cl->connection == 0);
+ cl->connection = con;
+ client_incref(cl);
+ }
+ else
+ {
+ client_lock(cl);
+ cl->connection = con;
+ client_unlock(cl);
+ client_destroy(cl);
+ }
+}
+
+void client_disconnect(struct client *cl)
+{
+ if (cl->state != Client_Idle)
+ client_set_state(cl, Client_Disconnected);
+ client_set_connection(cl, 0);
+}
+
+void client_mark_dead(struct client *cl)
+{
+ if (cl->connection)
+ connection_mark_dead(cl->connection);
+}
+
+void client_stop(struct client *cl)
+{
+ client_lock(cl);
+ if (cl->state == Client_Working || cl->state == Client_Connecting)
+ {
+ yaz_log(YLOG_LOG, "client_stop: %s release", client_get_id(cl));
+ if (cl->connection)
+ {
+ connection_release2(cl->connection);
+ assert(cl->ref_count > 1);
+ cl->ref_count--;
+ cl->connection = 0;
+ }
+ cl->state = Client_Disconnected;
+ }
+ else
+ yaz_log(YLOG_LOG, "client_stop: %s ignore", client_get_id(cl));
+ client_unlock(cl);