Branch data Line data Source code
1 : : /* omqueryinternal.cc: Internals of query interface
2 : : *
3 : : * Copyright 1999,2000,2001 BrightStation PLC
4 : : * Copyright 2002 Ananova Ltd
5 : : * Copyright 2002,2003,2004,2005,2006,2007,2008,2009 Olly Betts
6 : : * Copyright 2006,2007,2008,2009 Lemur Consulting Ltd
7 : : *
8 : : * This program is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU General Public License as
10 : : * published by the Free Software Foundation; either version 2 of the
11 : : * License, or (at your option) any later version.
12 : : *
13 : : * This program is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program; if not, write to the Free Software
20 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 : : * USA
22 : : */
23 : :
24 : : #include <config.h>
25 : :
26 : : #include "omqueryinternal.h"
27 : :
28 : : #include "debuglog.h"
29 : : #include "registryinternal.h"
30 : : #include "serialise.h"
31 : : #include "serialise-double.h"
32 : : #include "str.h"
33 : :
34 : : #include <xapian/error.h>
35 : : #include <xapian/postingsource.h>
36 : : #include <xapian/termiterator.h>
37 : : #include <xapian/version.h>
38 : : #include "vectortermlist.h"
39 : :
40 : : #include <algorithm>
41 : : #include "autoptr.h"
42 : : #include <cfloat>
43 : : #include <climits>
44 : : #include <cmath>
45 : : #include <set>
46 : : #include <vector>
47 : :
48 : : using namespace std;
49 : :
50 : : // Properties for query operations.
51 : :
52 : : static unsigned int
53 : 325136 : get_min_subqs(Xapian::Query::Internal::op_t op)
54 : : {
55 [ + + + - ]: 325136 : switch (op) {
56 : : case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
57 : : case Xapian::Query::Internal::OP_LEAF:
58 : : case Xapian::Query::OP_AND:
59 : : case Xapian::Query::OP_OR:
60 : : case Xapian::Query::OP_XOR:
61 : : case Xapian::Query::OP_NEAR:
62 : : case Xapian::Query::OP_PHRASE:
63 : : case Xapian::Query::OP_ELITE_SET:
64 : : case Xapian::Query::OP_VALUE_RANGE:
65 : : case Xapian::Query::OP_VALUE_GE:
66 : : case Xapian::Query::OP_VALUE_LE:
67 : : case Xapian::Query::OP_SYNONYM:
68 : 321449 : return 0;
69 : : case Xapian::Query::OP_SCALE_WEIGHT:
70 : 2714 : return 1;
71 : : case Xapian::Query::OP_FILTER:
72 : : case Xapian::Query::OP_AND_MAYBE:
73 : : case Xapian::Query::OP_AND_NOT:
74 : 973 : return 2;
75 : : default:
76 : : Assert(false);
77 : 325136 : throw Xapian::InvalidOperationError("get_min_subqs called with invalid operator type");
78 : : }
79 : : }
80 : :
81 : : static unsigned int
82 : 325133 : get_max_subqs(Xapian::Query::Internal::op_t op)
83 : : {
84 [ + + + + : 325133 : switch (op) {
- ]
85 : : case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
86 : : case Xapian::Query::Internal::OP_LEAF:
87 : : case Xapian::Query::OP_VALUE_RANGE:
88 : : case Xapian::Query::OP_VALUE_GE:
89 : : case Xapian::Query::OP_VALUE_LE:
90 : 104463 : return 0;
91 : : case Xapian::Query::OP_SCALE_WEIGHT:
92 : 2714 : return 1;
93 : : case Xapian::Query::OP_FILTER:
94 : : case Xapian::Query::OP_AND_MAYBE:
95 : : case Xapian::Query::OP_AND_NOT:
96 : 970 : return 2;
97 : : case Xapian::Query::OP_AND:
98 : : case Xapian::Query::OP_OR:
99 : : case Xapian::Query::OP_XOR:
100 : : case Xapian::Query::OP_NEAR:
101 : : case Xapian::Query::OP_PHRASE:
102 : : case Xapian::Query::OP_ELITE_SET:
103 : : case Xapian::Query::OP_SYNONYM:
104 : 216986 : return UINT_MAX;
105 : : default:
106 : : Assert(false);
107 : 325133 : throw Xapian::InvalidOperationError("get_max_subqs called with invalid operator type");
108 : : }
109 : : }
110 : :
111 : : static inline bool
112 : 634232 : is_leaf(Xapian::Query::Internal::op_t op)
113 : : {
114 : 634232 : return (op == Xapian::Query::Internal::OP_LEAF);
115 : : }
116 : :
117 : : inline bool
118 : 937 : is_distributable(Xapian::Query::Internal::op_t op)
119 : : {
120 [ + + ]: 937 : switch (op) {
121 : : case Xapian::Query::OP_AND:
122 : : case Xapian::Query::OP_OR:
123 : : case Xapian::Query::OP_XOR:
124 : : case Xapian::Query::OP_SYNONYM:
125 : 935 : return true;
126 : : default:
127 : 937 : return false;
128 : : }
129 : : }
130 : :
131 : : // Methods for Xapian::Query::Internal
132 : :
133 : : /** serialising method, for network matches.
134 : : *
135 : : * The format is designed to be relatively easy to parse.
136 : : *
137 : : * A single-term query becomes `[<encodedtname>@<termpos>#<wqf>'
138 : : * where:
139 : : * <wqf> is the decimal within query frequency (1 if omitted),
140 : : * <termpos> is the decimal term position (index of term if omitted).
141 : : *
142 : : * A compound query becomes `(<subqueries><op>', where:
143 : : * <subqueries> is the list of subqueries
144 : : * <op> is one of: &|%+-^
145 : : * also ~N "N >F *N (N unsigned int; F floating point)
146 : : *
147 : : * If querylen != sum(wqf) we append `=len' (at present we always do this
148 : : * for compound queries as it's simpler than working out what sum(wqf) would
149 : : * be - FIXME).
150 : : */
151 : : string
152 : 9047 : Xapian::Query::Internal::serialise(Xapian::termpos & curpos) const
153 : : {
154 : : #ifdef XAPIAN_HAS_REMOTE_BACKEND
155 : 9047 : string result;
156 : :
157 [ + + ]: 9047 : if (op == Xapian::Query::Internal::OP_LEAF) {
158 : 5861 : result += '[';
159 : 5861 : result += encode_length(tname.length());
160 : 5861 : result += tname;
161 [ + + ]: 5861 : if (term_pos != curpos) result += '@' + encode_length(term_pos);
162 : : // parameter is wqf.
163 [ + + ]: 5861 : if (parameter != 1) result += '#' + encode_length(parameter);
164 : 5861 : ++curpos;
165 [ + + ]: 3186 : } else if (op == Xapian::Query::Internal::OP_EXTERNAL_SOURCE) {
166 : 121 : string sourcename = external_source->name();
167 [ + + ]: 121 : if (sourcename.empty())
168 : 12 : throw Xapian::UnimplementedError("This PostingSource doesn't support remote use.");
169 : 109 : result += '!';
170 : 109 : result += encode_length(sourcename.length());
171 : 109 : result += sourcename;
172 : 109 : string sourcedata = external_source->serialise();
173 : 109 : result += encode_length(sourcedata.length());
174 : 121 : result += sourcedata;
175 : : } else {
176 : 3065 : result += "(";
177 [ + + ]: 7665 : for (subquery_list::const_iterator i = subqs.begin();
178 : : i != subqs.end();
179 : : ++i) {
180 : 4606 : result += (*i)->serialise(curpos);
181 : : }
182 [ - - + + + : 3059 : switch (op) {
+ + + + +
+ + + + +
+ - ]
183 : : case Xapian::Query::Internal::OP_LEAF:
184 : : Assert(false);
185 : 0 : break;
186 : : case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
187 : : Assert(false);
188 : 0 : break;
189 : : case Xapian::Query::OP_AND:
190 : 198 : result += "&";
191 : 198 : break;
192 : : case Xapian::Query::OP_OR:
193 : 775 : result += "|";
194 : 775 : break;
195 : : case Xapian::Query::OP_FILTER:
196 : 18 : result += "%";
197 : 18 : break;
198 : : case Xapian::Query::OP_AND_MAYBE:
199 : 36 : result += "+";
200 : 36 : break;
201 : : case Xapian::Query::OP_AND_NOT:
202 : 48 : result += "-";
203 : 48 : break;
204 : : case Xapian::Query::OP_XOR:
205 : 48 : result += "^";
206 : 48 : break;
207 : : case Xapian::Query::OP_NEAR:
208 : 120 : result += "~" + encode_length(parameter);
209 : 120 : break;
210 : : case Xapian::Query::OP_PHRASE:
211 : 290 : result += "\"" + encode_length(parameter);
212 : 290 : break;
213 : : case Xapian::Query::OP_ELITE_SET:
214 : 30 : result += "*" + encode_length(parameter);
215 : 30 : break;
216 : : case Xapian::Query::OP_VALUE_RANGE:
217 : 876 : result += "]";
218 : 876 : result += encode_length(tname.length());
219 : 876 : result += tname;
220 : 876 : result += encode_length(str_parameter.length());
221 : 876 : result += str_parameter;
222 : 876 : result += encode_length(parameter);
223 : 876 : break;
224 : : case Xapian::Query::OP_VALUE_GE:
225 : 78 : result += "}";
226 : 78 : result += encode_length(tname.length());
227 : 78 : result += tname;
228 : 78 : result += encode_length(parameter);
229 : 78 : break;
230 : : case Xapian::Query::OP_VALUE_LE:
231 : 78 : result += "{";
232 : 78 : result += encode_length(tname.length());
233 : 78 : result += tname;
234 : 78 : result += encode_length(parameter);
235 : 78 : break;
236 : : case Xapian::Query::OP_SCALE_WEIGHT:
237 : 224 : result += ".";
238 : 224 : result += str_parameter; // serialise_double(get_dbl_parameter());
239 : 224 : break;
240 : : case Xapian::Query::OP_SYNONYM:
241 : 9029 : result += "=";
242 : : break;
243 : : }
244 : : }
245 : 18 : return result;
246 : : #else
247 : : (void)curpos;
248 : : throw Xapian::InternalError("query serialisation not compiled in");
249 : : #endif
250 : : }
251 : :
252 : : string
253 : 3054 : Xapian::Query::Internal::get_op_name(Xapian::Query::Internal::op_t op)
254 : : {
255 : 3054 : string name;
256 [ - - + + + : 3054 : switch (op) {
+ + + + +
- + + + +
+ - ]
257 : : case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
258 : 0 : name = "EXTERNAL_SOURCE"; break;
259 : 0 : case Xapian::Query::Internal::OP_LEAF: name = "LEAF"; break;
260 : 134 : case Xapian::Query::OP_AND: name = "AND"; break;
261 : 1257 : case Xapian::Query::OP_OR: name = "OR"; break;
262 : 26 : case Xapian::Query::OP_FILTER: name = "FILTER"; break;
263 : 65 : case Xapian::Query::OP_AND_MAYBE: name = "AND_MAYBE"; break;
264 : 109 : case Xapian::Query::OP_AND_NOT: name = "AND_NOT"; break;
265 : 58 : case Xapian::Query::OP_XOR: name = "XOR"; break;
266 : 18 : case Xapian::Query::OP_NEAR: name = "NEAR"; break;
267 : 483 : case Xapian::Query::OP_PHRASE: name = "PHRASE"; break;
268 : 0 : case Xapian::Query::OP_ELITE_SET: name = "ELITE_SET"; break;
269 : 54 : case Xapian::Query::OP_VALUE_RANGE: name = "VALUE_RANGE"; break;
270 : 4 : case Xapian::Query::OP_VALUE_GE: name = "VALUE_GE"; break;
271 : 1 : case Xapian::Query::OP_VALUE_LE: name = "VALUE_LE"; break;
272 : 417 : case Xapian::Query::OP_SCALE_WEIGHT: name = "SCALE_WEIGHT"; break;
273 : 3054 : case Xapian::Query::OP_SYNONYM: name = "SYNONYM"; break;
274 : : }
275 : 0 : return name;
276 : : }
277 : :
278 : : string
279 : 10072 : Xapian::Query::Internal::get_description() const
280 : : {
281 : 10072 : string opstr;
282 : :
283 [ + + ]: 10072 : if (is_leaf(op)) {
284 [ + + ]: 6954 : if (term_pos != 0) {
285 : 5294 : opstr += "pos=" + str(term_pos);
286 : : }
287 : : // parameter is wqf.
288 [ + + ]: 6954 : if (parameter != 1) {
289 [ + + ]: 28 : if (!opstr.empty()) opstr += ",";
290 : 28 : opstr += "wqf=" + str(parameter);
291 : : }
292 [ + + ]: 6954 : if (!opstr.empty()) opstr = ":(" + opstr + ")";
293 [ + + ]: 6954 : if (tname.empty()) return "<alldocuments>" + opstr;
294 : 6950 : return tname + opstr;
295 : : }
296 : :
297 [ + + + + : 3118 : switch (op) {
+ ]
298 : : case Xapian::Query::OP_VALUE_RANGE:
299 : 53 : opstr = get_op_name(op);
300 : 53 : opstr += ' ';
301 : 53 : opstr += str(parameter);
302 : 53 : opstr += ' ';
303 : 53 : opstr += tname;
304 : 53 : opstr += ' ';
305 : 53 : opstr += str_parameter;
306 : 53 : return opstr;
307 : : case Xapian::Query::OP_VALUE_GE:
308 : : case Xapian::Query::OP_VALUE_LE:
309 : 3 : opstr = get_op_name(op);
310 : 3 : opstr += ' ';
311 : 3 : opstr += str(parameter);
312 : 3 : opstr += ' ';
313 : 3 : opstr += tname;
314 : 3 : return opstr;
315 : : case Xapian::Query::OP_SCALE_WEIGHT:
316 : 469 : opstr += str(get_dbl_parameter());
317 : 469 : opstr += " * ";
318 : 469 : opstr += subqs[0]->get_description();
319 : 469 : return opstr;
320 : : case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
321 : 21 : opstr = "PostingSource(";
322 : 21 : opstr += external_source->get_description();
323 : 21 : opstr += ')';
324 : 21 : return opstr;
325 : : }
326 : :
327 : 2572 : opstr = " " + get_op_name(op) + " ";
328 [ + + + + ]: 2572 : if (op == Xapian::Query::OP_NEAR ||
[ - + ]
329 : : op == Xapian::Query::OP_PHRASE ||
330 : : op == Xapian::Query::OP_ELITE_SET)
331 : 501 : opstr += str(parameter) + " ";
332 : :
333 : 2572 : string description;
334 : 2572 : subquery_list::const_iterator i;
335 [ + + ]: 10122 : for (i = subqs.begin(); i != subqs.end(); i++) {
336 [ + + ]: 7550 : if (!description.empty()) description += opstr;
337 : 7550 : description += (**i).get_description();
338 : : }
339 : :
340 : 10072 : return "(" + description + ")";
341 : : }
342 : :
343 : : Xapian::termcount
344 : 19215 : Xapian::Query::Internal::get_length() const
345 : : {
346 [ + + ]: 19215 : if (is_leaf(op)) {
347 : : // parameter is wqf.
348 : 8724 : return parameter;
349 : : }
350 : 10491 : Xapian::termcount len = 0;
351 : 10491 : subquery_list::const_iterator i;
352 [ + + ]: 19258 : for (i = subqs.begin(); i != subqs.end(); ++i) {
353 : 8767 : len += (**i).get_length();
354 : : }
355 : 19215 : return len;
356 : : }
357 : :
358 : : /** Private function used to implement get_terms() */
359 : : void
360 : 516909 : Xapian::Query::Internal::accumulate_terms(
361 : : vector<pair<string, Xapian::termpos> > &terms) const
362 : : {
363 [ + + ]: 516909 : if (is_leaf(op)) {
364 : : // We're a leaf, so just return our term, but skip Query::MatchAllTerms
365 : : // (which is Query("")).
366 [ + + ]: 341380 : if (!tname.empty())
367 : 341307 : terms.push_back(make_pair(tname, term_pos));
368 : : } else {
369 : 175529 : subquery_list::const_iterator end = subqs.end();
370 : : // Not a leaf, concatenate results from all subqueries.
371 [ + + ]: 512787 : for (subquery_list::const_iterator i = subqs.begin(); i != end; ++i) {
372 : 337258 : (*i)->accumulate_terms(terms);
373 : : }
374 : : }
375 : 516909 : }
376 : :
377 : : struct LessByTermpos {
378 : : typedef const pair<string, Xapian::termpos> argtype;
379 : 178225 : bool operator()(argtype &left, argtype &right) {
380 [ + + ]: 178225 : if (left.second != right.second) {
381 : 5468 : return left.second < right.second;
382 : : } else {
383 : 178225 : return left.first < right.first;
384 : : }
385 : : }
386 : : };
387 : :
388 : : Xapian::TermIterator
389 : 179651 : Xapian::Query::Internal::get_terms() const
390 : : {
391 : 179651 : vector<pair<string, Xapian::termpos> > terms;
392 : 179651 : accumulate_terms(terms);
393 : :
394 : 179651 : sort(terms.begin(), terms.end(), LessByTermpos());
395 : :
396 : : // remove adjacent duplicates, and return an iterator pointing
397 : : // to just after the last unique element
398 : : vector<pair<string, Xapian::termpos> >::iterator newlast =
399 : 179651 : unique(terms.begin(), terms.end());
400 : : // and remove the rest... (See Stroustrup 18.6.3)
401 : 179651 : terms.erase(newlast, terms.end());
402 : :
403 : 179651 : vector<string> result;
404 : 179651 : vector<pair<string, Xapian::termpos> >::const_iterator i;
405 [ + + ]: 520220 : for (i = terms.begin(); i != terms.end(); ++i) {
406 : 340569 : result.push_back(i->first);
407 : : }
408 : :
409 : : return Xapian::TermIterator(new VectorTermList(result.begin(),
410 : 179651 : result.end()));
411 : : }
412 : :
413 : : #ifdef XAPIAN_HAS_REMOTE_BACKEND
414 : : // Methods.
415 : :
416 : : class QUnserial {
417 : : private:
418 : : const char *p;
419 : : const char *end;
420 : : Xapian::termpos curpos;
421 : : const Xapian::Registry & reg;
422 : :
423 : : Xapian::Query::Internal * readquery();
424 : : Xapian::Query::Internal * readexternal();
425 : : Xapian::Query::Internal * readcompound();
426 : :
427 : : public:
428 : 4420 : QUnserial(const string & s, const Xapian::Registry & reg_)
429 : 4420 : : p(s.c_str()), end(p + s.size()), curpos(1), reg(reg_) { }
430 : : Xapian::Query::Internal * decode();
431 : : };
432 : :
433 : : Xapian::Query::Internal *
434 : 4420 : QUnserial::decode() {
435 : : LOGLINE(UNKNOWN, "QUnserial::decode(" << p << ")");
436 : 4420 : AutoPtr<Xapian::Query::Internal> qint(readquery());
437 [ - + ]: 4418 : if (p != end)
438 : 0 : throw Xapian::InvalidArgumentError("Bad serialised query");
439 : 4418 : return qint.release();
440 : : }
441 : :
442 : : Xapian::Query::Internal *
443 : 8342 : QUnserial::readquery() {
444 [ - + ]: 8342 : if (p == end)
445 : 0 : throw Xapian::InvalidArgumentError("Bad serialised query");
446 [ + + + - ]: 8342 : switch (*p++) {
447 : : case '[': {
448 : 5839 : size_t length = decode_length(&p, end, true);
449 : 5839 : string tname(p, length);
450 : 5839 : p += length;
451 : 5839 : Xapian::termpos term_pos = curpos;
452 : 5839 : Xapian::termcount wqf = 1;
453 [ + + ]: 5839 : if (p != end) {
454 [ + + ]: 5766 : if (*p == '@') {
455 : 4547 : ++p;
456 : 4547 : term_pos = decode_length(&p, end, false);
457 : : }
458 [ + + ]: 5766 : if (*p == '#') {
459 : 36 : ++p;
460 : 36 : wqf = decode_length(&p, end, false);
461 : : }
462 : : }
463 : 5839 : ++curpos;
464 : 5839 : return new Xapian::Query::Internal(tname, wqf, term_pos);
465 : : }
466 : : case '!':
467 : 99 : return readexternal();
468 : : case '(':
469 : 2404 : return readcompound();
470 : : default:
471 : : LOGLINE(UNKNOWN, "Can't parse remainder `" << p - 1 << "'");
472 : 8340 : throw Xapian::InvalidArgumentError("Invalid query string");
473 : : }
474 : : }
475 : :
476 : : Xapian::Query::Internal *
477 : 111 : QUnserial::readexternal()
478 : : {
479 [ - + ]: 111 : if (p == end)
480 : 0 : throw Xapian::InvalidArgumentError("Bad serialised query");
481 : :
482 : 111 : size_t length = decode_length(&p, end, true);
483 : 111 : string sourcename(p, length);
484 : 111 : const Xapian::PostingSource * source = reg.get_posting_source(sourcename);
485 [ + + ]: 111 : if (source == NULL) {
486 : : throw Xapian::InvalidArgumentError("PostingSource " + sourcename +
487 : 2 : " not registered");
488 : : }
489 : :
490 : 109 : p += length;
491 : 109 : length = decode_length(&p, end, true);
492 : 109 : string sourcedata(p, length);
493 : 109 : p += length;
494 : :
495 : 111 : return new Xapian::Query::Internal(source->unserialise(sourcedata), true);
496 : : }
497 : :
498 : : static Xapian::Query::Internal *
499 : 1799 : qint_from_vector(Xapian::Query::op op,
500 : : const vector<Xapian::Query::Internal *> & vec,
501 : : Xapian::termcount parameter = 0)
502 : : {
503 : 1799 : Xapian::Query::Internal * qint = new Xapian::Query::Internal(op, parameter);
504 : 1799 : vector<Xapian::Query::Internal *>::const_iterator i;
505 [ + + ]: 6160 : for (i = vec.begin(); i != vec.end(); i++) {
506 : 4361 : qint->add_subquery_nocopy(*i);
507 : : }
508 : 1799 : Xapian::Query::Internal * r = qint->end_construction();
509 : : // We're only called during unserialisation, so no simplification should
510 : : // happen.
511 : : AssertEq(r, qint);
512 : 1799 : return r;
513 : : }
514 : :
515 : : static Xapian::Query::Internal *
516 : 223 : qint_from_vector(Xapian::Query::op op,
517 : : const vector<Xapian::Query::Internal *> & vec,
518 : : Xapian::termcount parameter,
519 : : double dbl_parameter)
520 : : {
521 : 223 : Xapian::Query::Internal * qint = new Xapian::Query::Internal(op, parameter);
522 : 223 : qint->set_dbl_parameter(dbl_parameter);
523 : 223 : vector<Xapian::Query::Internal *>::const_iterator i;
524 [ + + ]: 446 : for (i = vec.begin(); i != vec.end(); i++) {
525 : 223 : qint->add_subquery_nocopy(*i);
526 : : }
527 : 223 : Xapian::Query::Internal * r = qint->end_construction();
528 : : // We're only called during unserialisation, so no simplification should
529 : : // happen.
530 : : AssertEq(r, qint);
531 : 223 : return r;
532 : : }
533 : :
534 : : Xapian::Query::Internal *
535 : 3054 : QUnserial::readcompound() {
536 : 3054 : vector<Xapian::Query::Internal *> subqs;
537 : : try {
538 : 4584 : while (true) {
539 [ - + ]: 7638 : if (p == end)
540 : 0 : throw Xapian::InvalidArgumentError("Bad serialised query");
541 [ + + + + + : 7638 : switch (*p++) {
+ + + + +
+ + + + +
+ + - ]
542 : : case '[':
543 : 3922 : --p;
544 : 3922 : subqs.push_back(readquery());
545 : 3922 : break;
546 : : case '!':
547 : 12 : subqs.push_back(readexternal());
548 : 12 : break;
549 : : case '(': {
550 : 650 : subqs.push_back(readcompound());
551 : 650 : break;
552 : : }
553 : : case '&':
554 : 198 : return qint_from_vector(Xapian::Query::OP_AND, subqs);
555 : : case '|':
556 : 772 : return qint_from_vector(Xapian::Query::OP_OR, subqs);
557 : : case '%':
558 : 18 : return qint_from_vector(Xapian::Query::OP_FILTER, subqs);
559 : : case '^':
560 : 48 : return qint_from_vector(Xapian::Query::OP_XOR, subqs);
561 : : case '+':
562 : 36 : return qint_from_vector(Xapian::Query::OP_AND_MAYBE, subqs);
563 : : case '-':
564 : 48 : return qint_from_vector(Xapian::Query::OP_AND_NOT, subqs);
565 : : case '~': {
566 : 120 : Xapian::termcount window(decode_length(&p, end, false));
567 : 120 : return qint_from_vector(Xapian::Query::OP_NEAR, subqs, window);
568 : : }
569 : : case '"': {
570 : 289 : Xapian::termcount window(decode_length(&p, end, false));
571 : 289 : return qint_from_vector(Xapian::Query::OP_PHRASE, subqs, window);
572 : : }
573 : : case '*': {
574 : 30 : Xapian::termcount elite_set_size(decode_length(&p, end, false));
575 : : return qint_from_vector(Xapian::Query::OP_ELITE_SET, subqs,
576 : 30 : elite_set_size);
577 : : }
578 : : case ']': {
579 : 876 : size_t len = decode_length(&p, end, true);
580 : 876 : string start(p, len);
581 : 876 : p += len;
582 : 876 : len = decode_length(&p, end, true);
583 : 876 : string stop(p, len);
584 : 876 : p += len;
585 : 876 : Xapian::valueno valno(decode_length(&p, end, false));
586 : : return new Xapian::Query::Internal(Xapian::Query::OP_VALUE_RANGE, valno,
587 : 876 : start, stop);
588 : : }
589 : : case '}': {
590 : 78 : size_t len = decode_length(&p, end, true);
591 : 78 : string start(p, len);
592 : 78 : p += len;
593 : 78 : Xapian::valueno valno(decode_length(&p, end, false));
594 : : return new Xapian::Query::Internal(Xapian::Query::OP_VALUE_GE, valno,
595 : 78 : start);
596 : : }
597 : : case '{': {
598 : 78 : size_t len = decode_length(&p, end, true);
599 : 78 : string start(p, len);
600 : 78 : p += len;
601 : 78 : Xapian::valueno valno(decode_length(&p, end, false));
602 : : return new Xapian::Query::Internal(Xapian::Query::OP_VALUE_LE, valno,
603 : 78 : start);
604 : : }
605 : : case '.': {
606 : 223 : double param = unserialise_double(&p, end);
607 : : return qint_from_vector(Xapian::Query::OP_SCALE_WEIGHT,
608 : 223 : subqs, 0, param);
609 : : }
610 : : case '=': {
611 : 240 : return qint_from_vector(Xapian::Query::OP_SYNONYM, subqs);
612 : : }
613 : : default:
614 : : LOGLINE(UNKNOWN, "Can't parse remainder `" << p - 1 << "'");
615 : 0 : throw Xapian::InvalidArgumentError("Invalid query string");
616 : : }
617 : : }
618 : 0 : } catch (...) {
619 : 0 : vector<Xapian::Query::Internal *>::iterator i;
620 [ # # ]: 0 : for (i = subqs.begin(); i != subqs.end(); i++)
621 [ # # ]: 0 : delete *i;
622 : 0 : throw;
623 : 3054 : }
624 : : }
625 : :
626 : : Xapian::Query::Internal *
627 : 4420 : Xapian::Query::Internal::unserialise(const string &s,
628 : : const Xapian::Registry & reg)
629 : : {
630 : : Assert(s.length() > 1);
631 : 4420 : QUnserial u(s, reg);
632 : 4420 : Xapian::Query::Internal * qint = u.decode();
633 : : AssertEq(s, qint->serialise());
634 : 4418 : return qint;
635 : : }
636 : : #else
637 : : Xapian::Query::Internal *
638 : : Xapian::Query::Internal::unserialise(const string &, const Xapian::Registry &)
639 : : {
640 : : throw Xapian::InternalError("query serialisation not compiled in");
641 : : }
642 : : #endif
643 : :
644 : 143206 : Xapian::Query::Internal::Internal(const Xapian::Query::Internal ©me)
645 : : : Xapian::Internal::RefCntBase(),
646 : : op(copyme.op),
647 : : subqs(),
648 : : parameter(copyme.parameter),
649 : : tname(copyme.tname),
650 : : str_parameter(copyme.str_parameter),
651 : : term_pos(copyme.term_pos),
652 : : external_source(NULL),
653 : 143206 : external_source_owned(false)
654 : : {
655 [ + + ][ # # ]: 190011 : for (subquery_list::const_iterator i = copyme.subqs.begin();
656 : : i != copyme.subqs.end();
657 : : ++i) {
658 : 46805 : subqs.push_back(new Xapian::Query::Internal(**i));
659 : : }
660 [ + + ][ # # ]: 143206 : if (copyme.external_source) {
661 : 90 : external_source = copyme.external_source->clone();
662 [ + + # # ]: 90 : if (external_source == NULL) {
663 : 8 : external_source = copyme.external_source;
664 : 8 : external_source_owned = false;
665 : : } else {
666 : 82 : external_source_owned = true;
667 : : }
668 : : }
669 : 143206 : }
670 : :
671 : : //////////////////////////////////////////
672 : : // Methods for making new query objects
673 : :
674 : 72941 : Xapian::Query::Internal::Internal(const string & tname_, Xapian::termcount wqf_,
675 : : Xapian::termpos term_pos_)
676 : : : op(Xapian::Query::Internal::OP_LEAF),
677 : : subqs(),
678 : : parameter(wqf_),
679 : : tname(tname_),
680 : : term_pos(term_pos_),
681 : : external_source(NULL),
682 : 72941 : external_source_owned(false)
683 : : {
684 : 72941 : validate_query();
685 : 72941 : }
686 : :
687 : 33089 : Xapian::Query::Internal::Internal(op_t op_, Xapian::termcount parameter_)
688 : : : op(op_),
689 : : subqs(),
690 : : parameter(parameter_),
691 : : tname(),
692 : : term_pos(0),
693 : : external_source(NULL),
694 : 33089 : external_source_owned(false)
695 : : {
696 [ + + + + ]: 33089 : if (parameter != 0 && op != OP_PHRASE && op != OP_NEAR && op != OP_ELITE_SET)
[ + + ]
[ - + # # ]
[ # # ][ # # ]
[ # # ]
697 : 0 : throw Xapian::InvalidArgumentError("parameter is only meaningful for OP_NEAR, OP_PHRASE, or OP_ELITE_SET");
698 : 33089 : }
699 : :
700 : 6558 : Xapian::Query::Internal::Internal(op_t op_, Xapian::valueno valno,
701 : : const string &begin, const string &end)
702 : : : op(op_),
703 : : parameter(Xapian::termcount(valno)),
704 : : tname(begin),
705 : : str_parameter(end),
706 : : external_source(NULL),
707 : 6558 : external_source_owned(false)
708 : : {
709 [ - + # # ]: 6558 : if (op != OP_VALUE_RANGE)
710 : 0 : throw Xapian::InvalidArgumentError("This constructor is only meaningful for OP_VALUE_RANGE");
711 : 6558 : validate_query();
712 : 6558 : }
713 : :
714 : 508 : Xapian::Query::Internal::Internal(op_t op_, Xapian::valueno valno,
715 : : const std::string &value)
716 : : : op(op_),
717 : : parameter(Xapian::termcount(valno)),
718 : : tname(value),
719 : : external_source(NULL),
720 : 508 : external_source_owned(false)
721 : : {
722 [ + + - + # : 508 : if (op != OP_VALUE_GE && op != OP_VALUE_LE)
# ][ # # ]
723 : 0 : throw Xapian::InvalidArgumentError("This constructor is only meaningful for OP_VALUE_GE or OP_VALUE_LE");
724 [ + + ][ + + ]: 508 : if (op == OP_VALUE_GE && value.empty()) {
[ + + ][ # # ]
[ # # ][ # # ]
725 : : // Map '<value> >= ""' to MatchAll.
726 : 14 : op = OP_LEAF;
727 : 14 : parameter = 1; // wqf
728 : 14 : term_pos = 0;
729 : : }
730 : 508 : validate_query();
731 : 508 : }
732 : :
733 : 781 : Xapian::Query::Internal::Internal(PostingSource * external_source_, bool owned)
734 : : : op(OP_EXTERNAL_SOURCE), external_source(external_source_),
735 : 781 : external_source_owned(owned)
736 : : {
737 : : Assert(external_source);
738 : 781 : }
739 : :
740 : 257083 : Xapian::Query::Internal::~Internal()
741 : : {
742 : 257083 : subquery_list::iterator i;
743 [ + + ][ # # ]: 394651 : for (i = subqs.begin(); i != subqs.end(); i++) {
744 [ + + ][ # # ]: 137568 : delete *i;
745 : : }
746 [ + + ][ # # ]: 257083 : if (external_source_owned) {
747 [ + - ][ # # ]: 855 : delete external_source;
748 : : }
749 : 257083 : }
750 : :
751 : : Xapian::Query::Internal *
752 : 33089 : Xapian::Query::Internal::end_construction()
753 : : {
754 : : LOGCALL_VOID(MATCH, "Xapian::Query::Internal::end_construction", NO_ARGS);
755 : 33089 : validate_query();
756 : 32663 : Xapian::Query::Internal * qint = simplify_query();
757 [ + + ]: 32659 : if (qint) qint->validate_query();
758 : 32659 : return qint;
759 : : }
760 : :
761 : : void
762 : 325126 : Xapian::Query::Internal::validate_query() const
763 : : {
764 : : LOGCALL_VOID(MATCH, "Xapian::Query::Internal::validate_query", NO_ARGS);
765 : :
766 : : // Check that the number of subqueries is in acceptable limits for this op
767 [ + + ][ + + ]: 325126 : if (subqs.size() < get_min_subqs(op) ||
[ + + ]
768 : : subqs.size() > get_max_subqs(op)) {
769 : : throw Xapian::InvalidArgumentError("Xapian::Query: " + get_op_name(op) +
770 : : " requires a minimum of " + str(get_min_subqs(op)) +
771 : : " and a maximum of " + str(get_max_subqs(op)) +
772 : : " sub queries, had " +
773 : 10 : str(subqs.size()) + ".");
774 : : }
775 : :
776 [ + + ][ + + ]: 325116 : if (op == OP_SCALE_WEIGHT && get_dbl_parameter() < 0) {
[ + + ]
777 : 416 : throw Xapian::InvalidArgumentError("Xapian::Query: " + get_op_name(op) + " requires a non-negative parameter.");
778 : : }
779 : :
780 : : // Check that the termname is null in a branch query, unless the op
781 : : // is OP_VALUE_RANGE or OP_VALUE_GE or OP_VALUE_LE.
782 : : Assert(is_leaf(op) ||
783 : : op == OP_VALUE_RANGE ||
784 : : op == OP_VALUE_GE ||
785 : : op == OP_VALUE_LE ||
786 : : tname.empty());
787 : 324700 : }
788 : :
789 : : bool
790 : 32663 : Xapian::Query::Internal::simplify_matchnothing()
791 : : {
792 : 32663 : subquery_list::iterator sq;
793 [ + + + + ]: 32663 : switch (op) {
794 : : case OP_PHRASE:
795 : : case OP_NEAR:
796 : : case OP_AND:
797 : : case OP_FILTER:
798 : : // Doing an "AND" type operation - if we've got any MatchNothing
799 : : // nodes, we match nothing.
800 [ + + ]: 7197 : for (sq = subqs.begin(); sq != subqs.end(); sq++) {
801 [ + + ]: 5070 : if (*sq == 0) {
802 [ + + ]: 45 : for (sq = subqs.begin(); sq != subqs.end(); sq++)
803 [ + + ]: 30 : delete *sq;
804 : 15 : subqs.clear();
805 : 15 : return true;
806 : : }
807 : : }
808 : 2127 : break;
809 : : case OP_ELITE_SET:
810 : : case OP_OR:
811 : : case OP_XOR:
812 : : case OP_SYNONYM:
813 : : // Doing an "OR" type operation - if we've got any MatchNothing
814 : : // subnodes, drop them; except that we mustn't become an empty
815 : : // node due to this, so we never drop a MatchNothing subnode
816 : : // if it's the only subnode.
817 : 29377 : sq = subqs.begin();
818 [ + + ][ + + ]: 112995 : while (sq != subqs.end() && subqs.size() > 1) {
[ + + ]
819 [ + + ]: 83618 : if (*sq == 0) {
820 : 87 : sq = subqs.erase(sq);
821 : : } else {
822 : 83531 : ++sq;
823 : : }
824 : : }
825 : 29377 : break;
826 : : case OP_AND_MAYBE:
827 : : case OP_AND_NOT:
828 : : // If left hand side is MatchNothing, we match nothing.
829 : : // If right hand side is MatchNothing, replace node with LHS.
830 : : // So, if either node is MatchNothing, replace node with LHS.
831 : : // Easiest way to do this is to remove the right hand node,
832 : : // and let simplify_query() perform the replacement of
833 : : // the unary operator with its one remaining child.
834 : : Assert(subqs.size() == 2);
835 [ + + ][ + + ]: 282 : if (subqs[0] == 0 || subqs[1] == 0) {
[ + + ]
836 : 8 : sq = subqs.begin();
837 : 8 : ++sq;
838 [ + + ]: 8 : delete *sq;
839 : 8 : subqs.erase(sq);
840 : : }
841 : : break;
842 : : case OP_SCALE_WEIGHT:
843 : : Assert(subqs.size() == 1);
844 : : // We should have already handled OP_SCALE_WEIGHT applied to
845 : : // MatchNothing in the relevant constructor.
846 : : Assert(subqs[0]);
847 : : break;
848 : : case OP_LEAF:
849 : : // Do nothing.
850 : : break;
851 : : }
852 : 32663 : return false;
853 : : }
854 : :
855 : : Xapian::Query::Internal *
856 : 32663 : Xapian::Query::Internal::simplify_query()
857 : : {
858 : : LOGCALL(MATCH, Xapian::Query::Internal *, "Xapian::Query::Internal::simplify_query", NO_ARGS);
859 : :
860 : : // Simplify any MatchNothing nodes.
861 [ + + ]: 32663 : if (simplify_matchnothing()) {
862 : 15 : return 0;
863 : : }
864 : :
865 : : // General simplifications, dependent on operator.
866 [ - - - + + : 32648 : switch (op) {
+ + + ]
867 : : case OP_LEAF:
868 : 0 : return this;
869 : : case OP_VALUE_RANGE:
870 : : // If the start of the range is greater than the end then we won't
871 : : // match anything.
872 [ # # ]: 0 : if (tname > str_parameter) return 0;
873 : 0 : return this;
874 : : case OP_VALUE_GE:
875 : : case OP_VALUE_LE:
876 : 0 : return this;
877 : : case OP_SCALE_WEIGHT:
878 [ + + ]: 862 : if (fabs(get_dbl_parameter() - 1.0) > DBL_EPSILON) return this;
879 : : // If the multiplier is 1, this node doesn't actually do anything,
880 : : // so we leave it to be removed.
881 : 105 : break;
882 : : case OP_PHRASE: case OP_NEAR:
883 : : // Empty and single subquery OP_PHRASE and OP_NEAR get handled
884 : : // below.
885 [ + + ]: 1387 : if (subqs.size() <= 1) break;
886 : :
887 : : // Default to the number of subqueries.
888 [ + + ]: 1334 : if (!parameter) parameter = subqs.size();
889 : :
890 : : // Flatten out sub queries of a phrase or near operation.
891 : 1334 : return flatten_subqs();
892 : : case OP_ELITE_SET:
893 [ - + ]: 84 : if (!parameter) {
894 : : // Default to sqrt(number of subqueries), or a minimum of 10.
895 : : // Gives a reasonable default.
896 [ # # ]: 0 : if (subqs.size() <= 100) {
897 : 0 : parameter = 10;
898 : : } else {
899 : 0 : parameter = Xapian::termcount(ceil(sqrt(double(subqs.size()))));
900 : : Assert(parameter > 10);
901 : : }
902 : : }
903 : 84 : break;
904 : : case OP_OR: case OP_AND: case OP_XOR: case OP_SYNONYM:
905 : : // Remove duplicates if we can.
906 [ + + ]: 29934 : if (subqs.size() > 1) collapse_subqs();
907 : : break;
908 : : default:
909 : : break;
910 : : }
911 : :
912 : : // If we have no subqueries, then we're an empty query.
913 [ + + ]: 30557 : if (subqs.empty())
914 : 116 : return 0;
915 : :
916 : : // Any nodes which are valid with only one subquery can be replaced by
917 : : // that solitary subquery.
918 [ + + ]: 30441 : if (subqs.size() == 1) {
919 : 10558 : Xapian::Query::Internal * qint = subqs[0];
920 : 10558 : subqs[0] = 0;
921 : 10558 : return qint;
922 : : }
923 : :
924 : 32659 : return this;
925 : : }
926 : :
927 : : struct SortPosName {
928 : 619162 : bool operator()(const Xapian::Query::Internal * left,
929 : : const Xapian::Query::Internal * right) const {
930 : : Assert(left != 0);
931 : : Assert(right != 0);
932 : : Assert(is_leaf(left->op));
933 : : Assert(is_leaf(right->op));
934 [ + + ]: 619162 : if (left->term_pos != right->term_pos) {
935 : 525469 : return left->term_pos < right->term_pos;
936 : : } else {
937 : 619162 : return left->tname < right->tname;
938 : : }
939 : : }
940 : : };
941 : :
942 : : /** Collapse occurrences of a term at the same position into a
943 : : * single occurrence with higher wqf.
944 : : */
945 : : void
946 : 19445 : Xapian::Query::Internal::collapse_subqs()
947 : : {
948 : : Assert(op == OP_OR || op == OP_AND || op == OP_XOR || op == OP_SYNONYM);
949 : : typedef set<Xapian::Query::Internal *, SortPosName> subqtable;
950 : 19445 : subqtable sqtab;
951 : :
952 : 19445 : subquery_list::iterator sq = subqs.begin();
953 [ + + ]: 103802 : while (sq != subqs.end()) {
954 : : Assert(*sq != 0);
955 [ + + ]: 84357 : if (is_leaf((*sq)->op)) {
956 : : Assert((*sq)->subqs.empty());
957 : 67494 : subqtable::iterator s = sqtab.find(*sq);
958 [ + + ]: 67494 : if (s == sqtab.end()) {
959 : 57454 : sqtab.insert(*sq);
960 : 57454 : ++sq;
961 : : } else {
962 : : AssertEq((*s)->tname, (*sq)->tname);
963 : : AssertEq((*s)->term_pos, (*sq)->term_pos);
964 : : // parameter is wqf.
965 : 10040 : (*s)->parameter += (*sq)->parameter;
966 : : // Rather than incrementing sq, delete the current
967 : : // element, as it has been merged into the other
968 : : // equivalent term.
969 [ + - ]: 10040 : delete *sq;
970 : 10040 : sq = subqs.erase(sq);
971 : : }
972 : : } else {
973 : 16863 : ++sq;
974 : : }
975 : 19445 : }
976 : 19445 : }
977 : :
978 : : /// Change, eg, A NEAR (B AND C) to (A NEAR B) AND (A NEAR C)
979 : : Xapian::Query::Internal *
980 : 1500 : Xapian::Query::Internal::flatten_subqs()
981 : : {
982 : : Assert(op == Xapian::Query::OP_NEAR || op == Xapian::Query::OP_PHRASE);
983 : :
984 : 1500 : subquery_list::iterator sq;
985 [ + + ]: 5092 : for (sq = subqs.begin(); sq != subqs.end(); ++sq) {
986 [ + + ]: 3679 : if (!is_leaf((*sq)->op)) break;
987 : : }
988 : :
989 [ + + ]: 1500 : if (sq == subqs.end()) return this;
990 : :
991 [ + + ][ + + ]: 87 : if ((*sq)->op == Xapian::Query::OP_NEAR ||
[ + + ]
992 : : (*sq)->op == Xapian::Query::OP_PHRASE) {
993 : : // FIXME: A PHRASE (B PHRASE C) -> (A PHRASE B) AND (B PHRASE C)?
994 : 4 : throw Xapian::UnimplementedError("Can't use NEAR/PHRASE with a subexpression containing NEAR or PHRASE");
995 : : }
996 : :
997 : 83 : AutoPtr<Xapian::Query::Internal> flattenme(*sq);
998 : 83 : *sq = 0;
999 : :
1000 : 83 : subquery_list::iterator j;
1001 [ + + ]: 249 : for (j = flattenme->subqs.begin(); j != flattenme->subqs.end(); ++j) {
1002 : 166 : *sq = *j;
1003 : 166 : *j = 0;
1004 : 166 : AutoPtr<Xapian::Query::Internal> newq(new Xapian::Query::Internal(*this));
1005 [ + - ]: 166 : delete *sq;
1006 : 166 : *sq = 0;
1007 : 166 : newq.reset(newq->flatten_subqs());
1008 : 166 : *j = newq.release();
1009 : : }
1010 : :
1011 [ + + ][ - + ]: 83 : if (flattenme->op == OP_AND ||
[ # # ][ + - ]
1012 : : flattenme->op == OP_OR ||
1013 : : flattenme->op == OP_XOR) {
1014 : 83 : size_t i = flattenme->subqs.size();
1015 [ + + ]: 166 : do {
1016 : 166 : --i;
1017 : 166 : Xapian::Query::Internal * q = flattenme->subqs[i];
1018 [ + + ]: 166 : if (flattenme->op == q->op) {
1019 : 2 : subquery_list::iterator k;
1020 [ + + ]: 4 : for (k = q->subqs.begin(), ++k;
1021 : : k != q->subqs.end();
1022 : : ++k) {
1023 : 2 : flattenme->subqs.push_back(0);
1024 : 2 : flattenme->subqs.back() = *k;
1025 : 2 : *k = 0;
1026 : : }
1027 : 2 : flattenme->subqs[i] = q->subqs[0];
1028 : 2 : q->subqs.clear();
1029 [ + - ]: 2 : delete q;
1030 : : }
1031 : : } while (i != 0);
1032 : : }
1033 : :
1034 : 1496 : return flattenme.release();
1035 : : }
1036 : :
1037 : : void
1038 : 97281 : Xapian::Query::Internal::add_subquery(const Xapian::Query::Internal * subq)
1039 : : {
1040 : : Assert(!is_leaf(op));
1041 [ + + ]: 97281 : if (subq == 0) {
1042 : 111 : subqs.push_back(0);
1043 [ + + ][ + + ]: 97170 : } else if (op == subq->op && is_distributable(op)) {
[ + + ]
1044 : : // Distribute the subquery.
1045 [ + + ]: 5534 : for (subquery_list::const_iterator i = subq->subqs.begin();
1046 : : i != subq->subqs.end(); i++) {
1047 : 4599 : add_subquery(*i);
1048 : : }
1049 : : } else {
1050 : 96235 : subqs.push_back(new Xapian::Query::Internal(*subq));
1051 : : }
1052 : 97281 : }
1053 : :
1054 : : void
1055 : 4584 : Xapian::Query::Internal::add_subquery_nocopy(Xapian::Query::Internal * subq)
1056 : : {
1057 : : Assert(!is_leaf(op));
1058 [ - + ]: 4584 : if (subq == 0) {
1059 : 0 : subqs.push_back(0);
1060 [ - + ][ # # ]: 4584 : } else if (op == subq->op && is_distributable(op)) {
[ - + ]
1061 : : // Distribute the subquery.
1062 [ # # ]: 0 : for (subquery_list::const_iterator i = subq->subqs.begin();
1063 : : i != subq->subqs.end(); i++) {
1064 : 0 : add_subquery(*i);
1065 : : }
1066 [ # # ]: 0 : delete subq;
1067 : : } else {
1068 : 4584 : subqs.push_back(subq);
1069 : : }
1070 : 4584 : }
1071 : :
1072 : : void
1073 : 1278 : Xapian::Query::Internal::set_dbl_parameter(double dbl_parameter_)
1074 : : {
1075 : : // We store the double parameter encoded as a string because
1076 : : // Xapian::Query::Internal is defined in an external API header and we want
1077 : : // to avoid any risk of ABI breakage (we suspect it would be OK, but it's
1078 : : // not risking). FIXME:1.3: rework in 1.3.x series - see ticket #280
1079 : 1278 : str_parameter = serialise_double(dbl_parameter_);
1080 : 1278 : }
1081 : :
1082 : : double
1083 : 4647 : Xapian::Query::Internal::get_dbl_parameter() const
1084 : : {
1085 : : // We store the double parameter encoded as a string because
1086 : : // Xapian::Query::Internal is defined in an external API header and we want
1087 : : // to avoid any risk of ABI breakage (we suspect it would be OK, but it's
1088 : : // not risking). FIXME:1.3: rework in 1.3.x series - see ticket #280
1089 : 4647 : const char * p = str_parameter.data();
1090 : 4647 : const char * end = p + str_parameter.size();
1091 : 4647 : return unserialise_double(&p, end);
1092 : : }
|