libzypp  17.14.0
PoolQuery.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <sstream>
14 
15 #include "zypp/base/Gettext.h"
16 #include "zypp/base/LogTools.h"
17 #include "zypp/base/Algorithm.h"
18 #include "zypp/base/String.h"
20 #include "zypp/RelCompare.h"
21 
22 #include "zypp/sat/Pool.h"
23 #include "zypp/sat/Solvable.h"
24 #include "zypp/base/StrMatcher.h"
25 
26 #include "zypp/PoolQuery.h"
27 
28 #undef ZYPP_BASE_LOGGER_LOGGROUP
29 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
30 
31 using namespace std;
32 using namespace zypp::sat;
33 
35 namespace zypp
36 {
37 
39  namespace
40  {
41 
43  // some Helpers and Predicates
45 
46  bool isDependencyAttribute( sat::SolvAttr attr_r )
47  {
48  static sat::SolvAttr deps[] = {
49  SolvAttr::provides,
50  SolvAttr::requires,
51  SolvAttr::recommends,
52  SolvAttr::obsoletes,
53  SolvAttr::conflicts,
54  SolvAttr::suggests,
55  SolvAttr::supplements,
56  SolvAttr::enhances,
57  };
58  for_( it, arrayBegin(deps), arrayEnd(deps) )
59  if ( *it == attr_r )
60  return true;
61  return false;
62  }
63 
68  struct EditionRangePredicate
69  {
70  EditionRangePredicate( const Rel & op, const Edition & edition )
71  : _range( op, edition )
72  , _arch( Arch_empty )
73  {}
74  EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
75  : _range( op, edition )
76  , _arch( arch )
77  {}
78 
79  bool operator()( sat::LookupAttr::iterator iter_r )
80  {
81  if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
82  return false;
83 
84  CapDetail cap( iter_r.id() );
85  if ( ! cap.isSimple() )
86  return false;
87  if ( cap.isNamed() ) // no range to match
88  return true;
89  return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
90  }
91 
92  std::string serialize() const
93  {
94  std::string ret( "EditionRange" );
95  str::appendEscaped( ret, _range.op.asString() );
96  str::appendEscaped( ret, _range.value.asString() );
97  str::appendEscaped( ret, _arch.asString() );
98  return ret;
99  }
100 
101  Edition::MatchRange _range;
102  Arch _arch;
103  };
104 
106  struct SolvableRangePredicate
107  {
108  SolvableRangePredicate( const Rel & op, const Edition & edition )
109  : _range( op, edition )
110  , _arch( Arch_empty )
111  {}
112 
113  SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
114  : _range( op, edition )
115  , _arch( arch )
116  {}
117 
118  bool operator()( sat::LookupAttr::iterator iter_r )
119  {
120  if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
121  return false;
122  return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
123  }
124 
125  std::string serialize() const
126  {
127  std::string ret( "SolvableRange" );
128  str::appendEscaped( ret, _range.op.asString() );
129  str::appendEscaped( ret, _range.value.asString() );
130  str::appendEscaped( ret, _arch.asString() );
131  return ret;
132  }
133 
134  Edition::MatchRange _range;
135  Arch _arch;
136  };
137 
142  struct CapabilityMatchPredicate
143  {
144  CapabilityMatchPredicate( Capability cap_r )
145  : _cap( cap_r )
146  {}
147 
148  bool operator()( sat::LookupAttr::iterator iter_r ) const
149  {
150  return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
151  }
152 
153  std::string serialize() const
154  {
155  std::string ret( "CapabilityMatch" );
156  str::appendEscaped( ret, _cap.asString() );
157  return ret;
158  }
159 
160  Capability _cap;
161  };
162 
164  //
166 
191  struct AttrMatchData
192  {
193  typedef function<bool(sat::LookupAttr::iterator)> Predicate;
194 
195  static bool always( sat::LookupAttr::iterator ) { return true; }
196  static bool never( sat::LookupAttr::iterator ) { return false; }
197 
198  AttrMatchData()
199  {}
200 
201  AttrMatchData( sat::SolvAttr attr_r )
202  : attr( attr_r )
203  {}
204 
205  AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
206  : attr( attr_r )
207  , strMatcher( strMatcher_r )
208  {}
209 
210  AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
211  const Predicate & predicate_r, const std::string & predicateStr_r )
212  : attr( attr_r )
213  , strMatcher( strMatcher_r )
214  , predicate( predicate_r )
215  , predicateStr( predicateStr_r )
216  {}
217 
223  template<class TPredicate>
224  void addPredicate( const TPredicate & predicate_r )
225  {
226  predicate = predicate_r;
227  predicateStr = predicate_r.serialize();
228  }
229 
235  std::string serialize() const
236  {
237  std::string ret( "AttrMatchData" );
238  str::appendEscaped( ret, attr.asString() );
239  str::appendEscaped( ret, strMatcher.searchstring() );
240  str::appendEscaped( ret, serializeMode( strMatcher.flags().mode() ) );
242  return ret;
243  }
244 
248  static AttrMatchData deserialize( const std::string & str_r )
249  {
250  std::vector<std::string> words;
251  str::splitEscaped( str_r, std::back_inserter(words) );
252  if ( words.empty() || words[0] != "AttrMatchData" )
253  ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
254  if ( words.size() != 5 )
255  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
256 
257  AttrMatchData ret;
258  ret.attr = sat::SolvAttr( words[1] );
259  ret.strMatcher = StrMatcher( words[2] );
260  if ( Match::Mode mode = deserializeMode( words[3] ) )
261  ret.strMatcher.setFlags( mode );
262  ret.predicateStr = words[4];
263 
264  // now the predicate
265  words.clear();
266  str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
267  if ( ! words.empty() )
268  {
269  if ( words[0] == "EditionRange" )
270  {
271  switch( words.size() )
272  {
273  case 3:
274  ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
275  break;
276  case 4:
277  ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
278  break;
279  default:
280  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
281  break;
282  }
283  }
284  else if ( words[0] == "SolvableRange" )
285  {
286  switch( words.size() )
287  {
288  case 3:
289  ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
290  break;
291  case 4:
292  ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
293  break;
294  default:
295  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
296  break;
297  }
298  }
299  else if ( words[0] == "CapabilityMatch" )
300  {
301  if ( words.size() != 2 )
302  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
303  ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
304  }
305  else
306  ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
307  }
308  return ret;
309  }
310 
311  sat::SolvAttr attr;
312  StrMatcher strMatcher;
313  Predicate predicate;
314  std::string predicateStr;
315  ResKind kindPredicate = ResKind::nokind; // holds the 'kind' part if SolvAttr:name looks for an explicit 'kind:name'
316 
317  private:
319  static std::string serializeMode( Match::Mode mode_r )
320  {
321  // Legacy code used "[C|X]" to differ just between OTHER (need to (C)ompile) and
322  // using the default search mode. As we now allow to specify a SEARCHMODE we
323  // need to serialize it:
324  switch ( mode_r )
325  {
326 #define OUTS(M,S) case Match::M: return #S; break
327  // (C)ompile
328  OUTS( OTHER, C );
329  // well known modes:
330  OUTS( STRING, T );
331  OUTS( STRINGSTART, S );
332  OUTS( STRINGEND, E );
333  OUTS( SUBSTRING, B );
334  OUTS( GLOB, G );
335  OUTS( REGEX, R );
336 #undef OUTS
337  // everything else use default
338  case Match::NOTHING:
339  break;
340  }
341  return "X";
342  }
343 
345  static Match::Mode deserializeMode( const std::string & str_r )
346  {
347  switch ( str_r[0] )
348  {
349 #define OUTS(M,C) case *#C: return Match::M; break
350  // (C)ompile
351  OUTS( OTHER, C );
352  // well known modes:
353  OUTS( STRING, T );
354  OUTS( STRINGSTART, S );
355  OUTS( STRINGEND, E );
356  OUTS( SUBSTRING, B );
357  OUTS( GLOB, G );
358  OUTS( REGEX, R );
359 #undef OUTS
360  // everything else use default
361  default:
362  break;
363  }
364  return Match::NOTHING;
365  }
366  };
367 
369  inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
370  {
371  str << obj.attr << ": " << obj.strMatcher;
372  if ( obj.kindPredicate )
373  str << " +(" << obj.kindPredicate << ")";
374  if ( obj.predicate )
375  str << " +(" << obj.predicateStr << ")";
376  return str;
377  }
378 
380  inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
381  {
382  return ( lhs.attr == rhs.attr
383  && lhs.strMatcher == rhs.strMatcher
384  && lhs.predicateStr == rhs.predicateStr );
385  }
386 
388  inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
389  { return !( lhs == rhs ); }
390 
392  inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
393  {
394  if ( lhs.attr != rhs.attr )
395  return ( lhs.attr < rhs.attr );
396  if ( lhs.strMatcher != rhs.strMatcher )
397  return ( lhs.strMatcher < rhs.strMatcher );
398  if ( lhs.predicateStr != rhs.predicateStr )
399  return ( lhs.predicateStr < rhs.predicateStr );
400  return false;
401  }
402 
403  typedef std::list<AttrMatchData> AttrMatchList;
404 
405 
406  }
407  // namespace
409 
411  //
412  // CLASS NAME : PoolQuery::Impl
413  //
416  {
417  public:
419  : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
420  , _match_word(false)
421  , _status_flags(ALL)
422  {}
423 
424  ~Impl()
425  {}
426 
427  public:
429  string asString() const;
430 
438  std::set<AttrMatchData> _uncompiledPredicated;
439 
443 
446 
451 
454 
458 
459  public:
460 
461  bool operator<( const PoolQuery::Impl & rhs ) const
462  {
463 #define OUTS(A) if ( A != rhs.A ) return A < rhs.A;
464  OUTS( _strings );
465  OUTS( _attrs );
466  OUTS( _uncompiledPredicated );
467  OUTS( _flags.get() );
468  OUTS( _match_word );
469  OUTS( _status_flags );
470  OUTS( _edition );
471  OUTS( _op.inSwitch() );
472  OUTS( _repos );
473  OUTS( _kinds );
474 #undef OUTS
475  return false;
476  }
477 
478  bool operator==( const PoolQuery::Impl & rhs ) const
479  {
480  if ( _flags == rhs._flags
481  // bnc#792901: while libzypp uses exact match mode for a single
482  // package name lock, zypper always uses glob. :(
483  // We unify those two forms to enable zypper to remove zypp locks
484  // without need to actually evaluate the query (which would require
485  // repos to be loaded).
486  || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
487  || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
488  && _strings.empty()
489  && _attrs.size() == 1
490  && _attrs.begin()->first == sat::SolvAttr::name ) )
491  {
492  return ( _strings == rhs._strings
493  && _attrs == rhs._attrs
494  && _uncompiledPredicated == rhs._uncompiledPredicated
495  && _match_word == rhs._match_word
496  && _status_flags == rhs._status_flags
497  && _edition == rhs._edition
498  && _op == rhs._op
499  && _repos == rhs._repos
500  && _kinds == rhs._kinds );
501  }
502  return false;
503  }
504 
505  bool operator!=( const PoolQuery::Impl & rhs ) const
506  { return ! operator==( rhs ); }
507 
508  public:
513  void compile() const;
514 
516  mutable AttrMatchList _attrMatchList;
517 
518  private:
522  StrMatcher joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const;
523 
524  private:
525  friend Impl * rwcowClone<Impl>( const Impl * rhs );
527  Impl * clone() const
528  { return new Impl( *this ); }
529  };
530 
532 
533  struct MyInserter
534  {
535  MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
536 
537  bool operator()(const string & str)
538  {
539  _cont.insert(str);
540  return true;
541  }
542 
544  };
545 
546 
547  struct EmptyFilter
548  {
549  bool operator()(const string & str)
550  {
551  return !str.empty();
552  }
553  };
554 
555  void PoolQuery::Impl::compile() const
556  {
557  _attrMatchList.clear();
558 
559  if ( _flags.mode() == Match::OTHER ) // this will never succeed...
561 
562  // 'different' - will have to iterate through all and match by ourselves (slow)
563  // 'same' - will pass the compiled string to dataiterator_init
564  // 'one-attr' - will pass it to dataiterator_init
565  // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
566 
567  // // NO ATTRIBUTE
568  // else
569  // for all _strings
570  // create regex; store in rcstrings; if more strings flag regex;
571  if (_attrs.empty())
572  {
573  ; // A default 'query-all' will be added after all sources are processed.
574  }
575 
576  // // ONE ATTRIBUTE
577  // else if _attrs is not empty but it contains just one attr
578  // for all _strings and _attr[key] strings
579  // create regex; flag 'one-attr'; if more strings flag regex;
580  else if (_attrs.size() == 1)
581  {
582  StrContainer joined;
583  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
584  invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
585 
586  _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) );
587  }
588 
589  // // MULTIPLE ATTRIBUTES
590  else
591  {
592  // check whether there are any per-attribute strings
593  bool attrvals_empty = true;
594  for_( ai, _attrs.begin(), _attrs.end() )
595  {
596  if ( ai->second.empty() )
597  continue;
598  for_( it, ai->second.begin(), ai->second.end() )
599  {
600  if ( !it->empty() )
601  {
602  attrvals_empty = false;
603  break;
604  }
605  }
606  if ( ! attrvals_empty )
607  break;
608  }
609 
610  // chceck whether the per-attribute strings are all the same
611  bool attrvals_thesame = true;
612  AttrRawStrMap::const_iterator ai = _attrs.begin();
613  const StrContainer & set1 = ai->second;
614  ++ai;
615  for (; ai != _attrs.end(); ++ai)
616  {
617  StrContainer result;
618  set_difference(
619  set1.begin(), set1.end(),
620  ai->second.begin(), ai->second.end(),
621  inserter(result, result.begin())/*, ltstr()*/);
622  if (!result.empty())
623  {
624  attrvals_thesame = false;
625  break;
626  }
627  }
628 
629  // // THE SAME STRINGS FOR DIFFERENT ATTRS
630  // else if _attrs is not empty but it does not contain strings
631  // for each key in _attrs take all _strings
632  // create regex; store in rcstrings; flag 'same'; if more strings flag regex;
633  if (attrvals_empty || attrvals_thesame)
634  {
635  StrContainer joined;
636  if (attrvals_empty)
637  {
638  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
639  }
640  else
641  {
642  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
643  invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
644  }
645 
646  // May use the same StrMatcher for all
647  StrMatcher matcher( joinedStrMatcher( joined, _flags ) );
648  for_( ai, _attrs.begin(), _attrs.end() )
649  {
650  _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
651  }
652  }
653 
654  // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
655  // if _attrs is not empty and it contains non-empty vectors with non-empty strings
656  // for each key in _attrs take all _strings + all _attrs[key] strings
657  // create regex; flag 'different'; if more strings flag regex;
658  else
659  {
660  for_(ai, _attrs.begin(), _attrs.end())
661  {
662  StrContainer joined;
663  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
664  invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
665 
666  _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) );
667  }
668  }
669  }
670 
671  // Now handle any predicated queries
672  if ( ! _uncompiledPredicated.empty() )
673  {
674  StrContainer global;
675  invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
676  for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
677  {
678  if ( it->strMatcher.flags().mode() == Match::OTHER )
679  {
680  // need to compile:
681  StrContainer joined( global );
682  const std::string & mstr( it->strMatcher.searchstring() );
683  if ( ! mstr.empty() )
684  joined.insert( mstr );
685 
686  // copy and exchange the StrMatcher
687  AttrMatchData nattr( *it );
688  nattr.strMatcher = joinedStrMatcher( joined, _flags );
689  _attrMatchList.push_back( std::move(nattr) );
690  }
691  else
692  {
693  // copy matcher
694  _attrMatchList.push_back( *it );
695  }
696  }
697  }
698 
699  // If no attributes defined at all, then add 'query all'
700  if ( _attrMatchList.empty() )
701  {
702  _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) );
703  }
704 
705  // Finally check here, whether all involved regex compile.
706  for_( it, _attrMatchList.begin(), _attrMatchList.end() )
707  {
708  it->strMatcher.compile(); // throws on error
709  }
710  //DBG << asString() << endl;
711  }
712 
714  namespace
715  {
720  std::string rxEscape( std::string str_r, const Match & flags_r )
721  {
722  if ( str_r.empty() || flags_r.isModeRegex() )
723  return str_r;
724 
725  if ( flags_r.isModeGlob() )
726  return str::rxEscapeGlob( std::move(str_r) );
727 
728  return str::rxEscapeStr( std::move(str_r) );
729  }
730  } // namespace
732 
733  StrMatcher PoolQuery::Impl::joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const
734  {
735  if ( container_r.empty() )
736  return StrMatcher( std::string(), flags_r );
737 
738  if ( container_r.size() == 1 && !_match_word ) // use RX to match words
739  return StrMatcher( *container_r.begin(), flags_r );
740 
741  // Convert to a regex.
742  // Note: Modes STRING and GLOB match whole strings (anchored ^ $)
743  // SUBSTRING and REGEX match substrings (match_word anchores SUBSTRING \b)
744  Match retflags( flags_r );
745  retflags.setModeRegex();
746  str::Str ret;
747 
748  if ( flags_r.isModeString() || flags_r.isModeGlob() )
749  ret << "^";
750  else if ( _match_word )
751  ret << "\\b";
752 
753  // (..|..|..)
754  char sep = '(';
755  for ( const::std::string & s : container_r )
756  {
757  ret << sep << rxEscape( s, flags_r );
758  if ( sep == '(' )
759  sep = '|';
760  }
761  ret << ')';
762 
763  if ( flags_r.isModeString() || flags_r.isModeGlob() )
764  ret << "$";
765  else if ( _match_word )
766  ret << "\\b";
767 
768  return StrMatcher( ret, retflags );
769  }
770 
772  {
773  ostringstream o;
774 
775  o << "kinds: ";
776  if ( _kinds.empty() )
777  o << "ALL";
778  else
779  {
780  for(Kinds::const_iterator it = _kinds.begin();
781  it != _kinds.end(); ++it)
782  o << *it << " ";
783  }
784  o << endl;
785 
786  o << "repos: ";
787  if ( _repos.empty() )
788  o << "ALL";
789  else
790  {
791  for(StrContainer::const_iterator it = _repos.begin();
792  it != _repos.end(); ++it)
793  o << *it << " ";
794  }
795  o << endl;
796 
797  o << "version: "<< _op << " " << _edition.asString() << endl;
798  o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
799  : "ALL" ) << endl;
800 
801  o << "string match flags: " << Match(_flags) << endl;
802 
803  // raw
804  o << "strings: ";
805  for(StrContainer::const_iterator it = _strings.begin();
806  it != _strings.end(); ++it)
807  o << *it << " ";
808  o << endl;
809 
810  o << "attributes: " << endl;
811  for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
812  {
813  o << "* " << ai->first << ": ";
814  for(StrContainer::const_iterator vi = ai->second.begin();
815  vi != ai->second.end(); ++vi)
816  o << *vi << " ";
817  o << endl;
818  }
819 
820  o << "predicated: " << endl;
821  for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
822  {
823  o << "* " << *it << endl;
824  }
825 
826  // compiled
827  o << "last attribute matcher compiled: " << endl;
828  if ( _attrMatchList.empty() )
829  {
830  o << "not yet compiled" << endl;
831  }
832  else
833  {
834  for_( it, _attrMatchList.begin(), _attrMatchList.end() )
835  {
836  o << "* " << *it << endl;
837  }
838  }
839  return o.str();
840  }
841 
843 
845  //
846  // CLASS NAME : PoolQuery
847  //
849 
850  PoolQuery::PoolQuery()
851  : _pimpl(new Impl())
852  {}
853 
855  {}
856 
857  void PoolQuery::addRepo(const std::string &repoalias)
858  {
859  if (repoalias.empty())
860  {
861  WAR << "ignoring an empty repository alias" << endl;
862  return;
863  }
864  _pimpl->_repos.insert(repoalias);
865  }
866 
867  void PoolQuery::addKind(const ResKind & kind)
868  { _pimpl->_kinds.insert(kind); }
869 
870  void PoolQuery::addString(const string & value)
871  { _pimpl->_strings.insert(value); }
872 
873  void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
874  { _pimpl->_attrs[attr].insert(value); }
875 
876  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
877  {
878  // Default Match::OTHER indicates need to compile, i.e. to merge name into the global search string and mode.
879  return addDependency( attr, name, op, edition, Arch_empty, Match::OTHER );
880  }
881 
882  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
883  {
884  // Default Match::OTHER indicates need to compile, i.e. to merge name into the global search string and mode.
885  return addDependency( attr, name, op, edition, arch, Match::OTHER );
886  }
887 
888  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch, Match::Mode mode )
889  {
890  if ( op == Rel::NONE ) // will never match.
891  return;
892 
893  // SolvAttr::name with explicit 'kind:name' will overwrite the default _kinds
894  ResKind explicitKind;
895  if ( attr == sat::SolvAttr::name )
896  explicitKind = ResKind::explicitBuiltin( name );
897 
898  // Legacy: Match::OTHER and no additional constraints on edition/arch/kind
899  // require addAttribute, otherwise de-serialisation breaks (serialized
900  // and de-serialized query could be !=).
901  // From the results POV we could also use the predicated case below.
902  if ( op == Rel::ANY && arch.empty() && !explicitKind && mode == Match::OTHER )
903  {
904  addAttribute( attr, name );
905  return;
906  }
907 
908  // Match::OTHER indicates need to compile
909  // (merge global search strings into name).
910  AttrMatchData attrMatchData( attr );
911  if ( !explicitKind )
912  attrMatchData.strMatcher = StrMatcher( name, mode );
913  else
914  {
915  // ResKind::explicitBuiltin call above asserts the presence of the ':' in name
916  attrMatchData.strMatcher = StrMatcher( strchr( name.c_str(), ':')+1, mode );
917  attrMatchData.kindPredicate = explicitKind;
918  }
919 
920  if ( isDependencyAttribute( attr ) )
921  attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
922  else
923  attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
924 
925  _pimpl->_uncompiledPredicated.insert( attrMatchData );
926  }
927 
929  {
930  CapDetail cap( cap_r );
931  if ( ! cap.isSimple() ) // will never match.
932  return;
933 
934  // Matches STRING per default. (won't get compiled!)
935  AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
936 
937  if ( isDependencyAttribute( attr ) )
938  attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
939  else
940  attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
941 
942  _pimpl->_uncompiledPredicated.insert( attrMatchData );
943  }
944 
945  void PoolQuery::setEdition(const Edition & edition, const Rel & op)
946  {
948  _pimpl->_op = op;
949  }
950 
956 
958  { return _pimpl->_flags; }
959  void PoolQuery::setFlags( const Match & flags )
960  { _pimpl->_flags = flags; }
961 
962 
968  { _pimpl->_status_flags = flags; }
969 
970 
973  { return _pimpl->_strings; }
974 
977  { return _pimpl->_attrs; }
978 
981  {
982  static const PoolQuery::StrContainer nocontainer;
983  AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
984  return it != _pimpl->_attrs.end() ? it->second : nocontainer;
985  }
986 
988  { return _pimpl->_edition; }
990  { return _pimpl->_op; }
991 
992 
993  const PoolQuery::Kinds &
995  { return _pimpl->_kinds; }
996 
999  { return _pimpl->_repos; }
1000 
1001 
1003  { return !_pimpl->_flags.test( Match::NOCASE ); }
1004  void PoolQuery::setCaseSensitive( bool value )
1005  { _pimpl->_flags.turn( Match::NOCASE, !value ); }
1006 
1008  { return _pimpl->_flags.test( Match::FILES ); }
1010  { _pimpl->_flags.turn( Match::FILES, value ); }
1011 
1012  bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); }
1014  bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
1015  bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
1017 
1019  { return _pimpl->_status_flags; }
1020 
1021  bool PoolQuery::empty() const
1022  {
1023  try { return begin() == end(); }
1024  catch (const Exception & ex) {}
1025  return true;
1026  }
1027 
1029  {
1030  try
1031  {
1032  size_type count = 0;
1033  for_( it, begin(), end() )
1034  ++count;
1035  return count;
1036  }
1037  catch (const Exception & ex) {}
1038  return 0;
1039  }
1040 
1042  { invokeOnEach( begin(), end(), fnc); }
1043 
1044 
1045  /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
1046  /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const { return false; }
1047 
1049  //
1050  // CLASS NAME : PoolQuery::Attr
1051  //
1056  struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1057  {
1058  private:
1061  public:
1062 
1063  //noAttr
1065 
1066  explicit PoolQueryAttr( const char* cstr_r )
1067  : _str( cstr_r )
1068  {}
1069 
1070  explicit PoolQueryAttr( const std::string & str_r )
1071  : _str( str_r )
1072  {}
1073 
1074  // unknown atributes
1075  static const PoolQueryAttr noAttr;
1076 
1077  // PoolQuery's own attributes
1078  static const PoolQueryAttr repoAttr;
1079  static const PoolQueryAttr kindAttr;
1082  static const PoolQueryAttr requireAllAttr; // LEAGACY: attribute was defined but never implemented.
1087  };
1088 
1090 
1091  const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1092  const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1093  const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1094  const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1095  const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all"); // LEAGACY: attribute was defined but never implemented.
1096  const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1097  const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1098  const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1099  const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1100 
1101  class StringTypeAttr : public IdStringType<PoolQueryAttr>
1102  {
1105 
1106  public:
1108  explicit StringTypeAttr( const char* cstr_r )
1109  : _str( cstr_r ){}
1110  explicit StringTypeAttr( const std::string & str_r )
1111  : _str( str_r ){}
1112 
1113  static const StringTypeAttr noAttr;
1114 
1118  static const StringTypeAttr globAttr;
1119  static const StringTypeAttr wordAttr;
1120  };
1121 
1123 
1125  const StringTypeAttr StringTypeAttr::substringAttr("substring");
1129 
1131 
1132 
1133  //\TODO maybe ctor with stream can be usefull
1134  //\TODO let it throw, let it throw, let it throw.
1135  bool PoolQuery::recover( istream &str, char delim )
1136  {
1137  bool finded_something = false; //indicates some atributes is finded
1138  string s;
1139  do {
1140  if ( str.eof() )
1141  break;
1142 
1143  getline( str, s, delim );
1144 
1145  if ((!s.empty()) && s[0]=='#') //comment
1146  {
1147  continue;
1148  }
1149 
1150  string::size_type pos = s.find(':');
1151  if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1152  {
1153  if (finded_something) //is first blank line after record?
1154  {
1155  break;
1156  }
1157  else
1158  {
1159  continue;
1160  }
1161  }
1162 
1163  finded_something = true;
1164 
1165  string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1166  string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1167 
1168  PoolQueryAttr attribute( attrName );
1169 
1170  if ( attribute==PoolQueryAttr::repoAttr )
1171  {
1172  addRepo( attrValue );
1173  }
1174  /* some backwards compatibility */
1175  else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1176  {
1177  addKind( ResKind(attrValue) );
1178  }
1179  else if ( attribute==PoolQueryAttr::stringAttr
1180  || attribute=="global_string")
1181  {
1182  addString( attrValue );
1183  }
1184  else if ( attribute==PoolQueryAttr::stringTypeAttr
1185  || attribute=="string_type" )
1186  {
1187  StringTypeAttr s(attrValue);
1188  if( s == StringTypeAttr::regexAttr )
1189  {
1190  setMatchRegex();
1191  }
1192  else if ( s == StringTypeAttr::globAttr )
1193  {
1194  setMatchGlob();
1195  }
1196  else if ( s == StringTypeAttr::exactAttr )
1197  {
1198  setMatchExact();
1199  }
1200  else if ( s == StringTypeAttr::substringAttr )
1201  {
1203  }
1204  else if ( s == StringTypeAttr::wordAttr )
1205  {
1206  setMatchWord();
1207  }
1208  else if ( s == StringTypeAttr::noAttr )
1209  {
1210  WAR << "unknown string type " << attrValue << endl;
1211  }
1212  else
1213  {
1214  WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1215  }
1216  }
1217  else if ( attribute==PoolQueryAttr::requireAllAttr )
1218  {
1219  // LEAGACY: attribute was defined but never implemented.
1220  // Actually it should not occur outside our testcases.
1221  }
1222  else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1223  {
1224  if ( str::strToTrue(attrValue) )
1225  {
1226  setCaseSensitive(true);
1227  }
1228  else if ( !str::strToFalse(attrValue) )
1229  {
1230  setCaseSensitive(false);
1231  }
1232  else
1233  {
1234  WAR << "unknown boolean value " << attrValue << endl;
1235  }
1236  }
1237  else if ( attribute==PoolQueryAttr::installStatusAttr )
1238  {
1239  if( attrValue == "all" )
1240  {
1242  }
1243  else if( attrValue == "installed" )
1244  {
1245  setInstalledOnly();
1246  }
1247  else if( attrValue == "not-installed" )
1248  {
1250  }
1251  else
1252  {
1253  WAR << "Unknown value for install status " << attrValue << endl;
1254  }
1255  }
1256  else if ( attribute == PoolQueryAttr::editionAttr)
1257  {
1258  string::size_type pos;
1259  Rel rel("==");
1260  if (attrValue.find_first_of("=<>!") == 0)
1261  {
1262  pos = attrValue.find_last_of("=<>");
1263  rel = Rel(attrValue.substr(0, pos+1));
1264  attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1265  }
1266 
1267  setEdition(Edition(attrValue), rel);
1268  }
1269  else if ( attribute == PoolQueryAttr::complexAttr )
1270  {
1271  try
1272  {
1274  }
1275  catch ( const Exception & err )
1276  {
1277  WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1278 
1279  }
1280  }
1281  else if ( attribute==PoolQueryAttr::noAttr )
1282  {
1283  WAR << "empty attribute name" << endl;
1284  }
1285  else
1286  {
1287  string s = attrName;
1288  str::replaceAll( s,"_",":" );
1289  SolvAttr a(s);
1290  if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1291  {
1292  Capability c( attrValue );
1293  CapDetail d( c );
1294  if ( d.isVersioned() )
1295  addDependency( a, d.name().asString(), d.op(), d.ed() );
1296  else
1297  addDependency( a, attrValue );
1298  }
1299  else
1300  addAttribute( a, attrValue );
1301  }
1302 
1303  } while ( true );
1304 
1305  // OLD STYLE VERSIONED LOCKS:
1306  // solvable_name: kernel
1307  // version: > 1
1308  //
1309  // NEW STYLE VERSIONED LOCKS:
1310  // complex: AttrMatchData solvable:name kernel C SolvableRange\ >\ 1\ \"\"
1311  // or
1312  // solvable_name: kernel > 1
1313  //
1314  // Semantically equivalent as locks, but due to the different syntax
1315  // the complex lock is wrongly handled by zypper.
1316  //
1317  // bsc#1112911: Unfortunately all styles are found in real-life locks-files.
1318  // libzypp will try to make sure, when parsing the locks-file, that complex
1319  // locks are rewritten into to OLD STYLE queries zypper can handle.
1320  if ( !_pimpl->_attrs.count(SolvAttr::name) && _pimpl->_uncompiledPredicated.size() == 1 )
1321  {
1322  // No OLD STYLE lock for SolvAttr::name and exactly one complex lock...
1323  const AttrMatchData & attrmatch { *_pimpl->_uncompiledPredicated.begin() };
1324  if ( attrmatch.attr == SolvAttr::name && attrmatch.strMatcher.flags().mode() == Match::OTHER )
1325  {
1326  // ...for SolvAttr::name and following the global search flags.
1327  // A candidate for a rewrite?
1328 
1329  std::vector<std::string> words;
1330  str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
1331  if ( words.size() < 4 || words[3].empty() )
1332  {
1333  // We have _NO_ arch rule in the complex predicate, so we can simplify it.
1334  //
1335  // NOTE: AFAIK it's not possible to create (or have created) a complex lock
1336  // with arch rule with zypper means. Nevertheless, in case such a rule made it
1337  // into a locks file, it's better to have a strange looking 'zypper locks' list
1338  // than to lock the wrong packages.
1339  // (and remember that you can't use "addAttribute( SolvAttr::arch, ... )" because
1340  // attributes are `OR`ed)
1341 
1342  // kind
1343  if ( attrmatch.kindPredicate )
1344  {
1345  _pimpl->_kinds.clear(); // an explicit kind overwrites any global one
1346  addKind( attrmatch.kindPredicate );
1347  }
1348 
1349  // name
1350  addAttribute( SolvAttr::name, attrmatch.strMatcher.searchstring() );
1351 
1352  // edition
1353  std::vector<std::string> words;
1354  str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
1355  if ( ! words.empty() )
1356  {
1357  if ( words[0] == "EditionRange" || words[0] == "SolvableRange" )
1358  {
1359  setEdition( Edition(words[2]), Rel(words[1]) );
1360  }
1361  }
1362 
1363  // finally remove the complex lock
1364  _pimpl->_uncompiledPredicated.clear();
1365  }
1366  }
1367  }
1368 
1369  return finded_something;
1370  }
1371 
1372  void PoolQuery::serialize( ostream &str, char delim ) const
1373  {
1374  //separating delim
1375  str << delim;
1376  //iterate thrue all settings and write it
1377  static const zypp::PoolQuery q; //not save default options, so create default query example
1378 
1379  for_( it, repos().begin(), repos().end() )
1380  {
1381  str << "repo: " << *it << delim ;
1382  }
1383 
1384  for_( it, kinds().begin(), kinds().end() )
1385  {
1386  str << PoolQueryAttr::kindAttr.asString() << ": "
1387  << it->idStr() << delim ;
1388  }
1389 
1390  if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1391  str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1392 
1393  if (matchMode()!=q.matchMode())
1394  {
1395  switch( matchMode() )
1396  {
1397  case Match::STRING:
1398  str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1399  break;
1400  case Match::SUBSTRING:
1402  << ": substring" << delim;
1403  break;
1404  case Match::GLOB:
1405  str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1406  break;
1407  case Match::REGEX:
1408  str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1409  break;
1410  default:
1411  WAR << "unknown match type " << matchMode() << endl;
1412  }
1413  }
1414 
1415  if( caseSensitive() != q.caseSensitive() )
1416  {
1417  str << "case_sensitive: ";
1418  if (caseSensitive())
1419  {
1420  str << "on" << delim;
1421  }
1422  else
1423  {
1424  str << "off" << delim;
1425  }
1426  }
1427 
1428  if( statusFilterFlags() != q.statusFilterFlags() )
1429  {
1430  switch( statusFilterFlags() )
1431  {
1432  case ALL:
1433  str << "install_status: all" << delim;
1434  break;
1435  case INSTALLED_ONLY:
1436  str << "install_status: installed" << delim;
1437  break;
1438  case UNINSTALLED_ONLY:
1439  str << "install_status: not-installed" << delim;
1440  break;
1441  }
1442  }
1443 
1444  for_( it, strings().begin(), strings().end() )
1445  {
1446  str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1447  }
1448 
1449  for_( it, attributes().begin(), attributes().end() )
1450  {
1451  string s = it->first.asString();
1452  str::replaceAll(s,":","_");
1453  for_( it2,it->second.begin(),it->second.end() )
1454  {
1455  str << s <<": "<< *it2 << delim;
1456  }
1457  }
1458 
1460  {
1461  str << "complex: "<< it->serialize() << delim;
1462  }
1463 
1464  //separating delim - protection
1465  str << delim;
1466  }
1467 
1468  string PoolQuery::asString() const
1469  { return _pimpl->asString(); }
1470 
1471  ostream & operator<<( ostream & str, const PoolQuery & obj )
1472  { return str << obj.asString(); }
1473 
1474  std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1475  { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1476 
1477  bool PoolQuery::operator==( const PoolQuery & rhs ) const
1478  { return *_pimpl == *rhs._pimpl; }
1479 
1480  bool PoolQuery::operator<( const PoolQuery & rhs ) const
1481  { return *_pimpl < *rhs._pimpl; }
1482 
1484  namespace detail
1485  {
1486 
1488  //
1489  // CLASS NAME : PoolQueryMatcher
1490  //
1508  {
1509  public:
1511 
1512  public:
1513  const base_iterator & end() const
1514  {
1515  static base_iterator _end;
1516  return _end;
1517  }
1518 
1519  bool advance( base_iterator & base_r ) const
1520  {
1521  if ( base_r == end() )
1522  base_r = startNewQyery(); // first candidate
1523  else
1524  {
1525  base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1526  ++base_r; // advance to next candidate
1527  }
1528 
1529  while ( base_r != end() )
1530  {
1531  if ( isAMatch( base_r ) )
1532  return true;
1533  // No match: try next
1534  ++base_r;
1535  }
1536  return false;
1537  }
1538 
1542  void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1543  {
1544  if ( base_r == end() )
1545  return;
1546 
1547  sat::Solvable inSolvable( base_r.inSolvable() );
1548 
1549  if ( _attrMatchList.size() == 1 )
1550  {
1551  // base_r is already on the 1st matching attribute!
1552  // String matching is done by the base iterator. We must check the predicate here.
1553  // Let's see if there are more matches for this solvable:
1554  base_iterator base( base_r );
1555  base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1556  return_r.push_back( base );
1557 
1558  const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1559  for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1560  {
1561  if ( ! predicate || predicate( base ) )
1562  return_r.push_back( base );
1563  }
1564  }
1565  else
1566  {
1567  // Here: search all attributes ;(
1568  for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1569  {
1570  const AttrMatchData & matchData( *mi );
1571  sat::LookupAttr q( matchData.attr, inSolvable );
1572  if ( matchData.strMatcher ) // an empty searchstring matches always
1573  q.setStrMatcher( matchData.strMatcher );
1574 
1575  if ( ! q.empty() ) // there are matches.
1576  {
1577  // now check any predicate:
1578  const AttrMatchData::Predicate & predicate( matchData.predicate );
1579  for_( it, q.begin(), q.end() )
1580  {
1581  if ( ! predicate || predicate( it ) )
1582  return_r.push_back( it );
1583  }
1584  }
1585  }
1586  }
1587  }
1588 
1589  public:
1593  PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1594  {
1595  query_r->compile();
1596 
1597  // Repo restriction:
1598  sat::Pool satpool( sat::Pool::instance() );
1599 
1600  for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1601  {
1602  Repository r( satpool.reposFind( *it ) );
1603  if ( r )
1604  _repos.insert( r );
1605  else
1606  _neverMatchRepo = true;
1607  }
1608  // _neverMatchRepo: we just need to catch the case that no repo
1609  // matched, so we'd interpret the empty list as 'take from all'
1610  if ( _neverMatchRepo && ! _repos.empty() )
1611  _neverMatchRepo = false;
1612 
1613  // Kind restriction:
1614  _kinds = query_r->_kinds;
1615  // Edition restriction:
1616  _op = query_r->_op;
1617  _edition = query_r->_edition;
1618  // Status restriction:
1619  _status_flags = query_r->_status_flags;
1620  // StrMatcher
1621  _attrMatchList = query_r->_attrMatchList;
1622  }
1623 
1625  {}
1626 
1627  private:
1630  {
1631  sat::LookupAttr q;
1632 
1633  if ( _neverMatchRepo )
1634  return q.end();
1635 
1636  // Repo restriction:
1637  if ( _repos.size() == 1 )
1638  q.setRepo( *_repos.begin() );
1639  // else: handled in isAMatch.
1640 
1641  // Attribute restriction:
1642  if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1643  {
1644  const AttrMatchData & matchData( _attrMatchList.front() );
1645  q.setAttr( matchData.attr );
1646  if ( matchData.strMatcher ) // empty searchstring matches always
1647  q.setStrMatcher( matchData.strMatcher );
1648  }
1649  else // more than 1 attr (but not all)
1650  {
1651  // no restriction, it's all handled in isAMatch.
1653  }
1654 
1655  return q.begin();
1656  }
1657 
1658 
1669  bool isAMatch( base_iterator & base_r ) const
1670  {
1672  Repository inRepo( base_r.inRepo() );
1673  // Status restriction:
1674  if ( _status_flags
1675  && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1676  {
1677  base_r.nextSkipRepo();
1678  return false;
1679  }
1680  // Repo restriction:
1681  if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1682  {
1683  base_r.nextSkipRepo();
1684  return false;
1685  }
1687  sat::Solvable inSolvable( base_r.inSolvable() );
1688  // Edition restriction:
1689  if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1690  {
1691  base_r.nextSkipSolvable();
1692  return false;
1693  }
1694 
1695  // Kind restriction:
1696  // Delay the decision to nextSkipSolvable and return false, as there may be
1697  // some explicit kind:name predicate which overrules the global kinds.
1698  bool globalKindOk =( _kinds.empty() || inSolvable.isKind( _kinds.begin(), _kinds.end() ) );
1699 
1701  // string and predicate matching:
1702 
1703  if ( _attrMatchList.size() == 1 )
1704  {
1705  // String matching was done by the base iterator.
1706  // Now check any predicate:
1707  const AttrMatchData & matchData( _attrMatchList.front() );
1708 
1709  if ( matchData.kindPredicate )
1710  {
1711  if ( matchData.kindPredicate != inSolvable.kind() )
1712  {
1713  base_r.nextSkipSolvable(); // this matchData will never match in this solvable
1714  return false;
1715  }
1716  }
1717  else if ( !globalKindOk )
1718  return false; // only matching kindPredicate could overwrite this
1719 
1720  if ( !matchData.predicate || matchData.predicate( base_r ) )
1721  return true;
1722 
1723  return false; // no skip as there may be more occurrences in this solvable of this attr.
1724  }
1725 
1726  // Here: search all attributes ;(
1727  for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1728  {
1729  const AttrMatchData & matchData( *mi );
1730 
1731  if ( matchData.kindPredicate )
1732  {
1733  if ( matchData.kindPredicate != inSolvable.kind() )
1734  continue; // this matchData does not apply
1735  }
1736  else if ( !globalKindOk )
1737  continue; // only matching kindPredicate could overwrite this
1738 
1739  sat::LookupAttr q( matchData.attr, inSolvable );
1740  if ( matchData.strMatcher ) // an empty searchstring matches always
1741  q.setStrMatcher( matchData.strMatcher );
1742 
1743  if ( ! q.empty() ) // there are matches.
1744  {
1745  // now check any predicate:
1746  const AttrMatchData::Predicate & predicate( matchData.predicate );
1747  if ( predicate )
1748  {
1749  for_( it, q.begin(), q.end() )
1750  {
1751  if ( predicate( it ) )
1752  return true;
1753  }
1754  }
1755  else
1756  return true;
1757  }
1758  }
1759  base_r.nextSkipSolvable();
1760  return false;
1761  }
1762 
1763  private:
1765  std::set<Repository> _repos;
1768  std::set<ResKind> _kinds;
1775  AttrMatchList _attrMatchList;
1776  };
1778 
1780  {
1781  // matcher restarts if at end! It is called from the ctor
1782  // to get the 1st match. But if the end is reached, it should
1783  // be deleted, otherwise we'd start over again.
1784  if ( !_matcher )
1785  return; // at end
1786  if ( _matches )
1787  _matches.reset(); // invalidate old matches
1788  if ( ! _matcher->advance( base_reference() ) )
1789  _matcher.reset();
1790  }
1791 
1793  {
1794  if ( _matches )
1795  return *_matches;
1796 
1797  if ( !_matcher )
1798  {
1799  // at end of query:
1800  static const Matches _none;
1801  return _none;
1802  }
1803 
1804  _matches.reset( new Matches );
1805  _matcher->matchDetail( base_reference(), *_matches );
1806  return *_matches;
1807  }
1808 
1809  std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1810  {
1811  str << *obj;
1812  if ( ! obj.matchesEmpty() )
1813  {
1814  for_( it, obj.matchesBegin(), obj.matchesEnd() )
1815  {
1816  str << endl << " " << it->inSolvAttr() << "\t" << it->asString();
1817  }
1818  }
1819  return str;
1820  }
1821 
1823  } //namespace detail
1825 
1827  {
1828  return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1829  }
1830 
1832 } // namespace zypp
1834