1package deb 2 3import ( 4 "bytes" 5 "fmt" 6 "path/filepath" 7 8 "github.com/aptly-dev/aptly/aptly" 9 "github.com/aptly-dev/aptly/database" 10 "github.com/ugorji/go/codec" 11) 12 13// PackageCollection does management of packages in DB 14type PackageCollection struct { 15 db database.Storage 16 codecHandle *codec.MsgpackHandle 17} 18 19// Verify interface 20var ( 21 _ PackageCatalog = &PackageCollection{} 22) 23 24// NewPackageCollection creates new PackageCollection and binds it to database 25func NewPackageCollection(db database.Storage) *PackageCollection { 26 return &PackageCollection{ 27 db: db, 28 codecHandle: &codec.MsgpackHandle{}, 29 } 30} 31 32// oldPackage is Package struct for aptly < 0.4 with all fields in one struct 33// It is used to decode old aptly DBs 34type oldPackage struct { 35 IsSource bool 36 Name string 37 Version string 38 Architecture string 39 SourceArchitecture string 40 Source string 41 Provides []string 42 Depends []string 43 BuildDepends []string 44 BuildDependsInDep []string 45 PreDepends []string 46 Suggests []string 47 Recommends []string 48 Files []PackageFile 49 Extra Stanza 50} 51 52// ByKey find package in DB by its key 53func (collection *PackageCollection) ByKey(key []byte) (*Package, error) { 54 encoded, err := collection.db.Get(key) 55 if err != nil { 56 return nil, err 57 } 58 59 p := &Package{} 60 61 if len(encoded) > 2 && (encoded[0] != 0xc1 || encoded[1] != 0x1) { 62 oldp := &oldPackage{} 63 64 decoder := codec.NewDecoderBytes(encoded, collection.codecHandle) 65 err = decoder.Decode(oldp) 66 if err != nil { 67 return nil, err 68 } 69 70 p.Name = oldp.Name 71 p.Version = oldp.Version 72 p.Architecture = oldp.Architecture 73 p.IsSource = oldp.IsSource 74 p.SourceArchitecture = oldp.SourceArchitecture 75 p.Source = oldp.Source 76 p.Provides = oldp.Provides 77 78 p.deps = &PackageDependencies{ 79 Depends: oldp.Depends, 80 BuildDepends: oldp.BuildDepends, 81 BuildDependsInDep: oldp.BuildDependsInDep, 82 PreDepends: oldp.PreDepends, 83 Suggests: oldp.Suggests, 84 Recommends: oldp.Recommends, 85 } 86 87 p.extra = &oldp.Extra 88 for i := range oldp.Files { 89 oldp.Files[i].Filename = filepath.Base(oldp.Files[i].Filename) 90 } 91 p.UpdateFiles(PackageFiles(oldp.Files)) 92 93 // Save in new format 94 err = collection.Update(p) 95 if err != nil { 96 return nil, err 97 } 98 } else { 99 decoder := codec.NewDecoderBytes(encoded[2:], collection.codecHandle) 100 err = decoder.Decode(p) 101 if err != nil { 102 return nil, err 103 } 104 } 105 106 p.collection = collection 107 108 return p, nil 109} 110 111// loadExtra loads Stanza with all the xtra information about the package 112func (collection *PackageCollection) loadExtra(p *Package) *Stanza { 113 encoded, err := collection.db.Get(p.Key("xE")) 114 if err != nil { 115 panic("unable to load extra") 116 } 117 118 stanza := &Stanza{} 119 120 decoder := codec.NewDecoderBytes(encoded, collection.codecHandle) 121 err = decoder.Decode(stanza) 122 if err != nil { 123 panic("unable to decode extra") 124 } 125 126 return stanza 127} 128 129// loadDependencies loads dependencies for the package 130func (collection *PackageCollection) loadDependencies(p *Package) *PackageDependencies { 131 encoded, err := collection.db.Get(p.Key("xD")) 132 if err != nil { 133 panic(fmt.Sprintf("unable to load deps: %s, %s", p, err)) 134 } 135 136 deps := &PackageDependencies{} 137 138 decoder := codec.NewDecoderBytes(encoded, collection.codecHandle) 139 err = decoder.Decode(deps) 140 if err != nil { 141 panic("unable to decode deps") 142 } 143 144 return deps 145} 146 147// loadFiles loads additional PackageFiles record 148func (collection *PackageCollection) loadFiles(p *Package) *PackageFiles { 149 encoded, err := collection.db.Get(p.Key("xF")) 150 if err != nil { 151 panic("unable to load files") 152 } 153 154 files := &PackageFiles{} 155 156 decoder := codec.NewDecoderBytes(encoded, collection.codecHandle) 157 err = decoder.Decode(files) 158 if err != nil { 159 panic("unable to decode files") 160 } 161 162 return files 163} 164 165// loadContents loads or calculates and saves package contents 166func (collection *PackageCollection) loadContents(p *Package, packagePool aptly.PackagePool, progress aptly.Progress) []string { 167 encoded, err := collection.db.Get(p.Key("xC")) 168 if err == nil { 169 contents := []string{} 170 171 decoder := codec.NewDecoderBytes(encoded, collection.codecHandle) 172 err = decoder.Decode(&contents) 173 if err != nil { 174 panic("unable to decode contents") 175 } 176 177 return contents 178 } 179 180 if err != database.ErrNotFound { 181 panic("unable to load contents") 182 } 183 184 contents, err := p.CalculateContents(packagePool, progress) 185 if err != nil { 186 // failed to acquire contents, don't persist it 187 return contents 188 } 189 190 var buf bytes.Buffer 191 err = codec.NewEncoder(&buf, collection.codecHandle).Encode(contents) 192 if err != nil { 193 panic("unable to encode contents") 194 } 195 196 err = collection.db.Put(p.Key("xC"), buf.Bytes()) 197 if err != nil { 198 panic("unable to save contents") 199 } 200 201 return contents 202} 203 204// Update adds or updates information about package in DB checking for conficts first 205func (collection *PackageCollection) Update(p *Package) error { 206 var encodeBuffer bytes.Buffer 207 208 encoder := codec.NewEncoder(&encodeBuffer, collection.codecHandle) 209 210 encodeBuffer.Reset() 211 encodeBuffer.WriteByte(0xc1) 212 encodeBuffer.WriteByte(0x1) 213 err := encoder.Encode(p) 214 if err != nil { 215 return err 216 } 217 218 err = collection.db.Put(p.Key(""), encodeBuffer.Bytes()) 219 if err != nil { 220 return err 221 } 222 223 // Encode offloaded fields one by one 224 if p.files != nil { 225 encodeBuffer.Reset() 226 err = encoder.Encode(*p.files) 227 if err != nil { 228 return err 229 } 230 231 err = collection.db.Put(p.Key("xF"), encodeBuffer.Bytes()) 232 if err != nil { 233 return err 234 } 235 } 236 237 if p.deps != nil { 238 encodeBuffer.Reset() 239 err = encoder.Encode(*p.deps) 240 if err != nil { 241 return err 242 } 243 244 err = collection.db.Put(p.Key("xD"), encodeBuffer.Bytes()) 245 if err != nil { 246 return err 247 } 248 249 p.deps = nil 250 } 251 252 if p.extra != nil { 253 encodeBuffer.Reset() 254 err = encoder.Encode(*p.extra) 255 if err != nil { 256 return err 257 } 258 259 err = collection.db.Put(p.Key("xE"), encodeBuffer.Bytes()) 260 if err != nil { 261 return err 262 } 263 264 p.extra = nil 265 } 266 267 p.collection = collection 268 269 return nil 270} 271 272// AllPackageRefs returns list of all packages as PackageRefList 273func (collection *PackageCollection) AllPackageRefs() *PackageRefList { 274 return &PackageRefList{Refs: collection.db.KeysByPrefix([]byte("P"))} 275} 276 277// DeleteByKey deletes package in DB by key 278func (collection *PackageCollection) DeleteByKey(key []byte) error { 279 for _, key := range [][]byte{key, append([]byte("xF"), key...), append([]byte("xD"), key...), append([]byte("xE"), key...)} { 280 err := collection.db.Delete(key) 281 if err != nil { 282 return err 283 } 284 } 285 return nil 286} 287 288// Scan does full scan on all the packages 289func (collection *PackageCollection) Scan(q PackageQuery) (result *PackageList) { 290 result = NewPackageListWithDuplicates(true, 0) 291 292 for _, key := range collection.db.KeysByPrefix([]byte("P")) { 293 pkg, err := collection.ByKey(key) 294 if err != nil { 295 panic(fmt.Sprintf("unable to load package: %s", err)) 296 } 297 298 if q.Matches(pkg) { 299 result.Add(pkg) 300 } 301 } 302 303 return 304} 305 306// Search is not implemented 307func (collection *PackageCollection) Search(dep Dependency, allMatches bool) (searchResults []*Package) { 308 panic("Not implemented") 309} 310 311// SearchSupported returns false 312func (collection *PackageCollection) SearchSupported() bool { 313 return false 314} 315 316// SearchByKey finds package by exact key 317func (collection *PackageCollection) SearchByKey(arch, name, version string) (result *PackageList) { 318 result = NewPackageListWithDuplicates(true, 0) 319 320 for _, key := range collection.db.KeysByPrefix([]byte(fmt.Sprintf("P%s %s %s", arch, name, version))) { 321 pkg, err := collection.ByKey(key) 322 if err != nil { 323 panic(fmt.Sprintf("unable to load package: %s", err)) 324 } 325 326 if pkg.Architecture == arch && pkg.Name == name && pkg.Version == version { 327 result.Add(pkg) 328 } 329 } 330 331 return 332} 333