| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676 |
- //
- // $Id$
- //
- //
- // Copyright (c) 2001-2014, Andrew Aksyonoff
- // Copyright (c) 2008-2014, 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/
- //
- // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- // WARNING
- // We strongly recommend you to use SphinxQL instead of an API
- // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- #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
- };
- enum
- {
- VER_COMMAND_EXCERPT = 0x103,
- VER_COMMAND_UPDATE = 0x102,
- VER_COMMAND_KEYWORDS = 0x100,
- VER_COMMAND_STATUS = 0x101
- };
- //////////////////////////////////////////////////////////////////////////
- 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;
- }
- // DEPRECATED
- sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode )
- {
- fprintf ( stderr, "DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API\n" );
- 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;
- }
- // DEPRECATED
- 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;
- fprintf ( stderr, "DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.\n" );
- 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 );
- }
- //////////////////////////////////////////////////////////////////////////
- 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$
- //
|