From: Michael Andres Date: Fri, 29 May 2009 18:20:50 +0000 (+0200) Subject: Improve PoolQuery to allow queries on dependencies. (bnc #475682) X-Git-Tag: 6.7.0~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dd2cdca2c661bf98e4a5b5535409764324239d3f;p=platform%2Fupstream%2Flibzypp.git Improve PoolQuery to allow queries on dependencies. (bnc #475682) --- diff --git a/tests/zypp/PoolQuery_test.cc b/tests/zypp/PoolQuery_test.cc index 933a389..96372db 100644 --- a/tests/zypp/PoolQuery_test.cc +++ b/tests/zypp/PoolQuery_test.cc @@ -35,6 +35,24 @@ struct PrintAndCount unsigned _count; }; +void dumpQ( std::ostream & str, const PoolQuery & q, bool verbose = true ) +{ + q.begin(); + str << q << endl; + unsigned nc = 0; + if ( 1 ) + { + for_( it, q.begin(), q.end() ) + { + ++nc; + if ( verbose ) + str << it << endl; + } + str << "--> MATCHES: " << nc << endl; + } +} + + #if 0 BOOST_AUTO_TEST_CASE(pool_query_experiment) { @@ -58,7 +76,6 @@ BOOST_AUTO_TEST_CASE(pool_query_experiment) } #endif - ///////////////////////////////////////////////////////////////////////////// // 0xx basic queries ///////////////////////////////////////////////////////////////////////////// @@ -650,3 +667,71 @@ BOOST_AUTO_TEST_CASE(pool_query_equal) BOOST_CHECK(q==q4); BOOST_CHECK(q4!=q3); } + +///////////////////////////////////////////////////////////////////////////// +// Dependency Query +///////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE(addDependency) +{ + { + cout << "****addDependency1****" << endl; + PoolQuery q; + q.setCaseSensitive( false ); + q.setMatchSubstring(); + q.addString( "libzypp" ); + q.addDependency( sat::SolvAttr::provides, "FOO" ); // ! finds 'perl(CPAN::InfoObj)' 'foO' + std::for_each(q.begin(), q.end(), PrintAndCount()); + BOOST_CHECK_EQUAL( q.size(), 12 ); + } + { + cout << "****addDependency2****" << endl; + PoolQuery q; + q.setCaseSensitive( false ); + q.setMatchSubstring(); + q.addString( "libzypp" ); + q.addDependency( sat::SolvAttr::provides, "FOO", Rel::GT, Edition("5.0") ); + std::for_each(q.begin(), q.end(), PrintAndCount()); + //dumpQ( std::cout, q ); + BOOST_CHECK_EQUAL( q.size(), 6 ); + } + + { + cout << "****addDependency3****" << endl; + PoolQuery q; + // includes wine + q.addDependency( sat::SolvAttr::provides, "kernel" ); + std::for_each(q.begin(), q.end(), PrintAndCount()); + //dumpQ( std::cout, q ); + BOOST_CHECK_EQUAL( q.size(), 12 ); + } + { + cout << "****addDependency4****" << endl; + PoolQuery q; + // no wine + q.addDependency( sat::SolvAttr::name, "kernel" ); + std::for_each(q.begin(), q.end(), PrintAndCount()); + //dumpQ( std::cout, q ); + BOOST_CHECK_EQUAL( q.size(), 11 ); + } + { + cout << "****addDependency5****" << endl; + PoolQuery q; + // Capability always matches exact + q.addDependency( sat::SolvAttr::provides, Capability("kernel") ); + std::for_each(q.begin(), q.end(), PrintAndCount()); + //dumpQ( std::cout, q ); + BOOST_CHECK_EQUAL( q.size(), 2 ); + } + { + cout << "****addDependency6****" << endl; + PoolQuery q; + // non dependecy + Capability matches solvable name! + q.addDependency( sat::SolvAttr::summary, Capability("kernel") ); + std::for_each(q.begin(), q.end(), PrintAndCount()); + //dumpQ( std::cout, q ); + BOOST_CHECK_EQUAL( q.size(), 0 ); // non dependecy + } +} + + diff --git a/zypp/PoolQuery.cc b/zypp/PoolQuery.cc index b0715a6..375a349 100644 --- a/zypp/PoolQuery.cc +++ b/zypp/PoolQuery.cc @@ -13,7 +13,7 @@ #include #include "zypp/base/Gettext.h" -#include "zypp/base/Logger.h" +#include "zypp/base/LogTools.h" #include "zypp/base/Algorithm.h" #include "zypp/base/String.h" #include "zypp/repo/RepoException.h" @@ -67,6 +67,12 @@ namespace zypp , 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; @@ -79,6 +85,84 @@ namespace zypp typedef std::list AttrMatchList; + ///////////////////////////////////////////////////////////////// + // some Helpers and Predicates + ///////////////////////////////////////////////////////////////// + + bool isDependencyAttribute( sat::SolvAttr attr_r ) + { + static sat::SolvAttr deps[] = { + SolvAttr::provides, + SolvAttr::requires, + SolvAttr::recommends, + SolvAttr::obsoletes, + SolvAttr::conflicts, + SolvAttr::suggests, + SolvAttr::supplements, + SolvAttr::enhances, + }; + for_( it, arrayBegin(deps), arrayEnd(deps) ) + if ( *it == attr_r ) + return true; + return false; + } + + /** Whether the current capabilities edition range ovelaps. + * Query asserts \a iter_r points to a capability and we + * have to check the range only. + */ + struct EditionRangePredicate + { + EditionRangePredicate( const Rel & op, const Edition & edition ) + : _range( op, edition ) + {} + + bool operator()( sat::LookupAttr::iterator iter_r ) + { + CapDetail cap( iter_r.id() ); + if ( ! cap.isSimple() ) + return false; + if ( cap.isNamed() ) // no range to match + return true; + return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range ); + } + + Edition::MatchRange _range; + }; + + /** Whether the current Solvables edition is within a given range. */ + struct SolvableRangePredicate + { + SolvableRangePredicate( const Rel & op, const Edition & edition ) + : _range( op, edition ) + {} + + bool operator()( sat::LookupAttr::iterator iter_r ) + { + return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range ); + } + + Edition::MatchRange _range; + }; + + /** Whether the current capability matches a given one. + * Query asserts \a iter_r points to a capability and we + * have to check the match only. + */ + struct CapabilityMatchPredicate + { + CapabilityMatchPredicate( Capability cap_r ) + : _cap( cap_r ) + {} + + bool operator()( sat::LookupAttr::iterator iter_r ) const + { + return _cap.matches( iter_r.asType() ) == CapMatch::yes; + } + + Capability _cap; + }; + } ///////////////////////////////////////////////////////////////// // namespace /////////////////////////////////////////////////////////////////// @@ -111,6 +195,8 @@ namespace zypp StrContainer _strings; /** Raw attributes */ AttrRawStrMap _attrs; + /** Uncompiled attributes with predicate. */ + AttrMatchList _uncompiledPredicated; /** Sat solver search flags */ Match _flags; @@ -199,11 +285,7 @@ namespace zypp // create regex; store in rcstrings; if more strings flag regex; if (_attrs.empty()) { - rcstrings = createRegex(_strings, cflags); - if (_strings.size() > 1) // switch to regex for multiple strings - cflags.setModeRegex(); - _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, - sat::AttrMatcher( rcstrings, cflags ) ) ); + ; // A default 'query-all' will be added after all sources are processed. } // // ONE ATTRIBUTE @@ -305,7 +387,50 @@ attremptycheckend: } } - // Check here, whether all involved regex compile. + // Now handle any predicated queries + if ( ! _uncompiledPredicated.empty() ) + { + StrContainer global; + invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) ); + for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() ) + { + if ( it->attrMatcher.flags().mode() == Match::OTHER ) + { + // need to compile: + StrContainer joined( global ); + const std::string & mstr( it->attrMatcher.searchstring() ); + if ( ! mstr.empty() ) + joined.insert( mstr ); + + cflags = _flags; + rcstrings = createRegex( joined, cflags ); + if ( joined.size() > 1 ) // switch to regex for multiple strings + cflags.setModeRegex(); + + _attrMatchList.push_back( AttrMatchData( it->attr, + sat::AttrMatcher( rcstrings, cflags ), + it->predicate ) ); + } + else + { + // copy matcher + _attrMatchList.push_back( *it ); + } + } + } + + // If no attributes defined at all, then add 'query all' + if ( _attrMatchList.empty() ) + { + cflags = _flags; + rcstrings = createRegex( _strings, cflags ); + if ( _strings.size() > 1 ) // switch to regex for multiple strings + cflags.setModeRegex(); + _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, + sat::AttrMatcher( rcstrings, cflags ) ) ); + } + + // Finally check here, whether all involved regex compile. for_( it, _attrMatchList.begin(), _attrMatchList.end() ) { it->attrMatcher.compile(); // throws on error @@ -460,6 +585,12 @@ attremptycheckend: o << endl; } + o << "predicated: " << endl; + for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() ) + { + o << "* " << *it << endl; + } + // compiled o << "last attribute matcher compiled: " << endl; if ( _attrMatchList.empty() ) @@ -501,18 +632,59 @@ attremptycheckend: _pimpl->_repos.insert(repoalias); } - void PoolQuery::addKind(const ResKind & kind) { _pimpl->_kinds.insert(kind); } - void PoolQuery::addString(const string & value) { _pimpl->_strings.insert(value); } - void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value) { _pimpl->_attrs[attr].insert(value); } + void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition ) + { + switch ( op.inSwitch() ) + { + case Rel::ANY_e: // no additional constraint on edition. + addAttribute( attr, name ); + return; + + case Rel::NONE_e: // will never match. + return; + + default: // go and add the predicated query (uncompiled) + break; + } + + // Match::OTHER indicates need to compile. + sat::AttrMatcher matcher( name, Match::OTHER ); + + AttrMatchData::Predicate pred; + if ( isDependencyAttribute( attr ) ) + pred = EditionRangePredicate( op, edition ); + else + pred = SolvableRangePredicate( op, edition ); + + _pimpl->_uncompiledPredicated.push_back( AttrMatchData( attr, matcher, pred ) ); + } + + void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r ) + { + CapDetail cap( cap_r ); + if ( ! cap.isSimple() ) // will never match. + return; + + // Matches STRING per default. (won't get compiled!) + sat::AttrMatcher matcher( cap.name().asString() ); + + AttrMatchData::Predicate pred; + if ( isDependencyAttribute( attr ) ) + pred = CapabilityMatchPredicate( cap_r ); + else + pred = SolvableRangePredicate( cap.op(), cap.ed() ); + + _pimpl->_uncompiledPredicated.push_back( AttrMatchData( attr, matcher, pred ) ); + } void PoolQuery::setEdition(const Edition & edition, const Rel & op) { diff --git a/zypp/PoolQuery.h b/zypp/PoolQuery.h index b41d775..cd80ffc 100644 --- a/zypp/PoolQuery.h +++ b/zypp/PoolQuery.h @@ -209,48 +209,94 @@ namespace zypp * * \see sat::SolvAttr */ - void addAttribute(const sat::SolvAttr & attr, const std::string & value = ""); -#if 0 - /** - * Query for dependencies matching a broken down capability. + void addAttribute( const sat::SolvAttr & attr, const std::string & value = "" ); + + /** \name Filter by dependencies matching a broken down capability name [op edition]. + * + * The capabilities \c name part may be defined as query string + * like with \ref addAttribute. Globing and regex are supported. + * Global query strings defined by \ref addString are considered. + * + * So without any op edition addDependency behaves the + * same as \ref addAttribute. If an edition range is given, matches + * are restricted accordingly. Thete are various overloads, so pick + * the one you like best. * - * The capabilities \c name part may be defined as ordinary query - * string (\see \ref addAttribute), so globing and regex are supported. * \code - * addDependency( sat::SolvAttr::provides, "kde*", Edition("2.0"), Rel::GE ); + * { + * setMatchGlob(); + * setCaseSensitive( false ); + * addDependency( sat::SolvAttr::provides, "kde*", Rel::EQ, Edition("2.0") ); + * addDependency( sat::SolvAttr::provides, "kde*", Edition("2.0") ); // same as above + * } + * { + * setMatchGlob(); + * setCaseSensitive( false ); + * addString( "kde*" ); + * addDependency( sat::SolvAttr::provides, Rel::EQ, Edition("2.0") );// same as above + * addDependency( sat::SolvAttr::provides, Edition("2.0") ); // same as above + * } + * \endcode + * + * \note Thre's also a version of \ref addDependency provided, that takes a + * complete \ref Capability as argument. This always requires an exact match + * of the name part (as the resolver would do it). + * + * This is the list of valid dependency attributes: + * \code + * SolvAttr::provides + * SolvAttr::obsoletes + * SolvAttr::conflicts + * SolvAttr::requires + * SolvAttr::recommends + * SolvAttr::suggests + * SolvAttr::supplements + * SolvAttr::enhances + * \endcode + * + * \note What happens if a non dependency attribute is passed?<\b> + * If an edition range is given, it is matched against the matching + * solvables edition instead. Without edition range it behaves the + * same as \ref addAttribute. + * + * \code + * // Find all packages providing "kernel > 2.0" + * addDependency( sat::SolvAttr::provides, "kernel", Rel::GT, Edition("2.0") ); + * + * // // Find all packages named "kernel" and with edition "> 2.0" + * addDependency( sat::SolvAttr::name, "kernel", Rel::GT, Edition("2.0") ); * \endcode - * \throws Exception in case \a attr is not a dependency attribute. */ - void addDependency( const sat::SolvAttr & attr, const std::string & name, - const Edition & edition, const Rel & op = Rel::EQ ); - /** \overload Query provides */ - void addProvides( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::provides, name, edition, op ); } - /** \overload Query requires */ - void addRequires( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::requires, name, edition, op ); } - /** \overload Query obsoletes */ - void addObsoletes( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::obsoletes, name, edition, op ); } - /** \overload Query conflicts */ - void addConflicts( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::conflicts, name, edition, op ); } - /** \overload Query recommends */ - void addRecommends( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::recommends, name, edition, op ); } - /** \overload Query suggests */ - void addSuggests( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::suggests, name, edition, op ); } - /** \overload Query supplements */ - void addSupplements( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::supplements, name, edition, op ); } - /** \overload Query enhances */ - void addEnhances( const std::string & name, const Edition & edition, const Rel & op = Rel::EQ ) - { addDependency( sat::SolvAttr::enhances, name, edition, op ); } - - /** \overload Query taking a \ref Capability (always exact name match) */ + //@{ + /** Query "name|global op edition". */ + void addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition ); + + /** \overload Query "name|global == edition". */ + void addDependency( const sat::SolvAttr & attr, const std::string & name, const Edition & edition ) + { addDependency( attr, name, Rel::EQ, edition ); } + + /** \overload Query "name|global". */ + void addDependency( const sat::SolvAttr & attr, const std::string & name ) + { addDependency( attr, name, Rel::ANY, Edition() ); } + + /** \overload Query "global op edition".*/ + void addDependency( const sat::SolvAttr & attr, const Rel & op, const Edition & edition ) + { addDependency( attr, std::string(), op, edition ); } + + /** \overload Query "global == edition". */ + void addDependency( const sat::SolvAttr & attr, const Edition & edition ) + { addDependency( attr, std::string(), Rel::EQ, edition ); } + + /** \overload Query "global". */ + void addDependency( const sat::SolvAttr & attr ) + { addDependency( attr, std::string(), Rel::ANY, Edition() ); } + + /** \overload Query taking a \ref Capability (always exact name match). + * \note If a non dependency attribute is passed, the \ref Capability + * will always be matched against the Solvables \c name and \c edition. + */ void addDependency( const sat::SolvAttr & attr, Capability cap_r ); -#endif + //@} /** * Set version condition. This will filter out solvables not matching