- start transaction ordering
[platform/upstream/libsolv.git] / src / transaction.c
1 /*
2  * Copyright (c) 2007-2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * transaction.c
10  *
11  * Transaction handling
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <assert.h>
19
20 #include "transaction.h"
21 #include "solver.h"
22 #include "bitmap.h"
23 #include "pool.h"
24 #include "evr.h"
25 #include "util.h"
26
27 static int
28 obsq_sortcmp(const void *ap, const void *bp, void *dp)
29 {
30   Id a, b, oa, ob;
31   Pool *pool = dp;
32   Solvable *s, *oas, *obs;
33   int r;
34
35   a = ((Id *)ap)[0];
36   oa = ((Id *)ap)[1];
37   b = ((Id *)bp)[0];
38   ob = ((Id *)bp)[1];
39   if (a != b)
40     return a - b;
41   if (oa == ob)
42     return 0;
43   s = pool->solvables + a;
44   oas = pool->solvables + oa;
45   obs = pool->solvables + ob;
46   if (oas->name != obs->name)
47     {
48       if (oas->name == s->name)
49         return -1;
50       if (obs->name == s->name)
51         return 1;
52       return strcmp(id2str(pool, oas->name), id2str(pool, obs->name));
53     }
54   r = evrcmp(pool, oas->evr, obs->evr, EVRCMP_COMPARE);
55   if (r)
56     return -r;  /* highest version first */
57   return oa - ob;
58 }
59
60 void
61 solver_transaction_all_pkgs(Solver *solv, Id p, Queue *pkgs)
62 {
63   Pool *pool = solv->pool;
64   Solvable *s = pool->solvables + p;
65   Queue *ti = &solv->transaction_info;
66   Id q;
67   int i;
68
69   queue_empty(pkgs);
70   if (p <= 0 || !s->repo)
71     return;
72   if (s->repo == solv->installed)
73     {
74       q = solv->transaction_installed[p - solv->installed->start];
75       if (!q)
76         return;
77       if (q > 0)
78         {
79           queue_push(pkgs, q);
80           return;
81         }
82       /* find which packages obsolete us */
83       for (i = 0; i < ti->count; i += 2)
84         if (ti->elements[i + 1] == p)
85           {
86             queue_push(pkgs, p);
87             queue_push(pkgs, ti->elements[i]);
88           }
89       /* sort obsoleters */
90       if (pkgs->count > 2)
91         sat_sort(pkgs->elements, pkgs->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool);
92       for (i = 0; i < pkgs->count; i += 2)
93         pkgs->elements[i / 2] = pkgs->elements[i + 1];
94       pkgs->count /= 2;
95     }
96   else
97     {
98       /* find the packages we obsolete */
99       for (i = 0; i < ti->count; i += 2)
100         {
101           if (ti->elements[i] == p)
102             queue_push(pkgs, ti->elements[i + 1]);
103           else if (pkgs->count)
104             break;
105         }
106     }
107 }
108
109 Id
110 solver_transaction_pkg(Solver *solv, Id p)
111 {
112   Pool *pool = solv->pool;
113   Solvable *s = pool->solvables + p;
114   Queue ti;
115   Id tibuf[5];
116
117   if (p <= 0 || !s->repo)
118     return 0;
119   if (s->repo == solv->installed)
120     {
121       p = solv->transaction_installed[p - solv->installed->start];
122       return p < 0 ? -p : p;
123     }
124   queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
125   solver_transaction_all_pkgs(solv, p, &ti);
126   p = ti.count ? ti.elements[0] : 0;
127   queue_free(&ti);
128   return p;
129 }
130
131 /* type filtering, needed if either not all packages are shown
132  * or replaces are not shown, as otherwise parts of the
133  * transaction might not be shown to the user */
134
135 Id
136 solver_transaction_filter(Solver *solv, Id type, Id p, int flags)
137 {
138   Pool *pool = solv->pool;
139   Solvable *s = pool->solvables + p;
140   Queue oq, rq;
141   Id q;
142   int i, j, ref = 0;
143
144   if (type == SOLVER_TRANSACTION_ERASE || type == SOLVER_TRANSACTION_INSTALL || type == SOLVER_TRANSACTION_MULTIINSTALL)
145     return type;
146
147   if (s->repo == pool->installed && (flags & SOLVER_TRANSACTION_SHOW_ACTIVE) == 0)
148     {
149       /* erase element */
150       if ((flags & SOLVER_TRANSACTION_SHOW_REPLACES) == 0 && type == SOLVER_TRANSACTION_REPLACED)
151         type = SOLVER_TRANSACTION_ERASE;
152       return type;
153     }
154   if (s->repo != pool->installed && (flags & SOLVER_TRANSACTION_SHOW_ACTIVE) != 0)
155     {
156       if ((flags & SOLVER_TRANSACTION_SHOW_REPLACES) == 0 && type == SOLVER_TRANSACTION_REPLACE)
157         type = SOLVER_TRANSACTION_INSTALL;
158       return type;
159     }
160
161   /* most of the time there's only one reference, so check it first */
162   q = solver_transaction_pkg(solv, p);
163   if ((flags & SOLVER_TRANSACTION_SHOW_REPLACES) == 0)
164     {
165       Solvable *sq = pool->solvables + q;
166       if (sq->name != s->name)
167         {
168           if (s->repo == pool->installed)
169             return SOLVER_TRANSACTION_ERASE;
170           else if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
171             return SOLVER_TRANSACTION_MULTIINSTALL;
172           else
173             return SOLVER_TRANSACTION_INSTALL;
174         }
175     }
176   if (solver_transaction_pkg(solv, q) == p)
177     return type;
178
179   /* too bad, a miss. check em all */
180   queue_init(&oq);
181   queue_init(&rq);
182   solver_transaction_all_pkgs(solv, p, &oq);
183   for (i = 0; i < oq.count; i++)
184     {
185       q = oq.elements[i];
186       if ((flags & SOLVER_TRANSACTION_SHOW_REPLACES) == 0)
187         {
188           Solvable *sq = pool->solvables + q;
189           if (sq->name != s->name)
190             continue;
191         }
192       /* check if we are referenced? */
193       if ((flags & SOLVER_TRANSACTION_SHOW_ALL) != 0)
194         {
195           solver_transaction_all_pkgs(solv, q, &rq);
196           for (j = 0; j < rq.count; j++)
197             if (rq.elements[j] == p)
198               {
199                 ref = 1;
200                 break;
201               }
202           if (ref)
203             break;
204         }
205       else if (solver_transaction_pkg(solv, q) == p)
206         {
207           ref = 1;
208           break;
209         }
210     }
211   queue_free(&oq);
212   queue_free(&rq);
213
214   if (!ref)
215     {
216       if (s->repo == pool->installed)
217         type = SOLVER_TRANSACTION_ERASE;
218       else if (type == SOLVER_TRANSACTION_MULTIREINSTALL)
219         type = SOLVER_TRANSACTION_MULTIINSTALL;
220       else
221         type = SOLVER_TRANSACTION_INSTALL;
222     }
223   return type;
224 }
225
226 static void
227 create_transaction_info(Solver *solv)
228 {
229   Pool *pool = solv->pool;
230   Queue *ti = &solv->transaction_info;
231   Repo *installed = solv->installed;
232   int i, j, noobs;
233   Id p, p2, pp2;
234   Solvable *s, *s2;
235
236   queue_empty(ti);
237   if (!installed)
238     return;     /* no info needed */
239   for (i = 0; i < solv->decisionq.count; i++)
240     {
241       p = solv->decisionq.elements[i];
242       if (p <= 0 || p == SYSTEMSOLVABLE)
243         continue;
244       s = pool->solvables + p;
245       if (s->repo == installed)
246         continue;
247       noobs = solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p);
248       FOR_PROVIDES(p2, pp2, s->name)
249         {
250           if (solv->decisionmap[p2] > 0)
251             continue;
252           s2 = pool->solvables + p2;
253           if (s2->repo != installed)
254             continue;
255           if (noobs && (s->name != s2->name || s->evr != s2->evr || s->arch != s2->arch))
256             continue;
257           if (!solv->implicitobsoleteusesprovides && s->name != s2->name)
258             continue;
259           queue_push(ti, p);
260           queue_push(ti, p2);
261         }
262       if (s->obsoletes && !noobs)
263         {
264           Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
265           while ((obs = *obsp++) != 0)
266             {
267               FOR_PROVIDES(p2, pp2, obs)
268                 {
269                   s2 = pool->solvables + p2;
270                   if (s2->repo != installed)
271                     continue;
272                   if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
273                     continue;
274                   queue_push(ti, p);
275                   queue_push(ti, p2);
276                 }
277             }
278         }
279     }
280   sat_sort(ti->elements, ti->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool);
281   /* now unify */
282   for (i = j = 0; i < ti->count; i += 2)
283     {
284       if (j && ti->elements[i] == ti->elements[j - 2] && ti->elements[i + 1] == ti->elements[j - 1])
285         continue;
286       ti->elements[j++] = ti->elements[i];
287       ti->elements[j++] = ti->elements[i + 1];
288     }
289   ti->count = j;
290
291   /* create transaction_installed helper */
292   solv->transaction_installed = sat_calloc(installed->end - installed->start, sizeof(Id));
293   for (i = 0; i < ti->count; i += 2)
294     {
295       j = ti->elements[i + 1] - installed->start;
296       if (!solv->transaction_installed[j])
297         solv->transaction_installed[j] = ti->elements[i];
298       else
299         {
300           /* more than one package obsoletes us. compare */
301           Id q[4];
302           if (solv->transaction_installed[j] > 0)
303             solv->transaction_installed[j] = -solv->transaction_installed[j];
304           q[0] = q[2] = ti->elements[i + 1];
305           q[1] = ti->elements[i];
306           q[3] = -solv->transaction_installed[j];
307           if (obsq_sortcmp(q, q + 2, pool) < 0)
308             solv->transaction_installed[j] = -ti->elements[i];
309         }
310     }
311 }
312
313
314 void
315 solver_create_transaction(Solver *solv)
316 {
317   Pool *pool = solv->pool;
318   Repo *installed = solv->installed;
319   int i, r, noobs;
320   Id p, p2;
321   Solvable *s, *s2;
322
323   queue_empty(&solv->transaction);
324   create_transaction_info(solv);
325
326   if (installed)
327     {
328       FOR_REPO_SOLVABLES(installed, p, s)
329         {
330           if (solv->decisionmap[p] > 0)
331             continue;
332           p2 = solver_transaction_pkg(solv, p);
333           if (!p2)
334             queue_push(&solv->transaction, SOLVER_TRANSACTION_ERASE);
335           else
336             {
337               s2 = pool->solvables + p2;
338               if (s->name == s2->name)
339                 {
340                   if (s->evr == s2->evr && solvable_identical(s, s2))
341                     queue_push(&solv->transaction, SOLVER_TRANSACTION_REINSTALLED);
342                   else
343                     {
344                       r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
345                       if (r < 0)
346                         queue_push(&solv->transaction, SOLVER_TRANSACTION_UPGRADED);
347                       else if (r > 0)
348                         queue_push(&solv->transaction, SOLVER_TRANSACTION_DOWNGRADED);
349                       else
350                         queue_push(&solv->transaction, SOLVER_TRANSACTION_CHANGED);
351                     }
352                 }
353               else
354                 queue_push(&solv->transaction, SOLVER_TRANSACTION_REPLACED);
355             }
356           queue_push(&solv->transaction, p);
357         }
358     }
359   for (i = 0; i < solv->decisionq.count; i++)
360     {
361       p = solv->decisionq.elements[i];
362       if (p < 0 || p == SYSTEMSOLVABLE)
363         continue;
364       s = pool->solvables + p;
365       if (solv->installed && s->repo == solv->installed)
366         continue;
367       noobs = solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p);
368       p2 = solver_transaction_pkg(solv, p);
369       if (noobs)
370         queue_push(&solv->transaction, p2 ? SOLVER_TRANSACTION_MULTIREINSTALL : SOLVER_TRANSACTION_MULTIINSTALL);
371       else if (!p2)
372         queue_push(&solv->transaction, SOLVER_TRANSACTION_INSTALL);
373       else
374         {
375           s2 = pool->solvables + p2;
376           if (s->name == s2->name)
377             {
378               if (s->evr == s2->evr && solvable_identical(s, s2))
379                 queue_push(&solv->transaction, SOLVER_TRANSACTION_REINSTALL);
380               else
381                 {
382                   r = evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
383                   if (r > 0)
384                     queue_push(&solv->transaction, SOLVER_TRANSACTION_UPGRADE);
385                   else if (r < 0)
386                     queue_push(&solv->transaction, SOLVER_TRANSACTION_DOWNGRADE);
387                   else
388                     queue_push(&solv->transaction, SOLVER_TRANSACTION_CHANGE);
389                 }
390             }
391           else
392             queue_push(&solv->transaction, SOLVER_TRANSACTION_REPLACE);
393         }
394       queue_push(&solv->transaction, p);
395     }
396 }
397
398 #define TYPE_REQ    (1<<0)
399 #define TYPE_PREREQ (1<<1)
400 #define TYPE_CON    (1<<2)
401 #define TYPE_ERASE  (1<<3)
402
403 #define EDGEDATA_BLOCK 127
404
405 struct transel {
406   Id p;
407   Id edges;
408 };
409
410 struct orderdata {
411   Solver *solv;
412   struct transel *tes;
413   int ntes;
414   Id *edgedata;
415   int nedgedata;
416 };
417
418 static void
419 addedge(struct orderdata *od, Id from, Id to, int type)
420 {
421   Solver *solv = od->solv;
422   Pool *pool = solv->pool;
423   Solvable *s;
424   struct transel *te;
425   int i;
426
427   // printf("addedge %d %d type %d\n", from, to, type);
428   s = pool->solvables + from;
429   if (s->repo == solv->installed && solv->transaction_installed[from - solv->installed->start])
430     {
431       /* passive, map to active */
432       if (solv->transaction_installed[from - solv->installed->start] > 0)
433         from = solv->transaction_installed[from - solv->installed->start];
434       else
435         {
436           Queue ti;
437           Id tibuf[5];
438           queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
439           solver_transaction_all_pkgs(solv, from, &ti);
440           for (i = 0; i < ti.count; i++)
441             addedge(od, ti.elements[i], to, type);
442           queue_free(&ti);
443         }
444       return;
445     }
446   s = pool->solvables + to;
447   if (s->repo == solv->installed && solv->transaction_installed[to - solv->installed->start])
448     {
449       /* passive, map to active */
450       if (solv->transaction_installed[to - solv->installed->start] > 0)
451         to = solv->transaction_installed[to - solv->installed->start];
452       else
453         {
454           Queue ti;
455           Id tibuf[5];
456           queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
457           solver_transaction_all_pkgs(solv, to, &ti);
458           for (i = 0; i < ti.count; i++)
459             addedge(od, from, ti.elements[i], type);
460           queue_free(&ti);
461           return;
462         }
463     }
464
465   /* map target to te num */
466   for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
467     if (te->p == to)
468       break;
469   if (i == od->ntes)
470     return;
471   to = i;
472
473   for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
474     if (te->p == from)
475       break;
476   if (i == od->ntes)
477     return;
478
479   if (i == to)
480     return;     /* no edges to ourselfes */
481
482   // printf("edge %d -> %d type %x\n", i, to, type);
483
484   for (i = te->edges; od->edgedata[i]; i += 2)
485     if (od->edgedata[i] == to)
486       break;
487   if (od->edgedata[i])
488     {
489       od->edgedata[i + 1] |= type;
490       return;
491     }
492   if (i + 1 == od->nedgedata)
493     {
494       // printf("tail add %d\n", i - te->edges);
495       if (!i)
496         te->edges = ++i;
497       od->edgedata = sat_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
498     }
499   else
500     {
501       // printf("extend %d\n", i - te->edges);
502       od->edgedata = sat_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
503       if (i > te->edges)
504         memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
505       i = od->nedgedata + (i - te->edges);
506       te->edges = od->nedgedata;
507     }
508   od->edgedata[i] = to;
509   od->edgedata[i + 1] = type;
510   od->edgedata[i + 2] = 0;
511   od->nedgedata = i + 3;
512 }
513
514 void
515 solver_order_transaction(Solver *solv)
516 {
517   Pool *pool = solv->pool;
518   Queue *tr = &solv->transaction;
519   Repo *installed = solv->installed;
520   Id type, p;
521   Solvable *s, *s2;
522   Id req, *reqp, con, *conp;
523   Id p2, pp2;
524   int i, j, pre, numte, numedge;
525   struct orderdata od;
526   struct transel *te;
527
528   /* create a transaction element for every active component */
529   numte = 0;
530   for (i = 0; i < tr->count; i += 2)
531     {
532       p = tr->elements[i + 1];
533       s = pool->solvables + p;
534       if (s->repo != installed || !solv->transaction_installed[p - solv->installed->start])
535         numte++;
536     }
537   if (!numte)
538     return;     /* nothing to do... */
539
540
541   printf("numte = %d\n", numte);
542   numte++;      /* leave first one zero */
543   od.solv = solv;
544   od.ntes = numte;
545   od.tes = sat_calloc(numte, sizeof(*od.tes));
546   od.edgedata = sat_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
547   od.edgedata[0] = 0;
548   od.nedgedata = 1;
549
550   for (i = 0, te = od.tes + 1; i < tr->count; i += 2)
551     {
552       p = tr->elements[i + 1];
553       s = pool->solvables + p;
554       if (s->repo == installed && solv->transaction_installed[p - solv->installed->start])
555         continue;
556       te->p = p;
557       te++;
558     }
559
560   /* create dependency graph */
561   for (i = 0; i < tr->count; i += 2)
562     {
563       type = tr->elements[i];
564       p = tr->elements[i + 1];
565       s = pool->solvables + p;
566       if (s->requires)
567         {
568           reqp = s->repo->idarraydata + s->requires;
569           pre = TYPE_REQ;
570           while ((req = *reqp++) != 0)
571             {
572               int eraseonly = 0;
573               if (req == SOLVABLE_PREREQMARKER)
574                 {
575                   pre = TYPE_PREREQ;
576                   continue;
577                 }
578 #if 1
579               if (s->repo == installed && pre != TYPE_PREREQ)
580                 continue;
581 #endif
582               FOR_PROVIDES(p2, pp2, req)
583                 {
584                   if (p2 == p)
585                     continue;
586                   s2 = pool->solvables + p2;
587                   if (!s2->repo)
588                     continue;
589                   if (s2->repo == installed && solv->decisionmap[p2] > 0)
590                     continue;
591                   if (s2->repo != installed && solv->decisionmap[p2] < 0)
592                     continue;   /* not interesting */
593                   if (s->repo == installed)
594                     {
595                       /* we're uninstalling s */
596                       if (s2->repo == installed)
597                         {
598                           if (eraseonly == 0)
599                             eraseonly = 1;
600                         }
601                       if (s2->repo != installed)
602                         {
603                           /* update p2 before erasing p */
604 #if 1
605                           addedge(&od, p, p2, pre);
606 #endif
607                           eraseonly = -1;
608                         }
609                     }
610                   else
611                     {
612                       /* install p2 before installing p */
613                       if (s2->repo != installed)
614                         addedge(&od, p, p2, pre);
615                     }
616                 }
617               if (eraseonly == 1)
618                 {
619                   printf("eraseonlyedge for %s req %s\n", solvable2str(pool, s), dep2str(pool, req));
620                   /* need edges to uninstalled pkgs */
621 #if 1
622                   FOR_PROVIDES(p2, pp2, req)
623                     {
624                       if (p2 == p)
625                         continue;
626                       s2 = pool->solvables + p2;
627                       if (!s2->repo || s2->repo != installed)
628                         continue;
629                       if (solv->decisionmap[p2] > 0)
630                         continue;
631 #if 0
632                       addedge(&od, p2, p, pre);
633 #else
634                       addedge(&od, p2, p, TYPE_ERASE);
635 #endif
636                     }
637 #endif
638                 }
639             }
640         }
641       if (s->conflicts)
642         {
643           conp = s->repo->idarraydata + s->conflicts;
644           while ((con = *conp++) != 0)
645             {
646 #if 1
647               FOR_PROVIDES(p2, pp2, con)
648                 {
649                   if (p2 == p)
650                     continue;
651                   s2 = pool->solvables + p2;
652                   if (!s2->repo)
653                     continue;
654                   if (s->repo == installed)
655                     {
656                       if (s2->repo != installed && solv->decisionmap[p2] >= 0)
657                         {
658                           /* deinstall p before installing p2 */
659                           addedge(&od, p2, p, TYPE_CON);
660                         }
661                     }
662                   else
663                     {
664                       if (s2->repo == installed && solv->decisionmap[p2] < 0)
665                         {
666                           /* deinstall p2 before installing p */
667 #if 1
668                           addedge(&od, p, p2, TYPE_CON);
669 #endif
670                         }
671                     }
672
673                 }
674 #endif
675             }
676         }
677     }
678   numedge = 0;
679   for (i = 1, te = od.tes + i; i < numte; i++, te++)
680     {
681       printf("TE #%d, %d(%s)\n", i, te->p, solvid2str(pool, te->p));
682       for (j = te->edges; od.edgedata[j]; j += 2)
683         {
684           struct transel *te2 = od.tes + od.edgedata[j];
685           printf("  depends %x on TE %d, %d(%s)\n", od.edgedata[j + 1], od.edgedata[j], te2->p, solvid2str(pool, te2->p));
686           numedge++;
687         }
688     }
689   printf("TEs: %d, Edges: %d, Space: %d\n", numte - 1, numedge, od.nedgedata / 2);
690 }