Imported Upstream version 17.23.0
[platform/upstream/libzypp.git] / zypp / pool / PoolImpl.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/pool/PoolImpl.h
10  *
11 */
12 #ifndef ZYPP_POOL_POOLIMPL_H
13 #define ZYPP_POOL_POOLIMPL_H
14
15 #include <iosfwd>
16
17 #include "zypp/base/Easy.h"
18 #include "zypp/base/LogTools.h"
19 #include "zypp/base/SerialNumber.h"
20 #include "zypp/APIConfig.h"
21
22 #include "zypp/pool/PoolTraits.h"
23 #include "zypp/ResPoolProxy.h"
24 #include "zypp/PoolQueryResult.h"
25
26 #include "zypp/sat/Pool.h"
27 #include "zypp/Product.h"
28
29 using std::endl;
30
31 ///////////////////////////////////////////////////////////////////
32 namespace zypp
33 { /////////////////////////////////////////////////////////////////
34
35   namespace resstatus
36   {
37     /** Manipulator for \ref ResStatus::UserLockQueryField.
38      * Field is not public available. It is intended to remember the
39      * initial lock status usually derived from /etc/zypp/locks. So
40      * we are able to detect changes we have to write back on commit.
41     */
42     struct UserLockQueryManip
43     {
44       /** Set lock and UserLockQuery bit according to \c yesno_r. */
45       static void setLock( ResStatus & status_r, bool yesno_r )
46       {
47         status_r.setLock( yesno_r, ResStatus::USER );
48         status_r.setUserLockQueryMatch( yesno_r );
49       }
50
51       /** Update lock and UserLockQuery bit IFF the item gained the bit. */
52       static void reapplyLock( ResStatus & status_r, bool yesno_r )
53       {
54         if ( yesno_r && ! status_r.isUserLockQueryMatch() )
55         {
56           status_r.setLock( yesno_r, ResStatus::USER );
57           status_r.setUserLockQueryMatch( yesno_r );
58         }
59       }
60
61       /** Test whether the lock status differs from the remembered UserLockQuery bit. */
62       static int diffLock( const ResStatus & status_r )
63       {
64         bool userLock( status_r.isUserLocked() );
65         if ( userLock == status_r.isUserLockQueryMatch() )
66           return 0;
67         return userLock ? 1 : -1;
68       }
69
70     };
71   }
72
73   namespace
74   {
75     inline PoolQuery makeTrivialQuery( IdString ident_r )
76     {
77       sat::Solvable::SplitIdent ident( ident_r );
78
79       PoolQuery q;
80       q.addAttribute( sat::SolvAttr::name, ident.name().asString() );
81       q.addKind( ident.kind() );
82       q.setMatchExact();
83       q.setCaseSensitive(true);
84       return q;
85    }
86
87     inline bool hardLockQueriesRemove( pool::PoolTraits::HardLockQueries & activeLocks_r, IdString ident_r )
88     {
89       unsigned s( activeLocks_r.size() );
90       activeLocks_r.remove( makeTrivialQuery( ident_r ) );
91       return( activeLocks_r.size() != s );
92     }
93
94     inline bool hardLockQueriesAdd( pool::PoolTraits::HardLockQueries & activeLocks_r, IdString ident_r )
95     {
96       PoolQuery q( makeTrivialQuery( ident_r ) );
97       for_( it, activeLocks_r.begin(), activeLocks_r.end() )
98       {
99         if ( *it == q )
100           return false;
101       }
102       activeLocks_r.push_back( q );
103       return true;
104     }
105   }
106
107   ///////////////////////////////////////////////////////////////////
108   namespace solver {
109     namespace detail {
110       void establish( sat::Queue & pseudoItems_r, sat::Queue & pseudoFlags_r ); // in solver/detail/SATResolver.cc
111     }
112   }
113   ///////////////////////////////////////////////////////////////////
114   /// Store initial establish status of pseudo installed items.
115   ///
116   class ResPool::EstablishedStates::Impl
117   {
118   public:
119     Impl()
120     { solver::detail::establish( _pseudoItems, _pseudoFlags ); }
121
122     /** Return all pseudo installed items whose current state differs from their initial one. */
123     ResPool::EstablishedStates::ChangedPseudoInstalled changedPseudoInstalled() const
124     {
125       ChangedPseudoInstalled ret;
126
127       if ( ! _pseudoItems.empty() )
128       {
129         for ( sat::Queue::size_type i = 0; i < _pseudoItems.size(); ++i )
130         {
131           PoolItem pi { sat::Solvable(_pseudoItems[i]) };
132           ResStatus::ValidateValue vorig { validateValue( i ) };
133           if ( pi.status().validate() != vorig )
134             ret[pi] = vorig;
135         }
136       }
137       return ret;
138     }
139
140   private:
141     ResStatus::ValidateValue validateValue( sat::Queue::size_type i ) const
142     {
143       ResStatus::ValidateValue ret { ResStatus::UNDETERMINED };
144       switch ( _pseudoFlags[i] )
145       {
146         case  0: ret = ResStatus::BROKEN      /*2*/; break;
147         case  1: ret = ResStatus::SATISFIED   /*4*/; break;
148         case -1: ret = ResStatus::NONRELEVANT /*6*/; break;
149       }
150       return ret;
151     }
152
153   private:
154     sat::Queue _pseudoItems;
155     sat::Queue _pseudoFlags;
156   };
157
158   ///////////////////////////////////////////////////////////////////
159   namespace pool
160   { /////////////////////////////////////////////////////////////////
161
162     ///////////////////////////////////////////////////////////////////
163     //
164     //  CLASS NAME : PoolImpl
165     //
166     /** */
167     class PoolImpl
168     {
169       friend std::ostream & operator<<( std::ostream & str, const PoolImpl & obj );
170
171       public:
172         /** */
173         typedef PoolTraits::ItemContainerT              ContainerT;
174         typedef PoolTraits::size_type                   size_type;
175         typedef PoolTraits::const_iterator              const_iterator;
176         typedef PoolTraits::Id2ItemT                    Id2ItemT;
177
178         typedef PoolTraits::repository_iterator         repository_iterator;
179
180         typedef sat::detail::SolvableIdType             SolvableIdType;
181
182         typedef ResPool::EstablishedStates::Impl        EstablishedStatesImpl;
183
184       public:
185         /** Default ctor */
186         PoolImpl();
187         /** Dtor */
188         ~PoolImpl();
189
190       public:
191         /** convenience. */
192         const sat::Pool satpool() const
193         { return sat::Pool::instance(); }
194
195         /** Housekeeping data serial number. */
196         const SerialNumber & serial() const
197         { return satpool().serial(); }
198
199         ///////////////////////////////////////////////////////////////////
200         //
201         ///////////////////////////////////////////////////////////////////
202       public:
203         /**  */
204         bool empty() const
205         { return satpool().solvablesEmpty(); }
206
207         /**  */
208         size_type size() const
209         { return satpool().solvablesSize(); }
210
211         const_iterator begin() const
212         { return make_filter_begin( pool::ByPoolItem(), store() ); }
213
214         const_iterator end() const
215         { return make_filter_end( pool::ByPoolItem(), store() ); }
216
217       public:
218         /** Return the corresponding \ref PoolItem.
219          * Pool and sat pool should be in sync. Returns an empty
220          * \ref PoolItem if there is no corresponding \ref PoolItem.
221          * \see \ref PoolItem::satSolvable.
222          */
223         PoolItem find( const sat::Solvable & slv_r ) const
224         {
225           const ContainerT & mystore( store() );
226           return( slv_r.id() < mystore.size() ? mystore[slv_r.id()] : PoolItem() );
227         }
228
229         ///////////////////////////////////////////////////////////////////
230         //
231         ///////////////////////////////////////////////////////////////////
232       public:
233         /** \name Save and restore state. */
234         //@{
235         void SaveState( const ResKind & kind_r );
236
237         void RestoreState( const ResKind & kind_r );
238         //@}
239
240         ///////////////////////////////////////////////////////////////////
241         //
242         ///////////////////////////////////////////////////////////////////
243       public:
244         ResPoolProxy proxy( ResPool self ) const
245         {
246           checkSerial();
247           if ( !_poolProxy )
248           {
249             _poolProxy.reset( new ResPoolProxy( self, *this ) );
250           }
251           return *_poolProxy;
252         }
253
254         /** True factory for \ref ResPool::EstablishedStates.
255          * Internally we maintain the ResPool::EstablishedStates::Impl
256          * reference shared_ptr. Updated whenever the pool content changes.
257          * On demand hand it out as ResPool::EstablishedStates Impl.
258          */
259         ResPool::EstablishedStates establishedStates() const
260         { store(); return ResPool::EstablishedStates( _establishedStates ); }
261
262       public:
263         /** Forward list of Repositories that contribute ResObjects from \ref sat::Pool */
264         size_type knownRepositoriesSize() const
265         { checkSerial(); return satpool().reposSize(); }
266
267         repository_iterator knownRepositoriesBegin() const
268         { checkSerial(); return satpool().reposBegin(); }
269
270         repository_iterator knownRepositoriesEnd() const
271         { checkSerial(); return satpool().reposEnd(); }
272
273         Repository reposFind( const std::string & alias_r ) const
274         { checkSerial(); return satpool().reposFind( alias_r ); }
275
276         ///////////////////////////////////////////////////////////////////
277         //
278         ///////////////////////////////////////////////////////////////////
279       public:
280         typedef PoolTraits::HardLockQueries           HardLockQueries;
281         typedef PoolTraits::hardLockQueries_iterator  hardLockQueries_iterator;
282
283         const HardLockQueries & hardLockQueries() const
284         { return _hardLockQueries; }
285
286         void reapplyHardLocks() const
287         {
288           // It is assumed that reapplyHardLocks is called after new
289           // items were added to the pool, but the _hardLockQueries
290           // did not change since. Action is to be performed only on
291           // those items that gained the bit in the UserLockQueryField.
292           MIL << "Re-apply " << _hardLockQueries.size() << " HardLockQueries" << endl;
293           PoolQueryResult locked;
294           for_( it, _hardLockQueries.begin(), _hardLockQueries.end() )
295           {
296             locked += *it;
297           }
298           MIL << "HardLockQueries match " << locked.size() << " Solvables." << endl;
299           for_( it, begin(), end() )
300           {
301             resstatus::UserLockQueryManip::reapplyLock( it->status(), locked.contains( *it ) );
302           }
303         }
304
305         void setHardLockQueries( const HardLockQueries & newLocks_r )
306         {
307           MIL << "Apply " << newLocks_r.size() << " HardLockQueries" << endl;
308           _hardLockQueries = newLocks_r;
309           // now adjust the pool status
310           PoolQueryResult locked;
311           for_( it, _hardLockQueries.begin(), _hardLockQueries.end() )
312           {
313             locked += *it;
314           }
315           MIL << "HardLockQueries match " << locked.size() << " Solvables." << endl;
316           for_( it, begin(), end() )
317           {
318             resstatus::UserLockQueryManip::setLock( it->status(), locked.contains( *it ) );
319           }
320         }
321
322         bool getHardLockQueries( HardLockQueries & activeLocks_r )
323         {
324           activeLocks_r = _hardLockQueries; // current queries
325           // Now diff to the pool collecting names only.
326           // Thus added and removed locks are not necessarily
327           // disjoint. Added locks win.
328           typedef std::unordered_set<IdString> IdentSet;
329           IdentSet addedLocks;
330           IdentSet removedLocks;
331           for_( it, begin(), end() )
332           {
333             switch ( resstatus::UserLockQueryManip::diffLock( it->status() ) )
334             {
335               case 0:  // unchanged
336                 break;
337               case 1:
338                 addedLocks.insert( it->satSolvable().ident() );
339                 break;
340               case -1:
341                 removedLocks.insert( it->satSolvable().ident() );
342                break;
343             }
344           }
345           // now the bad part - adjust the queries
346           bool setChanged = false;
347           for_( it, removedLocks.begin(), removedLocks.end() )
348           {
349             if ( addedLocks.find( *it ) != addedLocks.end() )
350               continue; // Added locks win
351             if ( hardLockQueriesRemove( activeLocks_r, *it ) && ! setChanged )
352               setChanged = true;
353           }
354           for_( it, addedLocks.begin(), addedLocks.end() )
355           {
356             if ( hardLockQueriesAdd( activeLocks_r, *it ) && ! setChanged )
357               setChanged = true;
358           }
359           return setChanged;
360        }
361
362       public:
363         const ContainerT & store() const
364         {
365           checkSerial();
366           if ( _storeDirty )
367           {
368             sat::Pool pool( satpool() );
369             bool addedItems = false;
370             bool reusedIDs = _watcherIDs.remember( pool.serialIDs() );
371             std::list<PoolItem> addedProducts;
372
373             _store.resize( pool.capacity() );
374
375             if ( pool.capacity() )
376             {
377               for ( sat::detail::SolvableIdType i = pool.capacity()-1; i != 0; --i )
378               {
379                 sat::Solvable s( i );
380                 PoolItem & pi( _store[i] );
381                 if ( ! s &&  pi )
382                 {
383                   // the PoolItem got invalidated (e.g unloaded repo)
384                   pi = PoolItem();
385                 }
386                 else if ( reusedIDs || (s && ! pi) )
387                 {
388                   // new PoolItem to add
389                   pi = PoolItem::makePoolItem( s ); // the only way to create a new one!
390                   // remember products for buddy processing (requires clean store)
391                   if ( s.isKind( ResKind::product ) )
392                     addedProducts.push_back( pi );
393                   if ( !addedItems )
394                     addedItems = true;
395                 }
396               }
397             }
398             _storeDirty = false;
399
400             // Now, as the pool is adjusted, ....
401
402             // .... we check for product buddies.
403             if ( ! addedProducts.empty() )
404             {
405               for_( it, addedProducts.begin(), addedProducts.end() )
406               {
407                 it->setBuddy( asKind<Product>(*it)->referencePackage() );
408               }
409             }
410
411             // .... we must reapply those query based hard locks.
412             if ( addedItems )
413             {
414               reapplyHardLocks();
415             }
416
417             // Compute the initial status of Patches etc.
418             if ( !_establishedStates )
419               _establishedStates.reset( new EstablishedStatesImpl );
420           }
421           return _store;
422         }
423
424         const Id2ItemT & id2item () const
425         {
426           checkSerial();
427           if ( _id2itemDirty )
428           {
429             store();
430             _id2item = Id2ItemT( size() );
431             for_( it, begin(), end() )
432             {
433               const sat::Solvable &s = (*it)->satSolvable();
434               sat::detail::IdType id = s.ident().id();
435               if ( s.isKind( ResKind::srcpackage ) )
436                 id = -id;
437               _id2item.insert( std::make_pair( id, *it ) );
438             }
439             //INT << _id2item << endl;
440             _id2itemDirty = false;
441           }
442           return _id2item;
443         }
444
445         ///////////////////////////////////////////////////////////////////
446         //
447         ///////////////////////////////////////////////////////////////////
448       private:
449         void checkSerial() const
450         {
451           if ( _watcher.remember( serial() ) )
452             invalidate();
453           satpool().prepare(); // always ajust dependencies.
454         }
455
456         void invalidate() const
457         {
458           _storeDirty = true;
459           _id2itemDirty = true;
460           _id2item.clear();
461           _poolProxy.reset();
462           _establishedStates.reset();
463         }
464
465       private:
466         /** Watch sat pools serial number. */
467         SerialNumberWatcher                   _watcher;
468         /** Watch sat pools Serial number of IDs - changes whenever resusePoolIDs==true - ResPool must also invalidate it's PoolItems! */
469         SerialNumberWatcher                   _watcherIDs;
470         mutable ContainerT                    _store;
471         mutable DefaultIntegral<bool,true>    _storeDirty;
472         mutable Id2ItemT                      _id2item;
473         mutable DefaultIntegral<bool,true>    _id2itemDirty;
474
475       private:
476         mutable shared_ptr<ResPoolProxy>      _poolProxy;
477         mutable shared_ptr<EstablishedStatesImpl> _establishedStates;
478
479       private:
480         /** Set of queries that define hardlocks. */
481         HardLockQueries                       _hardLockQueries;
482     };
483     ///////////////////////////////////////////////////////////////////
484
485     /////////////////////////////////////////////////////////////////
486   } // namespace pool
487   ///////////////////////////////////////////////////////////////////
488   /////////////////////////////////////////////////////////////////
489 } // namespace zypp
490 ///////////////////////////////////////////////////////////////////
491 #endif // ZYPP_POOL_POOLIMPL_H