Last active
August 29, 2015 14:03
-
-
Save dengshilong/f7a6491d3d4a7bc863af to your computer and use it in GitHub Desktop.
发现sphinxclient.c的api竟然没有flush索引到硬盘的功能,于是写了增加了flush功能的sphinxclient.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// $Id: sphinxclient.c 4097 2013-08-20 09:28:24Z kevg $ | |
// | |
// | |
// Copyright (c) 2001-2013, Andrew Aksyonoff | |
// Copyright (c) 2008-2013, Sphinx Technologies Inc | |
// All rights reserved | |
// | |
// This program is free software; you can redistribute it and/or modify | |
// it under the terms of the GNU Library General Public License. You should | |
// have received a copy of the LGPL license along with this program; if you | |
// did not, you can find it at http://www.gnu.org/ | |
// | |
#ifdef _WIN32 | |
#if _MSC_VER>=1400 | |
// VS 2005 and above | |
#define _CRT_SECURE_NO_DEPRECATE 1 | |
#define _CRT_NONSTDC_NO_DEPRECATE 1 | |
#else | |
// VS 2003 and below | |
#define vsnprintf _vsnprintf | |
#endif | |
#endif | |
#include <stdlib.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <string.h> | |
#ifndef _WIN32 | |
#include "sphinxclient_config.h" | |
#endif | |
#include "sphinxclient.h" | |
#if _WIN32 | |
// Win-specific headers, calls, libraries | |
#include <io.h> | |
#include <winsock2.h> | |
#pragma comment(linker, "/defaultlib:wsock32.lib") | |
#pragma message("Automatically linking with wsock32.lib") | |
#define EWOULDBLOCK WSAEWOULDBLOCK | |
#define EINTR WSAEINTR | |
#else | |
// UNIX-specific headers and calls | |
#include <unistd.h> | |
#include <netinet/in.h> | |
#include <sys/file.h> | |
#include <sys/socket.h> | |
#include <sys/time.h> | |
#include <sys/wait.h> | |
#include <netdb.h> | |
#include <errno.h> | |
#include <sys/un.h> | |
#include <sys/fcntl.h> | |
#endif | |
////////////////////////////////////////////////////////////////////////// | |
#define MAX_REQS 32 | |
#define CONNECT_TIMEOUT_MSEC 1000 | |
#define MAX_PACKET_LEN (8*1024*1024) | |
enum | |
{ | |
SEARCHD_COMMAND_SEARCH = 0, | |
SEARCHD_COMMAND_EXCERPT = 1, | |
SEARCHD_COMMAND_UPDATE = 2, | |
SEARCHD_COMMAND_KEYWORDS = 3, | |
SEARCHD_COMMAND_PERSIST = 4, | |
SEARCHD_COMMAND_STATUS = 5, | |
SEARCHD_COMMAND_FLUSHATTRS = 7 | |
}; | |
enum | |
{ | |
VER_COMMAND_EXCERPT = 0x103, | |
VER_COMMAND_UPDATE = 0x102, | |
VER_COMMAND_KEYWORDS = 0x100, | |
VER_COMMAND_STATUS = 0x101, | |
VER_COMMAND_FLUSHATTRS = 0x100 | |
}; | |
////////////////////////////////////////////////////////////////////////// | |
struct st_filter | |
{ | |
const char * attr; | |
int filter_type; | |
int num_values; | |
const sphinx_int64_t * values; | |
sphinx_int64_t umin; | |
sphinx_int64_t umax; | |
float fmin; | |
float fmax; | |
int exclude; | |
const char * svalue; | |
}; | |
union un_attr_value | |
{ | |
sphinx_int64_t int_value; | |
float float_value; | |
unsigned int * mva_value; | |
const char * string; | |
}; | |
struct st_override | |
{ | |
const char * attr; | |
const sphinx_uint64_t * docids; | |
int num_values; | |
const unsigned int * uint_values; | |
}; | |
struct st_sphinx_client | |
{ | |
unsigned short ver_search; ///< compatibility mode | |
sphinx_bool copy_args; ///< whether to create a copy of each passed argument | |
void * head_alloc; ///< head of client-owned allocations list | |
const char * error; ///< last error | |
const char * warning; ///< last warning | |
char local_error_buf[256]; ///< buffer to store 'local' error messages (eg. connect() error) | |
const char * host; | |
int port; | |
float timeout; | |
int offset; | |
int limit; | |
int mode; | |
int num_weights; | |
const int * weights; | |
int sort; | |
const char * sortby; | |
sphinx_uint64_t minid; | |
sphinx_uint64_t maxid; | |
const char * group_by; | |
int group_func; | |
const char * group_sort; | |
const char * group_distinct; | |
int max_matches; | |
int cutoff; | |
int retry_count; | |
int retry_delay; | |
const char * geoanchor_attr_lat; | |
const char * geoanchor_attr_long; | |
float geoanchor_lat; | |
float geoanchor_long; | |
int num_filters; | |
int max_filters; | |
struct st_filter * filters; | |
int num_index_weights; | |
const char ** index_weights_names; | |
const int * index_weights_values; | |
int ranker; | |
const char * rankexpr; | |
int max_query_time; | |
int num_field_weights; | |
const char ** field_weights_names; | |
const int * field_weights_values; | |
int num_overrides; | |
int max_overrides; | |
struct st_override * overrides; | |
const char * select_list; | |
int query_flags; | |
int predicted_time; | |
const char * outer_orderby; | |
int outer_offset; | |
int outer_limit; | |
sphinx_bool has_outer; | |
int num_reqs; | |
int req_lens [ MAX_REQS ]; | |
char * reqs [ MAX_REQS ]; | |
int response_len; | |
char * response_buf; ///< where the buffer begins (might also contain heading warning) | |
char * response_start; ///< where the data to parse starts | |
int num_results; | |
sphinx_result results [ MAX_REQS ]; | |
int sock; ///< open socket for pconns; -1 if none | |
sphinx_bool persist; | |
}; | |
////////////////////////////////////////////////////////////////////////// | |
static void * chain ( sphinx_client * client, const void * ptr, size_t len ); | |
static const char * strchain ( sphinx_client * client, const char * s ); | |
static void unchain ( sphinx_client * client, const void * ptr ); | |
static void unchain_all ( sphinx_client * client ); | |
sphinx_client * sphinx_create ( sphinx_bool copy_args ) | |
{ | |
sphinx_client * client; | |
int i; | |
// allocate | |
client = malloc ( sizeof(sphinx_client) ); | |
if ( !client ) | |
return NULL; | |
// initialize defaults and return | |
client->ver_search = 0x11E; // 0x113 for 0.9.8, 0x116 for 0.9.9rc2 | |
client->copy_args = copy_args; | |
client->head_alloc = NULL; | |
client->error = NULL; | |
client->warning = NULL; | |
client->local_error_buf[0] = '\0'; | |
client->host = strchain ( client, "localhost" ); | |
client->port = 9312; | |
client->timeout = 0.0f; | |
client->offset = 0; | |
client->limit = 20; | |
client->mode = SPH_MATCH_EXTENDED2; | |
client->num_weights = 0; | |
client->weights = NULL; | |
client->sort = SPH_SORT_RELEVANCE; | |
client->sortby = NULL; | |
client->minid = 0; | |
client->maxid = 0; | |
client->group_by = NULL; | |
client->group_func = SPH_GROUPBY_ATTR; | |
client->group_sort = strchain ( client, "@groupby desc" ); | |
client->group_distinct = NULL; | |
client->max_matches = 1000; | |
client->cutoff = 0; | |
client->retry_count = 0; | |
client->retry_delay = 0; | |
client->geoanchor_attr_lat = NULL; | |
client->geoanchor_attr_long = NULL; | |
client->geoanchor_lat = 0.0f; | |
client->geoanchor_long = 0.0f; | |
client->num_filters = 0; | |
client->max_filters = 0; | |
client->filters = NULL; | |
client->num_index_weights = 0; | |
client->index_weights_names = NULL; | |
client->index_weights_values = NULL; | |
client->ranker = SPH_RANK_DEFAULT; | |
client->rankexpr = NULL; | |
client->max_query_time = 0; | |
client->num_field_weights = 0; | |
client->field_weights_names = NULL; | |
client->field_weights_values = NULL; | |
client->num_overrides = 0; | |
client->max_overrides = 0; | |
client->overrides = NULL; | |
client->select_list = NULL; | |
client->query_flags = 1<<6; | |
client->predicted_time = 0; | |
client->outer_orderby = NULL; | |
client->outer_offset = 0; | |
client->outer_limit = 0; | |
client->has_outer = SPH_FALSE; | |
client->num_reqs = 0; | |
client->response_len = 0; | |
client->response_buf = NULL; | |
client->num_results = 0; | |
for ( i=0; i<MAX_REQS; i++ ) | |
{ | |
client->results[i].values_pool = NULL; | |
client->results[i].words = NULL; | |
client->results[i].fields = NULL; | |
client->results[i].attr_names = NULL; | |
client->results[i].attr_types = NULL; | |
} | |
client->sock = -1; | |
client->persist = SPH_FALSE; | |
return client; | |
} | |
static void sphinx_free_results ( sphinx_client * client ) | |
{ | |
int i; | |
for ( i=0; i<client->num_results; i++ ) | |
{ | |
free ( client->results[i].values_pool ); | |
free ( client->results[i].words ); | |
free ( client->results[i].fields ); | |
free ( client->results[i].attr_names ); | |
free ( client->results[i].attr_types ); | |
client->results[i].values_pool = NULL; | |
client->results[i].words = NULL; | |
client->results[i].fields = NULL; | |
client->results[i].attr_names = NULL; | |
client->results[i].attr_types = NULL; | |
} | |
client->num_results = 0; | |
} | |
void sock_close ( int sock ); | |
#define safe_free(_ptr) \ | |
if ( _ptr ) \ | |
{ \ | |
free ( _ptr ); \ | |
_ptr = NULL; \ | |
} | |
void sphinx_cleanup ( sphinx_client * client ) | |
{ | |
int i; | |
if ( !client ) | |
return; | |
for ( i=0; i<client->num_reqs; i++ ) | |
safe_free ( client->reqs[i] ); | |
client->num_reqs = 0; | |
sphinx_free_results ( client ); | |
client->num_results = 0; | |
client->num_results = 0; | |
safe_free ( client->response_buf ); | |
} | |
void sphinx_destroy ( sphinx_client * client ) | |
{ | |
int i; | |
if ( !client ) | |
return; | |
for ( i=0; i<client->num_reqs; i++ ) | |
safe_free ( client->reqs[i] ); | |
sphinx_free_results ( client ); | |
unchain_all ( client ); | |
safe_free ( client->filters ); | |
safe_free ( client->response_buf ); | |
if ( client->sock>=0 ) | |
sock_close ( client->sock ); | |
free ( client ); | |
} | |
const char * sphinx_error ( sphinx_client * client ) | |
{ | |
return client->error ? client->error : ""; | |
} | |
const char * sphinx_warning ( sphinx_client * client ) | |
{ | |
return client->warning ? client->warning : ""; | |
} | |
static void set_error ( sphinx_client * client, const char * template, ... ) | |
{ | |
va_list ap; | |
if ( !client ) | |
return; | |
va_start ( ap, template ); | |
vsnprintf ( client->local_error_buf, sizeof(client->local_error_buf), template, ap ); | |
va_end ( ap ); | |
client->error = client->local_error_buf; | |
client->warning = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
struct st_memblock | |
{ | |
struct st_memblock * prev; | |
struct st_memblock * next; | |
}; | |
static void * chain ( sphinx_client * client, const void * ptr, size_t len ) | |
{ | |
struct st_memblock * entry; | |
if ( !client->copy_args || !ptr ) | |
return (void*) ptr; | |
entry = malloc ( sizeof(struct st_memblock) + len ); | |
if ( !entry ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", sizeof(struct st_memblock) + len ); | |
return NULL; | |
} | |
entry->prev = NULL; | |
entry->next = client->head_alloc; | |
if ( entry->next ) | |
entry->next->prev = entry; | |
client->head_alloc = entry; | |
entry++; | |
memcpy ( entry, ptr, len ); | |
return entry; | |
} | |
static const char * strchain ( sphinx_client * client, const char * s ) | |
{ | |
return s ? chain ( client, s, 1+strlen(s) ) : NULL; | |
} | |
static void unchain ( sphinx_client * client, const void * ptr ) | |
{ | |
struct st_memblock * entry; | |
if ( !client->copy_args || !ptr ) | |
return; | |
entry = (struct st_memblock*) ptr; | |
entry--; | |
if ( entry->prev ) | |
entry->prev->next = entry->next; | |
else | |
client->head_alloc = entry->next; | |
if ( entry->next ) | |
entry->next->prev = entry->prev; | |
free ( entry ); | |
} | |
static void unchain_all ( sphinx_client * client ) | |
{ | |
struct st_memblock *to_free, *cur; | |
if ( !client || !client->copy_args ) | |
return; | |
cur = client->head_alloc; | |
while ( cur ) | |
{ | |
to_free = cur; | |
cur = cur->next; | |
free ( to_free ); | |
} | |
client->head_alloc = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
sphinx_bool sphinx_set_server ( sphinx_client * client, const char * host, int port ) | |
{ | |
if ( !client || !host || !host[0] ) | |
{ | |
set_error ( client, "invalid arguments (host must not be empty)" ); | |
return SPH_FALSE; | |
} | |
unchain ( client, client->host ); | |
client->host = strchain ( client, host ); | |
client->port = port; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_connect_timeout ( sphinx_client * client, float seconds ) | |
{ | |
if ( !client ) | |
return SPH_FALSE; | |
client->timeout = seconds; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_limits ( sphinx_client * client, int offset, int limit, int max_matches, int cutoff ) | |
{ | |
if ( !client || offset<0 || limit<=0 || max_matches<0 || cutoff<0 ) | |
{ | |
if ( offset<0 ) set_error ( client, "invalid arguments (offset must be >= 0)" ); | |
else if ( limit<=0 ) set_error ( client, "invalid arguments (limit must be > 0)" ); | |
else if ( max_matches<0 ) set_error ( client, "invalid arguments (max_matches must be >= 0)" ); | |
else if ( cutoff<0 ) set_error ( client, "invalid arguments (cutoff must be >= 0)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
client->offset = offset; | |
client->limit = limit; | |
if ( max_matches>=0 ) | |
client->max_matches = max_matches; | |
if ( cutoff>=0 ) | |
client->cutoff = cutoff; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_max_query_time ( sphinx_client * client, int max_query_time ) | |
{ | |
if ( !client || max_query_time<=0 ) | |
{ | |
set_error ( client, "invalid arguments (max_query_time must be > 0)" ); | |
return SPH_FALSE; | |
} | |
client->max_query_time = max_query_time; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode ) | |
{ | |
if ( !client || mode<SPH_MATCH_ALL || mode>SPH_MATCH_EXTENDED2 ) // FIXME? | |
{ | |
set_error ( client, "invalid arguments (matching mode %d out of bounds)", mode ); | |
return SPH_FALSE; | |
} | |
client->mode = mode; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_ranking_mode ( sphinx_client * client, int ranker, const char * rankexpr ) | |
{ | |
if ( !client || ranker<SPH_RANK_PROXIMITY_BM25 || ranker>=SPH_RANK_TOTAL ) // FIXME? | |
{ | |
set_error ( client, "invalid arguments (ranking mode %d out of bounds)", ranker ); | |
return SPH_FALSE; | |
} | |
client->ranker = ranker; | |
client->rankexpr = strchain ( client, rankexpr ); | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_sort_mode ( sphinx_client * client, int mode, const char * sortby ) | |
{ | |
if ( !client | |
|| mode<SPH_SORT_RELEVANCE | |
|| mode>SPH_SORT_EXPR | |
|| ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) ) ) | |
{ | |
if ( mode<SPH_SORT_RELEVANCE || mode>SPH_SORT_EXPR ) | |
{ | |
set_error ( client, "invalid arguments (sorting mode %d out of bounds)", mode ); | |
} else if ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) ) | |
{ | |
set_error ( client, "invalid arguments (sortby clause must not be empty)", mode ); | |
} else | |
{ | |
set_error ( client, "invalid arguments", mode ); | |
} | |
return SPH_FALSE; | |
} | |
client->sort = mode; | |
unchain ( client, client->sortby ); | |
client->sortby = strchain ( client, sortby ); | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_field_weights ( sphinx_client * client, int num_weights, const char ** field_names, const int * field_weights ) | |
{ | |
int i; | |
if ( !client || num_weights<=0 || !field_names || !field_weights ) | |
{ | |
if ( num_weights<=0 ) set_error ( client, "invalid arguments (num_weights must be > 0)" ); | |
else if ( !field_names ) set_error ( client, "invalid arguments (field_names must not be NULL)" ); | |
else if ( !field_names ) set_error ( client, "invalid arguments (field_weights must not be NULL)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
if ( client->copy_args ) | |
{ | |
for ( i=0; i<client->num_field_weights; i++ ) | |
unchain ( client, client->field_weights_names[i] ); | |
unchain ( client, client->field_weights_names ); | |
unchain ( client, client->field_weights_values ); | |
field_names = chain ( client, field_names, num_weights*sizeof(const char*) ); | |
for ( i=0; i<num_weights; i++ ) | |
field_names[i] = strchain ( client, field_names[i] ); | |
field_weights = chain ( client, field_weights, num_weights*sizeof(int) ); | |
} | |
client->num_field_weights = num_weights; | |
client->field_weights_names = field_names; | |
client->field_weights_values = field_weights; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_index_weights ( sphinx_client * client, int num_weights, const char ** index_names, const int * index_weights ) | |
{ | |
int i; | |
if ( !client || num_weights<=0 || !index_names || !index_weights ) | |
{ | |
if ( num_weights<=0 ) set_error ( client, "invalid arguments (num_weights must be > 0)" ); | |
else if ( !index_names ) set_error ( client, "invalid arguments (index_names must not be NULL)" ); | |
else if ( !index_names ) set_error ( client, "invalid arguments (index_weights must not be NULL)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
if ( client->copy_args ) | |
{ | |
for ( i=0; i<client->num_index_weights; i++ ) | |
unchain ( client, client->index_weights_names[i] ); | |
unchain ( client, client->index_weights_names ); | |
unchain ( client, client->index_weights_values ); | |
index_names = chain ( client, index_names, num_weights*sizeof(const char*) ); | |
for ( i=0; i<num_weights; i++ ) | |
index_names[i] = strchain ( client, index_names[i] ); | |
index_weights = chain ( client, index_weights, num_weights*sizeof(int) ); | |
} | |
client->num_index_weights = num_weights; | |
client->index_weights_names = index_names; | |
client->index_weights_values = index_weights; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_id_range ( sphinx_client * client, sphinx_uint64_t minid, sphinx_uint64_t maxid ) | |
{ | |
if ( !client || minid>maxid ) | |
{ | |
set_error ( client, "invalid arguments (minid must be <= maxid)" ); | |
return SPH_FALSE; | |
} | |
client->minid = minid; | |
client->maxid = maxid; | |
return SPH_TRUE; | |
} | |
static struct st_filter * sphinx_add_filter_entry ( sphinx_client * client ) | |
{ | |
int len; | |
if ( client->num_filters>=client->max_filters ) | |
{ | |
len = ( client->max_filters<=0 ) ? client->num_filters + 8 : 2*client->max_filters; | |
len *= sizeof(struct st_filter); | |
client->filters = realloc ( client->filters, len ); | |
if ( !client->filters ) | |
{ | |
set_error ( client, "realloc() failed (bytes=%d)", len ); | |
return NULL; | |
} | |
} | |
return client->filters + client->num_filters++; | |
} | |
sphinx_bool sphinx_add_filter ( sphinx_client * client, const char * attr, int num_values, const sphinx_int64_t * values, sphinx_bool exclude ) | |
{ | |
struct st_filter * filter; | |
if ( !client || !attr || num_values<=0 || !values ) | |
{ | |
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" ); | |
else if ( num_values<=0 ) set_error ( client, "invalid arguments (num_values must be > 0)" ); | |
else if ( !values ) set_error ( client, "invalid arguments (values must not be NULL)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
filter = sphinx_add_filter_entry ( client ); | |
if ( !filter ) | |
return SPH_FALSE; | |
filter->attr = strchain ( client, attr ); | |
filter->filter_type = SPH_FILTER_VALUES; | |
filter->num_values = num_values; | |
filter->values = chain ( client, values, num_values*sizeof(sphinx_int64_t) ); | |
filter->exclude = exclude; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_add_filter_string ( sphinx_client * client, const char * attr, const char * value, sphinx_bool exclude ) | |
{ | |
struct st_filter * filter; | |
if ( !client || !attr || !value ) | |
{ | |
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" ); | |
else if ( !value ) set_error ( client, "invalid arguments (value must not be empty)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
filter = sphinx_add_filter_entry ( client ); | |
if ( !filter ) | |
return SPH_FALSE; | |
filter->attr = strchain ( client, attr ); | |
filter->filter_type = SPH_FILTER_STRING; | |
filter->svalue = strchain ( client, value ); | |
filter->exclude = exclude; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_add_filter_range ( sphinx_client * client, const char * attr, sphinx_int64_t umin, sphinx_int64_t umax, sphinx_bool exclude ) | |
{ | |
struct st_filter * filter; | |
if ( !client || !attr || umin>umax ) | |
{ | |
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" ); | |
else if ( umin>umax ) set_error ( client, "invalid arguments (umin must be <= umax)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
filter = sphinx_add_filter_entry ( client ); | |
if ( !filter ) | |
return SPH_FALSE; | |
filter->attr = strchain ( client, attr ); | |
filter->filter_type = SPH_FILTER_RANGE; | |
filter->umin = umin; | |
filter->umax = umax; | |
filter->exclude = exclude; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_add_filter_float_range ( sphinx_client * client, const char * attr, float fmin, float fmax, sphinx_bool exclude ) | |
{ | |
struct st_filter * filter; | |
if ( !client || !attr || fmin>fmax ) | |
{ | |
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" ); | |
else if ( fmin>fmax ) set_error ( client, "invalid arguments (fmin must be <= fmax)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
filter = sphinx_add_filter_entry ( client ); | |
if ( !filter ) | |
return SPH_FALSE; | |
filter->attr = strchain ( client, attr ); | |
filter->filter_type = SPH_FILTER_FLOATRANGE; | |
filter->fmin = fmin; | |
filter->fmax = fmax; | |
filter->exclude = exclude; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_geoanchor ( sphinx_client * client, const char * attr_latitude, const char * attr_longitude, float latitude, float longitude ) | |
{ | |
if ( !client || !attr_latitude || !attr_latitude[0] || !attr_longitude || !attr_longitude[0] ) | |
{ | |
if ( !attr_latitude || !attr_latitude[0] ) set_error ( client, "invalid arguments (attr_latitude must not be empty)" ); | |
else if ( !attr_longitude || !attr_longitude[0] ) set_error ( client, "invalid arguments (attr_longitude must not be empty)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
unchain ( client, client->geoanchor_attr_lat ); | |
unchain ( client, client->geoanchor_attr_long ); | |
client->geoanchor_attr_lat = strchain ( client, attr_latitude ); | |
client->geoanchor_attr_long = strchain ( client, attr_longitude ); | |
client->geoanchor_lat = latitude; | |
client->geoanchor_long = longitude; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_groupby ( sphinx_client * client, const char * attr, int groupby_func, const char * group_sort ) | |
{ | |
if ( !client || !attr || groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR ) | |
{ | |
if ( !attr ) | |
{ | |
set_error ( client, "invalid arguments (attr must not be empty)" ); | |
} else if ( groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR ) | |
{ | |
set_error ( client, "invalid arguments (groupby_func %d out of bounds)", groupby_func ); | |
} else | |
{ | |
set_error ( client, "invalid arguments" ); | |
} | |
return SPH_FALSE; | |
} | |
unchain ( client, client->group_by ); | |
unchain ( client, client->group_sort ); | |
client->group_by = strchain ( client, attr ); | |
client->group_func = groupby_func; | |
client->group_sort = strchain ( client, group_sort ? group_sort : "@groupby desc" ); | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_groupby_distinct ( sphinx_client * client, const char * attr ) | |
{ | |
if ( !client || !attr ) | |
{ | |
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
unchain ( client, client->group_distinct ); | |
client->group_distinct = strchain ( client, attr ); | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_retries ( sphinx_client * client, int count, int delay ) | |
{ | |
if ( !client || count<0 || count>1000 || delay<0 || delay>100000 ) | |
{ | |
if ( count<0 || count>1000 ) set_error ( client, "invalid arguments (count value %d out of bounds)", count ); | |
else if ( delay<0 || delay>100000 ) set_error ( client, "invalid arguments (delay value %d out of bounds)", delay ); | |
else set_error ( client, "invalid arguments" ); | |
return SPH_FALSE; | |
} | |
client->retry_count = count; | |
client->retry_delay = delay; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_add_override ( sphinx_client * client, const char * attr, const sphinx_uint64_t * docids, int num_values, const unsigned int * values ) | |
{ | |
struct st_override * p; | |
if ( !client ) | |
return SPH_FALSE; | |
if ( client->ver_search<0x115 ) | |
{ | |
set_error ( client, "sphinx_add_override not supported by chosen protocol version" ); | |
return SPH_FALSE; | |
} | |
if ( client->num_overrides>=client->max_overrides ) | |
{ | |
client->max_overrides = ( client->max_overrides<=0 ) ? 8 : 2*client->max_overrides; | |
client->overrides = realloc ( client->overrides, client->max_overrides *sizeof(struct st_override) ); | |
} | |
p = client->overrides + client->num_overrides; | |
client->num_overrides++; | |
p->attr = strchain ( client, attr ); | |
p->docids = chain ( client, docids, sizeof(sphinx_uint64_t)*num_values ); | |
p->num_values = num_values; | |
p->uint_values = chain ( client, values, sizeof(unsigned int)*num_values ); | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_set_select ( sphinx_client * client, const char * select_list ) | |
{ | |
if ( !client ) | |
return SPH_FALSE; | |
if ( client->ver_search<0x116 ) | |
{ | |
set_error ( client, "sphinx_set_select not supported by chosen protocol version" ); | |
return SPH_FALSE; | |
} | |
unchain ( client, client->select_list ); | |
client->select_list = strchain ( client, select_list ); | |
return SPH_TRUE; | |
} | |
void set_bit ( int * flags, int bit, sphinx_bool enable ) | |
{ | |
int bit_mask = 1<<bit; | |
if ( enable ) | |
*flags |= bit_mask; | |
else | |
*flags &= ( 0xff ^ bit_mask ); | |
} | |
sphinx_bool sphinx_set_query_flags ( sphinx_client * client, const char * flag_name, sphinx_bool enabled, int max_predicted_msec ) | |
{ | |
if ( !client ) | |
return SPH_FALSE; | |
if ( client->ver_search<0x11B ) | |
{ | |
set_error ( client, "sphinx_set_query_flags not supported by chosen protocol version" ); | |
return SPH_FALSE; | |
} | |
if ( !flag_name || !flag_name[0] ) | |
{ | |
set_error ( client, "invalid arguments (empty flag_name)" ); | |
return SPH_FALSE; | |
} | |
if ( strcmp ( flag_name, "max_predicted_time")==0 && max_predicted_msec<0 ) | |
{ | |
set_error ( client, "invalid arguments (max_predicted_time must be >0)" ); | |
return SPH_FALSE; | |
} | |
if ( strcmp ( flag_name, "reverse_scan")==0 ) | |
{ | |
set_bit ( &client->query_flags, 0, enabled ); | |
} else if ( strcmp ( flag_name, "sort_method_kbuffer")==0 ) | |
{ | |
set_bit ( &client->query_flags, 1, enabled ); | |
} else if ( strcmp ( flag_name, "max_predicted_time")==0 ) | |
{ | |
client->predicted_time = max_predicted_msec; | |
set_bit ( &client->query_flags, 2, max_predicted_msec>0 ); | |
} else if ( strcmp ( flag_name, "boolean_simplify")==0 ) | |
{ | |
set_bit ( &client->query_flags, 3, enabled ); | |
} else if ( strcmp ( flag_name, "idf_plain")==0 ) | |
{ | |
set_bit ( &client->query_flags, 4, enabled ); | |
} else if ( strcmp ( flag_name, "global_idf")==0 ) | |
{ | |
set_bit ( &client->query_flags, 5, enabled ); | |
} else if ( strcmp ( flag_name, "tfidf_normalized")==0 ) | |
{ | |
set_bit ( &client->query_flags, 6, enabled ); | |
} else | |
{ | |
set_error ( client, "invalid arguments (unknown flag_name)" ); | |
return SPH_FALSE; | |
} | |
return SPH_TRUE; | |
} | |
void sphinx_reset_query_flags ( sphinx_client * client ) | |
{ | |
client->query_flags = 1<<6; | |
client->predicted_time = 0; | |
} | |
sphinx_bool sphinx_set_outer_select ( sphinx_client * client, const char * orderby, int offset, int limit ) | |
{ | |
if ( !client ) | |
return SPH_FALSE; | |
if ( client->ver_search<0x11D ) | |
{ | |
set_error ( client, "sphinx_set_outer not supported by chosen protocol version" ); | |
return SPH_FALSE; | |
} | |
unchain ( client, client->outer_orderby ); | |
client->outer_orderby = strchain ( client, orderby ); | |
client->outer_offset = offset; | |
client->outer_limit = limit; | |
client->has_outer = SPH_TRUE; | |
return SPH_TRUE; | |
} | |
void sphinx_reset_outer_select ( sphinx_client * client ) | |
{ | |
if ( !client ) | |
return; | |
unchain ( client, client->outer_orderby ); | |
client->outer_orderby = NULL; | |
client->outer_offset = 0; | |
client->outer_limit = 0; | |
client->has_outer = SPH_FALSE; | |
} | |
void sphinx_reset_filters ( sphinx_client * client ) | |
{ | |
int i; | |
if ( !client ) | |
return; | |
if ( client->filters ) | |
{ | |
if ( client->copy_args ) | |
for ( i=0; i<client->num_filters; i++ ) | |
{ | |
unchain ( client, client->filters[i].attr ); | |
if ( client->filters[i].filter_type==SPH_FILTER_VALUES ) | |
unchain ( client, client->filters[i].values ); | |
if ( client->filters[i].filter_type==SPH_FILTER_STRING ) | |
unchain ( client, client->filters[i].svalue ); | |
} | |
free ( client->filters ); | |
client->filters = NULL; | |
} | |
client->num_filters = client->max_filters = 0; | |
} | |
void sphinx_reset_groupby ( sphinx_client * client ) | |
{ | |
if ( !client ) | |
return; | |
unchain ( client, client->group_by ); | |
unchain ( client, client->group_sort ); | |
client->group_by = NULL; | |
client->group_func = SPH_GROUPBY_ATTR; | |
client->group_sort = strchain ( client, "@groupby desc" ); | |
client->group_distinct = NULL; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
static int sphinx_dismiss_requests ( sphinx_client * client ) | |
{ | |
int nreqs = client->num_reqs, i; | |
for ( i=0; i<client->num_reqs; i++ ) | |
free ( client->reqs[i] ); | |
client->num_reqs = 0; | |
return nreqs; | |
} | |
sphinx_result * sphinx_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment ) | |
{ | |
sphinx_result * res; | |
if ( !client ) | |
return NULL; | |
if ( client->num_reqs!=0 ) | |
{ | |
set_error ( client, "sphinx_query() must not be called after sphinx_add_query()" ); | |
return NULL; | |
} | |
if ( sphinx_add_query ( client, query, index_list, comment )!=0 ) | |
return NULL; | |
res = sphinx_run_queries ( client ); // just a shortcut for client->results[0] | |
sphinx_dismiss_requests ( client ); // sphinx_query() is fire and forget; dismiss request in all cases | |
if ( !res ) | |
return NULL; | |
client->error = res->error; | |
client->warning = res->warning; | |
return ( res->status==SEARCHD_ERROR ) ? NULL : res; | |
} | |
static size_t safestrlen ( const char * s ) | |
{ | |
return s ? strlen(s) : 0; | |
} | |
static int calc_req_len ( sphinx_client * client, const char * query, const char * index_list, const char * comment ) | |
{ | |
int i, filter_val_size; | |
size_t res; | |
res = 96 + 2*(int)sizeof(sphinx_uint64_t) + 4*client->num_weights | |
+ safestrlen ( client->sortby ) | |
+ safestrlen ( query ) | |
+ safestrlen ( index_list ) | |
+ safestrlen ( client->group_by ) | |
+ safestrlen ( client->group_sort ) | |
+ safestrlen ( client->group_distinct ) | |
+ safestrlen ( comment ) | |
+ ( ( client->ranker==SPH_RANK_EXPR ) ? ( 4 + safestrlen ( client->rankexpr ) ) : 0 ); | |
filter_val_size = ( client->ver_search>=0x114 ) ? 8 : 4; | |
for ( i=0; i<client->num_filters; i++ ) | |
{ | |
const struct st_filter * filter = &client->filters[i]; | |
res += 12 + safestrlen ( filter->attr ); // string attr-name; int type; int exclude-flag | |
switch ( filter->filter_type ) | |
{ | |
case SPH_FILTER_VALUES: res += 4 + filter_val_size*filter->num_values; break; // int values-count; uint32/int64[] values | |
case SPH_FILTER_RANGE: res += 2*filter_val_size; break; // uint32/int64 min-val, max-val | |
case SPH_FILTER_FLOATRANGE: res += 8; break; // float min-val,max-val | |
case SPH_FILTER_STRING: res += 4 + safestrlen ( filter->svalue ); break; | |
} | |
} | |
if ( client->geoanchor_attr_lat && client->geoanchor_attr_long ) | |
res += 16 + safestrlen ( client->geoanchor_attr_lat ) + safestrlen ( client->geoanchor_attr_long ); // string lat-attr, long-attr; float lat, long | |
for ( i=0; i<client->num_index_weights; i++ ) | |
res += 8 + safestrlen ( client->index_weights_names[i] ); // string index-name; int index-weight | |
for ( i=0; i<client->num_field_weights; i++ ) | |
res += 8 + safestrlen ( client->field_weights_names[i] ); // string field-name; int field-weight | |
if ( client->ver_search>=0x115 ) | |
{ | |
res += 4; // int overrides-count | |
for ( i=0; i<client->num_overrides; i++ ) | |
{ | |
res += 8 + safestrlen ( client->overrides[i].attr ); // string attr, int attr-type | |
res += 4 + 12*client->overrides[i].num_values; // int values-count, { uint64 docid, uint32 value }[] override | |
} | |
} | |
if ( client->ver_search>=0x116 ) | |
res += 4 + safestrlen ( client->select_list ); // string select_list | |
if ( client->ver_search>=0x11B ) | |
res += 4 + ( client->predicted_time>0 ? 4 : 0 ); | |
if ( client->ver_search>=0x11D ) | |
res += safestrlen ( client->outer_orderby ) + 16; // string outer order by + int outer offset + int outer limit + has outer flag | |
return (int)res; | |
} | |
static void send_bytes ( char ** pp, const char * bytes, int len ) | |
{ | |
char * ptr; | |
int i; | |
ptr = *pp; | |
if ( ptr ) | |
for ( i=0; i<len; i++ ) | |
*ptr++ = bytes[i]; | |
*pp = ptr; | |
} | |
static void send_int ( char ** pp, unsigned int value ) | |
{ | |
unsigned char * b = (unsigned char*) *pp; | |
b[0] = ( value >> 24 ) & 0xff; | |
b[1] = ( value >> 16 ) & 0xff; | |
b[2] = ( value >> 8 ) & 0xff; | |
b[3] = ( value & 0xFF ); | |
*pp += 4; | |
} | |
static void send_word ( char ** pp, unsigned short value ) | |
{ | |
unsigned char * b = (unsigned char*) *pp; | |
b[0] = ( value >> 8 ); | |
b[1] = ( value & 0xFF ); | |
*pp += 2; | |
} | |
static void send_str ( char ** pp, const char * s ) | |
{ | |
int len; | |
len = s ? (int)strlen(s) : 0; | |
send_int ( pp, len ); | |
send_bytes ( pp, s, len ); | |
} | |
static void send_qword ( char ** pp, sphinx_uint64_t value ) | |
{ | |
send_int ( pp, (int)( value >> 32 ) ); | |
send_int ( pp, (int)( value & ((sphinx_uint64_t)0xffffffffL) ) ); | |
} | |
static void send_float ( char ** pp, float value ) | |
{ | |
union | |
{ | |
float f; | |
int i; | |
} u; | |
u.f = value; | |
send_int ( pp, u.i ); | |
} | |
int sphinx_add_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment ) | |
{ | |
int i, j, req_len; | |
char * req; | |
if ( client->num_reqs<0 || client->num_reqs>=MAX_REQS ) | |
{ | |
set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs ); | |
return -1; | |
} | |
req_len = calc_req_len ( client, query, index_list, comment ); | |
req = malloc ( req_len ); | |
if ( !req ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", req_len ); | |
return -1; | |
} | |
client->reqs[client->num_reqs] = req; | |
client->req_lens[client->num_reqs] = req_len; | |
client->num_reqs++; | |
if ( client->ver_search>=0x11B ) | |
send_int ( &req, client->query_flags ); | |
send_int ( &req, client->offset ); | |
send_int ( &req, client->limit ); | |
send_int ( &req, client->mode ); | |
send_int ( &req, client->ranker ); | |
if ( client->ranker==SPH_RANK_EXPR ) | |
send_str ( &req, client->rankexpr ); | |
send_int ( &req, client->sort ); | |
send_str ( &req, client->sortby ); | |
send_str ( &req, query ); | |
send_int ( &req, client->num_weights ); | |
for ( i=0; i<client->num_weights; i++ ) | |
send_int ( &req, client->weights[i] ); | |
send_str ( &req, index_list ); | |
send_int ( &req, 1 ); // id range bits | |
send_qword ( &req, client->minid ); | |
send_qword ( &req, client->maxid ); | |
send_int ( &req, client->num_filters ); | |
for ( i=0; i<client->num_filters; i++ ) | |
{ | |
send_str ( &req, client->filters[i].attr ); | |
send_int ( &req, client->filters[i].filter_type ); | |
switch ( client->filters[i].filter_type ) | |
{ | |
case SPH_FILTER_VALUES: | |
send_int ( &req, client->filters[i].num_values ); | |
if ( client->ver_search>=0x114 ) | |
{ | |
for ( j=0; j<client->filters[i].num_values; j++ ) | |
send_qword ( &req, client->filters[i].values[j] ); | |
} else | |
{ | |
for ( j=0; j<client->filters[i].num_values; j++ ) | |
send_int ( &req, (unsigned int)client->filters[i].values[j] ); | |
} | |
break; | |
case SPH_FILTER_RANGE: | |
if ( client->ver_search>=0x114 ) | |
{ | |
send_qword ( &req, client->filters[i].umin ); | |
send_qword ( &req, client->filters[i].umax ); | |
} else | |
{ | |
send_int ( &req, (unsigned int)client->filters[i].umin ); | |
send_int ( &req, (unsigned int)client->filters[i].umax ); | |
} | |
break; | |
case SPH_FILTER_FLOATRANGE: | |
send_float ( &req, client->filters[i].fmin ); | |
send_float ( &req, client->filters[i].fmax ); | |
break; | |
case SPH_FILTER_STRING: | |
send_str ( &req, client->filters[i].svalue ); | |
break; | |
} | |
send_int ( &req, client->filters[i].exclude ); | |
} | |
send_int ( &req, client->group_func ); | |
send_str ( &req, client->group_by ); | |
send_int ( &req, client->max_matches ); | |
send_str ( &req, client->group_sort ); | |
send_int ( &req, client->cutoff ); | |
send_int ( &req, client->retry_count ); | |
send_int ( &req, client->retry_delay ); | |
send_str ( &req, client->group_distinct ); | |
if ( client->geoanchor_attr_lat && client->geoanchor_attr_long ) | |
{ | |
send_int ( &req, 1 ); | |
send_str ( &req, client->geoanchor_attr_lat ); | |
send_str ( &req, client->geoanchor_attr_long ); | |
send_float ( &req, client->geoanchor_lat ); | |
send_float ( &req, client->geoanchor_long ); | |
} else | |
{ | |
send_int ( &req, 0 ); | |
} | |
send_int ( &req, client->num_index_weights ); | |
for ( i=0; i<client->num_index_weights; i++ ) | |
{ | |
send_str ( &req, client->index_weights_names[i] ); | |
send_int ( &req, client->index_weights_values[i] ); | |
} | |
send_int ( &req, client->max_query_time ); | |
send_int ( &req, client->num_field_weights ); | |
for ( i=0; i<client->num_field_weights; i++ ) | |
{ | |
send_str ( &req, client->field_weights_names[i] ); | |
send_int ( &req, client->field_weights_values[i] ); | |
} | |
send_str ( &req, comment ); | |
if ( client->ver_search>=0x115 ) | |
{ | |
send_int ( &req, client->num_overrides ); | |
for ( i=0; i<client->num_overrides; i++ ) | |
{ | |
send_str ( &req, client->overrides[i].attr ); | |
send_int ( &req, SPH_ATTR_INTEGER ); | |
send_int ( &req, client->overrides[i].num_values ); | |
for ( j=0; j<client->overrides[i].num_values; j++ ) | |
{ | |
send_qword ( &req, client->overrides[i].docids[j] ); | |
send_int ( &req, client->overrides[i].uint_values[j] ); | |
} | |
} | |
} | |
if ( client->ver_search>=0x116 ) | |
send_str ( &req, client->select_list ); | |
if ( client->ver_search>=0x11B && client->predicted_time>0 ) | |
send_int ( &req, client->predicted_time ); | |
if ( client->ver_search>=0x11D ) | |
{ | |
send_str ( &req, client->outer_orderby ); | |
send_int ( &req, client->outer_offset ); | |
send_int ( &req, client->outer_limit ); | |
send_int ( &req, client->has_outer ); | |
} | |
if ( !req ) | |
{ | |
set_error ( client, "internal error, failed to build request" ); | |
free ( client->reqs [ --client->num_reqs ] ); | |
return -1; | |
} | |
return client->num_reqs-1; | |
} | |
static const char * sock_error () | |
{ | |
#if _WIN32 | |
static char sBuf [ 256 ]; | |
int iErr; | |
iErr = WSAGetLastError (); | |
_snprintf ( sBuf, sizeof(sBuf), "WSA error %d", iErr ); | |
return sBuf; | |
#else | |
return strerror ( errno ); | |
#endif | |
} | |
static int sock_errno () | |
{ | |
#ifdef _WIN32 | |
return WSAGetLastError (); | |
#else | |
return errno; | |
#endif | |
} | |
static int sock_set_nonblocking ( int sock ) | |
{ | |
#if _WIN32 | |
u_long uMode = 1; | |
return ioctlsocket ( sock, FIONBIO, &uMode ); | |
#else | |
return fcntl ( sock, F_SETFL, O_NONBLOCK ); | |
#endif | |
} | |
static int sock_set_blocking ( int sock ) | |
{ | |
#if _WIN32 | |
u_long uMode = 0; | |
return ioctlsocket ( sock, FIONBIO, &uMode ); | |
#else | |
return fcntl ( sock, F_SETFL, 0 ); | |
#endif | |
} | |
void sock_close ( int sock ) | |
{ | |
if ( sock<0 ) | |
return; | |
#if _WIN32 | |
closesocket ( sock ); | |
#else | |
close ( sock ); | |
#endif | |
} | |
// wrap FD_SET to prevent warnings on Windows | |
#if _WIN32 | |
#pragma warning(disable:4127) // conditional expr is const | |
#pragma warning(disable:4389) // signed/unsigned mismatch | |
void SPH_FD_SET ( int fd, fd_set * fdset ) { FD_SET ( fd, fdset ); } | |
#pragma warning(default:4127) // conditional expr is const | |
#pragma warning(default:4389) // signed/unsigned mismatch | |
#else // !USE_WINDOWS | |
#define SPH_FD_SET FD_SET | |
#endif | |
static sphinx_bool net_write ( int fd, const char * bytes, int len, sphinx_client * client ) | |
{ | |
int res; | |
#if defined(_WIN32) || defined(SO_NOSIGPIPE) || !defined(MSG_NOSIGNAL) | |
res = send ( fd, bytes, len, 0 ); | |
#else | |
res = send ( fd, bytes, len, MSG_NOSIGNAL ); | |
#endif | |
if ( res<0 ) | |
{ | |
set_error ( client, "send() error: %s", sock_error() ); | |
return SPH_FALSE; | |
} | |
if ( res!=len ) | |
{ | |
set_error ( client, "send() error: incomplete write (len=%d, sent=%d)", len, res ); | |
return SPH_FALSE; | |
} | |
return SPH_TRUE; | |
} | |
static sphinx_bool net_read ( int fd, char * buf, int len, sphinx_client * client ) | |
{ | |
int res, err; | |
for ( ;; ) | |
{ | |
res = recv ( fd, buf, len, 0 ); | |
if ( res<0 ) | |
{ | |
err = sock_errno(); | |
if ( err==EINTR || err==EWOULDBLOCK ) // FIXME! remove non-blocking mode here; add timeout | |
continue; | |
set_error ( client, "recv(): read error (error=%s)", sock_error() ); | |
return SPH_FALSE; | |
} | |
len -= res; | |
buf += res; | |
if ( len==0 ) | |
return SPH_TRUE; | |
if ( res==0 ) | |
{ | |
set_error ( client, "recv(): incomplete read (len=%d, recv=%d)", len, res ); | |
return SPH_FALSE; | |
} | |
} | |
} | |
static int net_create_inet_sock ( sphinx_client * client ) | |
{ | |
struct hostent * hp; | |
struct sockaddr_in sa; | |
int sock, res, err, optval; | |
hp = gethostbyname ( client->host ); | |
if ( !hp ) | |
{ | |
set_error ( client, "host name lookup failed (host=%s, error=%s)", client->host, sock_error() ); | |
return -1; | |
} | |
memset ( &sa, 0, sizeof(sa) ); | |
memcpy ( &sa.sin_addr, hp->h_addr_list[0], hp->h_length ); | |
sa.sin_family = hp->h_addrtype; | |
sa.sin_port = htons ( (unsigned short)client->port ); | |
sock = (int) socket ( hp->h_addrtype, SOCK_STREAM, 0 ); | |
if ( sock<0 ) | |
{ | |
set_error ( client, "socket() failed: %s", sock_error() ); | |
return -1; | |
} | |
if ( sock_set_nonblocking ( sock )<0 ) | |
{ | |
set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() ); | |
return -1; | |
} | |
optval = 1; | |
#if defined(SO_NOSIGPIPE) | |
if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 ) | |
{ | |
set_error ( client, "setsockopt() failed: %s", sock_error() ); | |
return -1; | |
} | |
#endif | |
res = connect ( sock, (struct sockaddr*)&sa, sizeof(sa) ); | |
if ( res==0 ) | |
return sock; | |
err = sock_errno(); | |
#ifdef EINPROGRESS | |
if ( err!=EWOULDBLOCK && err!=EINPROGRESS ) | |
#else | |
if ( err!=EWOULDBLOCK ) | |
#endif | |
{ | |
set_error ( client, "connect() failed: %s", sock_error() ); | |
return -1; | |
} | |
return sock; | |
} | |
#ifndef _WIN32 | |
static int net_create_unix_sock ( sphinx_client * client ) | |
{ | |
struct hostent * hp; | |
struct sockaddr_un uaddr; | |
int sock, res, err, optval, len; | |
len = strlen ( client->host ); | |
if ( len + 1 > sizeof( uaddr.sun_path ) ) | |
set_error ( client, "UNIX socket path is too long (len=%d)", len ); | |
memset ( &uaddr, 0, sizeof(uaddr) ); | |
uaddr.sun_family = AF_UNIX; | |
memcpy ( uaddr.sun_path, client->host, len + 1 ); | |
sock = socket ( AF_UNIX, SOCK_STREAM, 0 ); | |
if ( sock<0 ) | |
{ | |
set_error ( client, "UNIX socket() failed: %s", sock_error() ); | |
return -1; | |
} | |
if ( sock_set_nonblocking ( sock )<0 ) | |
{ | |
set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() ); | |
return -1; | |
} | |
optval = 1; | |
#if defined(SO_NOSIGPIPE) | |
if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 ) | |
{ | |
set_error ( client, "setsockopt() failed: %s", sock_error() ); | |
return -1; | |
} | |
#endif | |
res = connect ( sock, (struct sockaddr *)&uaddr, sizeof(uaddr) ); | |
if ( res==0 ) | |
return sock; | |
err = sock_errno(); | |
#ifdef EINPROGRESS | |
if ( err!=EWOULDBLOCK && err!=EINPROGRESS ) | |
#else | |
if ( err!=EWOULDBLOCK ) | |
#endif | |
{ | |
set_error ( client, "connect() failed: %s", sock_error() ); | |
return -1; | |
} | |
return sock; | |
} | |
#endif | |
static int net_connect_get ( sphinx_client * client ) | |
{ | |
struct timeval timeout; | |
fd_set fds_write; | |
int sock, to_wait, res, my_proto; | |
if ( client->sock>=0 ) | |
return client->sock; | |
sock = -1; | |
if ( client->host[0]!='/' ) | |
{ | |
sock = net_create_inet_sock ( client ); | |
} else | |
{ | |
#ifdef _WIN32 | |
set_error ( client, "UNIX sockets are not supported on Windows" ); | |
return -1; | |
#else | |
sock = net_create_unix_sock ( client ); | |
#endif | |
} | |
if ( sock<0 ) | |
return -1; | |
to_wait = (int)( 1000*client->timeout ); | |
if ( to_wait<=0 ) | |
to_wait = CONNECT_TIMEOUT_MSEC; | |
{ | |
timeout.tv_sec = to_wait / 1000; // full seconds | |
timeout.tv_usec = ( to_wait % 1000 ) * 1000; // remainder is msec, so *1000 for usec | |
FD_ZERO ( &fds_write ); | |
SPH_FD_SET ( sock, &fds_write ); | |
res = select ( 1+sock, NULL, &fds_write, NULL, &timeout ); | |
if ( res>=0 && FD_ISSET ( sock, &fds_write ) ) | |
{ | |
sock_set_blocking ( sock ); | |
// now send major client protocol version | |
my_proto = htonl ( 1 ); | |
if ( !net_write ( sock, (char*)&my_proto, sizeof(my_proto), client ) ) | |
{ | |
sock_close ( sock ); | |
set_error ( client, "failed to send client protocol version" ); | |
return -1; | |
} | |
// check daemon version | |
if ( !net_read ( sock, (char*)&my_proto, sizeof(my_proto), client ) ) | |
{ | |
sock_close ( sock ); | |
return -1; | |
} | |
my_proto = ntohl ( my_proto ); | |
if ( my_proto<1 ) | |
{ | |
sock_close ( sock ); | |
set_error ( client, "expected searchd protocol version 1+, got version %d", my_proto ); | |
return -1; | |
} | |
return sock; | |
} | |
/*!COMMIT handle EINTR here*/ | |
sock_close ( sock ); | |
set_error ( client, "connect() timed out" ); | |
return -1; | |
} | |
} | |
static sphinx_bool net_sock_eof ( int sock ) | |
{ | |
struct timeval tv; | |
fd_set fds_read, fds_except; | |
int res; | |
char buf; | |
// wrong arg, consider dead | |
if ( sock<0 ) | |
return SPH_TRUE; | |
// select() on a socket and watch for exceptions | |
FD_ZERO ( &fds_read ); | |
FD_ZERO ( &fds_except ); | |
SPH_FD_SET ( sock, &fds_read ); | |
SPH_FD_SET ( sock, &fds_except ); | |
tv.tv_sec = 0; | |
tv.tv_usec = 0; | |
res = select ( 1+sock, &fds_read, NULL, &fds_except, &tv ); | |
// select() failed, assume something is wrong | |
if ( res<0 ) | |
return SPH_TRUE; | |
// got any events to read? (either normal via fds_read, or OOB via fds_except set) | |
if ( FD_ISSET ( sock, &fds_read ) || FD_ISSET ( sock, &fds_except ) ) | |
if ( recv ( sock, &buf, sizeof(buf), MSG_PEEK )<=0 ) | |
if ( sock_errno()!=EWOULDBLOCK ) | |
return SPH_TRUE; | |
// it seems alive | |
return SPH_FALSE; | |
} | |
static int net_connect_ex ( sphinx_client * client ) | |
{ | |
if ( client->sock>=0 ) | |
{ | |
// in case of a persistent connection, check for eof | |
// then attempt to reestablish lost pconn once | |
if ( !net_sock_eof ( client->sock ) ) | |
return client->sock; | |
sock_close ( client->sock ); | |
client->sock = -1; | |
} | |
if ( !client->persist ) | |
return net_connect_get ( client ); | |
sphinx_open ( client ); | |
return client->sock; | |
} | |
static unsigned short unpack_short ( char ** cur ) | |
{ | |
unsigned short v; | |
memcpy ( &v, *cur, sizeof(unsigned short) ); | |
(*cur) += sizeof(unsigned short); | |
return ntohs ( v ); | |
} | |
static unsigned int unpack_int ( char ** cur ) | |
{ | |
unsigned int v; | |
memcpy ( &v, *cur, sizeof(unsigned int) ); | |
(*cur) += sizeof(unsigned int); | |
return ntohl ( v ); | |
} | |
static char * unpack_str ( char ** cur ) | |
{ | |
// we play a trick | |
// we move the string in-place to free space for trailing zero but avoid malloc | |
unsigned int len; | |
len = unpack_int ( cur ); | |
memmove ( (*cur)-1, (*cur), len ); | |
(*cur) += len; | |
(*cur)[-1] = '\0'; | |
return (*cur)-len-1; | |
} | |
static sphinx_uint64_t unpack_qword ( char ** cur ) | |
{ | |
sphinx_uint64_t hi, lo; | |
hi = unpack_int ( cur ); | |
lo = unpack_int ( cur ); | |
return ( hi<<32 ) + lo; | |
} | |
static float unpack_float ( char ** cur ) | |
{ | |
union | |
{ | |
unsigned int n; | |
float f; | |
} u; | |
u.n = unpack_int ( cur ); | |
return u.f; | |
} | |
static void net_get_response ( int fd, sphinx_client * client ) | |
{ | |
int len; | |
char header_buf[32], *cur, *response; | |
unsigned short status, ver; | |
// dismiss previous response | |
if ( client->response_buf ) | |
{ | |
free ( client->response_buf ); | |
client->response_len = 0; | |
client->response_buf = NULL; | |
} | |
// read and parse the header | |
if ( !net_read ( fd, header_buf, 8, client ) ) | |
{ | |
sock_close ( fd ); | |
if ( client->sock>0 ) | |
client->sock = -1; | |
return; | |
} | |
cur = header_buf; | |
status = unpack_short ( &cur ); | |
ver = unpack_short ( &cur ); | |
len = unpack_int ( &cur ); | |
// sanity check the length, alloc the buffer | |
if ( len<0 || len>MAX_PACKET_LEN ) | |
{ | |
sock_close ( fd ); | |
if ( client->sock>0 ) | |
client->sock = -1; | |
set_error ( client, "response length out of bounds (len=%d)", len ); | |
return; | |
} | |
response = malloc ( len ); | |
if ( !response ) | |
{ | |
sock_close ( fd ); | |
if ( client->sock>0 ) | |
client->sock = -1; | |
set_error ( client, "malloc() failed (bytes=%d)", len ); | |
return; | |
} | |
// read the response | |
if ( !net_read ( fd, response, len, client ) ) | |
{ | |
sock_close ( fd ); | |
if ( client->sock>0 ) | |
client->sock = -1; | |
free ( response ); | |
return; | |
} | |
// check status | |
cur = response; | |
switch ( status ) | |
{ | |
case SEARCHD_OK: | |
case SEARCHD_WARNING: | |
client->error = NULL; // so far so good | |
if ( status==SEARCHD_WARNING ) | |
client->warning = unpack_str ( &cur ); | |
else | |
client->warning = NULL; | |
client->response_len = len; | |
client->response_buf = response; | |
client->response_start = cur; | |
break; | |
case SEARCHD_ERROR: | |
case SEARCHD_RETRY: | |
// copy error message, so that we can immediately free the response | |
set_error ( client, "%s", unpack_str ( &cur ) ); | |
free ( response ); | |
break; | |
default: | |
set_error ( client, "unknown status code (status=%d)", status ); | |
free ( response ); | |
break; | |
} | |
// close one-time socket on success | |
if ( client->sock<0 ) | |
sock_close ( fd ); | |
} | |
sphinx_bool sphinx_open ( sphinx_client * client ) | |
{ | |
char buf[16], *pbuf; | |
if ( client->sock>=0 ) | |
{ | |
set_error ( client, "already connected" ); | |
return SPH_FALSE; | |
} | |
client->sock = net_connect_get ( client ); | |
if ( client->sock<0 ) | |
return SPH_FALSE; | |
pbuf = buf; | |
send_word ( &pbuf, SEARCHD_COMMAND_PERSIST ); | |
send_word ( &pbuf, 0 ); // dummy version | |
send_int ( &pbuf, 4 ); // dummy body len | |
send_int ( &pbuf, 1 ); // dummy body | |
if ( !net_write ( client->sock, buf, (int)(pbuf-buf), client ) ) | |
{ | |
sock_close ( client->sock ); | |
client->sock = -1; | |
return SPH_FALSE; | |
} | |
client->persist = SPH_TRUE; | |
return SPH_TRUE; | |
} | |
sphinx_bool sphinx_close ( sphinx_client * client ) | |
{ | |
if ( client->sock<0 ) | |
{ | |
set_error ( client, "not connected" ); | |
return SPH_FALSE; | |
} | |
sock_close ( client->sock ); | |
client->sock = -1; | |
client->persist = SPH_FALSE; | |
return SPH_TRUE; | |
} | |
static void * sphinx_malloc ( int len, sphinx_client * client ) | |
{ | |
void * res; | |
if ( len<0 || len>MAX_PACKET_LEN ) | |
{ | |
set_error ( client, "malloc() length out of bounds, possibly broken response packet (len=%d)", len ); | |
return NULL; | |
} | |
res = malloc ( len ); | |
if ( !res ) | |
set_error ( client, "malloc() failed (bytes=%d)", len ); | |
return res; | |
} | |
sphinx_result * sphinx_run_queries ( sphinx_client * client ) | |
{ | |
int i, j, k, l, fd, len, nreqs, id64; | |
char req_header[32], *req, *p, *pmax; | |
sphinx_result * res; | |
union un_attr_value * pval; | |
if ( !client ) | |
return NULL; | |
if ( client->num_reqs<=0 || client->num_reqs>MAX_REQS ) | |
{ | |
set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs ); | |
return NULL; | |
} | |
fd = net_connect_ex ( client ); | |
if ( fd<0 ) | |
return NULL; | |
// free previous results | |
sphinx_free_results ( client ); | |
// send query, get response | |
len = 8; | |
for ( i=0; i<client->num_reqs; i++ ) | |
len += client->req_lens[i]; | |
req = req_header; | |
send_word ( &req, SEARCHD_COMMAND_SEARCH ); | |
send_word ( &req, client->ver_search ); | |
send_int ( &req, len ); | |
send_int ( &req, 0 ); // its a client | |
send_int ( &req, client->num_reqs ); | |
if ( !net_write ( fd, req_header, (int)(req-req_header), client ) ) | |
return NULL; | |
for ( i=0; i<client->num_reqs; i++ ) | |
if ( !net_write ( fd, client->reqs[i], client->req_lens[i], client ) ) | |
return NULL; | |
net_get_response ( fd, client ); | |
if ( !client->response_buf ) | |
return NULL; | |
// dismiss request data, memorize count | |
nreqs = sphinx_dismiss_requests ( client ); | |
// parse response | |
p = client->response_start; | |
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses | |
for ( i=0; i<nreqs && p<pmax; i++ ) | |
{ | |
res = &client->results[i]; | |
client->num_results++; | |
res->error = NULL; | |
res->warning = NULL; | |
res->status = unpack_int ( &p ); | |
if ( res->status!=SEARCHD_OK ) | |
{ | |
if ( res->status==SEARCHD_WARNING ) | |
{ | |
res->warning = unpack_str ( &p ); | |
} else | |
{ | |
res->error = unpack_str ( &p ); | |
continue; | |
} | |
} | |
// fields | |
res->num_fields = unpack_int ( &p ); | |
res->fields = sphinx_malloc ( res->num_fields*sizeof(const char*), client ); | |
if ( !res->fields ) | |
return NULL; | |
for ( j=0; j<res->num_fields; j++ ) | |
res->fields[j] = unpack_str ( &p ); | |
// attrs | |
res->num_attrs = unpack_int ( &p ); | |
res->attr_names = sphinx_malloc ( res->num_attrs*sizeof(const char*), client ); | |
if ( !res->attr_names ) | |
return NULL; | |
res->attr_types = sphinx_malloc ( res->num_attrs*sizeof(int), client ); | |
if ( !res->attr_types ) | |
return NULL; | |
for ( j=0; j<res->num_attrs; j++ ) | |
{ | |
res->attr_names[j] = unpack_str ( &p ); | |
res->attr_types[j] = unpack_int ( &p ); | |
} | |
// match count, id bits flag | |
res->num_matches = unpack_int ( &p ); | |
id64 = unpack_int ( &p ); | |
res->values_pool = sphinx_malloc ( (2+res->num_attrs) * res->num_matches * sizeof(union un_attr_value), client ); | |
if ( !res->values_pool ) | |
return NULL; | |
pval = res->values_pool; | |
// matches | |
for ( j=0; j<res->num_matches; j++ ) | |
{ | |
// id | |
if ( id64 ) | |
pval->int_value = unpack_qword ( &p ); | |
else | |
pval->int_value = unpack_int ( &p ); | |
pval++; | |
// weight | |
pval->int_value = unpack_int ( &p ); | |
pval++; | |
// attrs | |
for ( k=0; k<res->num_attrs; k++ ) | |
{ | |
switch ( res->attr_types[k] ) | |
{ | |
case SPH_ATTR_MULTI64: | |
case SPH_ATTR_MULTI: | |
/*!COMMIT this is totally unsafe on some arches (eg. SPARC)*/ | |
pval->mva_value = (unsigned int *) p; | |
len = unpack_int ( &p ); | |
for ( l=0; l<=len; l++ ) // including first one that is len | |
pval->mva_value[l] = ntohl ( pval->mva_value[l] ); | |
if ( res->attr_types[k]==SPH_ATTR_MULTI64 ) | |
{ | |
pval->mva_value[0] = pval->mva_value[0]/2; | |
} | |
p += len*sizeof(unsigned int); | |
break; | |
case SPH_ATTR_FLOAT: pval->float_value = unpack_float ( &p ); break; | |
case SPH_ATTR_BIGINT: pval->int_value = unpack_qword ( &p ); break; | |
case SPH_ATTR_STRING: pval->string = unpack_str ( &p ); break; | |
case SPH_ATTR_FACTORS: | |
len = unpack_int ( &p ); | |
if ( len ) | |
p += len-sizeof(unsigned int); | |
break; | |
default: pval->int_value = unpack_int ( &p ); break; | |
} | |
pval++; | |
} | |
} | |
// totals | |
res->total = unpack_int ( &p ); | |
res->total_found = unpack_int ( &p ); | |
res->time_msec = unpack_int ( &p ); | |
res->num_words = unpack_int ( &p ); | |
if ( res->words ) | |
free ( res->words ); | |
res->words = sphinx_malloc ( res->num_words*sizeof(struct st_sphinx_wordinfo), client ); | |
if ( !res->words ) | |
return NULL; | |
// words | |
for ( j=0; j<res->num_words; j++ ) | |
{ | |
res->words[j].word = unpack_str ( &p ); | |
res->words[j].docs = unpack_int ( &p ); | |
res->words[j].hits = unpack_int ( &p ); | |
} | |
// sanity check | |
// FIXME! add it to each unpack? | |
if ( p>pmax ) | |
{ | |
set_error ( client, "unpack error (req=%d, reqs=%d)", i, nreqs ); | |
return NULL; | |
} | |
} | |
return client->results; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
int sphinx_get_num_results ( sphinx_client * client ) | |
{ | |
return client ? client->num_results : -1; | |
} | |
sphinx_uint64_t sphinx_get_id ( sphinx_result * result, int match ) | |
{ | |
return sphinx_get_int ( result, match, -2 ); | |
} | |
int sphinx_get_weight ( sphinx_result * result, int match ) | |
{ | |
return (int)sphinx_get_int ( result, match, -1 ); | |
} | |
sphinx_int64_t sphinx_get_int ( sphinx_result * result, int match, int attr ) | |
{ | |
// FIXME! add safety and type checks | |
union un_attr_value * pval; | |
pval = result->values_pool; | |
return pval [ (2+result->num_attrs)*match+2+attr ].int_value; | |
} | |
float sphinx_get_float ( sphinx_result * result, int match, int attr ) | |
{ | |
// FIXME! add safety and type checks | |
union un_attr_value * pval; | |
pval = result->values_pool; | |
return pval [ (2+result->num_attrs)*match+2+attr ].float_value; | |
} | |
unsigned int * sphinx_get_mva ( sphinx_result * result, int match, int attr ) | |
{ | |
// FIXME! add safety and type checks | |
union un_attr_value * pval; | |
pval = result->values_pool; | |
return pval [ (2+result->num_attrs)*match+2+attr ].mva_value; | |
} | |
sphinx_uint64_t sphinx_get_mva64_value ( unsigned int * mva, int i ) | |
{ | |
sphinx_uint64_t uVal; | |
uVal = ( ( ( (sphinx_uint64_t)( mva[i*2] ) )<<32 ) | (sphinx_uint64_t)( mva[i*2+1] ) ); | |
return uVal; | |
} | |
const char * sphinx_get_string ( sphinx_result * result, int match, int attr ) | |
{ | |
// FIXME! add safety and type checks | |
union un_attr_value * pval; | |
pval = result->values_pool; | |
return pval [ (2+result->num_attrs)*match+2+attr ].string; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
static sphinx_bool net_simple_query ( sphinx_client * client, char * buf, int req_len ) | |
{ | |
int fd; | |
fd = net_connect_ex ( client ); | |
if ( fd<0 ) | |
{ | |
free ( buf ); | |
return SPH_FALSE; | |
} | |
if ( !net_write ( fd, buf, 8+req_len, client ) ) | |
{ | |
free ( buf ); | |
return SPH_FALSE; | |
} | |
free ( buf ); | |
net_get_response ( fd, client ); | |
if ( !client->response_buf ) | |
return SPH_FALSE; | |
return SPH_TRUE; | |
} | |
void sphinx_init_excerpt_options ( sphinx_excerpt_options * opts ) | |
{ | |
if ( !opts ) | |
return; | |
opts->before_match = "<b>"; | |
opts->after_match = "</b>"; | |
opts->chunk_separator = " ... "; | |
opts->html_strip_mode = "index"; | |
opts->passage_boundary = "none"; | |
opts->limit = 256; | |
opts->limit_passages = 0; | |
opts->limit_words = 0; | |
opts->around = 5; | |
opts->start_passage_id = 1; | |
opts->exact_phrase = SPH_FALSE; | |
opts->single_passage = SPH_FALSE; | |
opts->use_boundaries = SPH_FALSE; | |
opts->weight_order = SPH_FALSE; | |
opts->query_mode = SPH_FALSE; | |
opts->force_all_words = SPH_FALSE; | |
opts->load_files = SPH_FALSE; | |
opts->allow_empty = SPH_FALSE; | |
opts->emit_zones = SPH_FALSE; | |
} | |
char ** sphinx_build_excerpts ( sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts ) | |
{ | |
sphinx_excerpt_options def_opt; | |
int i, req_len, flags; | |
char *buf, *req, *p, *pmax, **result; | |
if ( !client || !docs || !index || !words || num_docs<=0 ) | |
{ | |
if ( !docs ) set_error ( client, "invalid arguments (docs must not be empty)" ); | |
else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" ); | |
else if ( !words ) set_error ( client, "invalid arguments (words must not be empty)" ); | |
else if ( num_docs<=0 ) set_error ( client, "invalid arguments (num_docs must be positive)" ); | |
return NULL; | |
} | |
// fixup options | |
if ( !opts ) | |
{ | |
sphinx_init_excerpt_options ( &def_opt ); | |
opts = &def_opt; | |
} | |
// alloc buffer | |
req_len = (int)( 60 | |
+ strlen(index) | |
+ strlen(words) | |
+ safestrlen(opts->before_match) | |
+ safestrlen(opts->after_match) | |
+ safestrlen(opts->chunk_separator) | |
+ safestrlen(opts->html_strip_mode) | |
+ safestrlen(opts->passage_boundary) ); | |
for ( i=0; i<num_docs; i++ ) | |
req_len += (int)( 4 + safestrlen(docs[i]) ); | |
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes | |
if ( !buf ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", req_len ); | |
return NULL; | |
} | |
// build request | |
req = buf; | |
send_word ( &req, SEARCHD_COMMAND_EXCERPT ); | |
send_word ( &req, VER_COMMAND_EXCERPT ); | |
send_int ( &req, req_len ); | |
flags = 1; // remove spaces | |
if ( opts->exact_phrase ) flags |= 2; | |
if ( opts->single_passage ) flags |= 4; | |
if ( opts->use_boundaries ) flags |= 8; | |
if ( opts->weight_order ) flags |= 16; | |
if ( opts->query_mode ) flags |= 32; | |
if ( opts->force_all_words ) flags |= 64; | |
if ( opts->load_files ) flags |= 128; | |
if ( opts->allow_empty ) flags |= 256; | |
if ( opts->emit_zones ) flags |= 512; | |
send_int ( &req, 0 ); | |
send_int ( &req, flags ); | |
send_str ( &req, index ); | |
send_str ( &req, words ); | |
send_str ( &req, opts->before_match ); | |
send_str ( &req, opts->after_match ); | |
send_str ( &req, opts->chunk_separator ); | |
send_int ( &req, opts->limit ); | |
send_int ( &req, opts->around ); | |
send_int ( &req, opts->limit_passages ); // v1.2 | |
send_int ( &req, opts->limit_words ); | |
send_int ( &req, opts->start_passage_id ); | |
send_str ( &req, opts->html_strip_mode ); | |
send_str ( &req, opts->passage_boundary ); | |
send_int ( &req, num_docs ); | |
for ( i=0; i<num_docs; i++ ) | |
send_str ( &req, docs[i] ); | |
if ( (int)(req-buf)!=8+req_len ) | |
{ | |
set_error ( client, "internal error: failed to build request in sphinx_build_excerpts()" ); | |
free ( buf ); | |
return NULL; | |
} | |
// send query, get response | |
if ( !net_simple_query ( client, buf, req_len ) ) | |
return NULL; | |
// parse response | |
p = client->response_start; | |
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses | |
result = malloc ( (1+num_docs)*sizeof(char*) ); | |
if ( !result ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", (1+num_docs)*sizeof(char*) ); | |
return NULL; | |
} | |
for ( i=0; i<=num_docs; i++ ) | |
result[i] = NULL; | |
for ( i=0; i<num_docs && p<pmax; i++ ) | |
result[i] = strdup ( unpack_str ( &p ) ); | |
if ( p>pmax ) | |
{ | |
for ( i=0; i<num_docs; i++ ) | |
if ( result[i] ) | |
free ( result[i] ); | |
set_error ( client, "unpack error" ); | |
return NULL; | |
} | |
// done | |
return result; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
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 ) | |
{ | |
int i, j, req_len; | |
char *buf, *req, *p; | |
// check args | |
if ( !client || num_attrs<=0 || !attrs || num_docs<=0 || !docids || !values ) | |
{ | |
if ( num_attrs<=0 ) set_error ( client, "invalid arguments (num_attrs must be positive)" ); | |
else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" ); | |
else if ( !attrs ) set_error ( client, "invalid arguments (attrs must not empty)" ); | |
else if ( num_docs<=0 ) set_error ( client, "invalid arguments (num_docs must be positive)" ); | |
else if ( !docids ) set_error ( client, "invalid arguments (docids must not be empty)" ); | |
else if ( !values ) set_error ( client, "invalid arguments (values must not be empty)" ); | |
} | |
// alloc buffer | |
req_len = (int)( 12 + safestrlen(index) + (12+4*num_attrs)*num_docs ); | |
for ( i=0; i<num_attrs; i++ ) | |
req_len += (int)( 4 + safestrlen(attrs[i]) ); | |
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes | |
if ( !buf ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", req_len ); | |
return -1; | |
} | |
// build request | |
req = buf; | |
send_word ( &req, SEARCHD_COMMAND_UPDATE ); | |
send_word ( &req, VER_COMMAND_UPDATE ); | |
send_int ( &req, req_len ); | |
send_str ( &req, index ); | |
send_int ( &req, num_attrs ); | |
for ( i=0; i<num_attrs; i++ ) | |
{ | |
send_str ( &req, attrs[i] ); | |
send_int ( &req, 0 ); // not SPH_ATTR_MULTI flag | |
} | |
send_int ( &req, num_docs ); | |
for ( i=0; i<num_docs; i++ ) | |
{ | |
send_qword ( &req, docids[i] ); | |
for ( j=0; j<num_attrs; j++ ) | |
send_int ( &req, (unsigned int)( *values++ ) ); | |
} | |
// send query, get response | |
if ( !net_simple_query ( client, buf, req_len ) ) | |
return -1; | |
// parse response | |
if ( client->response_len<4 ) | |
{ | |
set_error ( client, "incomplete reply" ); | |
return -1; | |
} | |
p = client->response_start; | |
return unpack_int ( &p ); | |
} | |
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 ) | |
{ | |
int i, req_len; | |
char *buf, *req, *p; | |
// check args | |
if ( !client || !index || !attr || num_values<=0 || !values ) | |
{ | |
if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" ); | |
else if ( !attr ) set_error ( client, "invalid arguments (attr must not empty)" ); | |
else if ( num_values<=0 ) set_error ( client, "invalid arguments (num_values must be positive)" ); | |
else if ( !values ) set_error ( client, "invalid arguments (values must not be empty)" ); | |
} | |
// alloc buffer | |
req_len = (int)( 38 + safestrlen(index) + safestrlen(attr) + num_values*4 ); | |
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes | |
if ( !buf ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", req_len ); | |
return -1; | |
} | |
// build request | |
req = buf; | |
send_word ( &req, SEARCHD_COMMAND_UPDATE ); | |
send_word ( &req, VER_COMMAND_UPDATE ); | |
send_int ( &req, req_len ); | |
send_str ( &req, index ); | |
send_int ( &req, 1 ); | |
send_str ( &req, attr ); | |
send_int ( &req, 1 ); // SPH_ATTR_MULTI flag | |
send_int ( &req, 1 ); | |
send_qword ( &req, docid ); | |
send_int ( &req, num_values ); | |
for ( i=0; i<num_values; i++ ) | |
send_int ( &req, values[i] ); | |
// send query, get response | |
if ( !net_simple_query ( client, buf, req_len ) ) | |
return -1; | |
// parse response | |
if ( client->response_len<4 ) | |
{ | |
set_error ( client, "incomplete reply" ); | |
return -1; | |
} | |
p = client->response_start; | |
return unpack_int ( &p ); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
// add by dengsl 20140711 | |
int sphinx_flush_attributes(sphinx_client * client) { | |
char *buf, *req, *p; | |
int req_len = 0; | |
if (!client) { | |
printf("not valid client\n"); | |
return -1; | |
} | |
buf = malloc ( 12 + req_len ); // request body length plus 12 header bytes | |
if ( !buf ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", req_len ); | |
return -1; | |
} | |
req = buf; | |
send_word ( &req, SEARCHD_COMMAND_FLUSHATTRS ); | |
send_word ( &req, VER_COMMAND_FLUSHATTRS ); | |
send_int ( &req, req_len ); | |
// send query, get response | |
if ( !net_simple_query ( client, buf, req_len ) ) | |
return -1; | |
// parse response | |
if ( client->response_len < 4 ) | |
{ | |
set_error ( client, "incomplete reply" ); | |
return -1; | |
} | |
p = client->response_start; | |
return unpack_int ( &p ); | |
} | |
//add end | |
sphinx_keyword_info * sphinx_build_keywords ( sphinx_client * client, const char * query, const char * index, sphinx_bool hits, int * out_num_keywords ) | |
{ | |
int i, req_len, nwords, len; | |
char *buf, *req, *p, *pmax; | |
sphinx_keyword_info *res; | |
// check args | |
if ( !client || !query || !index ) | |
{ | |
if ( !query ) set_error ( client, "invalid arguments (query must not be empty)" ); | |
else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" ); | |
else if ( !out_num_keywords ) set_error ( client, "invalid arguments (out_num_keywords must not be null)" ); | |
return NULL; | |
} | |
// alloc buffer | |
req_len = (int)( safestrlen(query) + safestrlen(index) + 12 ); | |
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes | |
if ( !buf ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", req_len ); | |
return NULL; | |
} | |
// build request | |
req = buf; | |
send_word ( &req, SEARCHD_COMMAND_KEYWORDS ); | |
send_word ( &req, VER_COMMAND_KEYWORDS ); | |
send_int ( &req, req_len ); | |
send_str ( &req, query ); | |
send_str ( &req, index ); | |
send_int ( &req, hits ); | |
// send query, get response | |
if ( !net_simple_query ( client, buf, req_len ) ) | |
return NULL; | |
// parse response | |
p = client->response_start; | |
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses | |
nwords = unpack_int ( &p ); | |
*out_num_keywords = nwords; | |
len = nwords*sizeof(sphinx_keyword_info); | |
res = (sphinx_keyword_info*) malloc ( len ); | |
if ( !res ) | |
{ | |
set_error ( client, "malloc() failed (bytes=%d)", len ); | |
return NULL; | |
} | |
memset ( res, 0, len ); | |
for ( i=0; i<nwords && p<pmax; i++ ) | |
{ | |
res[i].tokenized = strdup ( unpack_str ( &p ) ); | |
res[i].normalized = strdup ( unpack_str ( &p ) ); | |
if ( hits ) | |
{ | |
res[i].num_docs = unpack_int ( &p ); | |
res[i].num_hits = unpack_int ( &p ); | |
} | |
} | |
// FIXME! add check for incomplete reply | |
return res; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
char ** sphinx_status ( sphinx_client * client, int * num_rows, int * num_cols ) | |
{ | |
return sphinx_status_extended ( client, num_rows, num_cols, 0 ); | |
} | |
char ** sphinx_status_extended ( sphinx_client * client, int * num_rows, int * num_cols, int local ) | |
{ | |
int i, j, k, n; | |
char *p, *pmax, *req, *buf, **res; | |
// check args | |
if ( !client || !num_rows || !num_cols ) | |
{ | |
if ( !num_rows ) set_error ( client, "invalid arguments (num_rows must not be NULL)" ); | |
else if ( !num_cols ) set_error ( client, "invalid arguments (num_cols must not be NULL)" ); | |
return NULL; | |
} | |
// build request | |
buf = malloc ( 12 ); | |
if ( !buf ) | |
{ | |
set_error ( client, "malloc() failed (bytes=12)" ); | |
return NULL; | |
} | |
if (local) | |
local=0; | |
else | |
local=1; | |
req = buf; | |
send_word ( &req, SEARCHD_COMMAND_STATUS ); | |
send_word ( &req, VER_COMMAND_STATUS ); | |
send_int ( &req, 4 ); | |
send_int ( &req, local ); | |
// send query, get response | |
if ( !net_simple_query ( client, buf, 12 ) ) | |
return NULL; | |
// parse response | |
p = client->response_start; | |
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses | |
*num_rows = unpack_int ( &p ); | |
*num_cols = unpack_int ( &p ); | |
n = (*num_rows)*(*num_cols); | |
res = (char**) malloc ( n*sizeof(char*) ); | |
for ( i=0; i<n; i++ ) | |
res[i] = NULL; | |
// FIXME! error checking? | |
k = 0; | |
for ( i=0; i<*num_rows; i++ ) | |
for ( j=0; j<*num_cols; j++ ) | |
res[k++] = strdup ( unpack_str ( &p ) ); | |
return res; | |
} | |
void sphinx_status_destroy ( char ** status, int num_rows, int num_cols ) | |
{ | |
int i; | |
for ( i=0; i<num_rows*num_cols; i++ ) | |
free ( status[i] ); | |
free ( status ); | |
} | |
// | |
// $Id: sphinxclient.c 4097 2013-08-20 09:28:24Z kevg $ | |
// |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// $Id: sphinxclient.h 3883 2013-05-24 07:15:19Z tomat $ | |
// | |
// | |
// Copyright (c) 2001-2013, Andrew Aksyonoff | |
// Copyright (c) 2008-2013, Sphinx Technologies Inc | |
// All rights reserved | |
// | |
// This program is free software; you can redistribute it and/or modify | |
// it under the terms of the GNU Library General Public License. You should | |
// have received a copy of the LGPL license along with this program; if you | |
// did not, you can find it at http://www.gnu.org/ | |
// | |
#ifndef _sphinxclient_ | |
#define _sphinxclient_ | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/// known searchd status codes | |
enum | |
{ | |
SEARCHD_OK = 0, | |
SEARCHD_ERROR = 1, | |
SEARCHD_RETRY = 2, | |
SEARCHD_WARNING = 3 | |
}; | |
/// known match modes | |
enum | |
{ | |
SPH_MATCH_ALL = 0, | |
SPH_MATCH_ANY = 1, | |
SPH_MATCH_PHRASE = 2, | |
SPH_MATCH_BOOLEAN = 3, | |
SPH_MATCH_EXTENDED = 4, | |
SPH_MATCH_FULLSCAN = 5, | |
SPH_MATCH_EXTENDED2 = 6 | |
}; | |
/// known ranking modes (ext2 only) | |
enum | |
{ | |
SPH_RANK_PROXIMITY_BM25 = 0, | |
SPH_RANK_BM25 = 1, | |
SPH_RANK_NONE = 2, | |
SPH_RANK_WORDCOUNT = 3, | |
SPH_RANK_PROXIMITY = 4, | |
SPH_RANK_MATCHANY = 5, | |
SPH_RANK_FIELDMASK = 6, | |
SPH_RANK_SPH04 = 7, | |
SPH_RANK_EXPR = 8, | |
SPH_RANK_TOTAL = 9, | |
SPH_RANK_DEFAULT = SPH_RANK_PROXIMITY_BM25 | |
}; | |
/// known sort modes | |
enum | |
{ | |
SPH_SORT_RELEVANCE = 0, | |
SPH_SORT_ATTR_DESC = 1, | |
SPH_SORT_ATTR_ASC = 2, | |
SPH_SORT_TIME_SEGMENTS = 3, | |
SPH_SORT_EXTENDED = 4, | |
SPH_SORT_EXPR = 5 | |
}; | |
/// known filter types | |
enum | |
{ SPH_FILTER_VALUES = 0, | |
SPH_FILTER_RANGE = 1, | |
SPH_FILTER_FLOATRANGE = 2, | |
SPH_FILTER_STRING = 3 | |
}; | |
/// known attribute types | |
enum | |
{ | |
SPH_ATTR_INTEGER = 1, | |
SPH_ATTR_TIMESTAMP = 2, | |
SPH_ATTR_ORDINAL = 3, | |
SPH_ATTR_BOOL = 4, | |
SPH_ATTR_FLOAT = 5, | |
SPH_ATTR_BIGINT = 6, | |
SPH_ATTR_STRING = 7, | |
SPH_ATTR_FACTORS = 1001, | |
SPH_ATTR_MULTI = 0x40000001UL, | |
SPH_ATTR_MULTI64 = 0x40000002UL | |
}; | |
/// known grouping functions | |
enum | |
{ SPH_GROUPBY_DAY = 0, | |
SPH_GROUPBY_WEEK = 1, | |
SPH_GROUPBY_MONTH = 2, | |
SPH_GROUPBY_YEAR = 3, | |
SPH_GROUPBY_ATTR = 4, | |
SPH_GROUPBY_ATTRPAIR = 5 | |
}; | |
////////////////////////////////////////////////////////////////////////// | |
#if defined(_MSC_VER) | |
typedef __int64 sphinx_int64_t; | |
typedef unsigned __int64 sphinx_uint64_t; | |
#else // !defined(_MSC_VER) | |
typedef long long sphinx_int64_t; | |
typedef unsigned long long sphinx_uint64_t; | |
#endif // !defined(_MSC_VER) | |
typedef int sphinx_bool; | |
#define SPH_TRUE 1 | |
#define SPH_FALSE 0 | |
////////////////////////////////////////////////////////////////////////// | |
typedef struct st_sphinx_client sphinx_client; | |
typedef struct st_sphinx_wordinfo | |
{ | |
const char * word; | |
int docs; | |
int hits; | |
} sphinx_wordinfo; | |
typedef struct st_sphinx_result | |
{ | |
const char * error; | |
const char * warning; | |
int status; | |
int num_fields; | |
char ** fields; | |
int num_attrs; | |
char ** attr_names; | |
int * attr_types; | |
int num_matches; | |
void * values_pool; | |
int total; | |
int total_found; | |
int time_msec; | |
int num_words; | |
sphinx_wordinfo * words; | |
} sphinx_result; | |
typedef struct st_sphinx_excerpt_options | |
{ | |
const char * before_match; | |
const char * after_match; | |
const char * chunk_separator; | |
const char * html_strip_mode; | |
const char * passage_boundary; | |
int limit; | |
int limit_passages; | |
int limit_words; | |
int around; | |
int start_passage_id; | |
sphinx_bool exact_phrase; | |
sphinx_bool single_passage; | |
sphinx_bool use_boundaries; | |
sphinx_bool weight_order; | |
sphinx_bool query_mode; | |
sphinx_bool force_all_words; | |
sphinx_bool load_files; | |
sphinx_bool allow_empty; | |
sphinx_bool emit_zones; | |
} sphinx_excerpt_options; | |
typedef struct st_sphinx_keyword_info | |
{ | |
char * tokenized; | |
char * normalized; | |
int num_docs; | |
int num_hits; | |
} sphinx_keyword_info; | |
////////////////////////////////////////////////////////////////////////// | |
sphinx_client * sphinx_create ( sphinx_bool copy_args ); | |
void sphinx_cleanup ( sphinx_client * client ); | |
void sphinx_destroy ( sphinx_client * client ); | |
const char * sphinx_error ( sphinx_client * client ); | |
const char * sphinx_warning ( sphinx_client * client ); | |
sphinx_bool sphinx_set_server ( sphinx_client * client, const char * host, int port ); | |
sphinx_bool sphinx_set_connect_timeout ( sphinx_client * client, float seconds ); | |
sphinx_bool sphinx_open ( sphinx_client * client ); | |
sphinx_bool sphinx_close ( sphinx_client * client ); | |
sphinx_bool sphinx_set_limits ( sphinx_client * client, int offset, int limit, int max_matches, int cutoff ); | |
sphinx_bool sphinx_set_max_query_time ( sphinx_client * client, int max_query_time ); | |
sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode ); | |
sphinx_bool sphinx_set_ranking_mode ( sphinx_client * client, int ranker, const char * rankexpr ); | |
sphinx_bool sphinx_set_sort_mode ( sphinx_client * client, int mode, const char * sortby ); | |
sphinx_bool sphinx_set_field_weights ( sphinx_client * client, int num_weights, const char ** field_names, const int * field_weights ); | |
sphinx_bool sphinx_set_index_weights ( sphinx_client * client, int num_weights, const char ** index_names, const int * index_weights ); | |
sphinx_bool sphinx_set_id_range ( sphinx_client * client, sphinx_uint64_t minid, sphinx_uint64_t maxid ); | |
sphinx_bool sphinx_add_filter ( sphinx_client * client, const char * attr, int num_values, const sphinx_int64_t * values, sphinx_bool exclude ); | |
sphinx_bool sphinx_add_filter_string ( sphinx_client * client, const char * attr, const char * value, sphinx_bool exclude ); | |
sphinx_bool sphinx_add_filter_range ( sphinx_client * client, const char * attr, sphinx_int64_t umin, sphinx_int64_t umax, sphinx_bool exclude ); | |
sphinx_bool sphinx_add_filter_float_range ( sphinx_client * client, const char * attr, float fmin, float fmax, sphinx_bool exclude ); | |
sphinx_bool sphinx_set_geoanchor ( sphinx_client * client, const char * attr_latitude, const char * attr_longitude, float latitude, float longitude ); | |
sphinx_bool sphinx_set_groupby ( sphinx_client * client, const char * attr, int groupby_func, const char * group_sort ); | |
sphinx_bool sphinx_set_groupby_distinct ( sphinx_client * client, const char * attr ); | |
sphinx_bool sphinx_set_retries ( sphinx_client * client, int count, int delay ); | |
sphinx_bool sphinx_add_override ( sphinx_client * client, const char * attr, const sphinx_uint64_t * docids, int num_values, const unsigned int * values ); | |
sphinx_bool sphinx_set_select ( sphinx_client * client, const char * select_list ); | |
sphinx_bool sphinx_set_query_flags ( sphinx_client * client, const char * flag_name, sphinx_bool enabled, int max_predicted_msec ); | |
void sphinx_reset_query_flags ( sphinx_client * client ); | |
sphinx_bool sphinx_set_outer_select ( sphinx_client * client, const char * orderby, int offset, int limit ); | |
void sphinx_reset_outer_select ( sphinx_client * client ); | |
void sphinx_reset_filters ( sphinx_client * client ); | |
void sphinx_reset_groupby ( sphinx_client * client ); | |
sphinx_result * sphinx_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment ); | |
int sphinx_add_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment ); | |
sphinx_result * sphinx_run_queries ( sphinx_client * client ); | |
int sphinx_get_num_results ( sphinx_client * client ); | |
sphinx_uint64_t sphinx_get_id ( sphinx_result * result, int match ); | |
int sphinx_get_weight ( sphinx_result * result, int match ); | |
sphinx_int64_t sphinx_get_int ( sphinx_result * result, int match, int attr ); | |
float sphinx_get_float ( sphinx_result * result, int match, int attr ); | |
unsigned int * sphinx_get_mva ( sphinx_result * result, int match, int attr ); | |
sphinx_uint64_t sphinx_get_mva64_value ( unsigned int * mva, int i ); | |
const char * sphinx_get_string ( sphinx_result * result, int match, int attr ); | |
void sphinx_init_excerpt_options ( sphinx_excerpt_options * opts ); | |
char ** sphinx_build_excerpts ( sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts ); | |
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 ); | |
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 ); | |
int sphinx_flush_attributes(sphinx_client * client); | |
sphinx_keyword_info * sphinx_build_keywords ( sphinx_client * client, const char * query, const char * index, sphinx_bool hits, int * out_num_keywords ); | |
char ** sphinx_status ( sphinx_client * client, int * num_rows, int * num_cols ); | |
char ** sphinx_status_extended ( sphinx_client * client, int * num_rows, int * num_cols, int local ); | |
void sphinx_status_destroy ( char ** status, int num_rows, int num_cols ); | |
///////////////////////////////////////////////////////////////////////////// | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif // _sphinxclient_ | |
// | |
// $Id: sphinxclient.h 3883 2013-05-24 07:15:19Z tomat $ | |
// |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment