LCOV - code coverage report
Current view: top level - api - omqueryinternal.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 442 472 93.6 %
Date: 2011-08-21 Functions: 37 44 84.1 %
Branches: 264 346 76.3 %

           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 &copyme)
     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                 :            : }

Generated by: LCOV version 1.8