| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674 |
- //
- // Copyright (c) 2001-2016, Andrew Aksyonoff
- // Copyright (c) 2008-2016, Sphinx Technologies Inc
- // Copyright (c) 2017-2026, Manticore Software LTD (https://manticoresearch.com)
- // 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 version 3 or later.
- // 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>
- #ifndef EWOULDBLOCK
- #define EWOULDBLOCK WSAEWOULDBLOCK
- #endif
- #ifndef EINTR
- #define EINTR WSAEINTR
- #endif
- #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;
- 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_weights ) 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_weights ) 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 )
- {
- client->max_filters = ( client->max_filters<=0 ) ? client->num_filters + 8 : 2*client->max_filters;
- len = client->max_filters*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_error ( client, "reverse_scan is deprecated" );
- return SPH_FALSE;
- }
- 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 // !_WIN32
- #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;
- #if defined(SO_NOSIGPIPE)
- int optval;
- #endif
- 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;
- }
- #if defined(SO_NOSIGPIPE)
- optval = 1;
- 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 sockaddr_un uaddr;
- int sock, res, err, len;
- #if defined(SO_NOSIGPIPE)
- int optval;
- #endif
- 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;
- }
- #if defined(SO_NOSIGPIPE)
- optval = 1;
- 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;
- // 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 );
- unpack_short ( &cur ); // ver
- 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->single_passage = SPH_FALSE;
- opts->use_boundaries = SPH_FALSE;
- opts->weight_order = 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->single_passage ) flags |= 4;
- if ( opts->use_boundaries ) flags |= 8;
- if ( opts->weight_order ) flags |= 16;
- 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, *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;
- *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 );
- }
|