Update for 1.4.20
[xapian.git] / xapian-bindings / php7 / smoketest.php
blob22ca5175485c2dcd80519f5d25e27e0a86151d70
1 <?php
2 // Run this PHP script using 'make check' in the build tree.
4 /* Simple test to ensure that we can load the xapian module and exercise basic
5 * functionality successfully.
7 * Copyright (C) 2004-2022 Olly Betts
8 * Copyright (C) 2010 Richard Boulton
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 * USA
26 # Die on any error, warning, notice, etc.
27 function die_on_error($errno, $errstr, $file, $line) {
28 if ($file !== Null) {
29 print $file;
30 if ($line !== Null) print ":$line";
31 print ": ";
33 print "$errstr\n";
34 exit(1);
36 set_error_handler("die_on_error", -1);
38 include "xapian.php";
40 # Test the version number reporting functions give plausible results.
41 $v = Xapian::major_version().'.'.Xapian::minor_version().'.'.Xapian::revision();
42 $v2 = Xapian::version_string();
43 if ($v != $v2) {
44 print "Unexpected version output ($v != $v2)\n";
45 exit(1);
48 $db = new XapianWritableDatabase('', Xapian::DB_BACKEND_INMEMORY);
49 $db2 = new XapianWritableDatabase('', Xapian::DB_BACKEND_INMEMORY);
51 # Check handling of Xapian::DocNotFoundError
52 try {
53 $doc2 = $db->get_document(2);
54 print "Retrieved non-existent document\n";
55 exit(1);
56 } catch (Exception $e) {
57 if ($e->getMessage() !== "DocNotFoundError: Docid 2 not found") {
58 print "DocNotFoundError Exception string not as expected, got: '{$e->getMessage()}'\n";
59 exit(1);
63 # Check QueryParser parsing error.
64 try {
65 $qp = new XapianQueryParser;
66 $qp->set_stemmer(new XapianStem("en"));
67 $qp->parse_query("test AND");
68 print "Successfully parsed bad query\n";
69 exit(1);
70 } catch (Exception $e) {
71 if ($e->getMessage() !== "QueryParserError: Syntax: <expression> AND <expression>") {
72 print "QueryParserError Exception string not as expected, got: '$e->getMessage()'\n";
73 exit(1);
77 # Check that open_stub() is wrapped as expected.
78 try {
79 $db = Xapian::auto_open_stub("nosuchdir/nosuchdb");
80 print "Opened non-existent stub database\n";
81 exit(1);
82 } catch (Exception $e) {
83 if ($e->getMessage() !== "DatabaseNotFoundError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
84 print "DatabaseOpeningError Exception string not as expected, got: '{$e->getMessage()}'\n";
85 exit(1);
89 # Check that DB_BACKEND_STUB works as expected.
90 try {
91 $db = new XapianDatabase("nosuchdir/nosuchdb", Xapian::DB_BACKEND_STUB);
92 print "Opened non-existent stub database\n";
93 exit(1);
94 } catch (Exception $e) {
95 if ($e->getMessage() !== "DatabaseNotFoundError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
96 print "DatabaseNotFoundError Exception string not as expected, got: '{$e->getMessage()}'\n";
97 exit(1);
101 # Check that open_stub() writable form is wrapped as expected.
102 try {
103 $db = Xapian::auto_open_stub("nosuchdir/nosuchdb", Xapian::DB_OPEN);
104 print "Opened non-existent stub database\n";
105 exit(1);
106 } catch (Exception $e) {
107 if ($e->getMessage() !== "DatabaseNotFoundError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
108 print "DatabaseOpeningError Exception string not as expected, got: '{$e->getMessage()}'\n";
109 exit(1);
113 # Check that DB_BACKEND_STUB works as expected.
114 try {
115 $db = new XapianWritableDatabase("nosuchdir/nosuchdb",
116 Xapian::DB_OPEN|Xapian::DB_BACKEND_STUB);
117 print "Opened non-existent stub database\n";
118 exit(1);
119 } catch (Exception $e) {
120 if ($e->getMessage() !== "DatabaseNotFoundError: Couldn't open stub database file: nosuchdir/nosuchdb (No such file or directory)") {
121 print "DatabaseNotFoundError Exception string not as expected, got: '{$e->getMessage()}'\n";
122 exit(1);
126 # Regression test for bug#193, fixed in 1.0.3.
127 $vrp = new XapianNumberValueRangeProcessor(0, '$', true);
128 $a = '$10';
129 $b = '20';
130 $vrp->apply($a, $b);
131 if (Xapian::sortable_unserialise($a) != 10) {
132 print Xapian::sortable_unserialise($a)." != 10\n";
133 exit(1);
135 if (Xapian::sortable_unserialise($b) != 20) {
136 print Xapian::sortable_unserialise($b)." != 20\n";
137 exit(1);
140 $stem = new XapianStem("english");
141 if ($stem->get_description() != "Xapian::Stem(english)") {
142 print "Unexpected \$stem->get_description()\n";
143 exit(1);
146 $doc = new XapianDocument();
147 $doc->set_data("a\x00b");
148 if ($doc->get_data() === "a") {
149 print "get_data+set_data truncates at a zero byte\n";
150 exit(1);
152 if ($doc->get_data() !== "a\x00b") {
153 print "get_data+set_data doesn't transparently handle a zero byte\n";
154 exit(1);
156 $doc->set_data("is there anybody out there?");
157 $doc->add_term("XYzzy");
158 $doc->add_posting($stem->apply("is"), 1);
159 $doc->add_posting($stem->apply("there"), 2);
160 $doc->add_posting($stem->apply("anybody"), 3);
161 $doc->add_posting($stem->apply("out"), 4);
162 $doc->add_posting($stem->apply("there"), 5);
164 // Check virtual function dispatch.
165 if (substr($db->get_description(), 0, 17) !== "WritableDatabase(") {
166 print "Unexpected \$db->get_description()\n";
167 exit(1);
169 $db->add_document($doc);
170 if ($db->get_doccount() != 1) {
171 print "Unexpected \$db->get_doccount()\n";
172 exit(1);
175 $terms = array("smoke", "test", "terms");
176 $query = new XapianQuery(XapianQuery::OP_OR, $terms);
177 if ($query->get_description() != "Query((smoke OR test OR terms))") {
178 print "Unexpected \$query->get_description()\n";
179 exit(1);
181 $query1 = new XapianQuery(XapianQuery::OP_PHRASE, array("smoke", "test", "tuple"));
182 if ($query1->get_description() != "Query((smoke PHRASE 3 test PHRASE 3 tuple))") {
183 print "Unexpected \$query1->get_description()\n";
184 exit(1);
186 $query1b = new XapianQuery(XapianQuery::OP_NEAR, array("smoke", "test", "tuple"), 4);
187 if ($query1b->get_description() != "Query((smoke NEAR 4 test NEAR 4 tuple))") {
188 print "Unexpected \$query1b->get_description()\n";
189 exit(1);
191 $query2 = new XapianQuery(XapianQuery::OP_XOR, array(new XapianQuery("smoke"), $query1, "string"));
192 if ($query2->get_description() != "Query((smoke XOR (smoke PHRASE 3 test PHRASE 3 tuple) XOR string))") {
193 print "Unexpected \$query2->get_description()\n";
194 exit(1);
196 $subqs = array("a", "b");
197 $query3 = new XapianQuery(XapianQuery::OP_OR, $subqs);
198 if ($query3->get_description() != "Query((a OR b))") {
199 print "Unexpected \$query3->get_description()\n";
200 exit(1);
202 $enq = new XapianEnquire($db);
204 // Check Xapian::BAD_VALUENO is wrapped suitably.
205 $enq->set_collapse_key(Xapian::BAD_VALUENO);
207 // Test that the non-constant wrapping prior to 1.4.10 still works.
208 $enq->set_collapse_key(Xapian::BAD_VALUENO_get());
210 $enq->set_query(new XapianQuery(XapianQuery::OP_OR, "there", "is"));
211 $mset = $enq->get_mset(0, 10);
212 if ($mset->size() != 1) {
213 print "Unexpected \$mset->size()\n";
214 exit(1);
216 $terms = join(" ", $enq->get_matching_terms($mset->get_hit(0)));
217 if ($terms != "is there") {
218 print "Unexpected matching terms: $terms\n";
219 exit(1);
222 # Feature test for MatchDecider
223 $doc = new XapianDocument();
224 $doc->set_data("Two");
225 $doc->add_posting($stem->apply("out"), 1);
226 $doc->add_posting($stem->apply("outside"), 1);
227 $doc->add_posting($stem->apply("source"), 2);
228 $doc->add_value(0, "yes");
229 $db->add_document($doc);
231 class testmatchdecider extends XapianMatchDecider {
232 function apply($doc) {
233 return ($doc->get_value(0) == "yes");
237 $query = new XapianQuery($stem->apply("out"));
238 $enquire = new XapianEnquire($db);
239 $enquire->set_query($query);
240 $mdecider = new testmatchdecider();
241 $mset = $enquire->get_mset(0, 10, null, $mdecider);
242 if ($mset->size() != 1) {
243 print "Unexpected number of documents returned by match decider (".$mset->size().")\n";
244 exit(1);
246 if ($mset->get_docid(0) != 2) {
247 print "MatchDecider mset has wrong docid in\n";
248 exit(1);
251 class testexpanddecider extends XapianExpandDecider {
252 function apply($term) {
253 return ($term[0] !== 'a');
257 $enquire = new XapianEnquire($db);
258 $rset = new XapianRSet();
259 $rset->add_document(1);
260 $eset = $enquire->get_eset(10, $rset, XapianEnquire::USE_EXACT_TERMFREQ, 1.0, new testexpanddecider());
261 foreach ($eset->begin() as $t) {
262 if ($t[0] === 'a') {
263 print "XapianExpandDecider was not used\n";
264 exit(1);
268 # Check min_wt argument to get_eset() works (new in 1.2.5).
269 $eset = $enquire->get_eset(100, $rset, XapianEnquire::USE_EXACT_TERMFREQ);
270 $min_wt = 0;
271 foreach ($eset->begin() as $i => $dummy) {
272 $min_wt = $i->get_weight();
274 if ($min_wt >= 1.9) {
275 print "ESet min weight too high for testcase\n";
276 exit(1);
278 $eset = $enquire->get_eset(100, $rset, XapianEnquire::USE_EXACT_TERMFREQ, 1.0, NULL, 1.9);
279 $min_wt = 0;
280 foreach ($eset->begin() as $i => $dummy) {
281 $min_wt = $i->get_weight();
283 if ($min_wt < 1.9) {
284 print "ESet min_wt threshold not applied\n";
285 exit(1);
288 if (XapianQuery::OP_ELITE_SET != 10) {
289 print "OP_ELITE_SET is XapianQuery::OP_ELITE_SET not 10\n";
290 exit(1);
293 # Regression test - overload resolution involving boolean types failed.
294 $enq->set_sort_by_value(1, TRUE);
296 # Regression test - fixed in 0.9.10.1.
297 $oqparser = new XapianQueryParser();
298 $oquery = $oqparser->parse_query("I like tea");
300 # Regression test for bug#192 - fixed in 1.0.3.
301 $enq->set_cutoff(100);
303 # Check DateRangeProcessor works.
304 function add_rp_date(&$qp) {
305 $rpdate = new XapianDateRangeProcessor(1, Xapian::RP_DATE_PREFER_MDY, 1960);
306 $qp->add_rangeprocessor($rpdate);
308 $qp = new XapianQueryParser();
309 add_rp_date($qp);
310 $query = $qp->parse_query('12/03/99..12/04/01');
311 if ($query->get_description() !== 'Query(VALUE_RANGE 1 19991203 20011204)') {
312 print "XapianDateRangeProcessor didn't work - result was ".$query->get_description()."\n";
313 exit(1);
316 # Check DateValueRangeProcessor works.
317 function add_vrp_date(&$qp) {
318 $vrpdate = new XapianDateValueRangeProcessor(1, 1, 1960);
319 $qp->add_valuerangeprocessor($vrpdate);
321 $qp = new XapianQueryParser();
322 add_vrp_date($qp);
323 $query = $qp->parse_query('12/03/99..12/04/01');
324 if ($query->get_description() !== 'Query(VALUE_RANGE 1 19991203 20011204)') {
325 print "XapianDateValueRangeProcessor didn't work - result was ".$query->get_description()."\n";
326 exit(1);
329 # Feature test for XapianFieldProcessor
330 class testfieldprocessor extends XapianFieldProcessor {
331 function apply($str) {
332 if ($str === 'spam') throw new Exception('already spam');
333 return new XapianQuery("spam");
337 $qp->add_prefix('spam', new testfieldprocessor);
338 $qp->add_boolean_prefix('filter', new testfieldprocessor);
339 $query = $qp->parse_query('spam:ignored');
340 if ($query->get_description() !== 'Query(spam)') {
341 print "testfieldprocessor didn't work - result was ".$query->get_description()."\n";
342 exit(1);
344 $query = $qp->parse_query('filter:ignored');
345 if ($query->get_description() !== 'Query(0 * spam)') {
346 print "Boolean testfieldprocessor didn't work - result was ".$query->get_description()."\n";
347 exit(1);
350 try {
351 $query = $qp->parse_query('spam:spam');
352 print "testfieldprocessor exception not rethrown\n";
353 exit(1);
354 } catch (Exception $e) {
355 if ($e->getMessage() !== 'already spam') {
356 print "Exception has wrong message\n";
357 exit(1);
361 # Test setting and getting metadata
362 if ($db->get_metadata('Foo') !== '') {
363 print "Unexpected value for metadata associated with 'Foo' (expected ''): '".$db->get_metadata('Foo')."'\n";
364 exit(1);
366 $db->set_metadata('Foo', 'Foo');
367 if ($db->get_metadata('Foo') !== 'Foo') {
368 print "Unexpected value for metadata associated with 'Foo' (expected 'Foo'): '".$db->get_metadata('Foo')."'\n";
369 exit(1);
372 # Test OP_SCALE_WEIGHT and corresponding constructor
373 $query4 = new XapianQuery(XapianQuery::OP_SCALE_WEIGHT, new XapianQuery('foo'), 5.0);
374 if ($query4->get_description() != "Query(5 * foo)") {
375 print "Unexpected \$query4->get_description()\n";
376 exit(1);
379 # Test MultiValueKeyMaker.
381 $doc = new XapianDocument();
382 $doc->add_term("foo");
383 $doc->add_value(0, "ABB");
384 $db2->add_document($doc);
385 $doc->add_value(0, "ABC");
386 $db2->add_document($doc);
387 $doc->add_value(0, "ABC\0");
388 $db2->add_document($doc);
389 $doc->add_value(0, "ABCD");
390 $db2->add_document($doc);
391 $doc->add_value(0, "ABC\xff");
392 $db2->add_document($doc);
394 $enquire = new XapianEnquire($db2);
395 $enquire->set_query(new XapianQuery("foo"));
398 $sorter = new XapianMultiValueKeyMaker();
399 $sorter->add_value(0);
400 $enquire->set_sort_by_key($sorter, true);
401 $mset = $enquire->get_mset(0, 10);
402 mset_expect_order($mset, array(5, 4, 3, 2, 1));
406 $sorter = new XapianMultiValueKeyMaker();
407 $sorter->add_value(0, true);
408 $enquire->set_sort_by_key($sorter, true);
409 $mset = $enquire->get_mset(0, 10);
410 mset_expect_order($mset, array(1, 2, 3, 4, 5));
414 $sorter = new XapianMultiValueKeyMaker();
415 $sorter->add_value(0);
416 $sorter->add_value(1);
417 $enquire->set_sort_by_key($sorter, true);
418 $mset = $enquire->get_mset(0, 10);
419 mset_expect_order($mset, array(5, 4, 3, 2, 1));
423 $sorter = new XapianMultiValueKeyMaker();
424 $sorter->add_value(0, true);
425 $sorter->add_value(1);
426 $enquire->set_sort_by_key($sorter, true);
427 $mset = $enquire->get_mset(0, 10);
428 mset_expect_order($mset, array(1, 2, 3, 4, 5));
432 $sorter = new XapianMultiValueKeyMaker();
433 $sorter->add_value(0);
434 $sorter->add_value(1, true);
435 $enquire->set_sort_by_key($sorter, true);
436 $mset = $enquire->get_mset(0, 10);
437 mset_expect_order($mset, array(5, 4, 3, 2, 1));
441 $sorter = new XapianMultiValueKeyMaker();
442 $sorter->add_value(0, true);
443 $sorter->add_value(1, true);
444 $enquire->set_sort_by_key($sorter, true);
445 $mset = $enquire->get_mset(0, 10);
446 mset_expect_order($mset, array(1, 2, 3, 4, 5));
449 # Feature test for ValueSetMatchDecider:
451 $md = new XapianValueSetMatchDecider(0, true);
452 $md->add_value("ABC");
453 $doc = new XapianDocument();
454 $doc->add_value(0, "ABCD");
455 if ($md->apply($doc)) {
456 print "Unexpected result from ValueSetMatchDecider->apply(); expected false\n";
457 exit(1);
460 $doc = new XapianDocument();
461 $doc->add_value(0, "ABC");
462 if (!$md->apply($doc)) {
463 print "Unexpected result from ValueSetMatchDecider->apply(); expected true\n";
464 exit(1);
467 $mset = $enquire->get_mset(0, 10, 0, null, $md);
468 mset_expect_order($mset, array(2));
470 $md = new XapianValueSetMatchDecider(0, false);
471 $md->add_value("ABC");
472 $mset = $enquire->get_mset(0, 10, 0, null, $md);
473 mset_expect_order($mset, array(1, 3, 4, 5));
476 function mset_expect_order($mset, $a) {
477 if ($mset->size() != sizeof($a)) {
478 print "MSet has ".$mset->size()." entries, expected ".sizeof($a)."\n";
479 exit(1);
481 for ($j = 0; $j < sizeof($a); ++$j) {
482 $docid = $mset->get_hit($j)->get_docid();
483 if ($docid != $a[$j]) {
484 print "Expected MSet[$j] to be $a[$j], got ".$docid()."\n";
485 exit(1);
490 # Feature tests for Query "term" constructor optional arguments:
491 $query_wqf = new XapianQuery('wqf', 3);
492 if ($query_wqf->get_description() != 'Query(wqf#3)') {
493 print "Unexpected \$query_wqf->get_description():\n";
494 print $query_wqf->get_description() . "\n";
495 exit(1);
498 $query = new XapianQuery(XapianQuery::OP_VALUE_GE, 0, "100");
499 if ($query->get_description() != 'Query(VALUE_GE 0 100)') {
500 print "Unexpected \$query->get_description():\n";
501 print $query->get_description() . "\n";
502 exit(1);
505 $query = XapianQuery::MatchAll();
506 if ($query->get_description() != 'Query(<alldocuments>)') {
507 print "Unexpected \$query->get_description():\n";
508 print $query->get_description() . "\n";
509 exit(1);
512 $query = XapianQuery::MatchNothing();
513 if ($query->get_description() != 'Query()') {
514 print "Unexpected \$query->get_description():\n";
515 print $query->get_description() . "\n";
516 exit(1);
519 # Test access to matchspy values:
521 $matchspy = new XapianValueCountMatchSpy(0);
522 $enquire->add_matchspy($matchspy);
523 $enquire->get_mset(0, 10);
524 $values = array();
525 foreach ($matchspy->values_begin() as $k => $term) {
526 $values[$term] = $k->get_termfreq();
528 $expected = array(
529 "ABB" => 1,
530 "ABC" => 1,
531 "ABC\0" => 1,
532 "ABCD" => 1,
533 "ABC\xff" => 1,
535 if ($values != $expected) {
536 print "Unexpected matchspy values():\n";
537 var_dump($values);
538 var_dump($expected);
539 print "\n";
540 exit(1);
545 class testspy extends XapianMatchSpy {
546 public $matchspy_count = 0;
548 function apply($doc, $wt) {
549 if (substr($doc->get_value(0), 0, 3) == "ABC") ++$this->matchspy_count;
553 $matchspy = new testspy();
554 $enquire->clear_matchspies();
555 $enquire->add_matchspy($matchspy);
556 $enquire->get_mset(0, 10);
557 if ($matchspy->matchspy_count != 4) {
558 print "Unexpected matchspy count of {$matchspy->matchspy_count}\n";
559 exit(1);
563 # Regression test for SWIG bug - it was generating "return $r;" in wrapper
564 # functions which didn't set $r.
565 $indexer = new XapianTermGenerator();
566 $doc = new XapianDocument();
568 $indexer->set_document($doc);
569 $indexer->index_text("I ask nothing in return");
570 $indexer->index_text_without_positions("Tea time");
571 $indexer->index_text("Return in time");
573 $s = '';
574 foreach ($doc->termlist_begin() as $term) {
575 $s .= $term . ' ';
577 if ($s !== 'ask i in nothing return tea time ') {
578 print "PHP Iterator wrapping of TermIterator doesn't work ($s)\n";
579 exit(1);
582 $s = '';
583 foreach ($doc->termlist_begin() as $k => $term) {
584 $s .= $term . ':' . $k->get_wdf() . ' ';
586 if ($s !== 'ask:1 i:1 in:2 nothing:1 return:2 tea:1 time:2 ') {
587 print "PHP Iterator wrapping of TermIterator keys doesn't work ($s)\n";
588 exit(1);
591 // Test that XapianTermGenerator keeps a reference to XapianStopper.
592 $indexer->set_stopper_strategy(XapianTermGenerator::STOP_ALL);
594 $stop = new XapianSimpleStopper();
595 $stop->add('a');
596 $indexer->set_stopper($stop);
597 $stop = null;
599 $indexer->index_text("a b");
601 # Test GeoSpatial API
602 $coord = new XapianLatLongCoord();
603 $coord = new XapianLatLongCoord(-41.288889, 174.777222);
605 define('COORD_SLOT', 2);
606 $metric = new XapianGreatCircleMetric();
607 $range = 42.0;
609 $centre = new XapianLatLongCoords($coord);
610 $query = new XapianQuery(new XapianLatLongDistancePostingSource(COORD_SLOT, $centre, $metric, $range));
612 $db = new XapianWritableDatabase('', Xapian::DB_BACKEND_INMEMORY);
613 $coords = new XapianLatLongCoords();
614 $coords->append(new XapianLatLongCoord(40.6048, -74.4427));
615 $doc = new XapianDocument();
616 $doc->add_term("coffee");
617 $doc->add_value(COORD_SLOT, $coords->serialise());
618 $db->add_document($doc);
620 $centre = new XapianLatLongCoords();
621 $centre->append(new XapianLatLongCoord(40.6048, -74.4427));
623 $ps = new XapianLatLongDistancePostingSource(COORD_SLOT, $centre, $metric, $range);
624 $q = new XapianQuery("coffee");
625 $q = new XapianQuery(XapianQuery::OP_AND, $q, new XapianQuery($ps));
626 $q = new XapianQuery(XapianQuery::OP_OR, [$q, XapianQuery::MatchNothing()]);
627 // Check that we keep a reference via XapianQuery.
628 $ps = null;
630 $enq = new XapianEnquire($db);
631 $enq->set_query($q);
632 $mset = $enq->get_mset(0, 10);
633 if ($mset->size() != 1) {
634 print "Expected one result with XapianLatLongDistancePostingSource, got ";
635 print $mset->size() . "\n";
636 exit(1);
639 $s = '';
640 foreach ($db->allterms_begin() as $k => $term) {
641 $s .= "($term:{$k->get_termfreq()})";
643 if ($s !== '(coffee:1)') {
644 print "PHP Iterator iteration of allterms doesn't work ($s)\n";
645 exit(1);
648 # Test reference tracking and regression test for #659.
649 $qp = new XapianQueryParser();
651 $stop = new XapianSimpleStopper();
652 $stop->add('a');
653 $qp->set_stopper($stop);
654 // Test that XapianQueryParser keeps a reference to XapianStopper.
655 $stop = null;
657 $query = $qp->parse_query('a b');
658 if ($query->get_description() !== 'Query(b@2)') {
659 print "XapianQueryParser::set_stopper() didn't work as expected - result was ".$query->get_description()."\n";
660 exit(1);