1 //
2 // $Id: snippets_udf.cc 3508 2012-11-05 11:48:48Z kevg $
3 //
4
5 //
6 // Copyright (c) 2001-2012, Andrew Aksyonoff
7 // Copyright (c) 2008-2012, Sphinx Technologies Inc
8 // All rights reserved
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License. You should have
12 // received a copy of the GPL license along with this program; if you
13 // did not, you can find it at http://www.gnu.org/
14 //
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <assert.h>
19
20 #include <sys/un.h>
21 #include <netdb.h>
22
23 #include <mysql_version.h>
24
25 #if MYSQL_VERSION_ID>=50515
26 #include "sql_class.h"
27 #include "sql_array.h"
28 #elif MYSQL_VERSION_ID>50100
29 #include "mysql_priv.h"
30 #include <mysql/plugin.h>
31 #else
32 #include "../mysql_priv.h"
33 #endif
34
35 #include <mysys_err.h>
36 #include <my_sys.h>
37
38 #if MYSQL_VERSION_ID>=50120
39 typedef uchar byte;
40 #endif
41
42 /// partially copy-pasted stuff that should be moved elsewhere
43
44 #if UNALIGNED_RAM_ACCESS
45
46 /// pass-through wrapper
sphUnalignedRead(const T & tRef)47 template < typename T > inline T sphUnalignedRead ( const T & tRef )
48 {
49 return tRef;
50 }
51
52 /// pass-through wrapper
sphUnalignedWrite(void * pPtr,const T & tVal)53 template < typename T > void sphUnalignedWrite ( void * pPtr, const T & tVal )
54 {
55 *(T*)pPtr = tVal;
56 }
57
58 #else
59
60 /// unaligned read wrapper for some architectures (eg. SPARC)
61 template < typename T >
sphUnalignedRead(const T & tRef)62 inline T sphUnalignedRead ( const T & tRef )
63 {
64 T uTmp;
65 byte * pSrc = (byte *) &tRef;
66 byte * pDst = (byte *) &uTmp;
67 for ( int i=0; i<(int)sizeof(T); i++ )
68 *pDst++ = *pSrc++;
69 return uTmp;
70 }
71
72 /// unaligned write wrapper for some architectures (eg. SPARC)
73 template < typename T >
sphUnalignedWrite(void * pPtr,const T & tVal)74 void sphUnalignedWrite ( void * pPtr, const T & tVal )
75 {
76 byte * pDst = (byte *) pPtr;
77 byte * pSrc = (byte *) &tVal;
78 for ( int i=0; i<(int)sizeof(T); i++ )
79 *pDst++ = *pSrc++;
80 }
81
82 #endif
83
84 #define SPHINXSE_MAX_ALLOC (16*1024*1024)
85
86 #define SafeDelete(_arg) { if ( _arg ) delete ( _arg ); (_arg) = NULL; }
87 #define SafeDeleteArray(_arg) { if ( _arg ) delete [] ( _arg ); (_arg) = NULL; }
88
89 #define Min(a,b) ((a)<(b)?(a):(b))
90
91 typedef unsigned int DWORD;
92
sphF2DW(float f)93 inline DWORD sphF2DW ( float f ) { union { float f; uint32 d; } u; u.f = f; return u.d; }
94
sphDup(const char * sSrc,int iLen=-1)95 static char * sphDup ( const char * sSrc, int iLen=-1 )
96 {
97 if ( !sSrc )
98 return NULL;
99
100 if ( iLen<0 )
101 iLen = strlen(sSrc);
102
103 char * sRes = new char [ 1+iLen ];
104 memcpy ( sRes, sSrc, iLen );
105 sRes[iLen] = '\0';
106 return sRes;
107 }
108
sphShowErrno(const char * sCall)109 static inline void sphShowErrno ( const char * sCall )
110 {
111 char sError[256];
112 snprintf ( sError, sizeof(sError), "%s() failed: [%d] %s", sCall, errno, strerror(errno) );
113 my_error ( ER_QUERY_ON_FOREIGN_DATA_SOURCE, MYF(0), sError );
114 }
115
116 static const bool sphReportErrors = true;
117
sphSend(int iFd,const char * pBuffer,int iSize,bool bReportErrors=false)118 static bool sphSend ( int iFd, const char * pBuffer, int iSize, bool bReportErrors = false )
119 {
120 assert ( pBuffer );
121 assert ( iSize > 0 );
122
123 const int iResult = send ( iFd, pBuffer, iSize, 0 );
124 if ( iResult!=iSize )
125 {
126 if ( bReportErrors ) sphShowErrno("send");
127 return false;
128 }
129 return true;
130 }
131
sphRecv(int iFd,char * pBuffer,int iSize,bool bReportErrors=false)132 static bool sphRecv ( int iFd, char * pBuffer, int iSize, bool bReportErrors = false )
133 {
134 assert ( pBuffer );
135 assert ( iSize > 0 );
136
137 while ( iSize )
138 {
139 const int iResult = recv ( iFd, pBuffer, iSize, 0 );
140 if ( iResult > 0 )
141 {
142 iSize -= iResult;
143 pBuffer += iSize;
144 } else if ( iResult==0 )
145 {
146 if ( bReportErrors )
147 my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), "recv() failed: disconnected" );
148 return false;
149 } else
150 {
151 if ( bReportErrors ) sphShowErrno("recv");
152 return false;
153 }
154 }
155 return true;
156 }
157
158 enum
159 {
160 SPHINX_SEARCHD_PROTO = 1,
161
162 SEARCHD_COMMAND_EXCERPT = 1,
163
164 VER_COMMAND_EXCERPT = 0x104,
165 };
166
167 /// known answers
168 enum
169 {
170 SEARCHD_OK = 0, ///< general success, command-specific reply follows
171 SEARCHD_ERROR = 1, ///< general failure, error message follows
172 SEARCHD_RETRY = 2, ///< temporary failure, error message follows, client should retry later
173 SEARCHD_WARNING = 3 ///< general success, warning message and command-specific reply follow
174 };
175
176 #define SPHINXSE_DEFAULT_SCHEME "sphinx"
177 #define SPHINXSE_DEFAULT_HOST "127.0.0.1"
178 #define SPHINXSE_DEFAULT_PORT 9312
179 #define SPHINXSE_DEFAULT_INDEX "*"
180
181 class CSphBuffer
182 {
183 private:
184 bool m_bOverrun;
185 int m_iSize;
186 int m_iLeft;
187 char * m_pBuffer;
188 char * m_pCurrent;
189
190 public:
CSphBuffer(const int iSize)191 explicit CSphBuffer ( const int iSize )
192 : m_bOverrun ( false )
193 , m_iSize ( iSize )
194 , m_iLeft ( iSize )
195 {
196 assert ( iSize > 0 );
197 m_pBuffer = new char[iSize];
198 m_pCurrent = m_pBuffer;
199 }
200
~CSphBuffer()201 ~CSphBuffer ()
202 {
203 SafeDeleteArray ( m_pBuffer );
204 }
205
Ptr() const206 const char * Ptr() const { return m_pBuffer; }
207
Finalize()208 bool Finalize()
209 {
210 return !( m_bOverrun || m_iLeft!=0 || ( m_pCurrent - m_pBuffer )!=m_iSize );
211 }
212
213 void SendBytes ( const void * pBytes, int iBytes );
214
SendWord(short int v)215 void SendWord ( short int v ) { v = ntohs(v); SendBytes ( &v, sizeof(v) ); } // NOLINT
SendInt(int v)216 void SendInt ( int v ) { v = ntohl(v); SendBytes ( &v, sizeof(v) ); }
SendDword(DWORD v)217 void SendDword ( DWORD v ) { v = ntohl(v) ;SendBytes ( &v, sizeof(v) ); }
SendUint64(ulonglong v)218 void SendUint64 ( ulonglong v ) { SendDword ( uint ( v>>32 ) ); SendDword ( uint ( v&0xFFFFFFFFUL ) ); }
SendString(const char * v)219 void SendString ( const char * v ) { SendString ( v, strlen(v) ); }
SendString(const char * v,int iLen)220 void SendString ( const char * v, int iLen ) { SendDword(iLen); SendBytes ( v, iLen ); }
SendFloat(float v)221 void SendFloat ( float v ) { SendDword ( sphF2DW(v) ); }
222 };
223
SendBytes(const void * pBytes,int iBytes)224 void CSphBuffer::SendBytes ( const void * pBytes, int iBytes )
225 {
226 if ( m_iLeft < iBytes )
227 {
228 m_bOverrun = true;
229 return;
230 }
231
232 memcpy ( m_pCurrent, pBytes, iBytes );
233
234 m_pCurrent += iBytes;
235 m_iLeft -= iBytes;
236 }
237
238 struct CSphUrl
239 {
240 char * m_sBuffer;
241 char * m_sFormatted;
242
243 char * m_sScheme;
244 char * m_sHost;
245 char * m_sIndex;
246
247 int m_iPort;
248
CSphUrlCSphUrl249 CSphUrl()
250 : m_sBuffer ( NULL )
251 , m_sFormatted ( NULL )
252 , m_sScheme ( SPHINXSE_DEFAULT_SCHEME )
253 , m_sHost ( SPHINXSE_DEFAULT_HOST )
254 , m_sIndex ( SPHINXSE_DEFAULT_INDEX )
255 , m_iPort ( SPHINXSE_DEFAULT_PORT )
256 {}
257
~CSphUrlCSphUrl258 ~CSphUrl()
259 {
260 SafeDeleteArray ( m_sFormatted );
261 SafeDeleteArray ( m_sBuffer );
262 }
263
264 bool Parse ( const char * sUrl, int iLen );
265 int Connect();
266 const char * Format();
267 };
268
Format()269 const char * CSphUrl::Format()
270 {
271 if ( !m_sFormatted )
272 {
273 int iSize = 15 + strlen(m_sHost) + strlen(m_sIndex);
274 m_sFormatted = new char [ iSize ];
275 if ( m_iPort )
276 snprintf ( m_sFormatted, iSize, "inet://%s:%d/%s", m_sHost, m_iPort, m_sIndex );
277 else
278 snprintf ( m_sFormatted, iSize, "unix://%s/%s", m_sHost, m_sIndex );
279 }
280 return m_sFormatted;
281 }
282
283 // the following scheme variants are recognized
284 //
285 // inet://host/index
286 // inet://host:port/index
287 // unix://unix/domain/socket:index
288 // unix://unix/domain/socket
Parse(const char * sUrl,int iLen)289 bool CSphUrl::Parse ( const char * sUrl, int iLen )
290 {
291 bool bOk = true;
292 while ( iLen )
293 {
294 bOk = false;
295
296 m_sBuffer = sphDup ( sUrl, iLen );
297 m_sScheme = m_sBuffer;
298
299 m_sHost = strstr ( m_sBuffer, "://" );
300 if ( !m_sHost )
301 break;
302 m_sHost[0] = '\0';
303 m_sHost += 2;
304
305 if ( !strcmp ( m_sScheme, "unix" ) )
306 {
307 // unix-domain socket
308 m_iPort = 0;
309 if (!( m_sIndex = strrchr ( m_sHost, ':' ) ))
310 m_sIndex = SPHINXSE_DEFAULT_INDEX;
311 else
312 {
313 *m_sIndex++ = '\0';
314 if ( !*m_sIndex )
315 m_sIndex = SPHINXSE_DEFAULT_INDEX;
316 }
317 bOk = true;
318 break;
319 }
320 if ( strcmp ( m_sScheme, "sphinx" )!=0 && strcmp ( m_sScheme, "inet" )!=0 )
321 break;
322
323 // inet
324 m_sHost++;
325 char * sPort = strchr ( m_sHost, ':' );
326 if ( sPort )
327 {
328 *sPort++ = '\0';
329 if ( *sPort )
330 {
331 m_sIndex = strchr ( sPort, '/' );
332 if ( m_sIndex )
333 *m_sIndex++ = '\0';
334 else
335 m_sIndex = SPHINXSE_DEFAULT_INDEX;
336
337 m_iPort = atoi(sPort);
338 if ( !m_iPort )
339 m_iPort = SPHINXSE_DEFAULT_PORT;
340 }
341 } else
342 {
343 m_sIndex = strchr ( m_sHost, '/' );
344 if ( m_sIndex )
345 *m_sIndex++ = '\0';
346 else
347 m_sIndex = SPHINXSE_DEFAULT_INDEX;
348 }
349
350 bOk = true;
351 break;
352 }
353
354 return bOk;
355 }
356
Connect()357 int CSphUrl::Connect()
358 {
359 struct sockaddr_in sin;
360 #ifndef __WIN__
361 struct sockaddr_un saun;
362 #endif
363
364 int iDomain = 0;
365 int iSockaddrSize = 0;
366 struct sockaddr * pSockaddr = NULL;
367
368 in_addr_t ip_addr;
369
370 if ( m_iPort )
371 {
372 iDomain = AF_INET;
373 iSockaddrSize = sizeof(sin);
374 pSockaddr = (struct sockaddr *) &sin;
375
376 memset ( &sin, 0, sizeof(sin) );
377 sin.sin_family = AF_INET;
378 sin.sin_port = htons ( m_iPort );
379
380 // resolve address
381 if ( (int)( ip_addr = inet_addr ( m_sHost ) )!=(int)INADDR_NONE )
382 memcpy ( &sin.sin_addr, &ip_addr, sizeof(ip_addr) );
383 else
384 {
385 int tmp_errno;
386 bool bError = false;
387
388 #if MYSQL_VERSION_ID>=50515
389 struct addrinfo *hp = NULL;
390 tmp_errno = getaddrinfo ( m_sHost, NULL, NULL, &hp );
391 if ( !tmp_errno || !hp || !hp->ai_addr )
392 {
393 bError = true;
394 if ( hp )
395 freeaddrinfo ( hp );
396 }
397 #else
398 struct hostent tmp_hostent, *hp;
399 char buff2 [ GETHOSTBYNAME_BUFF_SIZE ];
400 hp = my_gethostbyname_r ( m_sHost, &tmp_hostent, buff2, sizeof(buff2), &tmp_errno );
401 if ( !hp )
402 {
403 my_gethostbyname_r_free();
404 bError = true;
405 }
406 #endif
407
408 if ( bError )
409 {
410 char sError[256];
411 my_snprintf ( sError, sizeof(sError), "failed to resolve searchd host (name=%s)", m_sHost );
412
413 my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), sError );
414 return -1;
415 }
416
417 #if MYSQL_VERSION_ID>=50515
418 memcpy ( &sin.sin_addr, hp->ai_addr, Min ( sizeof(sin.sin_addr), (size_t)hp->ai_addrlen ) );
419 freeaddrinfo ( hp );
420 #else
421 memcpy ( &sin.sin_addr, hp->h_addr, Min ( sizeof(sin.sin_addr), (size_t)hp->h_length ) );
422 my_gethostbyname_r_free();
423 #endif
424 }
425 } else
426 {
427 #ifndef __WIN__
428 iDomain = AF_UNIX;
429 iSockaddrSize = sizeof(saun);
430 pSockaddr = (struct sockaddr *) &saun;
431
432 memset ( &saun, 0, sizeof(saun) );
433 saun.sun_family = AF_UNIX;
434 strncpy ( saun.sun_path, m_sHost, sizeof(saun.sun_path)-1 );
435 #else
436 my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), "Unix-domain sockets are not supported on Windows" );
437 return -1;
438 #endif
439 }
440
441 // connect to searchd and exchange versions
442 uint uServerVersion;
443 uint uClientVersion = htonl ( SPHINX_SEARCHD_PROTO );
444 int iSocket = -1;
445 char * pError = NULL;
446 do
447 {
448 iSocket = socket ( iDomain, SOCK_STREAM, 0 );
449 if ( iSocket==-1 )
450 {
451 pError = "Failed to create client socket";
452 break;
453 }
454
455 if ( connect ( iSocket, pSockaddr, iSockaddrSize )==-1 )
456 {
457 pError = "Failed to connect to searchd";
458 break;
459 }
460
461 if ( !sphRecv ( iSocket, (char *)&uServerVersion, sizeof(uServerVersion) ) )
462 {
463 pError = "Failed to receive searchd version";
464 break;
465 }
466
467 if ( !sphSend ( iSocket, (char *)&uClientVersion, sizeof(uClientVersion) ) )
468 {
469 pError = "Failed to send client version";
470 break;
471 }
472 }
473 while(0);
474
475 // fixme: compare versions?
476
477 if ( pError )
478 {
479 char sError[1024];
480 snprintf ( sError, sizeof(sError), "%s [%d] %s", Format(), errno, strerror(errno) );
481 my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), sError );
482
483 if ( iSocket!=-1 )
484 close ( iSocket );
485
486 return -1;
487 }
488
489 return iSocket;
490 }
491
492 struct CSphResponse
493 {
494 char * m_pBuffer;
495 char * m_pBody;
496
CSphResponseCSphResponse497 CSphResponse ()
498 : m_pBuffer ( NULL )
499 , m_pBody ( NULL )
500 {}
501
CSphResponseCSphResponse502 explicit CSphResponse ( DWORD uSize )
503 : m_pBody ( NULL )
504 {
505 m_pBuffer = new char[uSize];
506 }
507
~CSphResponseCSphResponse508 ~CSphResponse ()
509 {
510 SafeDeleteArray ( m_pBuffer );
511 }
512
513 static CSphResponse * Read ( int iSocket, int iClientVersion );
514 };
515
516 CSphResponse *
Read(int iSocket,int iClientVersion)517 CSphResponse::Read ( int iSocket, int iClientVersion )
518 {
519 char sHeader[8];
520 if ( !sphRecv ( iSocket, sHeader, sizeof(sHeader) ) )
521 return NULL;
522
523 int iStatus = ntohs ( sphUnalignedRead ( *(short int *) &sHeader[0] ) );
524 int iVersion = ntohs ( sphUnalignedRead ( *(short int *) &sHeader[2] ) );
525 DWORD uLength = ntohl ( sphUnalignedRead ( *(DWORD *) &sHeader[4] ) );
526
527 if ( iVersion<iClientVersion )
528 return NULL;
529
530 if ( uLength<=SPHINXSE_MAX_ALLOC )
531 {
532 CSphResponse * pResponse = new CSphResponse ( uLength );
533 if ( !sphRecv ( iSocket, pResponse->m_pBuffer, uLength ) )
534 {
535 SafeDelete ( pResponse );
536 return NULL;
537 }
538
539 pResponse->m_pBody = pResponse->m_pBuffer;
540 if ( iStatus!=SEARCHD_OK )
541 {
542 DWORD uSize = ntohl ( *(DWORD *)pResponse->m_pBuffer );
543 if ( iStatus==SEARCHD_WARNING )
544 {
545 pResponse->m_pBody += uSize; // fixme: report the warning somehow
546 } else
547 {
548 char * sMessage = sphDup ( pResponse->m_pBuffer + sizeof(DWORD), uSize );
549 my_error ( ER_QUERY_ON_FOREIGN_DATA_SOURCE, MYF(0), sMessage );
550 SafeDeleteArray ( sMessage );
551 SafeDelete ( pResponse );
552 return NULL;
553 }
554 }
555 return pResponse;
556 }
557 return NULL;
558 }
559
560 /// udf
561
562 extern "C"
563 {
564 my_bool sphinx_snippets_init ( UDF_INIT * pUDF, UDF_ARGS * pArgs, char * sMessage );
565 void sphinx_snippets_deinit ( UDF_INIT * pUDF );
566 char * sphinx_snippets ( UDF_INIT * pUDF, UDF_ARGS * pArgs, char * sResult, unsigned long * pLength, char * pIsNull, char * sError );
567 };
568
569 #define MAX_MESSAGE_LENGTH 255
570 #define MAX_RESULT_LENGTH 255
571
572 struct CSphSnippets
573 {
574 CSphUrl m_tUrl;
575 CSphResponse * m_pResponse;
576
577 int m_iBeforeMatch;
578 int m_iAfterMatch;
579 int m_iChunkSeparator;
580 int m_iStripMode;
581 int m_iPassageBoundary;
582 int m_iLimit;
583 int m_iLimitWords;
584 int m_iLimitPassages;
585 int m_iAround;
586 int m_iPassageId;
587 int m_iFlags;
588
CSphSnippetsCSphSnippets589 CSphSnippets()
590 : m_pResponse(NULL)
591 , m_iBeforeMatch(0)
592 , m_iAfterMatch(0)
593 , m_iChunkSeparator(0)
594 , m_iStripMode(0)
595 , m_iPassageBoundary(0)
596 // defaults
597 , m_iLimit(256)
598 , m_iLimitWords(0)
599 , m_iLimitPassages(0)
600 , m_iAround(5)
601 , m_iPassageId(1)
602 , m_iFlags(1)
603 {
604 }
605
~CSphSnippetsCSphSnippets606 ~CSphSnippets()
607 {
608 SafeDelete ( m_pResponse );
609 }
610 };
611
612 #define KEYWORD(NAME) else if ( strncmp ( NAME, pArgs->attributes[i], pArgs->attribute_lengths[i] )==0 )
613
614 #define CHECK_TYPE(TYPE) \
615 if ( pArgs->arg_type[i]!=TYPE ) \
616 { \
617 snprintf ( sMessage, MAX_MESSAGE_LENGTH, \
618 "%.*s argument must be a string", \
619 (int)pArgs->attribute_lengths[i], \
620 pArgs->attributes[i] ); \
621 bFail = true; \
622 break; \
623 } \
624 if ( TYPE==STRING_RESULT && !pArgs->args[i] ) \
625 { \
626 snprintf ( sMessage, MAX_MESSAGE_LENGTH, \
627 "%.*s argument must be constant (and not NULL)", \
628 (int)pArgs->attribute_lengths[i], \
629 pArgs->attributes[i] ); \
630 bFail = true; \
631 break; \
632 }
633
634 #define STRING CHECK_TYPE(STRING_RESULT)
635 #define INT CHECK_TYPE(INT_RESULT); int iValue = *(long long *)pArgs->args[i]
636
sphinx_snippets_init(UDF_INIT * pUDF,UDF_ARGS * pArgs,char * sMessage)637 my_bool sphinx_snippets_init ( UDF_INIT * pUDF, UDF_ARGS * pArgs, char * sMessage )
638 {
639 if ( pArgs->arg_count < 3 )
640 {
641 strncpy ( sMessage, "insufficient arguments", MAX_MESSAGE_LENGTH );
642 return 1;
643 }
644
645 bool bFail = false;
646 CSphSnippets * pOpts = new CSphSnippets;
647 for ( uint i = 0; i < pArgs->arg_count; i++ )
648 {
649 if ( i < 3 )
650 {
651 if ( pArgs->arg_type[i]!=STRING_RESULT )
652 {
653 strncpy ( sMessage, "first three arguments must be of string type", MAX_MESSAGE_LENGTH );
654 bFail = true;
655 break;
656 }
657 }
658 KEYWORD("sphinx")
659 {
660 STRING;
661 if ( !pOpts->m_tUrl.Parse ( pArgs->args[i], pArgs->lengths[i] ) )
662 {
663 strncpy ( sMessage, "failed to parse connection string", MAX_MESSAGE_LENGTH );
664 bFail = true;
665 break;
666 }
667 }
668 KEYWORD("before_match") { STRING; pOpts->m_iBeforeMatch = i; }
669 KEYWORD("after_match") { STRING; pOpts->m_iAfterMatch = i; }
670 KEYWORD("chunk_separator") { STRING; pOpts->m_iChunkSeparator = i; }
671 KEYWORD("html_strip_mode") { STRING; pOpts->m_iStripMode = i; }
672 KEYWORD("passage_boundary") { STRING; pOpts->m_iPassageBoundary = i; }
673
674 KEYWORD("limit") { INT; pOpts->m_iLimit = iValue; }
675 KEYWORD("limit_words") { INT; pOpts->m_iLimitWords = iValue; }
676 KEYWORD("limit_passages") { INT; pOpts->m_iLimitPassages = iValue; }
677 KEYWORD("around") { INT; pOpts->m_iAround = iValue; }
678 KEYWORD("start_passage_id") { INT; pOpts->m_iPassageId = iValue; }
679
680 KEYWORD("exact_phrase") { INT; if ( iValue ) pOpts->m_iFlags |= 2; }
681 KEYWORD("single_passage") { INT; if ( iValue ) pOpts->m_iFlags |= 4; }
682 KEYWORD("use_boundaries") { INT; if ( iValue ) pOpts->m_iFlags |= 8; }
683 KEYWORD("weight_order") { INT; if ( iValue ) pOpts->m_iFlags |= 16; }
684 KEYWORD("query_mode") { INT; if ( iValue ) pOpts->m_iFlags |= 32; }
685 KEYWORD("force_all_words") { INT; if ( iValue ) pOpts->m_iFlags |= 64; }
686 KEYWORD("load_files") { INT; if ( iValue ) pOpts->m_iFlags |= 128; }
687 KEYWORD("allow_empty") { INT; if ( iValue ) pOpts->m_iFlags |= 256; }
688 KEYWORD("emit_zones") { INT; if ( iValue ) pOpts->m_iFlags |= 512; }
689 KEYWORD("load_files_scattered") { INT; if ( iValue ) pOpts->m_iFlags |= 1024; }
690 else
691 {
692 snprintf ( sMessage, MAX_MESSAGE_LENGTH, "unrecognized argument: %.*s",
693 (int)pArgs->attribute_lengths[i], pArgs->attributes[i] );
694 bFail = true;
695 break;
696 }
697 }
698
699 if ( bFail )
700 {
701 SafeDelete ( pOpts );
702 return 1;
703 }
704 pUDF->ptr = (char *)pOpts;
705 return 0;
706 }
707
708 #undef STRING
709 #undef INT
710 #undef KEYWORD
711 #undef CHECK_TYPE
712
713 #define ARG(i) pArgs->args[i], pArgs->lengths[i]
714 #define ARG_LEN(VAR, LEN) ( VAR ? pArgs->lengths[VAR] : LEN )
715
716 #define SEND_STRING(INDEX, DEFAULT) \
717 if ( INDEX ) \
718 tBuffer.SendString ( ARG(INDEX) ); \
719 else \
720 tBuffer.SendString ( DEFAULT, sizeof(DEFAULT) - 1 );
721
722
sphinx_snippets(UDF_INIT * pUDF,UDF_ARGS * pArgs,char * sResult,unsigned long * pLength,char * pIsNull,char * pError)723 char * sphinx_snippets ( UDF_INIT * pUDF, UDF_ARGS * pArgs, char * sResult, unsigned long * pLength, char * pIsNull, char * pError )
724 {
725 CSphSnippets * pOpts = (CSphSnippets *)pUDF->ptr;
726 assert ( pOpts );
727
728 if ( !pArgs->args[0] || !pArgs->args[1] || !pArgs->args[2] )
729 {
730 *pIsNull = 1;
731 return sResult;
732 }
733
734 const int iSize = 68 +
735 pArgs->lengths[1] + // index
736 pArgs->lengths[2] + // words
737 ARG_LEN ( pOpts->m_iBeforeMatch, 3 ) +
738 ARG_LEN ( pOpts->m_iAfterMatch, 4 ) +
739 ARG_LEN ( pOpts->m_iChunkSeparator, 5 ) +
740 ARG_LEN ( pOpts->m_iStripMode, 5 ) +
741 ARG_LEN ( pOpts->m_iPassageBoundary, 0 ) +
742 4 + pArgs->lengths[0]; // document
743
744 CSphBuffer tBuffer(iSize);
745
746 tBuffer.SendWord ( SEARCHD_COMMAND_EXCERPT );
747 tBuffer.SendWord ( VER_COMMAND_EXCERPT );
748 tBuffer.SendDword ( iSize - 8 );
749
750 tBuffer.SendDword ( 0 );
751 tBuffer.SendDword ( pOpts->m_iFlags );
752
753 tBuffer.SendString ( ARG(1) ); // index
754 tBuffer.SendString ( ARG(2) ); // words
755
756 SEND_STRING ( pOpts->m_iBeforeMatch, "<b>" );
757 SEND_STRING ( pOpts->m_iAfterMatch, "</b>" );
758 SEND_STRING ( pOpts->m_iChunkSeparator, " ... " );
759
760 tBuffer.SendInt ( pOpts->m_iLimit );
761 tBuffer.SendInt ( pOpts->m_iAround );
762
763 tBuffer.SendInt ( pOpts->m_iLimitPassages );
764 tBuffer.SendInt ( pOpts->m_iLimitWords );
765 tBuffer.SendInt ( pOpts->m_iPassageId );
766
767 SEND_STRING ( pOpts->m_iStripMode, "index" );
768 SEND_STRING ( pOpts->m_iPassageBoundary, "" );
769
770 // single document
771 tBuffer.SendInt ( 1 );
772 tBuffer.SendString ( ARG(0) );
773
774 int iSocket = -1;
775 do
776 {
777 if ( !tBuffer.Finalize() )
778 {
779 my_error ( ER_QUERY_ON_FOREIGN_DATA_SOURCE, MYF(0), "INTERNAL ERROR: failed to build request" );
780 break;
781 }
782
783 iSocket = pOpts->m_tUrl.Connect();
784 if ( iSocket==-1 ) break;
785 if ( !sphSend ( iSocket, tBuffer.Ptr(), iSize, sphReportErrors ) ) break;
786
787 CSphResponse * pResponse = CSphResponse::Read ( iSocket, VER_COMMAND_EXCERPT );
788 if ( !pResponse ) break;
789
790 close ( iSocket );
791 pOpts->m_pResponse = pResponse;
792 *pLength = ntohl ( *(DWORD *)pResponse->m_pBody );
793 return pResponse->m_pBody + sizeof(DWORD);
794 }
795 while(0);
796
797 if ( iSocket!=-1 )
798 close ( iSocket );
799
800 *pError = 1;
801 return sResult;
802 }
803
804 #undef SEND_STRING
805 #undef ARG_LEN
806 #undef ARG
807
sphinx_snippets_deinit(UDF_INIT * pUDF)808 void sphinx_snippets_deinit ( UDF_INIT * pUDF )
809 {
810 CSphSnippets * pOpts = (CSphSnippets *)pUDF->ptr;
811 SafeDelete ( pOpts );
812 }
813
814 //
815 // $Id: snippets_udf.cc 3508 2012-11-05 11:48:48Z kevg $
816 //
817