Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / PoolQuery.cc
index 79f5ee6..4e286b2 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "zypp/sat/Pool.h"
 #include "zypp/sat/Solvable.h"
-#include "zypp/sat/AttrMatcher.h"
+#include "zypp/base/StrMatcher.h"
 
 #include "zypp/PoolQuery.h"
 
@@ -39,52 +39,6 @@ namespace zypp
   namespace
   { /////////////////////////////////////////////////////////////////
 
-    /** Match data per attribtue.
-     *
-     * This includes the attribute itself, an optional \ref sat::AttrMatcher
-     * to restrict the query to certain string values, and an optional
-     * boolean \ref Predicate that may apply further restrictions that can
-     * not be expressed by the \ref attrMatcher.
-     *
-     * Example for such a \ref predicate would be an additional edition range
-     * check whan looking for dependencies. The \ref attrMatcher would
-     * find potential matches by looking at the dependencies name, the
-     * predicate will then check the edition ranges.
-     *
-     * As the \ref predicate takes an iterator pointing to the current
-     * match, it's also suitable for sub-structure (flexarray) inspection
-     * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
-     */
-    struct AttrMatchData
-    {
-      typedef function<bool(sat::LookupAttr::iterator)> Predicate;
-
-      static bool always( sat::LookupAttr::iterator ) { return true; }
-      static bool never( sat::LookupAttr::iterator ) { return false; }
-
-      AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r )
-        : attr( attr_r )
-        , attrMatcher( attrMatcher_r )
-      {}
-
-      AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r, const Predicate & predicate_r )
-        : attr( attr_r )
-        , attrMatcher( attrMatcher_r )
-        , predicate( predicate_r )
-      {}
-
-      sat::SolvAttr    attr;
-      sat::AttrMatcher attrMatcher;
-      Predicate        predicate;
-    };
-
-    std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
-    {
-      return str << obj.attr << ": " << obj.attrMatcher << ( obj.predicate ? " (+predicate)" : "" );
-    }
-
-    typedef std::list<AttrMatchData> AttrMatchList;
-
     /////////////////////////////////////////////////////////////////
     // some Helpers and Predicates
     /////////////////////////////////////////////////////////////////
@@ -107,7 +61,7 @@ namespace zypp
       return false;
     }
 
-    /** Whether the current capabilities edition range ovelaps.
+    /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
      * Query asserts \a iter_r points to a capability and we
      * have to check the range only.
      */
@@ -115,10 +69,18 @@ namespace zypp
     {
       EditionRangePredicate( const Rel & op, const Edition & edition )
         : _range( op, edition )
+        , _arch( Arch_empty )
+      {}
+      EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+        : _range( op, edition )
+        , _arch( arch )
       {}
 
       bool operator()( sat::LookupAttr::iterator iter_r )
       {
+       if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+         return false;
+
         CapDetail cap( iter_r.id() );
         if ( ! cap.isSimple() )
           return false;
@@ -127,22 +89,50 @@ namespace zypp
         return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
       }
 
+      std::string serialize() const
+      {
+        std::string ret( "EditionRange" );
+        str::appendEscaped( ret, _range.op.asString() );
+        str::appendEscaped( ret, _range.value.asString() );
+        str::appendEscaped( ret, _arch.asString() );
+        return ret;
+      }
+
       Edition::MatchRange _range;
-    };
+      Arch                _arch;
+   };
 
-    /** Whether the current Solvables edition is within a given range. */
+    /** Whether the current Solvables edition is within a given range and/or its arch matches. */
     struct SolvableRangePredicate
     {
       SolvableRangePredicate( const Rel & op, const Edition & edition )
         : _range( op, edition )
+        , _arch( Arch_empty )
+      {}
+
+      SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+        : _range( op, edition )
+        , _arch( arch )
       {}
 
       bool operator()( sat::LookupAttr::iterator iter_r )
       {
-        return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
+       if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+         return false;
+       return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "SolvableRange" );
+        str::appendEscaped( ret, _range.op.asString() );
+        str::appendEscaped( ret, _range.value.asString() );
+        str::appendEscaped( ret, _arch.asString() );
+        return ret;
       }
 
       Edition::MatchRange _range;
+      Arch                _arch;
     };
 
     /** Whether the current capability matches a given one.
@@ -160,9 +150,198 @@ namespace zypp
         return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
       }
 
+      std::string serialize() const
+      {
+        std::string ret( "CapabilityMatch" );
+        str::appendEscaped( ret, _cap.asString() );
+        return ret;
+      }
+
       Capability _cap;
     };
 
+    /////////////////////////////////////////////////////////////////
+    //
+    /////////////////////////////////////////////////////////////////
+    /** Match data per attribtue.
+     *
+     * This includes the attribute itself, an optional \ref StrMatcher
+     * to restrict the query to certain string values, and an optional
+     * boolean \ref Predicate that may apply further restrictions that can
+     * not be expressed by the \ref strMatcher.
+     *
+     * Example for such a \ref predicate would be an additional edition range
+     * check whan looking for dependencies. The \ref strMatcher would
+     * find potential matches by looking at the dependencies name, the
+     * predicate will then check the edition ranges.
+     *
+     * As the \ref predicate takes an iterator pointing to the current
+     * match, it's also suitable for sub-structure (flexarray) inspection
+     * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
+     *
+     * \note: \see \ref addPredicate for further constraints.
+     */
+    struct AttrMatchData
+    {
+      typedef function<bool(sat::LookupAttr::iterator)> Predicate;
+
+      static bool always( sat::LookupAttr::iterator ) { return true; }
+      static bool never( sat::LookupAttr::iterator ) { return false; }
+
+      AttrMatchData()
+      {}
+
+      AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
+        : attr( attr_r )
+        , strMatcher( strMatcher_r )
+      {}
+
+      AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
+                     const Predicate & predicate_r, const std::string & predicateStr_r )
+        : attr( attr_r )
+        , strMatcher( strMatcher_r )
+        , predicate( predicate_r )
+        , predicateStr( predicateStr_r )
+      {}
+
+      /** A usable Predicate must provide a string serialization.
+       * As there is no \c operator== for \ref Predicate, we compare it's
+       * string representation instead. If you add new predicated, check the
+       * deserialization code in \ref deserialize.
+       */
+      template<class _Predicate>
+      void addPredicate( const _Predicate & predicate_r )
+      {
+        predicate    = predicate_r;
+        predicateStr = predicate_r.serialize();
+      }
+
+      /** Dumb serialization.
+       * \code
+       *   AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
+       * \endcode
+      */
+      std::string serialize() const
+      {
+        std::string ret( "AttrMatchData" );
+        str::appendEscaped( ret, attr.asString() );
+        str::appendEscaped( ret, strMatcher.searchstring() );
+        // TODO: Actually the flag should be serialized too, but for PoolQuery
+        // it's by now sufficient to differ between mode OTHER and others,
+        // i.e. whether to compile or not compile.
+        str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
+        str::appendEscaped( ret, predicateStr );
+        return ret;
+      }
+
+       /** Dumb restore from serialized string.
+        * \throw Exception on parse error.
+        */
+      static AttrMatchData deserialize( const std::string & str_r )
+      {
+        std::vector<std::string> words;
+        str::splitEscaped( str_r, std::back_inserter(words) );
+        if ( words.empty() || words[0] != "AttrMatchData" )
+          ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
+        if ( words.size() != 5 )
+          ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+
+        AttrMatchData ret;
+        ret.attr = sat::SolvAttr( words[1] );
+        ret.strMatcher = StrMatcher( words[2] );
+        if ( words[3] == "C" )
+          ret.strMatcher.setFlags( Match::OTHER );
+        ret.predicateStr = words[4];
+
+        // now the predicate
+        words.clear();
+        str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
+        if ( ! words.empty() )
+        {
+          if ( words[0] == "EditionRange" )
+          {
+           switch( words.size() )
+           {
+             case 3:
+               ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
+               break;
+             case 4:
+               ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+               break;
+             default:
+               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+               break;
+           }
+          }
+          else if ( words[0] == "SolvableRange" )
+          {
+           switch( words.size() )
+           {
+             case 3:
+               ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
+               break;
+             case 4:
+               ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+               break;
+             default:
+               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+               break;
+           }
+          }
+          else if ( words[0] == "CapabilityMatch" )
+          {
+            if ( words.size() != 2 )
+              ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+            ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
+          }
+          else
+            ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
+        }
+        return ret;
+     }
+
+      sat::SolvAttr    attr;
+      StrMatcher strMatcher;
+      Predicate        predicate;
+      std::string      predicateStr;
+    };
+
+    /** \relates AttrMatchData */
+    inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
+    {
+      str << obj.attr << ": " << obj.strMatcher;
+      if ( obj.predicate )
+        str << " +(" << obj.predicateStr << ")";
+      return str;
+    }
+
+    /** \relates AttrMatchData */
+    inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    {
+      return ( lhs.attr == rhs.attr
+               && lhs.strMatcher == rhs.strMatcher
+               && lhs.predicateStr == rhs.predicateStr );
+    }
+
+    /** \relates AttrMatchData */
+    inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    { return !( lhs == rhs ); }
+
+    /** \relates AttrMatchData Arbitrary order for std::container. */
+    inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    {
+      if ( lhs.attr != rhs.attr )
+        return (  lhs.attr < rhs.attr );
+      if ( lhs.strMatcher != rhs.strMatcher )
+        return (  lhs.strMatcher < rhs.strMatcher );
+      if ( lhs.predicateStr != rhs.predicateStr )
+        return (  lhs.predicateStr < rhs.predicateStr );
+      return false;
+    }
+
+    typedef std::list<AttrMatchData> AttrMatchList;
+
+
   } /////////////////////////////////////////////////////////////////
   // namespace
   ///////////////////////////////////////////////////////////////////
@@ -196,7 +375,7 @@ namespace zypp
     /** Raw attributes */
     AttrRawStrMap _attrs;
     /** Uncompiled attributes with predicate. */
-    AttrMatchList _uncompiledPredicated;
+    std::set<AttrMatchData> _uncompiledPredicated;
 
     /** Sat solver search flags */
     Match _flags;
@@ -219,13 +398,46 @@ namespace zypp
     //@}
 
   public:
+
+    bool operator==( const PoolQuery::Impl & rhs ) const
+    {
+      if ( _flags == rhs._flags
+       // bnc#792901: while libzypp uses exact match mode for a single
+       // package name lock, zypper always uses glob. :(
+       // We unify those two forms to enable zypper to remove zypp locks
+       // without need to actually evaluate the query (which would require
+       // repos to be loaded).
+       || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
+           || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
+         && _strings.empty()
+         && _attrs.size() == 1
+         && _attrs.begin()->first == sat::SolvAttr::name ) )
+      {
+       return ( _strings == rhs._strings
+             && _attrs == rhs._attrs
+             && _uncompiledPredicated == rhs._uncompiledPredicated
+             && _match_word == rhs._match_word
+             && _require_all == rhs._require_all
+             && _status_flags == rhs._status_flags
+             && _edition == rhs._edition
+             && _op == rhs._op
+             && _repos == rhs._repos
+             && _kinds == rhs._kinds );
+      }
+      return false;
+    }
+
+    bool operator!=( const PoolQuery::Impl & rhs ) const
+    { return ! operator==( rhs ); }
+
+  public:
     /** Compile the regex.
      * Basically building the \ref _attrMatchList from strings.
-     * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
+     * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
      */
     void compile() const;
 
-    /** AttrMatcher per attribtue. */
+    /** StrMatcher per attribtue. */
     mutable AttrMatchList _attrMatchList;
 
   private:
@@ -239,6 +451,7 @@ namespace zypp
     { return new Impl( *this ); }
   };
 
+  ///////////////////////////////////////////////////////////////////
 
   struct MyInserter
   {
@@ -267,7 +480,7 @@ namespace zypp
     _attrMatchList.clear();
 
     Match cflags( _flags );
-    if ( cflags.mode() == Match::OTHER) // this will never succeed...
+    if ( cflags.mode() == Match::OTHER ) // this will never succeed...
       ZYPP_THROW( MatchUnknownModeException( cflags ) );
 
     /** Compiled search strings. */
@@ -301,7 +514,7 @@ namespace zypp
       if (joined.size() > 1) // switch to regex for multiple strings
         cflags.setModeRegex();
       _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
-                                sat::AttrMatcher( rcstrings, cflags ) ) );
+                                StrMatcher( rcstrings, cflags ) ) );
     }
 
     // // MULTIPLE ATTRIBUTES
@@ -359,8 +572,8 @@ attremptycheckend:
         }
         if (joined.size() > 1) // switch to regex for multiple strings
           cflags.setModeRegex();
-        // May use the same AttrMatcher for all
-        sat::AttrMatcher matcher( rcstrings, cflags );
+        // May use the same StrMatcher for all
+        StrMatcher matcher( rcstrings, cflags );
         for_( ai, _attrs.begin(), _attrs.end() )
         {
           _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
@@ -382,7 +595,7 @@ attremptycheckend:
           if (joined.size() > 1) // switch to regex for multiple strings
             cflags.setModeRegex();
           _attrMatchList.push_back( AttrMatchData( ai->first,
-                                    sat::AttrMatcher( s, cflags ) ) );
+                                    StrMatcher( s, cflags ) ) );
         }
       }
     }
@@ -394,11 +607,11 @@ attremptycheckend:
       invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
       for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
       {
-        if ( it->attrMatcher.flags().mode() == Match::OTHER )
+        if ( it->strMatcher.flags().mode() == Match::OTHER )
         {
           // need to compile:
           StrContainer joined( global );
-          const std::string & mstr( it->attrMatcher.searchstring() );
+          const std::string & mstr( it->strMatcher.searchstring() );
           if ( ! mstr.empty() )
             joined.insert( mstr );
 
@@ -408,8 +621,8 @@ attremptycheckend:
             cflags.setModeRegex();
 
           _attrMatchList.push_back( AttrMatchData( it->attr,
-                                    sat::AttrMatcher( rcstrings, cflags ),
-                                        it->predicate ) );
+                                    StrMatcher( rcstrings, cflags ),
+                                                      it->predicate, it->predicateStr ) );
         }
         else
         {
@@ -427,13 +640,13 @@ attremptycheckend:
       if ( _strings.size() > 1 ) // switch to regex for multiple strings
         cflags.setModeRegex();
       _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
-                                sat::AttrMatcher( rcstrings, cflags ) ) );
+                                StrMatcher( rcstrings, cflags ) ) );
     }
 
     // Finally check here, whether all involved regex compile.
     for_( it, _attrMatchList.begin(), _attrMatchList.end() )
     {
-      it->attrMatcher.compile(); // throws on error
+      it->strMatcher.compile(); // throws on error
     }
     //DBG << asString() << endl;
   }
@@ -642,12 +855,19 @@ attremptycheckend:
   { _pimpl->_attrs[attr].insert(value); }
 
   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
+  { return addDependency( attr, name, op, edition, Arch_empty ); }
+
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
   {
     switch ( op.inSwitch() )
     {
       case Rel::ANY_e: // no additional constraint on edition.
-        addAttribute( attr, name );
-        return;
+        if ( arch.empty() )    // no additional constraint on arch.
+       {
+         addAttribute( attr, name );
+         return;
+       }
+       break;
 
       case Rel::NONE_e:        // will never match.
         return;
@@ -656,16 +876,16 @@ attremptycheckend:
         break;
     }
 
-    // Match::OTHER indicates need to compile.
-    sat::AttrMatcher matcher( name, Match::OTHER );
+    // Match::OTHER indicates need to compile
+    // (merge global search strings into name).
+    AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
 
-    AttrMatchData::Predicate pred;
     if ( isDependencyAttribute( attr ) )
-      pred = EditionRangePredicate( op, edition );
+      attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
     else
-      pred = SolvableRangePredicate( op, edition );
+      attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
 
-    _pimpl->_uncompiledPredicated.push_back( AttrMatchData( attr, matcher, pred ) );
+    _pimpl->_uncompiledPredicated.insert( attrMatchData );
   }
 
   void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
@@ -675,15 +895,14 @@ attremptycheckend:
       return;
 
     // Matches STRING per default. (won't get compiled!)
-    sat::AttrMatcher matcher( cap.name().asString() );
+    AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
 
-    AttrMatchData::Predicate pred;
     if ( isDependencyAttribute( attr ) )
-      pred = CapabilityMatchPredicate( cap_r );
+      attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
     else
-      pred = SolvableRangePredicate( cap.op(), cap.ed() );
+      attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
 
-    _pimpl->_uncompiledPredicated.push_back( AttrMatchData( attr, matcher, pred ) );
+    _pimpl->_uncompiledPredicated.insert( attrMatchData );
   }
 
   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
@@ -837,6 +1056,7 @@ attremptycheckend:
     static const PoolQueryAttr caseSensitiveAttr;
     static const PoolQueryAttr installStatusAttr;
     static const PoolQueryAttr editionAttr;
+    static const PoolQueryAttr complexAttr;
   };
 
   const PoolQueryAttr PoolQueryAttr::noAttr;
@@ -849,6 +1069,7 @@ attremptycheckend:
   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
+  const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
 
   class StringTypeAttr : public IdStringType<PoolQueryAttr>
   {
@@ -883,6 +1104,7 @@ attremptycheckend:
 
 
   //\TODO maybe ctor with stream can be usefull
+  //\TODO let it throw, let it throw, let it throw.
   bool PoolQuery::recover( istream &str, char delim )
   {
     bool finded_something = false; //indicates some atributes is finded
@@ -918,8 +1140,6 @@ attremptycheckend:
 
       PoolQueryAttr attribute( attrName );
 
-      MIL << "attribute name: " << attrName << endl;
-
       if ( attribute==PoolQueryAttr::repoAttr )
       {
         addRepo( attrValue );
@@ -1029,6 +1249,18 @@ attremptycheckend:
 
         setEdition(Edition(attrValue), rel);
       }
+      else if ( attribute == PoolQueryAttr::complexAttr )
+      {
+        try
+        {
+          _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
+        }
+        catch ( const Exception & err )
+        {
+          WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
+
+        }
+      }
       else if ( attribute==PoolQueryAttr::noAttr )
       {
         WAR << "empty attribute name" << endl;
@@ -1038,7 +1270,17 @@ attremptycheckend:
         string s = attrName;
         str::replaceAll( s,"_",":" );
         SolvAttr a(s);
-        addAttribute(a,attrValue);
+       if ( a == SolvAttr::name || isDependencyAttribute( a ) )
+       {
+         Capability c( attrValue );
+         CapDetail d( c );
+         if ( d.isVersioned() )
+           addDependency( a, d.name().asString(), d.op(), d.ed() );
+         else
+           addDependency( a, attrValue );
+       }
+       else
+         addAttribute( a, attrValue );
       }
 
     } while ( true );
@@ -1146,6 +1388,11 @@ attremptycheckend:
       }
     }
 
+    for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
+    {
+      str << "complex: "<< it->serialize() << delim;
+    }
+
     //separating delim - protection
     str << delim;
   }
@@ -1156,25 +1403,11 @@ attremptycheckend:
   ostream & operator<<( ostream & str, const PoolQuery & obj )
   { return str << obj.asString(); }
 
-  bool PoolQuery::operator==(const PoolQuery& a) const
-  {
-    if( flags() != a.flags() )
-      return false;
-    if( a.matchWord() != matchWord())
-      return false;
-    if( a.requireAll() != requireAll() )
-      return false;
-    if ( a.kinds() != kinds() )
-      return false;
-    if ( a.repos() != repos() )
-      return false;
-    if(a.edition() != edition())
-      return false;
-    if(a.editionRel() != editionRel())
-      return false;
+  std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
+  { return dumpRange( str << obj, obj.begin(), obj.end() ); }
 
-    return true;
-  }
+  bool PoolQuery::operator==( const PoolQuery & rhs ) const
+  { return *_pimpl == *rhs._pimpl; }
 
   ///////////////////////////////////////////////////////////////////
   namespace detail
@@ -1265,8 +1498,8 @@ attremptycheckend:
             {
               const AttrMatchData & matchData( *mi );
               sat::LookupAttr q( matchData.attr, inSolvable );
-              if ( matchData.attrMatcher ) // an empty searchstring matches always
-                q.setAttrMatcher( matchData.attrMatcher );
+              if ( matchData.strMatcher ) // an empty searchstring matches always
+                q.setStrMatcher( matchData.strMatcher );
 
               if ( ! q.empty() ) // there are matches.
               {
@@ -1292,12 +1525,20 @@ attremptycheckend:
 
          // Repo restriction:
          sat::Pool satpool( sat::Pool::instance() );
+
          for_( it, query_r->_repos.begin(), query_r->_repos.end() )
          {
            Repository r( satpool.reposFind( *it ) );
            if ( r )
              _repos.insert( r );
+           else
+             _neverMatchRepo = true;
          }
+         // _neverMatchRepo: we just need to catch the case that no repo
+         // matched, so we'd interpret the empty list as 'take from all'
+         if ( _neverMatchRepo && ! _repos.empty() )
+           _neverMatchRepo = false;
+
          // Kind restriction:
          _kinds = query_r->_kinds;
          // Edition restriction:
@@ -1305,7 +1546,7 @@ attremptycheckend:
          _edition = query_r->_edition;
          // Status restriction:
          _status_flags = query_r->_status_flags;
-          // AttrMatcher
+          // StrMatcher
           _attrMatchList = query_r->_attrMatchList;
        }
 
@@ -1318,6 +1559,9 @@ attremptycheckend:
        {
          sat::LookupAttr q;
 
+         if ( _neverMatchRepo )
+           return q.end();
+
          // Repo restriction:
          if ( _repos.size() == 1 )
            q.setRepo( *_repos.begin() );
@@ -1328,8 +1572,8 @@ attremptycheckend:
          {
             const AttrMatchData & matchData( _attrMatchList.front() );
            q.setAttr( matchData.attr );
-            if ( matchData.attrMatcher ) // empty searchstring matches always
-              q.setAttrMatcher( matchData.attrMatcher );
+            if ( matchData.strMatcher ) // empty searchstring matches always
+              q.setStrMatcher( matchData.strMatcher );
          }
           else // more than 1 attr (but not all)
           {
@@ -1402,8 +1646,8 @@ attremptycheckend:
           {
             const AttrMatchData & matchData( *mi );
             sat::LookupAttr q( matchData.attr, inSolvable );
-            if ( matchData.attrMatcher ) // an empty searchstring matches always
-              q.setAttrMatcher( matchData.attrMatcher );
+            if ( matchData.strMatcher ) // an empty searchstring matches always
+              q.setStrMatcher( matchData.strMatcher );
 
             if ( ! q.empty() ) // there are matches.
             {
@@ -1428,6 +1672,7 @@ attremptycheckend:
       private:
         /** Repositories include in the search. */
         std::set<Repository> _repos;
+       DefaultIntegral<bool,false> _neverMatchRepo;
         /** Resolvable kinds to include. */
         std::set<ResKind> _kinds;
         /** Edition filter. */
@@ -1435,7 +1680,7 @@ attremptycheckend:
         Edition _edition;
         /** Installed status filter flags. \see PoolQuery::StatusFilter */
         int _status_flags;
-        /** AttrMatcher per attribtue. */
+        /** StrMatcher per attribtue. */
         AttrMatchList _attrMatchList;
     };
     ///////////////////////////////////////////////////////////////////