1 //
2 // $Id$
3 //
4
5 //
6 // Copyright (c) 2001-2016, Andrew Aksyonoff
7 // Copyright (c) 2008-2016, Sphinx Technologies Inc
8 // All rights reserved
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License. You should
12 // have received a copy of the LGPL license along with this program; if you
13 // did not, you can find it at http://www.gnu.org/
14 //
15
16 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
17 // WARNING
18 // We strongly recommend you to use SphinxQL instead of an API
19 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20
21 #ifdef _WIN32
22 #if _MSC_VER>=1400
23 // VS 2005 and above
24 #define _CRT_SECURE_NO_DEPRECATE 1
25 #define _CRT_NONSTDC_NO_DEPRECATE 1
26 #else
27 // VS 2003 and below
28 #define vsnprintf _vsnprintf
29 #endif
30 #endif
31
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #ifndef _WIN32
38 #include "sphinxclient_config.h"
39 #endif
40
41 #include "sphinxclient.h"
42
43 #if _WIN32
44 // Win-specific headers, calls, libraries
45 #include <io.h>
46 #include <winsock2.h>
47
48 #pragma comment(linker, "/defaultlib:wsock32.lib")
49 #pragma message("Automatically linking with wsock32.lib")
50
51 #define EWOULDBLOCK WSAEWOULDBLOCK
52 #define EINTR WSAEINTR
53
54 #else
55 // UNIX-specific headers and calls
56 #include <unistd.h>
57 #include <netinet/in.h>
58 #include <sys/file.h>
59 #include <sys/socket.h>
60 #include <sys/time.h>
61 #include <sys/wait.h>
62 #include <netdb.h>
63 #include <errno.h>
64 #include <sys/un.h>
65 #include <sys/fcntl.h>
66 #endif
67
68 //////////////////////////////////////////////////////////////////////////
69
70 #define MAX_REQS 32
71 #define CONNECT_TIMEOUT_MSEC 1000
72 #define MAX_PACKET_LEN (8*1024*1024)
73
74 enum
75 {
76 SEARCHD_COMMAND_SEARCH = 0,
77 SEARCHD_COMMAND_EXCERPT = 1,
78 SEARCHD_COMMAND_UPDATE = 2,
79 SEARCHD_COMMAND_KEYWORDS = 3,
80 SEARCHD_COMMAND_PERSIST = 4,
81 SEARCHD_COMMAND_STATUS = 5
82 };
83
84 enum
85 {
86 VER_COMMAND_EXCERPT = 0x103,
87 VER_COMMAND_UPDATE = 0x102,
88 VER_COMMAND_KEYWORDS = 0x100,
89 VER_COMMAND_STATUS = 0x101
90 };
91
92 //////////////////////////////////////////////////////////////////////////
93
94 struct st_filter
95 {
96 const char * attr;
97 int filter_type;
98 int num_values;
99 const sphinx_int64_t * values;
100 sphinx_int64_t umin;
101 sphinx_int64_t umax;
102 float fmin;
103 float fmax;
104 int exclude;
105 const char * svalue;
106 };
107
108
109 union un_attr_value
110 {
111 sphinx_int64_t int_value;
112 float float_value;
113 unsigned int * mva_value;
114 const char * string;
115 };
116
117
118 struct st_override
119 {
120 const char * attr;
121 const sphinx_uint64_t * docids;
122 int num_values;
123 const unsigned int * uint_values;
124 };
125
126
127 struct st_sphinx_client
128 {
129 unsigned short ver_search; ///< compatibility mode
130 sphinx_bool copy_args; ///< whether to create a copy of each passed argument
131 void * head_alloc; ///< head of client-owned allocations list
132
133 const char * error; ///< last error
134 const char * warning; ///< last warning
135 char local_error_buf[256]; ///< buffer to store 'local' error messages (eg. connect() error)
136
137 const char * host;
138 int port;
139 float timeout;
140 int offset;
141 int limit;
142 int mode;
143 int num_weights;
144 const int * weights;
145 int sort;
146 const char * sortby;
147 sphinx_uint64_t minid;
148 sphinx_uint64_t maxid;
149 const char * group_by;
150 int group_func;
151 const char * group_sort;
152 const char * group_distinct;
153 int max_matches;
154 int cutoff;
155 int retry_count;
156 int retry_delay;
157 const char * geoanchor_attr_lat;
158 const char * geoanchor_attr_long;
159 float geoanchor_lat;
160 float geoanchor_long;
161 int num_filters;
162 int max_filters;
163 struct st_filter * filters;
164 int num_index_weights;
165 const char ** index_weights_names;
166 const int * index_weights_values;
167 int ranker;
168 const char * rankexpr;
169 int max_query_time;
170 int num_field_weights;
171 const char ** field_weights_names;
172 const int * field_weights_values;
173 int num_overrides;
174 int max_overrides;
175 struct st_override * overrides;
176 const char * select_list;
177 int query_flags;
178 int predicted_time;
179 const char * outer_orderby;
180 int outer_offset;
181 int outer_limit;
182 sphinx_bool has_outer;
183
184
185 int num_reqs;
186 int req_lens [ MAX_REQS ];
187 char * reqs [ MAX_REQS ];
188
189 int response_len;
190 char * response_buf; ///< where the buffer begins (might also contain heading warning)
191 char * response_start; ///< where the data to parse starts
192
193 int num_results;
194 sphinx_result results [ MAX_REQS ];
195
196 int sock; ///< open socket for pconns; -1 if none
197 sphinx_bool persist;
198 };
199
200 //////////////////////////////////////////////////////////////////////////
201
202 static void * chain ( sphinx_client * client, const void * ptr, size_t len );
203 static const char * strchain ( sphinx_client * client, const char * s );
204 static void unchain ( sphinx_client * client, const void * ptr );
205 static void unchain_all ( sphinx_client * client );
206
sphinx_create(sphinx_bool copy_args)207 sphinx_client * sphinx_create ( sphinx_bool copy_args )
208 {
209 sphinx_client * client;
210 int i;
211
212 // allocate
213 client = malloc ( sizeof(sphinx_client) );
214 if ( !client )
215 return NULL;
216
217 // initialize defaults and return
218 client->ver_search = 0x11E; // 0x113 for 0.9.8, 0x116 for 0.9.9rc2
219 client->copy_args = copy_args;
220 client->head_alloc = NULL;
221
222 client->error = NULL;
223 client->warning = NULL;
224 client->local_error_buf[0] = '\0';
225
226 client->host = strchain ( client, "localhost" );
227 client->port = 9312;
228 client->timeout = 0.0f;
229 client->offset = 0;
230 client->limit = 20;
231 client->mode = SPH_MATCH_EXTENDED2;
232 client->num_weights = 0;
233 client->weights = NULL;
234 client->sort = SPH_SORT_RELEVANCE;
235 client->sortby = NULL;
236 client->minid = 0;
237 client->maxid = 0;
238 client->group_by = NULL;
239 client->group_func = SPH_GROUPBY_ATTR;
240 client->group_sort = strchain ( client, "@groupby desc" );
241 client->group_distinct = NULL;
242 client->max_matches = 1000;
243 client->cutoff = 0;
244 client->retry_count = 0;
245 client->retry_delay = 0;
246 client->geoanchor_attr_lat = NULL;
247 client->geoanchor_attr_long = NULL;
248 client->geoanchor_lat = 0.0f;
249 client->geoanchor_long = 0.0f;
250 client->num_filters = 0;
251 client->max_filters = 0;
252 client->filters = NULL;
253 client->num_index_weights = 0;
254 client->index_weights_names = NULL;
255 client->index_weights_values = NULL;
256 client->ranker = SPH_RANK_DEFAULT;
257 client->rankexpr = NULL;
258 client->max_query_time = 0;
259 client->num_field_weights = 0;
260 client->field_weights_names = NULL;
261 client->field_weights_values = NULL;
262 client->num_overrides = 0;
263 client->max_overrides = 0;
264 client->overrides = NULL;
265 client->select_list = NULL;
266 client->query_flags = 1<<6;
267 client->predicted_time = 0;
268 client->outer_orderby = NULL;
269 client->outer_offset = 0;
270 client->outer_limit = 0;
271 client->has_outer = SPH_FALSE;
272
273 client->num_reqs = 0;
274 client->response_len = 0;
275 client->response_buf = NULL;
276 client->num_results = 0;
277
278 for ( i=0; i<MAX_REQS; i++ )
279 {
280 client->results[i].values_pool = NULL;
281 client->results[i].words = NULL;
282 client->results[i].fields = NULL;
283 client->results[i].attr_names = NULL;
284 client->results[i].attr_types = NULL;
285 }
286
287 client->sock = -1;
288 client->persist = SPH_FALSE;
289 return client;
290 }
291
292
sphinx_free_results(sphinx_client * client)293 static void sphinx_free_results ( sphinx_client * client )
294 {
295 int i;
296 for ( i=0; i<client->num_results; i++ )
297 {
298 free ( client->results[i].values_pool );
299 free ( client->results[i].words );
300 free ( client->results[i].fields );
301 free ( client->results[i].attr_names );
302 free ( client->results[i].attr_types );
303
304 client->results[i].values_pool = NULL;
305 client->results[i].words = NULL;
306 client->results[i].fields = NULL;
307 client->results[i].attr_names = NULL;
308 client->results[i].attr_types = NULL;
309 }
310 client->num_results = 0;
311 }
312
313
314 void sock_close ( int sock );
315
316
317 #define safe_free(_ptr) \
318 if ( _ptr ) \
319 { \
320 free ( _ptr ); \
321 _ptr = NULL; \
322 }
323
324
sphinx_cleanup(sphinx_client * client)325 void sphinx_cleanup ( sphinx_client * client )
326 {
327 int i;
328 if ( !client )
329 return;
330
331 for ( i=0; i<client->num_reqs; i++ )
332 safe_free ( client->reqs[i] );
333 client->num_reqs = 0;
334
335 sphinx_free_results ( client );
336 client->num_results = 0;
337
338 safe_free ( client->response_buf );
339 }
340
341
sphinx_destroy(sphinx_client * client)342 void sphinx_destroy ( sphinx_client * client )
343 {
344 int i;
345 if ( !client )
346 return;
347
348 for ( i=0; i<client->num_reqs; i++ )
349 safe_free ( client->reqs[i] );
350
351 sphinx_free_results ( client );
352 unchain_all ( client );
353 safe_free ( client->filters );
354 safe_free ( client->response_buf );
355
356 if ( client->sock>=0 )
357 sock_close ( client->sock );
358
359 free ( client );
360 }
361
362
sphinx_error(sphinx_client * client)363 const char * sphinx_error ( sphinx_client * client )
364 {
365 return client->error ? client->error : "";
366 }
367
368
sphinx_warning(sphinx_client * client)369 const char * sphinx_warning ( sphinx_client * client )
370 {
371 return client->warning ? client->warning : "";
372 }
373
374
set_error(sphinx_client * client,const char * template,...)375 static void set_error ( sphinx_client * client, const char * template, ... )
376 {
377 va_list ap;
378
379 if ( !client )
380 return;
381
382 va_start ( ap, template );
383 vsnprintf ( client->local_error_buf, sizeof(client->local_error_buf), template, ap );
384 va_end ( ap );
385
386 client->error = client->local_error_buf;
387 client->warning = NULL;
388 }
389
390
391 //////////////////////////////////////////////////////////////////////////
392
393 struct st_memblock
394 {
395 struct st_memblock * prev;
396 struct st_memblock * next;
397 };
398
399
chain(sphinx_client * client,const void * ptr,size_t len)400 static void * chain ( sphinx_client * client, const void * ptr, size_t len )
401 {
402 struct st_memblock * entry;
403
404 if ( !client->copy_args || !ptr )
405 return (void*) ptr;
406
407 entry = malloc ( sizeof(struct st_memblock) + len );
408 if ( !entry )
409 {
410 set_error ( client, "malloc() failed (bytes=%d)", sizeof(struct st_memblock) + len );
411 return NULL;
412 }
413
414 entry->prev = NULL;
415 entry->next = client->head_alloc;
416 if ( entry->next )
417 entry->next->prev = entry;
418 client->head_alloc = entry;
419
420 entry++;
421 memcpy ( entry, ptr, len );
422 return entry;
423 }
424
425
strchain(sphinx_client * client,const char * s)426 static const char * strchain ( sphinx_client * client, const char * s )
427 {
428 return s ? chain ( client, s, 1+strlen(s) ) : NULL;
429 }
430
431
unchain(sphinx_client * client,const void * ptr)432 static void unchain ( sphinx_client * client, const void * ptr )
433 {
434 struct st_memblock * entry;
435
436 if ( !client->copy_args || !ptr )
437 return;
438
439 entry = (struct st_memblock*) ptr;
440 entry--;
441
442 if ( entry->prev )
443 entry->prev->next = entry->next;
444 else
445 client->head_alloc = entry->next;
446
447 if ( entry->next )
448 entry->next->prev = entry->prev;
449
450 free ( entry );
451 }
452
453
unchain_all(sphinx_client * client)454 static void unchain_all ( sphinx_client * client )
455 {
456 struct st_memblock *to_free, *cur;
457
458 if ( !client || !client->copy_args )
459 return;
460
461 cur = client->head_alloc;
462 while ( cur )
463 {
464 to_free = cur;
465 cur = cur->next;
466 free ( to_free );
467 }
468 client->head_alloc = NULL;
469 }
470
471 //////////////////////////////////////////////////////////////////////////
472
sphinx_set_server(sphinx_client * client,const char * host,int port)473 sphinx_bool sphinx_set_server ( sphinx_client * client, const char * host, int port )
474 {
475 if ( !client || !host || !host[0] )
476 {
477 set_error ( client, "invalid arguments (host must not be empty)" );
478 return SPH_FALSE;
479 }
480
481 unchain ( client, client->host );
482 client->host = strchain ( client, host );
483 client->port = port;
484 return SPH_TRUE;
485 }
486
487
sphinx_set_connect_timeout(sphinx_client * client,float seconds)488 sphinx_bool sphinx_set_connect_timeout ( sphinx_client * client, float seconds )
489 {
490 if ( !client )
491 return SPH_FALSE;
492
493 client->timeout = seconds;
494 return SPH_TRUE;
495 }
496
497
sphinx_set_limits(sphinx_client * client,int offset,int limit,int max_matches,int cutoff)498 sphinx_bool sphinx_set_limits ( sphinx_client * client, int offset, int limit, int max_matches, int cutoff )
499 {
500 if ( !client || offset<0 || limit<=0 || max_matches<0 || cutoff<0 )
501 {
502 if ( offset<0 ) set_error ( client, "invalid arguments (offset must be >= 0)" );
503 else if ( limit<=0 ) set_error ( client, "invalid arguments (limit must be > 0)" );
504 else if ( max_matches<0 ) set_error ( client, "invalid arguments (max_matches must be >= 0)" );
505 else if ( cutoff<0 ) set_error ( client, "invalid arguments (cutoff must be >= 0)" );
506 else set_error ( client, "invalid arguments" );
507 return SPH_FALSE;
508 }
509
510 client->offset = offset;
511 client->limit = limit;
512 if ( max_matches>0 )
513 client->max_matches = max_matches;
514 if ( cutoff>=0 )
515 client->cutoff = cutoff;
516 return SPH_TRUE;
517 }
518
519
sphinx_set_max_query_time(sphinx_client * client,int max_query_time)520 sphinx_bool sphinx_set_max_query_time ( sphinx_client * client, int max_query_time )
521 {
522 if ( !client || max_query_time<=0 )
523 {
524 set_error ( client, "invalid arguments (max_query_time must be > 0)" );
525 return SPH_FALSE;
526 }
527
528 client->max_query_time = max_query_time;
529 return SPH_TRUE;
530 }
531
532 // DEPRECATED
sphinx_set_match_mode(sphinx_client * client,int mode)533 sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode )
534 {
535 fprintf ( stderr, "DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API\n" );
536
537 if ( !client || mode<SPH_MATCH_ALL || mode>SPH_MATCH_EXTENDED2 ) // FIXME?
538 {
539 set_error ( client, "invalid arguments (matching mode %d out of bounds)", mode );
540 return SPH_FALSE;
541 }
542
543 client->mode = mode;
544 return SPH_TRUE;
545 }
546
547
sphinx_set_ranking_mode(sphinx_client * client,int ranker,const char * rankexpr)548 sphinx_bool sphinx_set_ranking_mode ( sphinx_client * client, int ranker, const char * rankexpr )
549 {
550 if ( !client || ranker<SPH_RANK_PROXIMITY_BM25 || ranker>=SPH_RANK_TOTAL ) // FIXME?
551 {
552 set_error ( client, "invalid arguments (ranking mode %d out of bounds)", ranker );
553 return SPH_FALSE;
554 }
555
556 client->ranker = ranker;
557 client->rankexpr = strchain ( client, rankexpr );
558 return SPH_TRUE;
559 }
560
561
sphinx_set_sort_mode(sphinx_client * client,int mode,const char * sortby)562 sphinx_bool sphinx_set_sort_mode ( sphinx_client * client, int mode, const char * sortby )
563 {
564 if ( !client
565 || mode<SPH_SORT_RELEVANCE
566 || mode>SPH_SORT_EXPR
567 || ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) ) )
568 {
569 if ( mode<SPH_SORT_RELEVANCE || mode>SPH_SORT_EXPR )
570 {
571 set_error ( client, "invalid arguments (sorting mode %d out of bounds)", mode );
572
573 } else if ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) )
574 {
575 set_error ( client, "invalid arguments (sortby clause must not be empty)", mode );
576
577 } else
578 {
579 set_error ( client, "invalid arguments", mode );
580 }
581 return SPH_FALSE;
582 }
583
584 client->sort = mode;
585 unchain ( client, client->sortby );
586 client->sortby = strchain ( client, sortby );
587 return SPH_TRUE;
588 }
589
590
sphinx_set_field_weights(sphinx_client * client,int num_weights,const char ** field_names,const int * field_weights)591 sphinx_bool sphinx_set_field_weights ( sphinx_client * client, int num_weights, const char ** field_names, const int * field_weights )
592 {
593 int i;
594
595 if ( !client || num_weights<=0 || !field_names || !field_weights )
596 {
597 if ( num_weights<=0 ) set_error ( client, "invalid arguments (num_weights must be > 0)" );
598 else if ( !field_names ) set_error ( client, "invalid arguments (field_names must not be NULL)" );
599 else if ( !field_weights ) set_error ( client, "invalid arguments (field_weights must not be NULL)" );
600 else set_error ( client, "invalid arguments" );
601 return SPH_FALSE;
602 }
603
604 if ( client->copy_args )
605 {
606 for ( i=0; i<client->num_field_weights; i++ )
607 unchain ( client, client->field_weights_names[i] );
608 unchain ( client, client->field_weights_names );
609 unchain ( client, client->field_weights_values );
610
611 field_names = chain ( client, field_names, num_weights*sizeof(const char*) );
612 for ( i=0; i<num_weights; i++ )
613 field_names[i] = strchain ( client, field_names[i] );
614 field_weights = chain ( client, field_weights, num_weights*sizeof(int) );
615 }
616
617 client->num_field_weights = num_weights;
618 client->field_weights_names = field_names;
619 client->field_weights_values = field_weights;
620 return SPH_TRUE;
621 }
622
623
sphinx_set_index_weights(sphinx_client * client,int num_weights,const char ** index_names,const int * index_weights)624 sphinx_bool sphinx_set_index_weights ( sphinx_client * client, int num_weights, const char ** index_names, const int * index_weights )
625 {
626 int i;
627
628 if ( !client || num_weights<=0 || !index_names || !index_weights )
629 {
630 if ( num_weights<=0 ) set_error ( client, "invalid arguments (num_weights must be > 0)" );
631 else if ( !index_names ) set_error ( client, "invalid arguments (index_names must not be NULL)" );
632 else if ( !index_weights ) set_error ( client, "invalid arguments (index_weights must not be NULL)" );
633 else set_error ( client, "invalid arguments" );
634 return SPH_FALSE;
635 }
636
637 if ( client->copy_args )
638 {
639 for ( i=0; i<client->num_index_weights; i++ )
640 unchain ( client, client->index_weights_names[i] );
641 unchain ( client, client->index_weights_names );
642 unchain ( client, client->index_weights_values );
643
644 index_names = chain ( client, index_names, num_weights*sizeof(const char*) );
645 for ( i=0; i<num_weights; i++ )
646 index_names[i] = strchain ( client, index_names[i] );
647 index_weights = chain ( client, index_weights, num_weights*sizeof(int) );
648 }
649
650 client->num_index_weights = num_weights;
651 client->index_weights_names = index_names;
652 client->index_weights_values = index_weights;
653 return SPH_TRUE;
654 }
655
656
sphinx_set_id_range(sphinx_client * client,sphinx_uint64_t minid,sphinx_uint64_t maxid)657 sphinx_bool sphinx_set_id_range ( sphinx_client * client, sphinx_uint64_t minid, sphinx_uint64_t maxid )
658 {
659 if ( !client || minid>maxid )
660 {
661 set_error ( client, "invalid arguments (minid must be <= maxid)" );
662 return SPH_FALSE;
663 }
664
665 client->minid = minid;
666 client->maxid = maxid;
667 return SPH_TRUE;
668 }
669
670
sphinx_add_filter_entry(sphinx_client * client)671 static struct st_filter * sphinx_add_filter_entry ( sphinx_client * client )
672 {
673 int len;
674 if ( client->num_filters>=client->max_filters )
675 {
676 client->max_filters = ( client->max_filters<=0 ) ? client->num_filters + 8 : 2*client->max_filters;
677 len = client->max_filters*sizeof(struct st_filter);
678 client->filters = realloc ( client->filters, len );
679 if ( !client->filters )
680 {
681 set_error ( client, "realloc() failed (bytes=%d)", len );
682 return NULL;
683 }
684 }
685 return client->filters + client->num_filters++;
686 }
687
688
sphinx_add_filter(sphinx_client * client,const char * attr,int num_values,const sphinx_int64_t * values,sphinx_bool exclude)689 sphinx_bool sphinx_add_filter ( sphinx_client * client, const char * attr, int num_values, const sphinx_int64_t * values, sphinx_bool exclude )
690 {
691 struct st_filter * filter;
692
693 if ( !client || !attr || num_values<=0 || !values )
694 {
695 if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
696 else if ( num_values<=0 ) set_error ( client, "invalid arguments (num_values must be > 0)" );
697 else if ( !values ) set_error ( client, "invalid arguments (values must not be NULL)" );
698 else set_error ( client, "invalid arguments" );
699 return SPH_FALSE;
700 }
701
702 filter = sphinx_add_filter_entry ( client );
703 if ( !filter )
704 return SPH_FALSE;
705
706 filter->attr = strchain ( client, attr );
707 filter->filter_type = SPH_FILTER_VALUES;
708 filter->num_values = num_values;
709 filter->values = chain ( client, values, num_values*sizeof(sphinx_int64_t) );
710 filter->exclude = exclude;
711 return SPH_TRUE;
712 }
713
714
sphinx_add_filter_string(sphinx_client * client,const char * attr,const char * value,sphinx_bool exclude)715 sphinx_bool sphinx_add_filter_string ( sphinx_client * client, const char * attr, const char * value, sphinx_bool exclude )
716 {
717 struct st_filter * filter;
718
719 if ( !client || !attr || !value )
720 {
721 if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
722 else if ( !value ) set_error ( client, "invalid arguments (value must not be empty)" );
723 else set_error ( client, "invalid arguments" );
724 return SPH_FALSE;
725 }
726
727 filter = sphinx_add_filter_entry ( client );
728 if ( !filter )
729 return SPH_FALSE;
730
731 filter->attr = strchain ( client, attr );
732 filter->filter_type = SPH_FILTER_STRING;
733 filter->svalue = strchain ( client, value );
734 filter->exclude = exclude;
735 return SPH_TRUE;
736 }
737
738
sphinx_add_filter_range(sphinx_client * client,const char * attr,sphinx_int64_t umin,sphinx_int64_t umax,sphinx_bool exclude)739 sphinx_bool sphinx_add_filter_range ( sphinx_client * client, const char * attr, sphinx_int64_t umin, sphinx_int64_t umax, sphinx_bool exclude )
740 {
741 struct st_filter * filter;
742
743 if ( !client || !attr || umin>umax )
744 {
745 if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
746 else if ( umin>umax ) set_error ( client, "invalid arguments (umin must be <= umax)" );
747 else set_error ( client, "invalid arguments" );
748 return SPH_FALSE;
749 }
750
751 filter = sphinx_add_filter_entry ( client );
752 if ( !filter )
753 return SPH_FALSE;
754
755 filter->attr = strchain ( client, attr );
756 filter->filter_type = SPH_FILTER_RANGE;
757 filter->umin = umin;
758 filter->umax = umax;
759 filter->exclude = exclude;
760 return SPH_TRUE;
761 }
762
763
sphinx_add_filter_float_range(sphinx_client * client,const char * attr,float fmin,float fmax,sphinx_bool exclude)764 sphinx_bool sphinx_add_filter_float_range ( sphinx_client * client, const char * attr, float fmin, float fmax, sphinx_bool exclude )
765 {
766 struct st_filter * filter;
767
768 if ( !client || !attr || fmin>fmax )
769 {
770 if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
771 else if ( fmin>fmax ) set_error ( client, "invalid arguments (fmin must be <= fmax)" );
772 else set_error ( client, "invalid arguments" );
773 return SPH_FALSE;
774 }
775
776 filter = sphinx_add_filter_entry ( client );
777 if ( !filter )
778 return SPH_FALSE;
779
780 filter->attr = strchain ( client, attr );
781 filter->filter_type = SPH_FILTER_FLOATRANGE;
782 filter->fmin = fmin;
783 filter->fmax = fmax;
784 filter->exclude = exclude;
785 return SPH_TRUE;
786 }
787
788
sphinx_set_geoanchor(sphinx_client * client,const char * attr_latitude,const char * attr_longitude,float latitude,float longitude)789 sphinx_bool sphinx_set_geoanchor ( sphinx_client * client, const char * attr_latitude, const char * attr_longitude, float latitude, float longitude )
790 {
791 if ( !client || !attr_latitude || !attr_latitude[0] || !attr_longitude || !attr_longitude[0] )
792 {
793 if ( !attr_latitude || !attr_latitude[0] ) set_error ( client, "invalid arguments (attr_latitude must not be empty)" );
794 else if ( !attr_longitude || !attr_longitude[0] ) set_error ( client, "invalid arguments (attr_longitude must not be empty)" );
795 else set_error ( client, "invalid arguments" );
796 return SPH_FALSE;
797 }
798
799 unchain ( client, client->geoanchor_attr_lat );
800 unchain ( client, client->geoanchor_attr_long );
801 client->geoanchor_attr_lat = strchain ( client, attr_latitude );
802 client->geoanchor_attr_long = strchain ( client, attr_longitude );
803 client->geoanchor_lat = latitude;
804 client->geoanchor_long = longitude;
805 return SPH_TRUE;
806 }
807
808
sphinx_set_groupby(sphinx_client * client,const char * attr,int groupby_func,const char * group_sort)809 sphinx_bool sphinx_set_groupby ( sphinx_client * client, const char * attr, int groupby_func, const char * group_sort )
810 {
811 if ( !client || !attr || groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR )
812 {
813 if ( !attr )
814 {
815 set_error ( client, "invalid arguments (attr must not be empty)" );
816
817 } else if ( groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR )
818 {
819 set_error ( client, "invalid arguments (groupby_func %d out of bounds)", groupby_func );
820
821 } else
822 {
823 set_error ( client, "invalid arguments" );
824 }
825 return SPH_FALSE;
826 }
827
828 unchain ( client, client->group_by );
829 unchain ( client, client->group_sort );
830 client->group_by = strchain ( client, attr );
831 client->group_func = groupby_func;
832 client->group_sort = strchain ( client, group_sort ? group_sort : "@groupby desc" );
833 return SPH_TRUE;
834 }
835
836
sphinx_set_groupby_distinct(sphinx_client * client,const char * attr)837 sphinx_bool sphinx_set_groupby_distinct ( sphinx_client * client, const char * attr )
838 {
839 if ( !client || !attr )
840 {
841 if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
842 else set_error ( client, "invalid arguments" );
843 return SPH_FALSE;
844 }
845
846 unchain ( client, client->group_distinct );
847 client->group_distinct = strchain ( client, attr );
848 return SPH_TRUE;
849 }
850
851
sphinx_set_retries(sphinx_client * client,int count,int delay)852 sphinx_bool sphinx_set_retries ( sphinx_client * client, int count, int delay )
853 {
854 if ( !client || count<0 || count>1000 || delay<0 || delay>100000 )
855 {
856 if ( count<0 || count>1000 ) set_error ( client, "invalid arguments (count value %d out of bounds)", count );
857 else if ( delay<0 || delay>100000 ) set_error ( client, "invalid arguments (delay value %d out of bounds)", delay );
858 else set_error ( client, "invalid arguments" );
859 return SPH_FALSE;
860 }
861
862 client->retry_count = count;
863 client->retry_delay = delay;
864 return SPH_TRUE;
865 }
866
867 // DEPRECATED
sphinx_add_override(sphinx_client * client,const char * attr,const sphinx_uint64_t * docids,int num_values,const unsigned int * values)868 sphinx_bool sphinx_add_override ( sphinx_client * client, const char * attr, const sphinx_uint64_t * docids, int num_values, const unsigned int * values )
869 {
870 struct st_override * p;
871
872 fprintf ( stderr, "DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.\n" );
873
874 if ( !client )
875 return SPH_FALSE;
876
877 if ( client->ver_search<0x115 )
878 {
879 set_error ( client, "sphinx_add_override not supported by chosen protocol version" );
880 return SPH_FALSE;
881 }
882
883 if ( client->num_overrides>=client->max_overrides )
884 {
885 client->max_overrides = ( client->max_overrides<=0 ) ? 8 : 2*client->max_overrides;
886 client->overrides = realloc ( client->overrides, client->max_overrides *sizeof(struct st_override) );
887 }
888
889 p = client->overrides + client->num_overrides;
890 client->num_overrides++;
891
892 p->attr = strchain ( client, attr );
893 p->docids = chain ( client, docids, sizeof(sphinx_uint64_t)*num_values );
894 p->num_values = num_values;
895 p->uint_values = chain ( client, values, sizeof(unsigned int)*num_values );
896 return SPH_TRUE;
897 }
898
899
sphinx_set_select(sphinx_client * client,const char * select_list)900 sphinx_bool sphinx_set_select ( sphinx_client * client, const char * select_list )
901 {
902 if ( !client )
903 return SPH_FALSE;
904
905 if ( client->ver_search<0x116 )
906 {
907 set_error ( client, "sphinx_set_select not supported by chosen protocol version" );
908 return SPH_FALSE;
909 }
910
911 unchain ( client, client->select_list );
912 client->select_list = strchain ( client, select_list );
913 return SPH_TRUE;
914 }
915
916
set_bit(int * flags,int bit,sphinx_bool enable)917 void set_bit ( int * flags, int bit, sphinx_bool enable )
918 {
919 int bit_mask = 1<<bit;
920 if ( enable )
921 *flags |= bit_mask;
922 else
923 *flags &= ( 0xff ^ bit_mask );
924 }
925
926
sphinx_set_query_flags(sphinx_client * client,const char * flag_name,sphinx_bool enabled,int max_predicted_msec)927 sphinx_bool sphinx_set_query_flags ( sphinx_client * client, const char * flag_name, sphinx_bool enabled, int max_predicted_msec )
928 {
929 if ( !client )
930 return SPH_FALSE;
931
932 if ( client->ver_search<0x11B )
933 {
934 set_error ( client, "sphinx_set_query_flags not supported by chosen protocol version" );
935 return SPH_FALSE;
936 }
937
938 if ( !flag_name || !flag_name[0] )
939 {
940 set_error ( client, "invalid arguments (empty flag_name)" );
941 return SPH_FALSE;
942 }
943 if ( strcmp ( flag_name, "max_predicted_time")==0 && max_predicted_msec<0 )
944 {
945 set_error ( client, "invalid arguments (max_predicted_time must be >0)" );
946 return SPH_FALSE;
947 }
948
949 if ( strcmp ( flag_name, "reverse_scan")==0 )
950 {
951 set_bit ( &client->query_flags, 0, enabled );
952 } else if ( strcmp ( flag_name, "sort_method_kbuffer")==0 )
953 {
954 set_bit ( &client->query_flags, 1, enabled );
955 } else if ( strcmp ( flag_name, "max_predicted_time")==0 )
956 {
957 client->predicted_time = max_predicted_msec;
958 set_bit ( &client->query_flags, 2, max_predicted_msec>0 );
959 } else if ( strcmp ( flag_name, "boolean_simplify")==0 )
960 {
961 set_bit ( &client->query_flags, 3, enabled );
962 } else if ( strcmp ( flag_name, "idf_plain")==0 )
963 {
964 set_bit ( &client->query_flags, 4, enabled );
965 } else if ( strcmp ( flag_name, "global_idf")==0 )
966 {
967 set_bit ( &client->query_flags, 5, enabled );
968 } else if ( strcmp ( flag_name, "tfidf_normalized")==0 )
969 {
970 set_bit ( &client->query_flags, 6, enabled );
971 } else
972 {
973 set_error ( client, "invalid arguments (unknown flag_name)" );
974 return SPH_FALSE;
975 }
976
977 return SPH_TRUE;
978 }
979
980
sphinx_reset_query_flags(sphinx_client * client)981 void sphinx_reset_query_flags ( sphinx_client * client )
982 {
983 client->query_flags = 1<<6;
984 client->predicted_time = 0;
985 }
986
987
sphinx_set_outer_select(sphinx_client * client,const char * orderby,int offset,int limit)988 sphinx_bool sphinx_set_outer_select ( sphinx_client * client, const char * orderby, int offset, int limit )
989 {
990 if ( !client )
991 return SPH_FALSE;
992
993 if ( client->ver_search<0x11D )
994 {
995 set_error ( client, "sphinx_set_outer not supported by chosen protocol version" );
996 return SPH_FALSE;
997 }
998
999 unchain ( client, client->outer_orderby );
1000 client->outer_orderby = strchain ( client, orderby );
1001 client->outer_offset = offset;
1002 client->outer_limit = limit;
1003 client->has_outer = SPH_TRUE;
1004 return SPH_TRUE;
1005 }
1006
1007
sphinx_reset_outer_select(sphinx_client * client)1008 void sphinx_reset_outer_select ( sphinx_client * client )
1009 {
1010 if ( !client )
1011 return;
1012
1013 unchain ( client, client->outer_orderby );
1014 client->outer_orderby = NULL;
1015 client->outer_offset = 0;
1016 client->outer_limit = 0;
1017 client->has_outer = SPH_FALSE;
1018 }
1019
1020
sphinx_reset_filters(sphinx_client * client)1021 void sphinx_reset_filters ( sphinx_client * client )
1022 {
1023 int i;
1024
1025 if ( !client )
1026 return;
1027
1028 if ( client->filters )
1029 {
1030 if ( client->copy_args )
1031 for ( i=0; i<client->num_filters; i++ )
1032 {
1033 unchain ( client, client->filters[i].attr );
1034 if ( client->filters[i].filter_type==SPH_FILTER_VALUES )
1035 unchain ( client, client->filters[i].values );
1036 if ( client->filters[i].filter_type==SPH_FILTER_STRING )
1037 unchain ( client, client->filters[i].svalue );
1038 }
1039
1040 free ( client->filters );
1041 client->filters = NULL;
1042 }
1043 client->num_filters = client->max_filters = 0;
1044 }
1045
1046
sphinx_reset_groupby(sphinx_client * client)1047 void sphinx_reset_groupby ( sphinx_client * client )
1048 {
1049 if ( !client )
1050 return;
1051
1052 unchain ( client, client->group_by );
1053 unchain ( client, client->group_sort );
1054 client->group_by = NULL;
1055 client->group_func = SPH_GROUPBY_ATTR;
1056 client->group_sort = strchain ( client, "@groupby desc" );
1057 client->group_distinct = NULL;
1058 }
1059
1060 //////////////////////////////////////////////////////////////////////////
1061
sphinx_dismiss_requests(sphinx_client * client)1062 static int sphinx_dismiss_requests ( sphinx_client * client )
1063 {
1064 int nreqs = client->num_reqs, i;
1065 for ( i=0; i<client->num_reqs; i++ )
1066 free ( client->reqs[i] );
1067 client->num_reqs = 0;
1068 return nreqs;
1069 }
1070
1071
sphinx_query(sphinx_client * client,const char * query,const char * index_list,const char * comment)1072 sphinx_result * sphinx_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
1073 {
1074 sphinx_result * res;
1075
1076 if ( !client )
1077 return NULL;
1078
1079 if ( client->num_reqs!=0 )
1080 {
1081 set_error ( client, "sphinx_query() must not be called after sphinx_add_query()" );
1082 return NULL;
1083 }
1084
1085 if ( sphinx_add_query ( client, query, index_list, comment )!=0 )
1086 return NULL;
1087
1088 res = sphinx_run_queries ( client ); // just a shortcut for client->results[0]
1089 sphinx_dismiss_requests ( client ); // sphinx_query() is fire and forget; dismiss request in all cases
1090 if ( !res )
1091 return NULL;
1092
1093 client->error = res->error;
1094 client->warning = res->warning;
1095 return ( res->status==SEARCHD_ERROR ) ? NULL : res;
1096 }
1097
1098
safestrlen(const char * s)1099 static size_t safestrlen ( const char * s )
1100 {
1101 return s ? strlen(s) : 0;
1102 }
1103
1104
calc_req_len(sphinx_client * client,const char * query,const char * index_list,const char * comment)1105 static int calc_req_len ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
1106 {
1107 int i, filter_val_size;
1108 size_t res;
1109
1110 res = 96 + 2*(int)sizeof(sphinx_uint64_t) + 4*client->num_weights
1111 + safestrlen ( client->sortby )
1112 + safestrlen ( query )
1113 + safestrlen ( index_list )
1114 + safestrlen ( client->group_by )
1115 + safestrlen ( client->group_sort )
1116 + safestrlen ( client->group_distinct )
1117 + safestrlen ( comment )
1118 + ( ( client->ranker==SPH_RANK_EXPR ) ? ( 4 + safestrlen ( client->rankexpr ) ) : 0 );
1119
1120 filter_val_size = ( client->ver_search>=0x114 ) ? 8 : 4;
1121 for ( i=0; i<client->num_filters; i++ )
1122 {
1123 const struct st_filter * filter = &client->filters[i];
1124 res += 12 + safestrlen ( filter->attr ); // string attr-name; int type; int exclude-flag
1125
1126 switch ( filter->filter_type )
1127 {
1128 case SPH_FILTER_VALUES: res += 4 + filter_val_size*filter->num_values; break; // int values-count; uint32/int64[] values
1129 case SPH_FILTER_RANGE: res += 2*filter_val_size; break; // uint32/int64 min-val, max-val
1130 case SPH_FILTER_FLOATRANGE: res += 8; break; // float min-val,max-val
1131 case SPH_FILTER_STRING: res += 4 + safestrlen ( filter->svalue ); break;
1132 }
1133 }
1134
1135 if ( client->geoanchor_attr_lat && client->geoanchor_attr_long )
1136 res += 16 + safestrlen ( client->geoanchor_attr_lat ) + safestrlen ( client->geoanchor_attr_long ); // string lat-attr, long-attr; float lat, long
1137
1138 for ( i=0; i<client->num_index_weights; i++ )
1139 res += 8 + safestrlen ( client->index_weights_names[i] ); // string index-name; int index-weight
1140
1141 for ( i=0; i<client->num_field_weights; i++ )
1142 res += 8 + safestrlen ( client->field_weights_names[i] ); // string field-name; int field-weight
1143
1144 if ( client->ver_search>=0x115 )
1145 {
1146 res += 4; // int overrides-count
1147 for ( i=0; i<client->num_overrides; i++ )
1148 {
1149 res += 8 + safestrlen ( client->overrides[i].attr ); // string attr, int attr-type
1150 res += 4 + 12*client->overrides[i].num_values; // int values-count, { uint64 docid, uint32 value }[] override
1151 }
1152 }
1153
1154 if ( client->ver_search>=0x116 )
1155 res += 4 + safestrlen ( client->select_list ); // string select_list
1156
1157 if ( client->ver_search>=0x11B )
1158 res += 4 + ( client->predicted_time>0 ? 4 : 0 );
1159
1160 if ( client->ver_search>=0x11D )
1161 res += safestrlen ( client->outer_orderby ) + 16; // string outer order by + int outer offset + int outer limit + has outer flag
1162
1163 return (int)res;
1164 }
1165
1166
send_bytes(char ** pp,const char * bytes,int len)1167 static void send_bytes ( char ** pp, const char * bytes, int len )
1168 {
1169 char * ptr;
1170 int i;
1171
1172 ptr = *pp;
1173 if ( ptr )
1174 for ( i=0; i<len; i++ )
1175 *ptr++ = bytes[i];
1176 *pp = ptr;
1177 }
1178
1179
send_int(char ** pp,unsigned int value)1180 static void send_int ( char ** pp, unsigned int value )
1181 {
1182 unsigned char * b = (unsigned char*) *pp;
1183 b[0] = ( value >> 24 ) & 0xff;
1184 b[1] = ( value >> 16 ) & 0xff;
1185 b[2] = ( value >> 8 ) & 0xff;
1186 b[3] = ( value & 0xFF );
1187 *pp += 4;
1188 }
1189
1190
send_word(char ** pp,unsigned short value)1191 static void send_word ( char ** pp, unsigned short value )
1192 {
1193 unsigned char * b = (unsigned char*) *pp;
1194 b[0] = ( value >> 8 );
1195 b[1] = ( value & 0xFF );
1196 *pp += 2;
1197 }
1198
1199
send_str(char ** pp,const char * s)1200 static void send_str ( char ** pp, const char * s )
1201 {
1202 int len;
1203 len = s ? (int)strlen(s) : 0;
1204 send_int ( pp, len );
1205 send_bytes ( pp, s, len );
1206 }
1207
1208
send_qword(char ** pp,sphinx_uint64_t value)1209 static void send_qword ( char ** pp, sphinx_uint64_t value )
1210 {
1211 send_int ( pp, (int)( value >> 32 ) );
1212 send_int ( pp, (int)( value & ((sphinx_uint64_t)0xffffffffL) ) );
1213 }
1214
1215
send_float(char ** pp,float value)1216 static void send_float ( char ** pp, float value )
1217 {
1218 union
1219 {
1220 float f;
1221 int i;
1222 } u;
1223
1224 u.f = value;
1225 send_int ( pp, u.i );
1226 }
1227
1228
sphinx_add_query(sphinx_client * client,const char * query,const char * index_list,const char * comment)1229 int sphinx_add_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
1230 {
1231 int i, j, req_len;
1232 char * req;
1233
1234 if ( client->num_reqs<0 || client->num_reqs>=MAX_REQS )
1235 {
1236 set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs );
1237 return -1;
1238 }
1239
1240 req_len = calc_req_len ( client, query, index_list, comment );
1241
1242 req = malloc ( req_len );
1243 if ( !req )
1244 {
1245 set_error ( client, "malloc() failed (bytes=%d)", req_len );
1246 return -1;
1247 }
1248
1249 client->reqs[client->num_reqs] = req;
1250 client->req_lens[client->num_reqs] = req_len;
1251 client->num_reqs++;
1252
1253 if ( client->ver_search>=0x11B )
1254 send_int ( &req, client->query_flags );
1255
1256 send_int ( &req, client->offset );
1257 send_int ( &req, client->limit );
1258 send_int ( &req, client->mode );
1259 send_int ( &req, client->ranker );
1260 if ( client->ranker==SPH_RANK_EXPR )
1261 send_str ( &req, client->rankexpr );
1262 send_int ( &req, client->sort );
1263 send_str ( &req, client->sortby );
1264 send_str ( &req, query );
1265 send_int ( &req, client->num_weights );
1266 for ( i=0; i<client->num_weights; i++ )
1267 send_int ( &req, client->weights[i] );
1268 send_str ( &req, index_list );
1269 send_int ( &req, 1 ); // id range bits
1270 send_qword ( &req, client->minid );
1271 send_qword ( &req, client->maxid );
1272 send_int ( &req, client->num_filters );
1273 for ( i=0; i<client->num_filters; i++ )
1274 {
1275 send_str ( &req, client->filters[i].attr );
1276 send_int ( &req, client->filters[i].filter_type );
1277
1278 switch ( client->filters[i].filter_type )
1279 {
1280 case SPH_FILTER_VALUES:
1281 send_int ( &req, client->filters[i].num_values );
1282 if ( client->ver_search>=0x114 )
1283 {
1284 for ( j=0; j<client->filters[i].num_values; j++ )
1285 send_qword ( &req, client->filters[i].values[j] );
1286 } else
1287 {
1288 for ( j=0; j<client->filters[i].num_values; j++ )
1289 send_int ( &req, (unsigned int)client->filters[i].values[j] );
1290 }
1291 break;
1292
1293 case SPH_FILTER_RANGE:
1294 if ( client->ver_search>=0x114 )
1295 {
1296 send_qword ( &req, client->filters[i].umin );
1297 send_qword ( &req, client->filters[i].umax );
1298 } else
1299 {
1300 send_int ( &req, (unsigned int)client->filters[i].umin );
1301 send_int ( &req, (unsigned int)client->filters[i].umax );
1302 }
1303 break;
1304
1305 case SPH_FILTER_FLOATRANGE:
1306 send_float ( &req, client->filters[i].fmin );
1307 send_float ( &req, client->filters[i].fmax );
1308 break;
1309
1310 case SPH_FILTER_STRING:
1311 send_str ( &req, client->filters[i].svalue );
1312 break;
1313 }
1314
1315 send_int ( &req, client->filters[i].exclude );
1316 }
1317 send_int ( &req, client->group_func );
1318 send_str ( &req, client->group_by );
1319 send_int ( &req, client->max_matches );
1320 send_str ( &req, client->group_sort );
1321 send_int ( &req, client->cutoff );
1322 send_int ( &req, client->retry_count );
1323 send_int ( &req, client->retry_delay );
1324 send_str ( &req, client->group_distinct );
1325 if ( client->geoanchor_attr_lat && client->geoanchor_attr_long )
1326 {
1327 send_int ( &req, 1 );
1328 send_str ( &req, client->geoanchor_attr_lat );
1329 send_str ( &req, client->geoanchor_attr_long );
1330 send_float ( &req, client->geoanchor_lat );
1331 send_float ( &req, client->geoanchor_long );
1332 } else
1333 {
1334 send_int ( &req, 0 );
1335 }
1336 send_int ( &req, client->num_index_weights );
1337 for ( i=0; i<client->num_index_weights; i++ )
1338 {
1339 send_str ( &req, client->index_weights_names[i] );
1340 send_int ( &req, client->index_weights_values[i] );
1341 }
1342 send_int ( &req, client->max_query_time );
1343 send_int ( &req, client->num_field_weights );
1344 for ( i=0; i<client->num_field_weights; i++ )
1345 {
1346 send_str ( &req, client->field_weights_names[i] );
1347 send_int ( &req, client->field_weights_values[i] );
1348 }
1349 send_str ( &req, comment );
1350
1351 if ( client->ver_search>=0x115 )
1352 {
1353 send_int ( &req, client->num_overrides );
1354 for ( i=0; i<client->num_overrides; i++ )
1355 {
1356 send_str ( &req, client->overrides[i].attr );
1357 send_int ( &req, SPH_ATTR_INTEGER );
1358 send_int ( &req, client->overrides[i].num_values );
1359 for ( j=0; j<client->overrides[i].num_values; j++ )
1360 {
1361 send_qword ( &req, client->overrides[i].docids[j] );
1362 send_int ( &req, client->overrides[i].uint_values[j] );
1363 }
1364 }
1365 }
1366
1367 if ( client->ver_search>=0x116 )
1368 send_str ( &req, client->select_list );
1369
1370 if ( client->ver_search>=0x11B && client->predicted_time>0 )
1371 send_int ( &req, client->predicted_time );
1372
1373 if ( client->ver_search>=0x11D )
1374 {
1375 send_str ( &req, client->outer_orderby );
1376 send_int ( &req, client->outer_offset );
1377 send_int ( &req, client->outer_limit );
1378 send_int ( &req, client->has_outer );
1379 }
1380
1381 if ( !req )
1382 {
1383 set_error ( client, "internal error, failed to build request" );
1384 free ( client->reqs [ --client->num_reqs ] );
1385 return -1;
1386 }
1387
1388 return client->num_reqs-1;
1389 }
1390
1391
sock_error()1392 static const char * sock_error ()
1393 {
1394 #if _WIN32
1395 static char sBuf [ 256 ];
1396 int iErr;
1397
1398 iErr = WSAGetLastError ();
1399 _snprintf ( sBuf, sizeof(sBuf), "WSA error %d", iErr );
1400 return sBuf;
1401 #else
1402 return strerror ( errno );
1403 #endif
1404 }
1405
1406
sock_errno()1407 static int sock_errno ()
1408 {
1409 #ifdef _WIN32
1410 return WSAGetLastError ();
1411 #else
1412 return errno;
1413 #endif
1414 }
1415
1416
sock_set_nonblocking(int sock)1417 static int sock_set_nonblocking ( int sock )
1418 {
1419 #if _WIN32
1420 u_long uMode = 1;
1421 return ioctlsocket ( sock, FIONBIO, &uMode );
1422 #else
1423 return fcntl ( sock, F_SETFL, O_NONBLOCK );
1424 #endif
1425 }
1426
1427
sock_set_blocking(int sock)1428 static int sock_set_blocking ( int sock )
1429 {
1430 #if _WIN32
1431 u_long uMode = 0;
1432 return ioctlsocket ( sock, FIONBIO, &uMode );
1433 #else
1434 return fcntl ( sock, F_SETFL, 0 );
1435 #endif
1436 }
1437
1438
sock_close(int sock)1439 void sock_close ( int sock )
1440 {
1441 if ( sock<0 )
1442 return;
1443
1444 #if _WIN32
1445 closesocket ( sock );
1446 #else
1447 close ( sock );
1448 #endif
1449 }
1450
1451
1452 // wrap FD_SET to prevent warnings on Windows
1453 #if _WIN32
1454 #pragma warning(disable:4127) // conditional expr is const
1455 #pragma warning(disable:4389) // signed/unsigned mismatch
SPH_FD_SET(int fd,fd_set * fdset)1456 void SPH_FD_SET ( int fd, fd_set * fdset ) { FD_SET ( fd, fdset ); }
1457 #pragma warning(default:4127) // conditional expr is const
1458 #pragma warning(default:4389) // signed/unsigned mismatch
1459 #else // !USE_WINDOWS
1460 #define SPH_FD_SET FD_SET
1461 #endif
1462
1463
net_write(int fd,const char * bytes,int len,sphinx_client * client)1464 static sphinx_bool net_write ( int fd, const char * bytes, int len, sphinx_client * client )
1465 {
1466 int res;
1467 #if defined(_WIN32) || defined(SO_NOSIGPIPE) || !defined(MSG_NOSIGNAL)
1468 res = send ( fd, bytes, len, 0 );
1469 #else
1470 res = send ( fd, bytes, len, MSG_NOSIGNAL );
1471 #endif
1472
1473 if ( res<0 )
1474 {
1475 set_error ( client, "send() error: %s", sock_error() );
1476 return SPH_FALSE;
1477 }
1478
1479 if ( res!=len )
1480 {
1481 set_error ( client, "send() error: incomplete write (len=%d, sent=%d)", len, res );
1482 return SPH_FALSE;
1483 }
1484
1485 return SPH_TRUE;
1486 }
1487
1488
net_read(int fd,char * buf,int len,sphinx_client * client)1489 static sphinx_bool net_read ( int fd, char * buf, int len, sphinx_client * client )
1490 {
1491 int res, err;
1492 for ( ;; )
1493 {
1494 res = recv ( fd, buf, len, 0 );
1495
1496 if ( res<0 )
1497 {
1498 err = sock_errno();
1499 if ( err==EINTR || err==EWOULDBLOCK ) // FIXME! remove non-blocking mode here; add timeout
1500 continue;
1501 set_error ( client, "recv(): read error (error=%s)", sock_error() );
1502 return SPH_FALSE;
1503 }
1504
1505 len -= res;
1506 buf += res;
1507
1508 if ( len==0 )
1509 return SPH_TRUE;
1510
1511 if ( res==0 )
1512 {
1513 set_error ( client, "recv(): incomplete read (len=%d, recv=%d)", len, res );
1514 return SPH_FALSE;
1515 }
1516 }
1517 }
1518
1519
net_create_inet_sock(sphinx_client * client)1520 static int net_create_inet_sock ( sphinx_client * client )
1521 {
1522 struct hostent * hp;
1523 struct sockaddr_in sa;
1524 int sock, res, err, optval;
1525
1526 hp = gethostbyname ( client->host );
1527 if ( !hp )
1528 {
1529 set_error ( client, "host name lookup failed (host=%s, error=%s)", client->host, sock_error() );
1530 return -1;
1531 }
1532
1533 memset ( &sa, 0, sizeof(sa) );
1534 memcpy ( &sa.sin_addr, hp->h_addr_list[0], hp->h_length );
1535 sa.sin_family = hp->h_addrtype;
1536 sa.sin_port = htons ( (unsigned short)client->port );
1537
1538 sock = (int) socket ( hp->h_addrtype, SOCK_STREAM, 0 );
1539 if ( sock<0 )
1540 {
1541 set_error ( client, "socket() failed: %s", sock_error() );
1542 return -1;
1543 }
1544
1545 if ( sock_set_nonblocking ( sock )<0 )
1546 {
1547 set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() );
1548 return -1;
1549 }
1550
1551 optval = 1;
1552 #if defined(SO_NOSIGPIPE)
1553 if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 )
1554 {
1555 set_error ( client, "setsockopt() failed: %s", sock_error() );
1556 return -1;
1557 }
1558 #endif
1559
1560 res = connect ( sock, (struct sockaddr*)&sa, sizeof(sa) );
1561 if ( res==0 )
1562 return sock;
1563
1564 err = sock_errno();
1565 #ifdef EINPROGRESS
1566 if ( err!=EWOULDBLOCK && err!=EINPROGRESS )
1567 #else
1568 if ( err!=EWOULDBLOCK )
1569 #endif
1570 {
1571 set_error ( client, "connect() failed: %s", sock_error() );
1572 return -1;
1573 }
1574
1575 return sock;
1576 }
1577
1578 #ifndef _WIN32
net_create_unix_sock(sphinx_client * client)1579 static int net_create_unix_sock ( sphinx_client * client )
1580 {
1581 struct hostent * hp;
1582 struct sockaddr_un uaddr;
1583 int sock, res, err, optval, len;
1584
1585 len = strlen ( client->host );
1586
1587 if ( len + 1 > sizeof( uaddr.sun_path ) )
1588 set_error ( client, "UNIX socket path is too long (len=%d)", len );
1589
1590 memset ( &uaddr, 0, sizeof(uaddr) );
1591 uaddr.sun_family = AF_UNIX;
1592 memcpy ( uaddr.sun_path, client->host, len + 1 );
1593
1594 sock = socket ( AF_UNIX, SOCK_STREAM, 0 );
1595 if ( sock<0 )
1596 {
1597 set_error ( client, "UNIX socket() failed: %s", sock_error() );
1598 return -1;
1599 }
1600
1601 if ( sock_set_nonblocking ( sock )<0 )
1602 {
1603 set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() );
1604 return -1;
1605 }
1606
1607 optval = 1;
1608 #if defined(SO_NOSIGPIPE)
1609 if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 )
1610 {
1611 set_error ( client, "setsockopt() failed: %s", sock_error() );
1612 return -1;
1613 }
1614 #endif
1615
1616 res = connect ( sock, (struct sockaddr *)&uaddr, sizeof(uaddr) );
1617 if ( res==0 )
1618 return sock;
1619
1620 err = sock_errno();
1621 #ifdef EINPROGRESS
1622 if ( err!=EWOULDBLOCK && err!=EINPROGRESS )
1623 #else
1624 if ( err!=EWOULDBLOCK )
1625 #endif
1626 {
1627 set_error ( client, "connect() failed: %s", sock_error() );
1628 return -1;
1629 }
1630
1631 return sock;
1632 }
1633 #endif
1634
1635
net_connect_get(sphinx_client * client)1636 static int net_connect_get ( sphinx_client * client )
1637 {
1638 struct timeval timeout;
1639 fd_set fds_write;
1640 int sock, to_wait, res, my_proto;
1641
1642 if ( client->sock>=0 )
1643 return client->sock;
1644
1645 sock = -1;
1646 if ( client->host[0]!='/' )
1647 {
1648 sock = net_create_inet_sock ( client );
1649 } else
1650 {
1651 #ifdef _WIN32
1652 set_error ( client, "UNIX sockets are not supported on Windows" );
1653 return -1;
1654 #else
1655 sock = net_create_unix_sock ( client );
1656 #endif
1657 }
1658
1659 if ( sock<0 )
1660 return -1;
1661
1662 to_wait = (int)( 1000*client->timeout );
1663 if ( to_wait<=0 )
1664 to_wait = CONNECT_TIMEOUT_MSEC;
1665
1666 {
1667 timeout.tv_sec = to_wait / 1000; // full seconds
1668 timeout.tv_usec = ( to_wait % 1000 ) * 1000; // remainder is msec, so *1000 for usec
1669 FD_ZERO ( &fds_write );
1670 SPH_FD_SET ( sock, &fds_write );
1671
1672 res = select ( 1+sock, NULL, &fds_write, NULL, &timeout );
1673
1674 if ( res>=0 && FD_ISSET ( sock, &fds_write ) )
1675 {
1676 sock_set_blocking ( sock );
1677
1678 // now send major client protocol version
1679 my_proto = htonl ( 1 );
1680 if ( !net_write ( sock, (char*)&my_proto, sizeof(my_proto), client ) )
1681 {
1682 sock_close ( sock );
1683 set_error ( client, "failed to send client protocol version" );
1684 return -1;
1685 }
1686
1687 // check daemon version
1688 if ( !net_read ( sock, (char*)&my_proto, sizeof(my_proto), client ) )
1689 {
1690 sock_close ( sock );
1691 return -1;
1692 }
1693
1694 my_proto = ntohl ( my_proto );
1695 if ( my_proto<1 )
1696 {
1697 sock_close ( sock );
1698 set_error ( client, "expected searchd protocol version 1+, got version %d", my_proto );
1699 return -1;
1700 }
1701
1702 return sock;
1703 }
1704
1705 /*!COMMIT handle EINTR here*/
1706
1707 sock_close ( sock );
1708 set_error ( client, "connect() timed out" );
1709 return -1;
1710 }
1711 }
1712
1713
net_sock_eof(int sock)1714 static sphinx_bool net_sock_eof ( int sock )
1715 {
1716 struct timeval tv;
1717 fd_set fds_read, fds_except;
1718 int res;
1719 char buf;
1720
1721 // wrong arg, consider dead
1722 if ( sock<0 )
1723 return SPH_TRUE;
1724
1725 // select() on a socket and watch for exceptions
1726 FD_ZERO ( &fds_read );
1727 FD_ZERO ( &fds_except );
1728 SPH_FD_SET ( sock, &fds_read );
1729 SPH_FD_SET ( sock, &fds_except );
1730 tv.tv_sec = 0;
1731 tv.tv_usec = 0;
1732 res = select ( 1+sock, &fds_read, NULL, &fds_except, &tv );
1733
1734 // select() failed, assume something is wrong
1735 if ( res<0 )
1736 return SPH_TRUE;
1737
1738 // got any events to read? (either normal via fds_read, or OOB via fds_except set)
1739 if ( FD_ISSET ( sock, &fds_read ) || FD_ISSET ( sock, &fds_except ) )
1740 if ( recv ( sock, &buf, sizeof(buf), MSG_PEEK )<=0 )
1741 if ( sock_errno()!=EWOULDBLOCK )
1742 return SPH_TRUE;
1743
1744 // it seems alive
1745 return SPH_FALSE;
1746 }
1747
1748
net_connect_ex(sphinx_client * client)1749 static int net_connect_ex ( sphinx_client * client )
1750 {
1751 if ( client->sock>=0 )
1752 {
1753 // in case of a persistent connection, check for eof
1754 // then attempt to reestablish lost pconn once
1755 if ( !net_sock_eof ( client->sock ) )
1756 return client->sock;
1757
1758 sock_close ( client->sock );
1759 client->sock = -1;
1760 }
1761
1762 if ( !client->persist )
1763 return net_connect_get ( client );
1764
1765 sphinx_open ( client );
1766 return client->sock;
1767 }
1768
unpack_short(char ** cur)1769 static unsigned short unpack_short ( char ** cur )
1770 {
1771 unsigned short v;
1772 memcpy ( &v, *cur, sizeof(unsigned short) );
1773 (*cur) += sizeof(unsigned short);
1774 return ntohs ( v );
1775 }
1776
1777
unpack_int(char ** cur)1778 static unsigned int unpack_int ( char ** cur )
1779 {
1780 unsigned int v;
1781 memcpy ( &v, *cur, sizeof(unsigned int) );
1782 (*cur) += sizeof(unsigned int);
1783 return ntohl ( v );
1784 }
1785
1786
unpack_str(char ** cur)1787 static char * unpack_str ( char ** cur )
1788 {
1789 // we play a trick
1790 // we move the string in-place to free space for trailing zero but avoid malloc
1791 unsigned int len;
1792 len = unpack_int ( cur );
1793 memmove ( (*cur)-1, (*cur), len );
1794 (*cur) += len;
1795 (*cur)[-1] = '\0';
1796 return (*cur)-len-1;
1797 }
1798
1799
unpack_qword(char ** cur)1800 static sphinx_uint64_t unpack_qword ( char ** cur )
1801 {
1802 sphinx_uint64_t hi, lo;
1803 hi = unpack_int ( cur );
1804 lo = unpack_int ( cur );
1805 return ( hi<<32 ) + lo;
1806 }
1807
1808
unpack_float(char ** cur)1809 static float unpack_float ( char ** cur )
1810 {
1811 union
1812 {
1813 unsigned int n;
1814 float f;
1815 } u;
1816 u.n = unpack_int ( cur );
1817 return u.f;
1818 }
1819
1820
net_get_response(int fd,sphinx_client * client)1821 static void net_get_response ( int fd, sphinx_client * client )
1822 {
1823 int len;
1824 char header_buf[32], *cur, *response;
1825 unsigned short status, ver;
1826
1827 // dismiss previous response
1828 if ( client->response_buf )
1829 {
1830 free ( client->response_buf );
1831 client->response_len = 0;
1832 client->response_buf = NULL;
1833 }
1834
1835 // read and parse the header
1836 if ( !net_read ( fd, header_buf, 8, client ) )
1837 {
1838 sock_close ( fd );
1839 if ( client->sock>0 )
1840 client->sock = -1;
1841 return;
1842 }
1843
1844 cur = header_buf;
1845 status = unpack_short ( &cur );
1846 ver = unpack_short ( &cur );
1847 len = unpack_int ( &cur );
1848
1849 // sanity check the length, alloc the buffer
1850 if ( len<0 || len>MAX_PACKET_LEN )
1851 {
1852 sock_close ( fd );
1853 if ( client->sock>0 )
1854 client->sock = -1;
1855 set_error ( client, "response length out of bounds (len=%d)", len );
1856 return;
1857 }
1858
1859 response = malloc ( len );
1860 if ( !response )
1861 {
1862 sock_close ( fd );
1863 if ( client->sock>0 )
1864 client->sock = -1;
1865 set_error ( client, "malloc() failed (bytes=%d)", len );
1866 return;
1867 }
1868
1869 // read the response
1870 if ( !net_read ( fd, response, len, client ) )
1871 {
1872 sock_close ( fd );
1873 if ( client->sock>0 )
1874 client->sock = -1;
1875 free ( response );
1876 return;
1877 }
1878
1879 // check status
1880 cur = response;
1881 switch ( status )
1882 {
1883 case SEARCHD_OK:
1884 case SEARCHD_WARNING:
1885 client->error = NULL; // so far so good
1886 if ( status==SEARCHD_WARNING )
1887 client->warning = unpack_str ( &cur );
1888 else
1889 client->warning = NULL;
1890 client->response_len = len;
1891 client->response_buf = response;
1892 client->response_start = cur;
1893 break;
1894
1895 case SEARCHD_ERROR:
1896 case SEARCHD_RETRY:
1897 // copy error message, so that we can immediately free the response
1898 set_error ( client, "%s", unpack_str ( &cur ) );
1899 free ( response );
1900 break;
1901
1902 default:
1903 set_error ( client, "unknown status code (status=%d)", status );
1904 free ( response );
1905 break;
1906 }
1907
1908 // close one-time socket on success
1909 if ( client->sock<0 )
1910 sock_close ( fd );
1911 }
1912
1913
sphinx_open(sphinx_client * client)1914 sphinx_bool sphinx_open ( sphinx_client * client )
1915 {
1916 char buf[16], *pbuf;
1917
1918 if ( client->sock>=0 )
1919 {
1920 set_error ( client, "already connected" );
1921 return SPH_FALSE;
1922 }
1923
1924 client->sock = net_connect_get ( client );
1925 if ( client->sock<0 )
1926 return SPH_FALSE;
1927
1928 pbuf = buf;
1929 send_word ( &pbuf, SEARCHD_COMMAND_PERSIST );
1930 send_word ( &pbuf, 0 ); // dummy version
1931 send_int ( &pbuf, 4 ); // dummy body len
1932 send_int ( &pbuf, 1 ); // dummy body
1933 if ( !net_write ( client->sock, buf, (int)(pbuf-buf), client ) )
1934 {
1935 sock_close ( client->sock );
1936 client->sock = -1;
1937 return SPH_FALSE;
1938 }
1939 client->persist = SPH_TRUE;
1940 return SPH_TRUE;
1941 }
1942
1943
sphinx_close(sphinx_client * client)1944 sphinx_bool sphinx_close ( sphinx_client * client )
1945 {
1946 if ( client->sock<0 )
1947 {
1948 set_error ( client, "not connected" );
1949 return SPH_FALSE;
1950 }
1951
1952 sock_close ( client->sock );
1953 client->sock = -1;
1954 client->persist = SPH_FALSE;
1955 return SPH_TRUE;
1956 }
1957
1958
sphinx_malloc(int len,sphinx_client * client)1959 static void * sphinx_malloc ( int len, sphinx_client * client )
1960 {
1961 void * res;
1962
1963 if ( len<0 || len>MAX_PACKET_LEN )
1964 {
1965 set_error ( client, "malloc() length out of bounds, possibly broken response packet (len=%d)", len );
1966 return NULL;
1967 }
1968
1969 res = malloc ( len );
1970 if ( !res )
1971 set_error ( client, "malloc() failed (bytes=%d)", len );
1972
1973 return res;
1974 }
1975
1976
sphinx_run_queries(sphinx_client * client)1977 sphinx_result * sphinx_run_queries ( sphinx_client * client )
1978 {
1979 int i, j, k, l, fd, len, nreqs, id64;
1980 char req_header[32], *req, *p, *pmax;
1981 sphinx_result * res;
1982 union un_attr_value * pval;
1983
1984 if ( !client )
1985 return NULL;
1986
1987 if ( client->num_reqs<=0 || client->num_reqs>MAX_REQS )
1988 {
1989 set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs );
1990 return NULL;
1991 }
1992
1993 fd = net_connect_ex ( client );
1994 if ( fd<0 )
1995 return NULL;
1996
1997 // free previous results
1998 sphinx_free_results ( client );
1999
2000 // send query, get response
2001 len = 8;
2002 for ( i=0; i<client->num_reqs; i++ )
2003 len += client->req_lens[i];
2004
2005 req = req_header;
2006 send_word ( &req, SEARCHD_COMMAND_SEARCH );
2007 send_word ( &req, client->ver_search );
2008 send_int ( &req, len );
2009 send_int ( &req, 0 ); // its a client
2010 send_int ( &req, client->num_reqs );
2011
2012 if ( !net_write ( fd, req_header, (int)(req-req_header), client ) )
2013 return NULL;
2014
2015 for ( i=0; i<client->num_reqs; i++ )
2016 if ( !net_write ( fd, client->reqs[i], client->req_lens[i], client ) )
2017 return NULL;
2018
2019 net_get_response ( fd, client );
2020 if ( !client->response_buf )
2021 return NULL;
2022
2023 // dismiss request data, memorize count
2024 nreqs = sphinx_dismiss_requests ( client );
2025
2026 // parse response
2027 p = client->response_start;
2028 pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2029
2030 for ( i=0; i<nreqs && p<pmax; i++ )
2031 {
2032 res = &client->results[i];
2033 client->num_results++;
2034 res->error = NULL;
2035 res->warning = NULL;
2036
2037 res->status = unpack_int ( &p );
2038 if ( res->status!=SEARCHD_OK )
2039 {
2040 if ( res->status==SEARCHD_WARNING )
2041 {
2042 res->warning = unpack_str ( &p );
2043 } else
2044 {
2045 res->error = unpack_str ( &p );
2046 continue;
2047 }
2048 }
2049
2050 // fields
2051 res->num_fields = unpack_int ( &p );
2052 res->fields = sphinx_malloc ( res->num_fields*sizeof(const char*), client );
2053 if ( !res->fields )
2054 return NULL;
2055
2056 for ( j=0; j<res->num_fields; j++ )
2057 res->fields[j] = unpack_str ( &p );
2058
2059 // attrs
2060 res->num_attrs = unpack_int ( &p );
2061 res->attr_names = sphinx_malloc ( res->num_attrs*sizeof(const char*), client );
2062 if ( !res->attr_names )
2063 return NULL;
2064
2065 res->attr_types = sphinx_malloc ( res->num_attrs*sizeof(int), client );
2066 if ( !res->attr_types )
2067 return NULL;
2068
2069 for ( j=0; j<res->num_attrs; j++ )
2070 {
2071 res->attr_names[j] = unpack_str ( &p );
2072 res->attr_types[j] = unpack_int ( &p );
2073 }
2074
2075 // match count, id bits flag
2076 res->num_matches = unpack_int ( &p );
2077 id64 = unpack_int ( &p );
2078
2079 res->values_pool = sphinx_malloc ( (2+res->num_attrs) * res->num_matches * sizeof(union un_attr_value), client );
2080 if ( !res->values_pool )
2081 return NULL;
2082 pval = res->values_pool;
2083
2084 // matches
2085 for ( j=0; j<res->num_matches; j++ )
2086 {
2087 // id
2088 if ( id64 )
2089 pval->int_value = unpack_qword ( &p );
2090 else
2091 pval->int_value = unpack_int ( &p );
2092 pval++;
2093
2094 // weight
2095 pval->int_value = unpack_int ( &p );
2096 pval++;
2097
2098 // attrs
2099 for ( k=0; k<res->num_attrs; k++ )
2100 {
2101 switch ( res->attr_types[k] )
2102 {
2103 case SPH_ATTR_MULTI64:
2104 case SPH_ATTR_MULTI:
2105 /*!COMMIT this is totally unsafe on some arches (eg. SPARC)*/
2106 pval->mva_value = (unsigned int *) p;
2107 len = unpack_int ( &p );
2108 for ( l=0; l<=len; l++ ) // including first one that is len
2109 pval->mva_value[l] = ntohl ( pval->mva_value[l] );
2110
2111 if ( res->attr_types[k]==SPH_ATTR_MULTI64 )
2112 {
2113 pval->mva_value[0] = pval->mva_value[0]/2;
2114 }
2115 p += len*sizeof(unsigned int);
2116 break;
2117
2118 case SPH_ATTR_FLOAT: pval->float_value = unpack_float ( &p ); break;
2119 case SPH_ATTR_BIGINT: pval->int_value = unpack_qword ( &p ); break;
2120 case SPH_ATTR_STRING: pval->string = unpack_str ( &p ); break;
2121 case SPH_ATTR_FACTORS:
2122 len = unpack_int ( &p );
2123 if ( len )
2124 p += len-sizeof(unsigned int);
2125 break;
2126 default: pval->int_value = unpack_int ( &p ); break;
2127 }
2128 pval++;
2129 }
2130 }
2131
2132 // totals
2133 res->total = unpack_int ( &p );
2134 res->total_found = unpack_int ( &p );
2135 res->time_msec = unpack_int ( &p );
2136 res->num_words = unpack_int ( &p );
2137
2138 if ( res->words )
2139 free ( res->words );
2140 res->words = sphinx_malloc ( res->num_words*sizeof(struct st_sphinx_wordinfo), client );
2141 if ( !res->words )
2142 return NULL;
2143
2144 // words
2145 for ( j=0; j<res->num_words; j++ )
2146 {
2147 res->words[j].word = unpack_str ( &p );
2148 res->words[j].docs = unpack_int ( &p );
2149 res->words[j].hits = unpack_int ( &p );
2150 }
2151
2152 // sanity check
2153 // FIXME! add it to each unpack?
2154 if ( p>pmax )
2155 {
2156 set_error ( client, "unpack error (req=%d, reqs=%d)", i, nreqs );
2157 return NULL;
2158 }
2159 }
2160
2161 return client->results;
2162 }
2163
2164 //////////////////////////////////////////////////////////////////////////
2165
sphinx_get_num_results(sphinx_client * client)2166 int sphinx_get_num_results ( sphinx_client * client )
2167 {
2168 return client ? client->num_results : -1;
2169 }
2170
2171
sphinx_get_id(sphinx_result * result,int match)2172 sphinx_uint64_t sphinx_get_id ( sphinx_result * result, int match )
2173 {
2174 return sphinx_get_int ( result, match, -2 );
2175 }
2176
2177
sphinx_get_weight(sphinx_result * result,int match)2178 int sphinx_get_weight ( sphinx_result * result, int match )
2179 {
2180 return (int)sphinx_get_int ( result, match, -1 );
2181 }
2182
2183
sphinx_get_int(sphinx_result * result,int match,int attr)2184 sphinx_int64_t sphinx_get_int ( sphinx_result * result, int match, int attr )
2185 {
2186 // FIXME! add safety and type checks
2187 union un_attr_value * pval;
2188 pval = result->values_pool;
2189 return pval [ (2+result->num_attrs)*match+2+attr ].int_value;
2190 }
2191
2192
sphinx_get_float(sphinx_result * result,int match,int attr)2193 float sphinx_get_float ( sphinx_result * result, int match, int attr )
2194 {
2195 // FIXME! add safety and type checks
2196 union un_attr_value * pval;
2197 pval = result->values_pool;
2198 return pval [ (2+result->num_attrs)*match+2+attr ].float_value;
2199 }
2200
2201
sphinx_get_mva(sphinx_result * result,int match,int attr)2202 unsigned int * sphinx_get_mva ( sphinx_result * result, int match, int attr )
2203 {
2204 // FIXME! add safety and type checks
2205 union un_attr_value * pval;
2206 pval = result->values_pool;
2207 return pval [ (2+result->num_attrs)*match+2+attr ].mva_value;
2208 }
2209
sphinx_get_mva64_value(unsigned int * mva,int i)2210 sphinx_uint64_t sphinx_get_mva64_value ( unsigned int * mva, int i )
2211 {
2212 sphinx_uint64_t uVal;
2213 uVal = ( ( ( (sphinx_uint64_t)( mva[i*2] ) )<<32 ) | (sphinx_uint64_t)( mva[i*2+1] ) );
2214 return uVal;
2215 }
2216
sphinx_get_string(sphinx_result * result,int match,int attr)2217 const char * sphinx_get_string ( sphinx_result * result, int match, int attr )
2218 {
2219 // FIXME! add safety and type checks
2220 union un_attr_value * pval;
2221 pval = result->values_pool;
2222 return pval [ (2+result->num_attrs)*match+2+attr ].string;
2223 }
2224
2225 //////////////////////////////////////////////////////////////////////////
2226
net_simple_query(sphinx_client * client,char * buf,int req_len)2227 static sphinx_bool net_simple_query ( sphinx_client * client, char * buf, int req_len )
2228 {
2229 int fd;
2230
2231 fd = net_connect_ex ( client );
2232 if ( fd<0 )
2233 {
2234 free ( buf );
2235 return SPH_FALSE;
2236 }
2237
2238 if ( !net_write ( fd, buf, 8+req_len, client ) )
2239 {
2240 free ( buf );
2241 return SPH_FALSE;
2242 }
2243 free ( buf );
2244
2245 net_get_response ( fd, client );
2246 if ( !client->response_buf )
2247 return SPH_FALSE;
2248
2249 return SPH_TRUE;
2250 }
2251
2252
sphinx_init_excerpt_options(sphinx_excerpt_options * opts)2253 void sphinx_init_excerpt_options ( sphinx_excerpt_options * opts )
2254 {
2255 if ( !opts )
2256 return;
2257
2258 opts->before_match = "<b>";
2259 opts->after_match = "</b>";
2260 opts->chunk_separator = " ... ";
2261 opts->html_strip_mode = "index";
2262 opts->passage_boundary = "none";
2263
2264 opts->limit = 256;
2265 opts->limit_passages = 0;
2266 opts->limit_words = 0;
2267 opts->around = 5;
2268 opts->start_passage_id = 1;
2269
2270 opts->exact_phrase = SPH_FALSE;
2271 opts->single_passage = SPH_FALSE;
2272 opts->use_boundaries = SPH_FALSE;
2273 opts->weight_order = SPH_FALSE;
2274 opts->query_mode = SPH_FALSE;
2275 opts->force_all_words = SPH_FALSE;
2276 opts->load_files = SPH_FALSE;
2277 opts->allow_empty = SPH_FALSE;
2278 opts->emit_zones = SPH_FALSE;
2279 }
2280
2281
sphinx_build_excerpts(sphinx_client * client,int num_docs,const char ** docs,const char * index,const char * words,sphinx_excerpt_options * opts)2282 char ** sphinx_build_excerpts ( sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts )
2283 {
2284 sphinx_excerpt_options def_opt;
2285 int i, req_len, flags;
2286 char *buf, *req, *p, *pmax, **result;
2287
2288 if ( !client || !docs || !index || !words || num_docs<=0 )
2289 {
2290 if ( !docs ) set_error ( client, "invalid arguments (docs must not be empty)" );
2291 else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
2292 else if ( !words ) set_error ( client, "invalid arguments (words must not be empty)" );
2293 else if ( num_docs<=0 ) set_error ( client, "invalid arguments (num_docs must be positive)" );
2294 return NULL;
2295 }
2296
2297 // fixup options
2298 if ( !opts )
2299 {
2300 sphinx_init_excerpt_options ( &def_opt );
2301 opts = &def_opt;
2302 }
2303
2304 // alloc buffer
2305 req_len = (int)( 60
2306 + strlen(index)
2307 + strlen(words)
2308 + safestrlen(opts->before_match)
2309 + safestrlen(opts->after_match)
2310 + safestrlen(opts->chunk_separator)
2311 + safestrlen(opts->html_strip_mode)
2312 + safestrlen(opts->passage_boundary) );
2313 for ( i=0; i<num_docs; i++ )
2314 req_len += (int)( 4 + safestrlen(docs[i]) );
2315
2316 buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2317 if ( !buf )
2318 {
2319 set_error ( client, "malloc() failed (bytes=%d)", req_len );
2320 return NULL;
2321 }
2322
2323 // build request
2324 req = buf;
2325
2326 send_word ( &req, SEARCHD_COMMAND_EXCERPT );
2327 send_word ( &req, VER_COMMAND_EXCERPT );
2328 send_int ( &req, req_len );
2329
2330 flags = 1; // remove spaces
2331 if ( opts->exact_phrase ) flags |= 2;
2332 if ( opts->single_passage ) flags |= 4;
2333 if ( opts->use_boundaries ) flags |= 8;
2334 if ( opts->weight_order ) flags |= 16;
2335 if ( opts->query_mode ) flags |= 32;
2336 if ( opts->force_all_words ) flags |= 64;
2337 if ( opts->load_files ) flags |= 128;
2338 if ( opts->allow_empty ) flags |= 256;
2339 if ( opts->emit_zones ) flags |= 512;
2340
2341 send_int ( &req, 0 );
2342 send_int ( &req, flags );
2343 send_str ( &req, index );
2344 send_str ( &req, words );
2345
2346 send_str ( &req, opts->before_match );
2347 send_str ( &req, opts->after_match );
2348 send_str ( &req, opts->chunk_separator );
2349 send_int ( &req, opts->limit );
2350 send_int ( &req, opts->around );
2351
2352 send_int ( &req, opts->limit_passages ); // v1.2
2353 send_int ( &req, opts->limit_words );
2354 send_int ( &req, opts->start_passage_id );
2355 send_str ( &req, opts->html_strip_mode );
2356 send_str ( &req, opts->passage_boundary );
2357
2358 send_int ( &req, num_docs );
2359 for ( i=0; i<num_docs; i++ )
2360 send_str ( &req, docs[i] );
2361
2362 if ( (int)(req-buf)!=8+req_len )
2363 {
2364 set_error ( client, "internal error: failed to build request in sphinx_build_excerpts()" );
2365 free ( buf );
2366 return NULL;
2367 }
2368
2369 // send query, get response
2370 if ( !net_simple_query ( client, buf, req_len ) )
2371 return NULL;
2372
2373 // parse response
2374 p = client->response_start;
2375 pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2376
2377 result = malloc ( (1+num_docs)*sizeof(char*) );
2378 if ( !result )
2379 {
2380 set_error ( client, "malloc() failed (bytes=%d)", (1+num_docs)*sizeof(char*) );
2381 return NULL;
2382 }
2383
2384 for ( i=0; i<=num_docs; i++ )
2385 result[i] = NULL;
2386
2387 for ( i=0; i<num_docs && p<pmax; i++ )
2388 result[i] = strdup ( unpack_str ( &p ) );
2389
2390 if ( p>pmax )
2391 {
2392 for ( i=0; i<num_docs; i++ )
2393 if ( result[i] )
2394 free ( result[i] );
2395
2396 set_error ( client, "unpack error" );
2397 return NULL;
2398 }
2399
2400 // done
2401 return result;
2402 }
2403
2404 //////////////////////////////////////////////////////////////////////////
2405
sphinx_update_attributes(sphinx_client * client,const char * index,int num_attrs,const char ** attrs,int num_docs,const sphinx_uint64_t * docids,const sphinx_int64_t * values)2406 int sphinx_update_attributes ( sphinx_client * client, const char * index, int num_attrs, const char ** attrs, int num_docs, const sphinx_uint64_t * docids, const sphinx_int64_t * values )
2407 {
2408 int i, j, req_len;
2409 char *buf, *req, *p;
2410
2411 // check args
2412 if ( !client || num_attrs<=0 || !attrs || num_docs<=0 || !docids || !values )
2413 {
2414 if ( num_attrs<=0 ) set_error ( client, "invalid arguments (num_attrs must be positive)" );
2415 else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
2416 else if ( !attrs ) set_error ( client, "invalid arguments (attrs must not empty)" );
2417 else if ( num_docs<=0 ) set_error ( client, "invalid arguments (num_docs must be positive)" );
2418 else if ( !docids ) set_error ( client, "invalid arguments (docids must not be empty)" );
2419 else if ( !values ) set_error ( client, "invalid arguments (values must not be empty)" );
2420 }
2421
2422 // alloc buffer
2423 req_len = (int)( 12 + safestrlen(index) + (12+4*num_attrs)*num_docs );
2424 for ( i=0; i<num_attrs; i++ )
2425 req_len += (int)( 4 + safestrlen(attrs[i]) );
2426
2427 buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2428 if ( !buf )
2429 {
2430 set_error ( client, "malloc() failed (bytes=%d)", req_len );
2431 return -1;
2432 }
2433
2434 // build request
2435 req = buf;
2436
2437 send_word ( &req, SEARCHD_COMMAND_UPDATE );
2438 send_word ( &req, VER_COMMAND_UPDATE );
2439 send_int ( &req, req_len );
2440
2441 send_str ( &req, index );
2442 send_int ( &req, num_attrs );
2443 for ( i=0; i<num_attrs; i++ )
2444 {
2445 send_str ( &req, attrs[i] );
2446 send_int ( &req, 0 ); // not SPH_ATTR_MULTI flag
2447 }
2448
2449 send_int ( &req, num_docs );
2450 for ( i=0; i<num_docs; i++ )
2451 {
2452 send_qword ( &req, docids[i] );
2453 for ( j=0; j<num_attrs; j++ )
2454 send_int ( &req, (unsigned int)( *values++ ) );
2455 }
2456
2457 // send query, get response
2458 if ( !net_simple_query ( client, buf, req_len ) )
2459 return -1;
2460
2461 // parse response
2462 if ( client->response_len<4 )
2463 {
2464 set_error ( client, "incomplete reply" );
2465 return -1;
2466 }
2467
2468 p = client->response_start;
2469 return unpack_int ( &p );
2470 }
2471
sphinx_update_attributes_mva(sphinx_client * client,const char * index,const char * attr,sphinx_uint64_t docid,int num_values,const unsigned int * values)2472 int sphinx_update_attributes_mva ( sphinx_client * client, const char * index, const char * attr, sphinx_uint64_t docid, int num_values, const unsigned int * values )
2473 {
2474 int i, req_len;
2475 char *buf, *req, *p;
2476
2477 // check args
2478 if ( !client || !index || !attr || num_values<=0 || !values )
2479 {
2480 if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
2481 else if ( !attr ) set_error ( client, "invalid arguments (attr must not empty)" );
2482 else if ( num_values<=0 ) set_error ( client, "invalid arguments (num_values must be positive)" );
2483 else if ( !values ) set_error ( client, "invalid arguments (values must not be empty)" );
2484 }
2485
2486 // alloc buffer
2487 req_len = (int)( 38 + safestrlen(index) + safestrlen(attr) + num_values*4 );
2488
2489 buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2490 if ( !buf )
2491 {
2492 set_error ( client, "malloc() failed (bytes=%d)", req_len );
2493 return -1;
2494 }
2495
2496 // build request
2497 req = buf;
2498
2499 send_word ( &req, SEARCHD_COMMAND_UPDATE );
2500 send_word ( &req, VER_COMMAND_UPDATE );
2501 send_int ( &req, req_len );
2502
2503 send_str ( &req, index );
2504 send_int ( &req, 1 );
2505 send_str ( &req, attr );
2506 send_int ( &req, 1 ); // SPH_ATTR_MULTI flag
2507
2508 send_int ( &req, 1 );
2509 send_qword ( &req, docid );
2510 send_int ( &req, num_values );
2511 for ( i=0; i<num_values; i++ )
2512 send_int ( &req, values[i] );
2513
2514 // send query, get response
2515 if ( !net_simple_query ( client, buf, req_len ) )
2516 return -1;
2517
2518 // parse response
2519 if ( client->response_len<4 )
2520 {
2521 set_error ( client, "incomplete reply" );
2522 return -1;
2523 }
2524
2525 p = client->response_start;
2526 return unpack_int ( &p );
2527 }
2528
2529 //////////////////////////////////////////////////////////////////////////
2530
sphinx_build_keywords(sphinx_client * client,const char * query,const char * index,sphinx_bool hits,int * out_num_keywords)2531 sphinx_keyword_info * sphinx_build_keywords ( sphinx_client * client, const char * query, const char * index, sphinx_bool hits, int * out_num_keywords )
2532 {
2533 int i, req_len, nwords, len;
2534 char *buf, *req, *p, *pmax;
2535 sphinx_keyword_info *res;
2536
2537 // check args
2538 if ( !client || !query || !index )
2539 {
2540 if ( !query ) set_error ( client, "invalid arguments (query must not be empty)" );
2541 else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
2542 else if ( !out_num_keywords ) set_error ( client, "invalid arguments (out_num_keywords must not be null)" );
2543 return NULL;
2544 }
2545
2546 // alloc buffer
2547 req_len = (int)( safestrlen(query) + safestrlen(index) + 12 );
2548
2549 buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2550 if ( !buf )
2551 {
2552 set_error ( client, "malloc() failed (bytes=%d)", req_len );
2553 return NULL;
2554 }
2555
2556 // build request
2557 req = buf;
2558
2559 send_word ( &req, SEARCHD_COMMAND_KEYWORDS );
2560 send_word ( &req, VER_COMMAND_KEYWORDS );
2561 send_int ( &req, req_len );
2562
2563 send_str ( &req, query );
2564 send_str ( &req, index );
2565 send_int ( &req, hits );
2566
2567 // send query, get response
2568 if ( !net_simple_query ( client, buf, req_len ) )
2569 return NULL;
2570
2571 // parse response
2572 p = client->response_start;
2573 pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2574
2575 nwords = unpack_int ( &p );
2576 *out_num_keywords = nwords;
2577
2578 len = nwords*sizeof(sphinx_keyword_info);
2579 res = (sphinx_keyword_info*) malloc ( len );
2580 if ( !res )
2581 {
2582 set_error ( client, "malloc() failed (bytes=%d)", len );
2583 return NULL;
2584 }
2585 memset ( res, 0, len );
2586
2587 for ( i=0; i<nwords && p<pmax; i++ )
2588 {
2589 res[i].tokenized = strdup ( unpack_str ( &p ) );
2590 res[i].normalized = strdup ( unpack_str ( &p ) );
2591 if ( hits )
2592 {
2593 res[i].num_docs = unpack_int ( &p );
2594 res[i].num_hits = unpack_int ( &p );
2595 }
2596 }
2597 // FIXME! add check for incomplete reply
2598
2599 return res;
2600 }
2601
2602 //////////////////////////////////////////////////////////////////////////
sphinx_status(sphinx_client * client,int * num_rows,int * num_cols)2603 char ** sphinx_status ( sphinx_client * client, int * num_rows, int * num_cols )
2604 {
2605 return sphinx_status_extended ( client, num_rows, num_cols, 0 );
2606 }
2607
sphinx_status_extended(sphinx_client * client,int * num_rows,int * num_cols,int local)2608 char ** sphinx_status_extended ( sphinx_client * client, int * num_rows, int * num_cols, int local )
2609 {
2610 int i, j, k, n;
2611 char *p, *pmax, *req, *buf, **res;
2612
2613 // check args
2614 if ( !client || !num_rows || !num_cols )
2615 {
2616 if ( !num_rows ) set_error ( client, "invalid arguments (num_rows must not be NULL)" );
2617 else if ( !num_cols ) set_error ( client, "invalid arguments (num_cols must not be NULL)" );
2618 return NULL;
2619 }
2620
2621 // build request
2622 buf = malloc ( 12 );
2623 if ( !buf )
2624 {
2625 set_error ( client, "malloc() failed (bytes=12)" );
2626 return NULL;
2627 }
2628
2629 if (local)
2630 local=0;
2631 else
2632 local=1;
2633
2634 req = buf;
2635 send_word ( &req, SEARCHD_COMMAND_STATUS );
2636 send_word ( &req, VER_COMMAND_STATUS );
2637 send_int ( &req, 4 );
2638 send_int ( &req, local );
2639
2640 // send query, get response
2641 if ( !net_simple_query ( client, buf, 12 ) )
2642 return NULL;
2643
2644 // parse response
2645 p = client->response_start;
2646 pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2647
2648 *num_rows = unpack_int ( &p );
2649 *num_cols = unpack_int ( &p );
2650 n = (*num_rows)*(*num_cols);
2651
2652 res = (char**) malloc ( n*sizeof(char*) );
2653 for ( i=0; i<n; i++ )
2654 res[i] = NULL;
2655
2656 // FIXME! error checking?
2657 k = 0;
2658 for ( i=0; i<*num_rows; i++ )
2659 for ( j=0; j<*num_cols; j++ )
2660 res[k++] = strdup ( unpack_str ( &p ) );
2661
2662 return res;
2663 }
2664
sphinx_status_destroy(char ** status,int num_rows,int num_cols)2665 void sphinx_status_destroy ( char ** status, int num_rows, int num_cols )
2666 {
2667 int i;
2668 for ( i=0; i<num_rows*num_cols; i++ )
2669 free ( status[i] );
2670 free ( status );
2671 }
2672
2673 //
2674 // $Id$
2675 //
2676