1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PoolQuery.cc
15 #include "zypp/base/Gettext.h"
16 #include "zypp/base/LogTools.h"
17 #include "zypp/base/Algorithm.h"
18 #include "zypp/base/String.h"
19 #include "zypp/repo/RepoException.h"
20 #include "zypp/RelCompare.h"
22 #include "zypp/sat/Pool.h"
23 #include "zypp/sat/Solvable.h"
24 #include "zypp/base/StrMatcher.h"
26 #include "zypp/PoolQuery.h"
28 #undef ZYPP_BASE_LOGGER_LOGGROUP
29 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
32 using namespace zypp::sat;
34 ///////////////////////////////////////////////////////////////////
36 { /////////////////////////////////////////////////////////////////
38 ///////////////////////////////////////////////////////////////////
40 { /////////////////////////////////////////////////////////////////
42 /////////////////////////////////////////////////////////////////
43 // some Helpers and Predicates
44 /////////////////////////////////////////////////////////////////
46 bool isDependencyAttribute( sat::SolvAttr attr_r )
48 static sat::SolvAttr deps[] = {
55 SolvAttr::supplements,
58 for_( it, arrayBegin(deps), arrayEnd(deps) )
64 /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
65 * Query asserts \a iter_r points to a capability and we
66 * have to check the range only.
68 struct EditionRangePredicate
70 EditionRangePredicate( const Rel & op, const Edition & edition )
71 : _range( op, edition )
74 EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
75 : _range( op, edition )
79 bool operator()( sat::LookupAttr::iterator iter_r )
81 if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
84 CapDetail cap( iter_r.id() );
85 if ( ! cap.isSimple() )
87 if ( cap.isNamed() ) // no range to match
89 return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
92 std::string serialize() const
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() );
101 Edition::MatchRange _range;
105 /** Whether the current Solvables edition is within a given range and/or its arch matches. */
106 struct SolvableRangePredicate
108 SolvableRangePredicate( const Rel & op, const Edition & edition )
109 : _range( op, edition )
110 , _arch( Arch_empty )
113 SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
114 : _range( op, edition )
118 bool operator()( sat::LookupAttr::iterator iter_r )
120 if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
122 return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
125 std::string serialize() const
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() );
134 Edition::MatchRange _range;
138 /** Whether the current capability matches a given one.
139 * Query asserts \a iter_r points to a capability and we
140 * have to check the match only.
142 struct CapabilityMatchPredicate
144 CapabilityMatchPredicate( Capability cap_r )
148 bool operator()( sat::LookupAttr::iterator iter_r ) const
150 return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
153 std::string serialize() const
155 std::string ret( "CapabilityMatch" );
156 str::appendEscaped( ret, _cap.asString() );
163 /////////////////////////////////////////////////////////////////
165 /////////////////////////////////////////////////////////////////
166 /** Match data per attribtue.
168 * This includes the attribute itself, an optional \ref StrMatcher
169 * to restrict the query to certain string values, and an optional
170 * boolean \ref Predicate that may apply further restrictions that can
171 * not be expressed by the \ref strMatcher.
173 * Example for such a \ref predicate would be an additional edition range
174 * check whan looking for dependencies. The \ref strMatcher would
175 * find potential matches by looking at the dependencies name, the
176 * predicate will then check the edition ranges.
178 * As the \ref predicate takes an iterator pointing to the current
179 * match, it's also suitable for sub-structure (flexarray) inspection
180 * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
182 * \note: \see \ref addPredicate for further constraints.
186 typedef function<bool(sat::LookupAttr::iterator)> Predicate;
188 static bool always( sat::LookupAttr::iterator ) { return true; }
189 static bool never( sat::LookupAttr::iterator ) { return false; }
194 AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
196 , strMatcher( strMatcher_r )
199 AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
200 const Predicate & predicate_r, const std::string & predicateStr_r )
202 , strMatcher( strMatcher_r )
203 , predicate( predicate_r )
204 , predicateStr( predicateStr_r )
207 /** A usable Predicate must provide a string serialization.
208 * As there is no \c operator== for \ref Predicate, we compare it's
209 * string representation instead. If you add new predicated, check the
210 * deserialization code in \ref deserialize.
212 template<class _Predicate>
213 void addPredicate( const _Predicate & predicate_r )
215 predicate = predicate_r;
216 predicateStr = predicate_r.serialize();
219 /** Dumb serialization.
221 * AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
224 std::string serialize() const
226 std::string ret( "AttrMatchData" );
227 str::appendEscaped( ret, attr.asString() );
228 str::appendEscaped( ret, strMatcher.searchstring() );
229 // TODO: Actually the flag should be serialized too, but for PoolQuery
230 // it's by now sufficient to differ between mode OTHER and others,
231 // i.e. whether to compile or not compile.
232 str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
233 str::appendEscaped( ret, predicateStr );
237 /** Dumb restore from serialized string.
238 * \throw Exception on parse error.
240 static AttrMatchData deserialize( const std::string & str_r )
242 std::vector<std::string> words;
243 str::splitEscaped( str_r, std::back_inserter(words) );
244 if ( words.empty() || words[0] != "AttrMatchData" )
245 ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
246 if ( words.size() != 5 )
247 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
250 ret.attr = sat::SolvAttr( words[1] );
251 ret.strMatcher = StrMatcher( words[2] );
252 if ( words[3] == "C" )
253 ret.strMatcher.setFlags( Match::OTHER );
254 ret.predicateStr = words[4];
258 str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
259 if ( ! words.empty() )
261 if ( words[0] == "EditionRange" )
263 switch( words.size() )
266 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
269 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
272 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
276 else if ( words[0] == "SolvableRange" )
278 switch( words.size() )
281 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
284 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
287 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
291 else if ( words[0] == "CapabilityMatch" )
293 if ( words.size() != 2 )
294 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
295 ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
298 ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
304 StrMatcher strMatcher;
306 std::string predicateStr;
309 /** \relates AttrMatchData */
310 inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
312 str << obj.attr << ": " << obj.strMatcher;
314 str << " +(" << obj.predicateStr << ")";
318 /** \relates AttrMatchData */
319 inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
321 return ( lhs.attr == rhs.attr
322 && lhs.strMatcher == rhs.strMatcher
323 && lhs.predicateStr == rhs.predicateStr );
326 /** \relates AttrMatchData */
327 inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
328 { return !( lhs == rhs ); }
330 /** \relates AttrMatchData Arbitrary order for std::container. */
331 inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
333 if ( lhs.attr != rhs.attr )
334 return ( lhs.attr < rhs.attr );
335 if ( lhs.strMatcher != rhs.strMatcher )
336 return ( lhs.strMatcher < rhs.strMatcher );
337 if ( lhs.predicateStr != rhs.predicateStr )
338 return ( lhs.predicateStr < rhs.predicateStr );
342 typedef std::list<AttrMatchData> AttrMatchList;
345 } /////////////////////////////////////////////////////////////////
347 ///////////////////////////////////////////////////////////////////
349 ///////////////////////////////////////////////////////////////////
351 // CLASS NAME : PoolQuery::Impl
354 class PoolQuery::Impl
358 : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
367 /** String representation */
368 string asString() const;
370 /** \name Raw query options. */
372 /** Raw search strings. */
373 StrContainer _strings;
374 /** Raw attributes */
375 AttrRawStrMap _attrs;
376 /** Uncompiled attributes with predicate. */
377 std::set<AttrMatchData> _uncompiledPredicated;
379 /** Sat solver search flags */
383 /** Sat solver status flags */
384 StatusFilter _status_flags;
386 /** Edition condition operand */
388 /** Operator for edition condition */
391 /** Repos to search. */
394 /** Kinds to search */
400 bool operator==( const PoolQuery::Impl & rhs ) const
402 if ( _flags == rhs._flags
403 // bnc#792901: while libzypp uses exact match mode for a single
404 // package name lock, zypper always uses glob. :(
405 // We unify those two forms to enable zypper to remove zypp locks
406 // without need to actually evaluate the query (which would require
407 // repos to be loaded).
408 || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
409 || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
411 && _attrs.size() == 1
412 && _attrs.begin()->first == sat::SolvAttr::name ) )
414 return ( _strings == rhs._strings
415 && _attrs == rhs._attrs
416 && _uncompiledPredicated == rhs._uncompiledPredicated
417 && _match_word == rhs._match_word
418 && _status_flags == rhs._status_flags
419 && _edition == rhs._edition
421 && _repos == rhs._repos
422 && _kinds == rhs._kinds );
427 bool operator!=( const PoolQuery::Impl & rhs ) const
428 { return ! operator==( rhs ); }
431 /** Compile the regex.
432 * Basically building the \ref _attrMatchList from strings.
433 * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
435 void compile() const;
437 /** StrMatcher per attribtue. */
438 mutable AttrMatchList _attrMatchList;
441 /** Join patterns in \a container_r according to \a flags_r into a single \ref StrMatcher.
442 * The \ref StrMatcher returned will be a REGEX if more than one pattern was passed.
444 StrMatcher joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const;
447 friend Impl * rwcowClone<Impl>( const Impl * rhs );
448 /** clone for RWCOW_pointer */
450 { return new Impl( *this ); }
453 ///////////////////////////////////////////////////////////////////
457 MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
459 bool operator()(const string & str)
465 PoolQuery::StrContainer & _cont;
471 bool operator()(const string & str)
477 void PoolQuery::Impl::compile() const
479 _attrMatchList.clear();
481 if ( _flags.mode() == Match::OTHER ) // this will never succeed...
482 ZYPP_THROW( MatchUnknownModeException( _flags ) );
484 // 'different' - will have to iterate through all and match by ourselves (slow)
485 // 'same' - will pass the compiled string to dataiterator_init
486 // 'one-attr' - will pass it to dataiterator_init
487 // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
492 // create regex; store in rcstrings; if more strings flag regex;
495 ; // A default 'query-all' will be added after all sources are processed.
499 // else if _attrs is not empty but it contains just one attr
500 // for all _strings and _attr[key] strings
501 // create regex; flag 'one-attr'; if more strings flag regex;
502 else if (_attrs.size() == 1)
505 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
506 invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
508 _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) );
511 // // MULTIPLE ATTRIBUTES
514 // check whether there are any per-attribute strings
515 bool attrvals_empty = true;
516 for_( ai, _attrs.begin(), _attrs.end() )
518 if ( ai->second.empty() )
520 for_( it, ai->second.begin(), ai->second.end() )
524 attrvals_empty = false;
528 if ( ! attrvals_empty )
532 // chceck whether the per-attribute strings are all the same
533 bool attrvals_thesame = true;
534 AttrRawStrMap::const_iterator ai = _attrs.begin();
535 const StrContainer & set1 = ai->second;
537 for (; ai != _attrs.end(); ++ai)
541 set1.begin(), set1.end(),
542 ai->second.begin(), ai->second.end(),
543 inserter(result, result.begin())/*, ltstr()*/);
546 attrvals_thesame = false;
551 // // THE SAME STRINGS FOR DIFFERENT ATTRS
552 // else if _attrs is not empty but it does not contain strings
553 // for each key in _attrs take all _strings
554 // create regex; store in rcstrings; flag 'same'; if more strings flag regex;
555 if (attrvals_empty || attrvals_thesame)
560 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
564 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
565 invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
568 // May use the same StrMatcher for all
569 StrMatcher matcher( joinedStrMatcher( joined, _flags ) );
570 for_( ai, _attrs.begin(), _attrs.end() )
572 _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
576 // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
577 // if _attrs is not empty and it contains non-empty vectors with non-empty strings
578 // for each key in _attrs take all _strings + all _attrs[key] strings
579 // create regex; flag 'different'; if more strings flag regex;
582 for_(ai, _attrs.begin(), _attrs.end())
585 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
586 invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
588 _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) );
593 // Now handle any predicated queries
594 if ( ! _uncompiledPredicated.empty() )
597 invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
598 for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
600 if ( it->strMatcher.flags().mode() == Match::OTHER )
603 StrContainer joined( global );
604 const std::string & mstr( it->strMatcher.searchstring() );
605 if ( ! mstr.empty() )
606 joined.insert( mstr );
608 // copy and exchange the StrMatcher
609 AttrMatchData nattr( *it );
610 nattr.strMatcher = joinedStrMatcher( joined, _flags );
611 _attrMatchList.push_back( std::move(nattr) );
616 _attrMatchList.push_back( *it );
621 // If no attributes defined at all, then add 'query all'
622 if ( _attrMatchList.empty() )
624 _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) );
627 // Finally check here, whether all involved regex compile.
628 for_( it, _attrMatchList.begin(), _attrMatchList.end() )
630 it->strMatcher.compile(); // throws on error
632 //DBG << asString() << endl;
635 ///////////////////////////////////////////////////////////////////
638 /** Escape \a str_r for use in a regex.
639 * \a flags_r determines whether the input string is interpreted
640 * as regex, glob or plain string.
642 std::string rxEscape( std::string str_r, const Match & flags_r )
644 if ( str_r.empty() || flags_r.isModeRegex() )
647 if ( flags_r.isModeGlob() )
648 return str::rxEscapeGlob( std::move(str_r) );
650 return str::rxEscapeStr( std::move(str_r) );
653 ///////////////////////////////////////////////////////////////////
655 StrMatcher PoolQuery::Impl::joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const
657 if ( container_r.empty() )
658 return StrMatcher( std::string(), flags_r );
660 if ( container_r.size() == 1 && !_match_word ) // use RX to match words
661 return StrMatcher( *container_r.begin(), flags_r );
663 // Convert to a regex.
664 // Note: Modes STRING and GLOB match whole strings (anchored ^ $)
665 // SUBSTRING and REGEX match substrings (match_word anchores SUBSTRING \b)
666 Match retflags( flags_r );
667 retflags.setModeRegex();
670 if ( flags_r.isModeString() || flags_r.isModeGlob() )
672 else if ( _match_word )
677 for ( const::std::string & s : container_r )
679 ret << sep << rxEscape( s, flags_r );
685 if ( flags_r.isModeString() || flags_r.isModeGlob() )
687 else if ( _match_word )
690 return StrMatcher( ret, retflags );
693 string PoolQuery::Impl::asString() const
698 if ( _kinds.empty() )
702 for(Kinds::const_iterator it = _kinds.begin();
703 it != _kinds.end(); ++it)
709 if ( _repos.empty() )
713 for(StrContainer::const_iterator it = _repos.begin();
714 it != _repos.end(); ++it)
719 o << "version: "<< _op << " " << _edition.asString() << endl;
720 o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
723 o << "string match flags: " << Match(_flags) << endl;
727 for(StrContainer::const_iterator it = _strings.begin();
728 it != _strings.end(); ++it)
732 o << "attributes: " << endl;
733 for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
735 o << "* " << ai->first << ": ";
736 for(StrContainer::const_iterator vi = ai->second.begin();
737 vi != ai->second.end(); ++vi)
742 o << "predicated: " << endl;
743 for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
745 o << "* " << *it << endl;
749 o << "last attribute matcher compiled: " << endl;
750 if ( _attrMatchList.empty() )
752 o << "not yet compiled" << endl;
756 for_( it, _attrMatchList.begin(), _attrMatchList.end() )
758 o << "* " << *it << endl;
764 ///////////////////////////////////////////////////////////////////
766 ///////////////////////////////////////////////////////////////////
768 // CLASS NAME : PoolQuery
770 ///////////////////////////////////////////////////////////////////
772 PoolQuery::PoolQuery()
776 PoolQuery::~PoolQuery()
779 void PoolQuery::addRepo(const std::string &repoalias)
781 if (repoalias.empty())
783 WAR << "ignoring an empty repository alias" << endl;
786 _pimpl->_repos.insert(repoalias);
789 void PoolQuery::addKind(const ResKind & kind)
790 { _pimpl->_kinds.insert(kind); }
792 void PoolQuery::addString(const string & value)
793 { _pimpl->_strings.insert(value); }
795 void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
796 { _pimpl->_attrs[attr].insert(value); }
798 void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
799 { return addDependency( attr, name, op, edition, Arch_empty ); }
801 void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
803 switch ( op.inSwitch() )
805 case Rel::ANY_e: // no additional constraint on edition.
806 if ( arch.empty() ) // no additional constraint on arch.
808 addAttribute( attr, name );
813 case Rel::NONE_e: // will never match.
816 default: // go and add the predicated query (uncompiled)
820 // Match::OTHER indicates need to compile
821 // (merge global search strings into name).
822 AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
824 if ( isDependencyAttribute( attr ) )
825 attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
827 attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
829 _pimpl->_uncompiledPredicated.insert( attrMatchData );
832 void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
834 CapDetail cap( cap_r );
835 if ( ! cap.isSimple() ) // will never match.
838 // Matches STRING per default. (won't get compiled!)
839 AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
841 if ( isDependencyAttribute( attr ) )
842 attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
844 attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
846 _pimpl->_uncompiledPredicated.insert( attrMatchData );
849 void PoolQuery::setEdition(const Edition & edition, const Rel & op)
851 _pimpl->_edition = edition;
855 void PoolQuery::setMatchSubstring() { _pimpl->_flags.setModeSubstring(); _pimpl->_match_word = false; }
856 void PoolQuery::setMatchExact() { _pimpl->_flags.setModeString(); _pimpl->_match_word = false; }
857 void PoolQuery::setMatchRegex() { _pimpl->_flags.setModeRegex(); _pimpl->_match_word = false; }
858 void PoolQuery::setMatchGlob() { _pimpl->_flags.setModeGlob(); _pimpl->_match_word = false; }
859 void PoolQuery::setMatchWord() { _pimpl->_flags.setModeSubstring(); _pimpl->_match_word = true; }
861 Match PoolQuery::flags() const
862 { return _pimpl->_flags; }
863 void PoolQuery::setFlags( const Match & flags )
864 { _pimpl->_flags = flags; }
867 void PoolQuery::setInstalledOnly()
868 { _pimpl->_status_flags = INSTALLED_ONLY; }
869 void PoolQuery::setUninstalledOnly()
870 { _pimpl->_status_flags = UNINSTALLED_ONLY; }
871 void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
872 { _pimpl->_status_flags = flags; }
875 const PoolQuery::StrContainer &
876 PoolQuery::strings() const
877 { return _pimpl->_strings; }
879 const PoolQuery::AttrRawStrMap &
880 PoolQuery::attributes() const
881 { return _pimpl->_attrs; }
883 const PoolQuery::StrContainer &
884 PoolQuery::attribute(const sat::SolvAttr & attr) const
886 static const PoolQuery::StrContainer nocontainer;
887 AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
888 return it != _pimpl->_attrs.end() ? it->second : nocontainer;
891 const Edition PoolQuery::edition() const
892 { return _pimpl->_edition; }
893 const Rel PoolQuery::editionRel() const
894 { return _pimpl->_op; }
897 const PoolQuery::Kinds &
898 PoolQuery::kinds() const
899 { return _pimpl->_kinds; }
901 const PoolQuery::StrContainer &
902 PoolQuery::repos() const
903 { return _pimpl->_repos; }
906 bool PoolQuery::caseSensitive() const
907 { return !_pimpl->_flags.test( Match::NOCASE ); }
908 void PoolQuery::setCaseSensitive( bool value )
909 { _pimpl->_flags.turn( Match::NOCASE, !value ); }
911 bool PoolQuery::filesMatchFullPath() const
912 { return _pimpl->_flags.test( Match::FILES ); }
913 void PoolQuery::setFilesMatchFullPath( bool value )
914 { _pimpl->_flags.turn( Match::FILES, value ); }
916 bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); }
917 bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring() && !_pimpl->_match_word; }
918 bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
919 bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
920 bool PoolQuery::matchWord() const { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; }
922 PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
923 { return _pimpl->_status_flags; }
925 bool PoolQuery::empty() const
927 try { return begin() == end(); }
928 catch (const Exception & ex) {}
932 PoolQuery::size_type PoolQuery::size() const
937 for_( it, begin(), end() )
941 catch (const Exception & ex) {}
945 void PoolQuery::execute(ProcessResolvable fnc)
946 { invokeOnEach( begin(), end(), fnc); }
949 /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
950 /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const { return false; }
952 ///////////////////////////////////////////////////////////////////
954 // CLASS NAME : PoolQuery::Attr
957 * represents all atributes in PoolQuery except SolvAtributes, which are
958 * used as is (not needed extend anything if someone adds new solv attr)
960 struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
963 friend class IdStringType<PoolQueryAttr>;
970 explicit PoolQueryAttr( const char* cstr_r )
974 explicit PoolQueryAttr( const std::string & str_r )
979 static const PoolQueryAttr noAttr;
981 // PoolQuery's own attributes
982 static const PoolQueryAttr repoAttr;
983 static const PoolQueryAttr kindAttr;
984 static const PoolQueryAttr stringAttr;
985 static const PoolQueryAttr stringTypeAttr;
986 static const PoolQueryAttr requireAllAttr; // LEAGACY: attribute was defined but never implemented.
987 static const PoolQueryAttr caseSensitiveAttr;
988 static const PoolQueryAttr installStatusAttr;
989 static const PoolQueryAttr editionAttr;
990 static const PoolQueryAttr complexAttr;
993 const PoolQueryAttr PoolQueryAttr::noAttr;
995 const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
996 const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
997 const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
998 const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
999 const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all"); // LEAGACY: attribute was defined but never implemented.
1000 const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1001 const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1002 const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1003 const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1005 class StringTypeAttr : public IdStringType<PoolQueryAttr>
1007 friend class IdStringType<StringTypeAttr>;
1012 explicit StringTypeAttr( const char* cstr_r )
1014 explicit StringTypeAttr( const std::string & str_r )
1017 static const StringTypeAttr noAttr;
1019 static const StringTypeAttr exactAttr;
1020 static const StringTypeAttr substringAttr;
1021 static const StringTypeAttr regexAttr;
1022 static const StringTypeAttr globAttr;
1023 static const StringTypeAttr wordAttr;
1026 const StringTypeAttr StringTypeAttr::noAttr;
1028 const StringTypeAttr StringTypeAttr::exactAttr("exact");
1029 const StringTypeAttr StringTypeAttr::substringAttr("substring");
1030 const StringTypeAttr StringTypeAttr::regexAttr("regex");
1031 const StringTypeAttr StringTypeAttr::globAttr("glob");
1032 const StringTypeAttr StringTypeAttr::wordAttr("word");
1034 ///////////////////////////////////////////////////////////////////
1037 //\TODO maybe ctor with stream can be usefull
1038 //\TODO let it throw, let it throw, let it throw.
1039 bool PoolQuery::recover( istream &str, char delim )
1041 bool finded_something = false; //indicates some atributes is finded
1047 getline( str, s, delim );
1049 if ((!s.empty()) && s[0]=='#') //comment
1054 string::size_type pos = s.find(':');
1055 if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1057 if (finded_something) //is first blank line after record?
1067 finded_something = true;
1069 string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1070 string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1072 PoolQueryAttr attribute( attrName );
1074 if ( attribute==PoolQueryAttr::repoAttr )
1076 addRepo( attrValue );
1078 /* some backwards compatibility */
1079 else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1081 addKind( ResKind(attrValue) );
1083 else if ( attribute==PoolQueryAttr::stringAttr
1084 || attribute=="global_string")
1086 addString( attrValue );
1088 else if ( attribute==PoolQueryAttr::stringTypeAttr
1089 || attribute=="string_type" )
1091 StringTypeAttr s(attrValue);
1092 if( s == StringTypeAttr::regexAttr )
1096 else if ( s == StringTypeAttr::globAttr )
1100 else if ( s == StringTypeAttr::exactAttr )
1104 else if ( s == StringTypeAttr::substringAttr )
1106 setMatchSubstring();
1108 else if ( s == StringTypeAttr::wordAttr )
1112 else if ( s == StringTypeAttr::noAttr )
1114 WAR << "unknown string type " << attrValue << endl;
1118 WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1121 else if ( attribute==PoolQueryAttr::requireAllAttr )
1123 // LEAGACY: attribute was defined but never implemented.
1124 // Actually it should not occur outside our testcases.
1126 else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1128 if ( str::strToTrue(attrValue) )
1130 setCaseSensitive(true);
1132 else if ( !str::strToFalse(attrValue) )
1134 setCaseSensitive(false);
1138 WAR << "unknown boolean value " << attrValue << endl;
1141 else if ( attribute==PoolQueryAttr::installStatusAttr )
1143 if( attrValue == "all" )
1145 setStatusFilterFlags( ALL );
1147 else if( attrValue == "installed" )
1151 else if( attrValue == "not-installed" )
1153 setUninstalledOnly();
1157 WAR << "Unknown value for install status " << attrValue << endl;
1160 else if ( attribute == PoolQueryAttr::editionAttr)
1162 string::size_type pos;
1164 if (attrValue.find_first_of("=<>!") == 0)
1166 pos = attrValue.find_last_of("=<>");
1167 rel = Rel(attrValue.substr(0, pos+1));
1168 attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1171 setEdition(Edition(attrValue), rel);
1173 else if ( attribute == PoolQueryAttr::complexAttr )
1177 _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1179 catch ( const Exception & err )
1181 WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1185 else if ( attribute==PoolQueryAttr::noAttr )
1187 WAR << "empty attribute name" << endl;
1191 string s = attrName;
1192 str::replaceAll( s,"_",":" );
1194 if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1196 Capability c( attrValue );
1198 if ( d.isVersioned() )
1199 addDependency( a, d.name().asString(), d.op(), d.ed() );
1201 addDependency( a, attrValue );
1204 addAttribute( a, attrValue );
1209 return finded_something;
1212 void PoolQuery::serialize( ostream &str, char delim ) const
1216 //iterate thrue all settings and write it
1217 static const zypp::PoolQuery q; //not save default options, so create default query example
1219 for_( it, repos().begin(), repos().end() )
1221 str << "repo: " << *it << delim ;
1224 for_( it, kinds().begin(), kinds().end() )
1226 str << PoolQueryAttr::kindAttr.asString() << ": "
1227 << it->idStr() << delim ;
1230 if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1231 str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1233 if (matchMode()!=q.matchMode())
1235 switch( matchMode() )
1238 str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1240 case Match::SUBSTRING:
1241 str << PoolQueryAttr::stringTypeAttr.asString()
1242 << ": substring" << delim;
1245 str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1248 str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1251 WAR << "unknown match type " << matchMode() << endl;
1255 if( caseSensitive() != q.caseSensitive() )
1257 str << "case_sensitive: ";
1258 if (caseSensitive())
1260 str << "on" << delim;
1264 str << "off" << delim;
1268 if( statusFilterFlags() != q.statusFilterFlags() )
1270 switch( statusFilterFlags() )
1273 str << "install_status: all" << delim;
1275 case INSTALLED_ONLY:
1276 str << "install_status: installed" << delim;
1278 case UNINSTALLED_ONLY:
1279 str << "install_status: not-installed" << delim;
1284 for_( it, strings().begin(), strings().end() )
1286 str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1289 for_( it, attributes().begin(), attributes().end() )
1291 string s = it->first.asString();
1292 str::replaceAll(s,":","_");
1293 for_( it2,it->second.begin(),it->second.end() )
1295 str << s <<": "<< *it2 << delim;
1299 for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1301 str << "complex: "<< it->serialize() << delim;
1304 //separating delim - protection
1308 string PoolQuery::asString() const
1309 { return _pimpl->asString(); }
1311 ostream & operator<<( ostream & str, const PoolQuery & obj )
1312 { return str << obj.asString(); }
1314 std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1315 { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1317 bool PoolQuery::operator==( const PoolQuery & rhs ) const
1318 { return *_pimpl == *rhs._pimpl; }
1320 ///////////////////////////////////////////////////////////////////
1322 { /////////////////////////////////////////////////////////////////
1324 ///////////////////////////////////////////////////////////////////
1326 // CLASS NAME : PoolQueryMatcher
1328 /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1330 * Basically the matcher performs a base query, which should preselect
1331 * candidates for a match. And has some filter conditions on top of it.
1332 * Query and fileter depend on the \ref PoolQuery settings.
1334 * Matcher must be stateless, as it is shared between multiple
1335 * \ref PoolQueryIterator instances.
1337 * If \ref base_iterator is at the \ref end, \ref advance moves it
1338 * to the first match. Otherwise advance moves to the next match, or
1339 * to the \ref end, if there is no more match.
1341 * \note The original implementation treated an empty search string as
1342 * <it>"match always"</it>. We stay compatible.
1344 class PoolQueryMatcher
1347 typedef sat::LookupAttr::iterator base_iterator;
1350 const base_iterator & end() const
1352 static base_iterator _end;
1356 bool advance( base_iterator & base_r ) const
1358 if ( base_r == end() )
1359 base_r = startNewQyery(); // first candidate
1362 base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1363 ++base_r; // advance to next candidate
1366 while ( base_r != end() )
1368 if ( isAMatch( base_r ) )
1370 // No match: try next
1376 /** Provide all matching attributes within this solvable.
1379 void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1381 if ( base_r == end() )
1384 sat::Solvable inSolvable( base_r.inSolvable() );
1386 if ( _attrMatchList.size() == 1 )
1388 // base_r is already on the 1st matching attribute!
1389 // String matching is done by the base iterator. We must check the predicate here.
1390 // Let's see if there are more matches for this solvable:
1391 base_iterator base( base_r );
1392 base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1393 return_r.push_back( base );
1395 const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1396 for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1398 if ( ! predicate || predicate( base ) )
1399 return_r.push_back( base );
1404 // Here: search all attributes ;(
1405 for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1407 const AttrMatchData & matchData( *mi );
1408 sat::LookupAttr q( matchData.attr, inSolvable );
1409 if ( matchData.strMatcher ) // an empty searchstring matches always
1410 q.setStrMatcher( matchData.strMatcher );
1412 if ( ! q.empty() ) // there are matches.
1414 // now check any predicate:
1415 const AttrMatchData::Predicate & predicate( matchData.predicate );
1416 for_( it, q.begin(), q.end() )
1418 if ( ! predicate || predicate( it ) )
1419 return_r.push_back( it );
1427 /** Ctor stores the \ref PoolQuery settings.
1428 * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1430 PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1434 // Repo restriction:
1435 sat::Pool satpool( sat::Pool::instance() );
1437 for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1439 Repository r( satpool.reposFind( *it ) );
1443 _neverMatchRepo = true;
1445 // _neverMatchRepo: we just need to catch the case that no repo
1446 // matched, so we'd interpret the empty list as 'take from all'
1447 if ( _neverMatchRepo && ! _repos.empty() )
1448 _neverMatchRepo = false;
1450 // Kind restriction:
1451 _kinds = query_r->_kinds;
1452 // Edition restriction:
1454 _edition = query_r->_edition;
1455 // Status restriction:
1456 _status_flags = query_r->_status_flags;
1458 _attrMatchList = query_r->_attrMatchList;
1465 /** Initialize a new base query. */
1466 base_iterator startNewQyery() const
1470 if ( _neverMatchRepo )
1473 // Repo restriction:
1474 if ( _repos.size() == 1 )
1475 q.setRepo( *_repos.begin() );
1476 // else: handled in isAMatch.
1478 // Attribute restriction:
1479 if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1481 const AttrMatchData & matchData( _attrMatchList.front() );
1482 q.setAttr( matchData.attr );
1483 if ( matchData.strMatcher ) // empty searchstring matches always
1484 q.setStrMatcher( matchData.strMatcher );
1486 else // more than 1 attr (but not all)
1488 // no restriction, it's all handled in isAMatch.
1489 q.setAttr( sat::SolvAttr::allAttr );
1496 /** Check whether we are on a match.
1498 * The check covers the whole Solvable, not just the current
1499 * attribute \c base_r points to. If there's no match, also
1500 * prepare \c base_r to advance appropriately. If there is
1501 * a match, simply return \c true. \ref advance always moves
1502 * to the next Solvable if there was a match.
1504 * \note: Caller asserts we're not at \ref end.
1506 bool isAMatch( base_iterator & base_r ) const
1508 /////////////////////////////////////////////////////////////////////
1509 Repository inRepo( base_r.inRepo() );
1510 // Status restriction:
1512 && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1514 base_r.nextSkipRepo();
1517 // Repo restriction:
1518 if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1520 base_r.nextSkipRepo();
1523 /////////////////////////////////////////////////////////////////////
1524 sat::Solvable inSolvable( base_r.inSolvable() );
1525 // Kind restriction:
1526 if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1528 base_r.nextSkipSolvable();
1532 // Edition restriction:
1533 if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1535 base_r.nextSkipSolvable();
1538 /////////////////////////////////////////////////////////////////////
1539 // string and predicate matching:
1541 if ( _attrMatchList.size() == 1 )
1543 // String matching was done by the base iterator.
1544 // Now check any predicate:
1545 const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1546 if ( ! predicate || predicate( base_r ) )
1549 return false; // no skip as there may be more occurrences od this attr.
1552 // Here: search all attributes ;(
1553 for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1555 const AttrMatchData & matchData( *mi );
1556 sat::LookupAttr q( matchData.attr, inSolvable );
1557 if ( matchData.strMatcher ) // an empty searchstring matches always
1558 q.setStrMatcher( matchData.strMatcher );
1560 if ( ! q.empty() ) // there are matches.
1562 // now check any predicate:
1563 const AttrMatchData::Predicate & predicate( matchData.predicate );
1566 for_( it, q.begin(), q.end() )
1568 if ( predicate( it ) )
1576 base_r.nextSkipSolvable();
1581 /** Repositories include in the search. */
1582 std::set<Repository> _repos;
1583 DefaultIntegral<bool,false> _neverMatchRepo;
1584 /** Resolvable kinds to include. */
1585 std::set<ResKind> _kinds;
1586 /** Edition filter. */
1589 /** Installed status filter flags. \see PoolQuery::StatusFilter */
1591 /** StrMatcher per attribtue. */
1592 AttrMatchList _attrMatchList;
1594 ///////////////////////////////////////////////////////////////////
1596 void PoolQueryIterator::increment()
1598 // matcher restarts if at end! It is called from the ctor
1599 // to get the 1st match. But if the end is reached, it should
1600 // be deleted, otherwise we'd start over again.
1604 _matches.reset(); // invalidate old matches
1605 if ( ! _matcher->advance( base_reference() ) )
1609 const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1617 static const Matches _none;
1621 _matches.reset( new Matches );
1622 _matcher->matchDetail( base_reference(), *_matches );
1626 std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1629 if ( ! obj.matchesEmpty() )
1631 for_( it, obj.matchesBegin(), obj.matchesEnd() )
1633 str << endl << " " << it->inSolvAttr() << "\t" << it->asString();
1639 ///////////////////////////////////////////////////////////////////
1640 } //namespace detail
1641 ///////////////////////////////////////////////////////////////////
1643 detail::PoolQueryIterator PoolQuery::begin() const
1645 return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1648 /////////////////////////////////////////////////////////////////
1650 ///////////////////////////////////////////////////////////////////