d28f005552
Fix limit for databases other than sqlite go mod tidy && go mod vendor Remove unneeded break statements Make everything work with the new xorm version Fix xorm logging Fix lint Fix redis init Fix using id field Fix database init for testing Change default database log level Add xorm logger Use const for postgres go mod tidy Merge branch 'master' into update/xorm # Conflicts: # go.mod # go.sum # vendor/modules.txt go mod vendor Fix loading fixtures for postgres Go mod vendor1 Update xorm to version 1 Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/323
210 lines
5.3 KiB
Go
210 lines
5.3 KiB
Go
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
|
|
// All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package leveldb
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
|
"github.com/syndtr/goleveldb/leveldb/journal"
|
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
|
"github.com/syndtr/goleveldb/leveldb/storage"
|
|
)
|
|
|
|
// ErrManifestCorrupted records manifest corruption. This error will be
|
|
// wrapped with errors.ErrCorrupted.
|
|
type ErrManifestCorrupted struct {
|
|
Field string
|
|
Reason string
|
|
}
|
|
|
|
func (e *ErrManifestCorrupted) Error() string {
|
|
return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason)
|
|
}
|
|
|
|
func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error {
|
|
return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason})
|
|
}
|
|
|
|
// session represent a persistent database session.
|
|
type session struct {
|
|
// Need 64-bit alignment.
|
|
stNextFileNum int64 // current unused file number
|
|
stJournalNum int64 // current journal file number; need external synchronization
|
|
stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb
|
|
stTempFileNum int64
|
|
stSeqNum uint64 // last mem compacted seq; need external synchronization
|
|
|
|
stor *iStorage
|
|
storLock storage.Locker
|
|
o *cachedOptions
|
|
icmp *iComparer
|
|
tops *tOps
|
|
fileRef map[int64]int
|
|
|
|
manifest *journal.Writer
|
|
manifestWriter storage.Writer
|
|
manifestFd storage.FileDesc
|
|
|
|
stCompPtrs []internalKey // compaction pointers; need external synchronization
|
|
stVersion *version // current version
|
|
vmu sync.Mutex
|
|
}
|
|
|
|
// Creates new initialized session instance.
|
|
func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
|
|
if stor == nil {
|
|
return nil, os.ErrInvalid
|
|
}
|
|
storLock, err := stor.Lock()
|
|
if err != nil {
|
|
return
|
|
}
|
|
s = &session{
|
|
stor: newIStorage(stor),
|
|
storLock: storLock,
|
|
fileRef: make(map[int64]int),
|
|
}
|
|
s.setOptions(o)
|
|
s.tops = newTableOps(s)
|
|
s.setVersion(newVersion(s))
|
|
s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
|
|
return
|
|
}
|
|
|
|
// Close session.
|
|
func (s *session) close() {
|
|
s.tops.close()
|
|
if s.manifest != nil {
|
|
s.manifest.Close()
|
|
}
|
|
if s.manifestWriter != nil {
|
|
s.manifestWriter.Close()
|
|
}
|
|
s.manifest = nil
|
|
s.manifestWriter = nil
|
|
s.setVersion(&version{s: s, closing: true})
|
|
}
|
|
|
|
// Release session lock.
|
|
func (s *session) release() {
|
|
s.storLock.Unlock()
|
|
}
|
|
|
|
// Create a new database session; need external synchronization.
|
|
func (s *session) create() error {
|
|
// create manifest
|
|
return s.newManifest(nil, nil)
|
|
}
|
|
|
|
// Recover a database session; need external synchronization.
|
|
func (s *session) recover() (err error) {
|
|
defer func() {
|
|
if os.IsNotExist(err) {
|
|
// Don't return os.ErrNotExist if the underlying storage contains
|
|
// other files that belong to LevelDB. So the DB won't get trashed.
|
|
if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 {
|
|
err = &errors.ErrCorrupted{Fd: storage.FileDesc{Type: storage.TypeManifest}, Err: &errors.ErrMissingFiles{}}
|
|
}
|
|
}
|
|
}()
|
|
|
|
fd, err := s.stor.GetMeta()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
reader, err := s.stor.Open(fd)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer reader.Close()
|
|
|
|
var (
|
|
// Options.
|
|
strict = s.o.GetStrict(opt.StrictManifest)
|
|
|
|
jr = journal.NewReader(reader, dropper{s, fd}, strict, true)
|
|
rec = &sessionRecord{}
|
|
staging = s.stVersion.newStaging()
|
|
)
|
|
for {
|
|
var r io.Reader
|
|
r, err = jr.Next()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
err = nil
|
|
break
|
|
}
|
|
return errors.SetFd(err, fd)
|
|
}
|
|
|
|
err = rec.decode(r)
|
|
if err == nil {
|
|
// save compact pointers
|
|
for _, r := range rec.compPtrs {
|
|
s.setCompPtr(r.level, internalKey(r.ikey))
|
|
}
|
|
// commit record to version staging
|
|
staging.commit(rec)
|
|
} else {
|
|
err = errors.SetFd(err, fd)
|
|
if strict || !errors.IsCorrupted(err) {
|
|
return
|
|
}
|
|
s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd))
|
|
}
|
|
rec.resetCompPtrs()
|
|
rec.resetAddedTables()
|
|
rec.resetDeletedTables()
|
|
}
|
|
|
|
switch {
|
|
case !rec.has(recComparer):
|
|
return newErrManifestCorrupted(fd, "comparer", "missing")
|
|
case rec.comparer != s.icmp.uName():
|
|
return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer))
|
|
case !rec.has(recNextFileNum):
|
|
return newErrManifestCorrupted(fd, "next-file-num", "missing")
|
|
case !rec.has(recJournalNum):
|
|
return newErrManifestCorrupted(fd, "journal-file-num", "missing")
|
|
case !rec.has(recSeqNum):
|
|
return newErrManifestCorrupted(fd, "seq-num", "missing")
|
|
}
|
|
|
|
s.manifestFd = fd
|
|
s.setVersion(staging.finish())
|
|
s.setNextFileNum(rec.nextFileNum)
|
|
s.recordCommited(rec)
|
|
return nil
|
|
}
|
|
|
|
// Commit session; need external synchronization.
|
|
func (s *session) commit(r *sessionRecord) (err error) {
|
|
v := s.version()
|
|
defer v.release()
|
|
|
|
// spawn new version based on current version
|
|
nv := v.spawn(r)
|
|
|
|
if s.manifest == nil {
|
|
// manifest journal writer not yet created, create one
|
|
err = s.newManifest(r, nv)
|
|
} else {
|
|
err = s.flushManifest(r)
|
|
}
|
|
|
|
// finally, apply new version if no error rise
|
|
if err == nil {
|
|
s.setVersion(nv)
|
|
}
|
|
|
|
return
|
|
}
|