nix-archive-1(type directoryentry(namesharenode(type directoryentry(namedocnode(type directoryentry(name&go-github-com-ipfs-go-ds-leveldb-0.5.0node(type directoryentry(nameLICENSEnode(typeregularcontents3The MIT License Copyright (c) 2016 Jeromy Johnson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ))))))))entry(namesrcnode(type directoryentry(name github.comnode(type directoryentry(nameipfsnode(type directoryentry(name go-ds-leveldbnode(type directoryentry(name.githubnode(type directoryentry(nameISSUE_TEMPLATEnode(type directoryentry(name config.ymlnode(typeregularcontents7blank_issues_enabled: false contact_links: - name: Getting Help on IPFS url: https://ipfs.io/help about: All information about how and where to get help on IPFS. - name: IPFS Official Forum url: https://discuss.ipfs.io about: Please post general questions, support requests, and discussions here. ))entry(nameopen_an_issue.mdnode(typeregularcontentsŠ--- name: Open an issue about: Only for actionable issues relevant to this repository. title: '' labels: need/triage assignees: '' --- ))))entry(name config.ymlnode(typeregularcontentsT # Configuration for welcome - https://github.com/behaviorbot/welcome # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome # Comment to be posted to on first time issues newIssueWelcomeComment: > Thank you for submitting your first issue to this repository! A maintainer will be here shortly to triage and review. In the meantime, please double-check that you have provided all the necessary information to make this process easy! Any information that can help save additional round trips is useful! We currently aim to give initial feedback within **two business days**. If this does not happen, feel free to leave a comment. Please keep an eye on how this issue will be labeled, as labels give an overview of priorities, assignments and additional actions requested by the maintainers: - "Priority" labels will show how urgent this is for the team. - "Status" labels will show if this is ready to be worked on, blocked, or in progress. - "Need" labels will indicate if additional input or analysis is required. Finally, remember to use https://discuss.ipfs.io if you just need general support. # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome # Comment to be posted to on PRs from first time contributors in your repository newPRWelcomeComment: > Thank you for submitting this PR! A maintainer will be here shortly to review it. We are super grateful, but we are also overloaded! Help us by making sure that: * The context for this PR is clear, with relevant discussion, decisions and stakeholders linked/mentioned. * Your contribution itself is clear (code comments, self-review for the rest) and in its best form. Follow the [code contribution guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines) if they apply. Getting other community members to do a review would be great help too on complex PRs (you can ask in the chats/forums). If you are unsure about something, just leave us a comment. Next steps: * A maintainer will triage and assign priority to this PR, commenting on any missing things and potentially assigning a reviewer for high priority items. * The PR gets reviews, discussed and approvals as needed. * The PR is merged by maintainers when it has been approved and comments addressed. We currently aim to provide initial feedback/triaging within **two business days**. Please keep an eye on any labelling actions, as these will indicate priorities and status of your contribution. We are very grateful for your contribution! # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge # Comment to be posted to on pull requests merged by a first time user # Currently disabled #firstPRMergeComment: "" ))entry(name workflowsnode(type directoryentry(name automerge.ymlnode(typeregularcontentsc# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. # Automatically merge pull requests opened by web3-bot, as soon as (and only if) all tests pass. # This reduces the friction associated with updating with our workflows. on: [ pull_request ] name: Automerge jobs: automerge-check: if: github.event.pull_request.user.login == 'web3-bot' runs-on: ubuntu-latest outputs: status: ${{ steps.should-automerge.outputs.status }} steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Check if we should automerge id: should-automerge run: | for commit in $(git rev-list --first-parent origin/${{ github.event.pull_request.base.ref }}..${{ github.event.pull_request.head.sha }}); do committer=$(git show --format=$'%ce' -s $commit) echo "Committer: $committer" if [[ "$committer" != "web3-bot@users.noreply.github.com" ]]; then echo "Commit $commit wasn't committed by web3-bot, but by $committer." echo "::set-output name=status::false" exit fi done echo "::set-output name=status::true" automerge: needs: automerge-check runs-on: ubuntu-latest # The check for the user is redundant here, as this job depends on the automerge-check job, # but it prevents this job from spinning up, just to be skipped shortly after. if: github.event.pull_request.user.login == 'web3-bot' && needs.automerge-check.outputs.status == 'true' steps: - name: Wait on tests uses: lewagon/wait-on-check-action@bafe56a6863672c681c3cf671f5e10b20abf2eaa # v0.2 with: ref: ${{ github.event.pull_request.head.sha }} repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 10 running-workflow-name: 'automerge' # the name of this job - name: Merge PR uses: pascalgn/automerge-action@741c311a47881be9625932b0a0de1b0937aab1ae # v0.13.1 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" MERGE_LABELS: "" MERGE_METHOD: "squash" MERGE_DELETE_BRANCH: true ))entry(name go-check.ymlnode(typeregularcontents– # File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. on: [push, pull_request] name: Go Checks jobs: unit: runs-on: ubuntu-latest name: All env: RUNGOGENERATE: false steps: - uses: actions/checkout@v2 with: submodules: recursive - uses: actions/setup-go@v2 with: go-version: "1.17.x" - name: Run repo-specific setup uses: ./.github/actions/go-check-setup if: hashFiles('./.github/actions/go-check-setup') != '' - name: Read config if: hashFiles('./.github/workflows/go-check-config.json') != '' run: | if jq -re .gogenerate ./.github/workflows/go-check-config.json; then echo "RUNGOGENERATE=true" >> $GITHUB_ENV fi - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@df71e5d0e0ed317ebf43e6e59cf919430fa4b8f2 # 2021.1.1 (v0.2.1) - name: Check that go.mod is tidy uses: protocol/multiple-go-modules@v1.2 with: run: | go mod tidy if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then echo "go.sum was added by go mod tidy" exit 1 fi git diff --exit-code -- go.sum go.mod - name: gofmt if: ${{ success() || failure() }} # run this step even if the previous one failed run: | out=$(gofmt -s -l .) if [[ -n "$out" ]]; then echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}' exit 1 fi - name: go vet if: ${{ success() || failure() }} # run this step even if the previous one failed uses: protocol/multiple-go-modules@v1.2 with: run: go vet ./... - name: staticcheck if: ${{ success() || failure() }} # run this step even if the previous one failed uses: protocol/multiple-go-modules@v1.2 with: run: | set -o pipefail staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g' - name: go generate uses: protocol/multiple-go-modules@v1.2 if: (success() || failure()) && env.RUNGOGENERATE == 'true' with: run: | git clean -fd # make sure there aren't untracked files / directories go generate ./... # check if go generate modified or added any files if ! $(git add . && git diff-index HEAD --exit-code --quiet); then echo "go generated caused changes to the repository:" git status --short exit 1 fi ))entry(name go-test.ymlnode(typeregularcontents6# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. on: [push, pull_request] name: Go Test jobs: unit: strategy: fail-fast: false matrix: os: [ "ubuntu", "windows", "macos" ] go: [ "1.16.x", "1.17.x" ] env: COVERAGES: "" runs-on: ${{ matrix.os }}-latest name: ${{ matrix.os}} (go ${{ matrix.go }}) steps: - uses: actions/checkout@v2 with: submodules: recursive - uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Go information run: | go version go env - name: Run repo-specific setup uses: ./.github/actions/go-test-setup if: hashFiles('./.github/actions/go-test-setup') != '' - name: Run tests uses: protocol/multiple-go-modules@v1.2 with: run: go test -v -coverprofile module-coverage.txt ./... - name: Run tests (32 bit) if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX. uses: protocol/multiple-go-modules@v1.2 env: GOARCH: 386 with: run: go test -v ./... - name: Run tests with race detector if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow uses: protocol/multiple-go-modules@v1.2 with: run: go test -v -race ./... - name: Collect coverage files shell: bash run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV - name: Upload coverage to Codecov uses: codecov/codecov-action@51d810878be5422784e86451c0e7c14e5860ec47 # v2.0.2 with: files: '${{ env.COVERAGES }}' env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} ))entry(namerelease-check.ymlnode(typeregularcontents!# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Release Checker on: pull_request: paths: [ 'version.json' ] branches: [ master ] jobs: release-check: uses: protocol/.github/.github/workflows/release-check.yml@master ))entry(name releaser.ymlnode(typeregularcontents# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Releaser on: push: paths: [ 'version.json' ] branches: [ master ] jobs: releaser: uses: protocol/.github/.github/workflows/releaser.yml@master ))entry(name tagpush.ymlnode(typeregularcontentsí# File managed by web3-bot. DO NOT EDIT. # See https://github.com/protocol/.github/ for details. name: Tag Push Checker on: push: tags: - v* jobs: releaser: uses: protocol/.github/.github/workflows/tagpush.yml@master ))))))entry(name .gitignorenode(typeregularcontents*.swp ))entry(name.gxnode(type directoryentry(name lastpubvernode(typeregularcontents61.3.0: QmbgYmpUkuCDnXi4hci3Jt797iVXbpuBKRTCqGz57h48Sk ))))entry(nameLICENSEnode(typeregularcontents3The MIT License Copyright (c) 2016 Jeromy Johnson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ))entry(name README.mdnode(typeregularcontents!# go-ds-leveldb [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![GoDoc](https://godoc.org/github.com/ipfs/go-ds-leveldb?status.svg)](https://godoc.org/github.com/ipfs/go-ds-leveldb) [![Build Status](https://travis-ci.org/ipfs/go-ds-leveldb.svg?branch=master)](https://travis-ci.org/ipfs/go-ds-leveldb) > A go-datastore implementation using LevelDB `go-ds-leveldb` implements the [go-datastore](https://github.com/ipfs/go-datastore) interface using a LevelDB backend. ## Lead Maintainer [Steven Allen](https://github.com/Stebalien) ## Table of Contents - [Install](#install) - [Usage](#usage) - [Contribute](#contribute) - [License](#license) ## Install This module can be installed like a regular go module: ``` go get github.com/ipfs/go-ds-leveldb ``` It uses [Gx](https://github.com/whyrusleeping/gx) to manage dependencies. You can use `make deps` to rewrite imports to the gx-specified versions. ## Usage ``` import "github.com/ipfs/go-ds-leveldb" ``` Check the [GoDoc documentation](https://godoc.org/github.com/ipfs/go-ds-leveldb) ## Contribute PRs accepted. Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. ## License MIT © Protocol Labs, Inc. ))entry(name datastore.gonode(typeregularcontentsµpackage leveldb import ( "context" "os" "path/filepath" "sync" ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/storage" "github.com/syndtr/goleveldb/leveldb/util" ) type Datastore struct { *accessor DB *leveldb.DB path string } var _ ds.Datastore = (*Datastore)(nil) var _ ds.TxnDatastore = (*Datastore)(nil) // Options is an alias of syndtr/goleveldb/opt.Options which might be extended // in the future. type Options opt.Options // NewDatastore returns a new datastore backed by leveldb // // for path == "", an in memory backend will be chosen func NewDatastore(path string, opts *Options) (*Datastore, error) { var nopts opt.Options if opts != nil { nopts = opt.Options(*opts) } var err error var db *leveldb.DB if path == "" { db, err = leveldb.Open(storage.NewMemStorage(), &nopts) } else { db, err = leveldb.OpenFile(path, &nopts) if errors.IsCorrupted(err) && !nopts.GetReadOnly() { db, err = leveldb.RecoverFile(path, &nopts) } } if err != nil { return nil, err } ds := Datastore{ accessor: &accessor{ldb: db, syncWrites: true, closeLk: new(sync.RWMutex)}, DB: db, path: path, } return &ds, nil } // An extraction of the common interface between LevelDB Transactions and the DB itself. // // It allows to plug in either inside the `accessor`. type levelDbOps interface { Put(key, value []byte, wo *opt.WriteOptions) error Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) Delete(key []byte, wo *opt.WriteOptions) error NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator } // Datastore operations using either the DB or a transaction as the backend. type accessor struct { ldb levelDbOps syncWrites bool closeLk *sync.RWMutex } func (a *accessor) Put(ctx context.Context, key ds.Key, value []byte) (err error) { a.closeLk.RLock() defer a.closeLk.RUnlock() return a.ldb.Put(key.Bytes(), value, &opt.WriteOptions{Sync: a.syncWrites}) } func (a *accessor) Sync(ctx context.Context, prefix ds.Key) error { return nil } func (a *accessor) Get(ctx context.Context, key ds.Key) (value []byte, err error) { a.closeLk.RLock() defer a.closeLk.RUnlock() val, err := a.ldb.Get(key.Bytes(), nil) if err != nil { if err == leveldb.ErrNotFound { return nil, ds.ErrNotFound } return nil, err } return val, nil } func (a *accessor) Has(ctx context.Context, key ds.Key) (exists bool, err error) { a.closeLk.RLock() defer a.closeLk.RUnlock() return a.ldb.Has(key.Bytes(), nil) } func (a *accessor) GetSize(ctx context.Context, key ds.Key) (size int, err error) { return ds.GetBackedSize(ctx, a, key) } func (a *accessor) Delete(ctx context.Context, key ds.Key) (err error) { a.closeLk.RLock() defer a.closeLk.RUnlock() return a.ldb.Delete(key.Bytes(), &opt.WriteOptions{Sync: a.syncWrites}) } func (a *accessor) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) { a.closeLk.RLock() defer a.closeLk.RUnlock() var rnge *util.Range // make a copy of the query for the fallback naive query implementation. // don't modify the original so res.Query() returns the correct results. qNaive := q prefix := ds.NewKey(q.Prefix).String() if prefix != "/" { rnge = util.BytesPrefix([]byte(prefix + "/")) qNaive.Prefix = "" } i := a.ldb.NewIterator(rnge, nil) next := i.Next if len(q.Orders) > 0 { switch q.Orders[0].(type) { case dsq.OrderByKey, *dsq.OrderByKey: qNaive.Orders = nil case dsq.OrderByKeyDescending, *dsq.OrderByKeyDescending: next = func() bool { next = i.Prev return i.Last() } qNaive.Orders = nil default: } } r := dsq.ResultsFromIterator(q, dsq.Iterator{ Next: func() (dsq.Result, bool) { a.closeLk.RLock() defer a.closeLk.RUnlock() if !next() { return dsq.Result{}, false } k := string(i.Key()) e := dsq.Entry{Key: k, Size: len(i.Value())} if !q.KeysOnly { buf := make([]byte, len(i.Value())) copy(buf, i.Value()) e.Value = buf } return dsq.Result{Entry: e}, true }, Close: func() error { a.closeLk.RLock() defer a.closeLk.RUnlock() i.Release() return nil }, }) return dsq.NaiveQueryApply(qNaive, r), nil } // DiskUsage returns the current disk size used by this levelDB. // For in-mem datastores, it will return 0. func (d *Datastore) DiskUsage(ctx context.Context) (uint64, error) { d.closeLk.RLock() defer d.closeLk.RUnlock() if d.path == "" { // in-mem return 0, nil } var du uint64 err := filepath.Walk(d.path, func(path string, info os.FileInfo, err error) error { if err != nil { return err } du += uint64(info.Size()) return nil }) if err != nil { return 0, err } return du, nil } // LevelDB needs to be closed. func (d *Datastore) Close() (err error) { d.closeLk.Lock() defer d.closeLk.Unlock() return d.DB.Close() } type leveldbBatch struct { b *leveldb.Batch db *leveldb.DB closeLk *sync.RWMutex syncWrites bool } func (d *Datastore) Batch(ctx context.Context) (ds.Batch, error) { return &leveldbBatch{ b: new(leveldb.Batch), db: d.DB, closeLk: d.closeLk, syncWrites: d.syncWrites, }, nil } func (b *leveldbBatch) Put(ctx context.Context, key ds.Key, value []byte) error { b.b.Put(key.Bytes(), value) return nil } func (b *leveldbBatch) Commit(ctx context.Context) error { b.closeLk.RLock() defer b.closeLk.RUnlock() return b.db.Write(b.b, &opt.WriteOptions{Sync: b.syncWrites}) } func (b *leveldbBatch) Delete(ctx context.Context, key ds.Key) error { b.b.Delete(key.Bytes()) return nil } // A leveldb transaction embedding the accessor backed by the transaction. type transaction struct { *accessor tx *leveldb.Transaction } func (t *transaction) Commit(ctx context.Context) error { t.closeLk.RLock() defer t.closeLk.RUnlock() return t.tx.Commit() } func (t *transaction) Discard(ctx context.Context) { t.closeLk.RLock() defer t.closeLk.RUnlock() t.tx.Discard() } func (d *Datastore) NewTransaction(ctx context.Context, readOnly bool) (ds.Txn, error) { d.closeLk.RLock() defer d.closeLk.RUnlock() tx, err := d.DB.OpenTransaction() if err != nil { return nil, err } accessor := &accessor{ldb: tx, syncWrites: false, closeLk: d.closeLk} return &transaction{accessor, tx}, nil } ))entry(name ds_test.gonode(typeregularcontentsJ package leveldb import ( "bytes" "context" "fmt" "io/ioutil" "os" "sort" "testing" ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" dstest "github.com/ipfs/go-datastore/test" ) var testcases = map[string]string{ "/a": "a", "/a/b": "ab", "/a/b/c": "abc", "/a/b/d": "a/b/d", "/a/c": "ac", "/a/d": "ad", "/e": "e", "/f": "f", } var bg = context.Background() // returns datastore, and a function to call on exit. // (this garbage collects). So: // // d, close := newDS(t) // defer close() func newDS(t *testing.T) (*Datastore, func()) { path, err := ioutil.TempDir("", "testing_leveldb_") if err != nil { t.Fatal(err) } d, err := NewDatastore(path, nil) if err != nil { t.Fatal(err) } return d, func() { if err := d.Close(); err != nil { t.Fatal(err) } if err := os.RemoveAll(path); err != nil { t.Fatal(err) } } } // newDSMem returns an in-memory datastore. func newDSMem(t *testing.T) *Datastore { d, err := NewDatastore("", nil) if err != nil { t.Fatal(err) } return d } func addTestCases(t *testing.T, d *Datastore, testcases map[string]string) { for k, v := range testcases { dsk := ds.NewKey(k) if err := d.Put(bg, dsk, []byte(v)); err != nil { t.Fatal(err) } } for k, v := range testcases { dsk := ds.NewKey(k) v2, err := d.Get(bg, dsk) if err != nil { t.Fatal(err) } if string(v2) != v { t.Errorf("%s values differ: %s != %s", k, v, v2) } } } func testQuery(t *testing.T, d *Datastore) { addTestCases(t, d, testcases) rs, err := d.Query(bg, dsq.Query{Prefix: "/a/"}) if err != nil { t.Fatal(err) } expectMatches(t, []string{ "/a/b", "/a/b/c", "/a/b/d", "/a/c", "/a/d", }, rs) // test offset and limit rs, err = d.Query(bg, dsq.Query{Prefix: "/a/", Offset: 2, Limit: 2}) if err != nil { t.Fatal(err) } expectMatches(t, []string{ "/a/b/d", "/a/c", }, rs) // test order rs, err = d.Query(bg, dsq.Query{Orders: []dsq.Order{dsq.OrderByKey{}}}) if err != nil { t.Fatal(err) } keys := make([]string, 0, len(testcases)) for k := range testcases { keys = append(keys, k) } sort.Strings(keys) expectOrderedMatches(t, keys, rs) rs, err = d.Query(bg, dsq.Query{Orders: []dsq.Order{dsq.OrderByKeyDescending{}}}) if err != nil { t.Fatal(err) } // reverse for i, j := 0, len(keys)-1; i < j; i, j = i+1, j-1 { keys[i], keys[j] = keys[j], keys[i] } expectOrderedMatches(t, keys, rs) } func TestQuery(t *testing.T) { d, close := newDS(t) defer close() testQuery(t, d) } func TestQueryMem(t *testing.T) { d := newDSMem(t) testQuery(t, d) } func TestQueryRespectsProcess(t *testing.T) { d, close := newDS(t) defer close() addTestCases(t, d, testcases) } func TestCloseRace(t *testing.T) { d, close := newDS(t) for n := 0; n < 100; n++ { d.Put(bg, ds.NewKey(fmt.Sprintf("%d", n)), []byte(fmt.Sprintf("test%d", n))) } tx, _ := d.NewTransaction(bg, false) tx.Put(bg, ds.NewKey("txnversion"), []byte("bump")) closeCh := make(chan interface{}) go func() { close() closeCh <- nil }() for k := range testcases { tx.Get(bg, ds.NewKey(k)) } tx.Commit(bg) <-closeCh } func TestCloseSafety(t *testing.T) { d, close := newDS(t) addTestCases(t, d, testcases) tx, _ := d.NewTransaction(bg, false) err := tx.Put(bg, ds.NewKey("test"), []byte("test")) if err != nil { t.Error("Failed to put in a txn.") } close() err = tx.Commit(bg) if err == nil { t.Error("committing after close should fail.") } } func TestQueryRespectsProcessMem(t *testing.T) { d := newDSMem(t) addTestCases(t, d, testcases) } func expectMatches(t *testing.T, expect []string, actualR dsq.Results) { t.Helper() actual, err := actualR.Rest() if err != nil { t.Error(err) } if len(actual) != len(expect) { t.Error("not enough", expect, actual) } for _, k := range expect { found := false for _, e := range actual { if e.Key == k { found = true } } if !found { t.Error(k, "not found") } } } func expectOrderedMatches(t *testing.T, expect []string, actualR dsq.Results) { t.Helper() actual, err := actualR.Rest() if err != nil { t.Error(err) } if len(actual) != len(expect) { t.Error("not enough", expect, actual) } for i := range expect { if expect[i] != actual[i].Key { t.Errorf("expected %q, got %q", expect[i], actual[i].Key) } } } func testBatching(t *testing.T, d *Datastore) { b, err := d.Batch(bg) if err != nil { t.Fatal(err) } for k, v := range testcases { err := b.Put(bg, ds.NewKey(k), []byte(v)) if err != nil { t.Fatal(err) } } err = b.Commit(bg) if err != nil { t.Fatal(err) } for k, v := range testcases { val, err := d.Get(bg, ds.NewKey(k)) if err != nil { t.Fatal(err) } if v != string(val) { t.Fatal("got wrong data!") } } } func TestBatching(t *testing.T) { d, done := newDS(t) defer done() testBatching(t, d) } func TestBatchingMem(t *testing.T) { d := newDSMem(t) testBatching(t, d) } func TestDiskUsage(t *testing.T) { d, done := newDS(t) addTestCases(t, d, testcases) du, err := d.DiskUsage(bg) if err != nil { t.Fatal(err) } if du == 0 { t.Fatal("expected some disk usage") } k := ds.NewKey("more") err = d.Put(bg, k, []byte("value")) if err != nil { t.Fatal(err) } du2, err := d.DiskUsage(bg) if err != nil { t.Fatal(err) } if du2 <= du { t.Fatal("size should have increased") } done() // This should fail _, err = d.DiskUsage(bg) if err == nil { t.Fatal("DiskUsage should fail when we cannot walk path") } } func TestDiskUsageInMem(t *testing.T) { d := newDSMem(t) du, _ := d.DiskUsage(bg) if du != 0 { t.Fatal("inmem dbs have 0 disk usage") } } func TestTransactionCommit(t *testing.T) { key := ds.NewKey("/test/key1") d, done := newDS(t) defer done() txn, err := d.NewTransaction(bg, false) if err != nil { t.Fatal(err) } defer txn.Discard(bg) if err := txn.Put(bg, key, []byte("hello")); err != nil { t.Fatal(err) } if val, err := d.Get(bg, key); err != ds.ErrNotFound { t.Fatalf("expected ErrNotFound, got err: %v, value: %v", err, val) } if err := txn.Commit(bg); err != nil { t.Fatal(err) } if val, err := d.Get(bg, key); err != nil || !bytes.Equal(val, []byte("hello")) { t.Fatalf("expected entry present after commit, got err: %v, value: %v", err, val) } } func TestTransactionDiscard(t *testing.T) { key := ds.NewKey("/test/key1") d, done := newDS(t) defer done() txn, err := d.NewTransaction(bg, false) if err != nil { t.Fatal(err) } defer txn.Discard(bg) if err := txn.Put(bg, key, []byte("hello")); err != nil { t.Fatal(err) } if val, err := d.Get(bg, key); err != ds.ErrNotFound { t.Fatalf("expected ErrNotFound, got err: %v, value: %v", err, val) } if txn.Discard(bg); err != nil { t.Fatal(err) } if val, err := d.Get(bg, key); err != ds.ErrNotFound { t.Fatalf("expected ErrNotFound, got err: %v, value: %v", err, val) } } func TestTransactionManyOperations(t *testing.T) { keys := []ds.Key{ds.NewKey("/test/key1"), ds.NewKey("/test/key2"), ds.NewKey("/test/key3"), ds.NewKey("/test/key4"), ds.NewKey("/test/key5")} d, done := newDS(t) defer done() txn, err := d.NewTransaction(bg, false) if err != nil { t.Fatal(err) } defer txn.Discard(bg) // Insert all entries. for i := 0; i < 5; i++ { if err := txn.Put(bg, keys[i], []byte(fmt.Sprintf("hello%d", i))); err != nil { t.Fatal(err) } } // Remove the third entry. if err := txn.Delete(bg, keys[2]); err != nil { t.Fatal(err) } // Check existences. if has, err := txn.Has(bg, keys[1]); err != nil || !has { t.Fatalf("expected key[1] to be present, err: %v, has: %v", err, has) } if has, err := txn.Has(bg, keys[2]); err != nil || has { t.Fatalf("expected key[2] to be absent, err: %v, has: %v", err, has) } var res dsq.Results if res, err = txn.Query(bg, dsq.Query{Prefix: "/test"}); err != nil { t.Fatalf("query failed, err: %v", err) } if entries, err := res.Rest(); err != nil || len(entries) != 4 { t.Fatalf("query failed or contained unexpected number of entries, err: %v, results: %v", err, entries) } txn.Discard(bg) } func TestSuite(t *testing.T) { d := newDSMem(t) defer d.Close() dstest.SubtestAll(t, d) } ))entry(namego.modnode(typeregularcontents„module github.com/ipfs/go-ds-leveldb require ( github.com/ipfs/go-datastore v0.5.0 github.com/syndtr/goleveldb v1.0.0 ) go 1.16 ))entry(namego.sumnode(typeregularcontentskgithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ipfs/go-datastore v0.5.0 h1:rQicVCEacWyk4JZ6G5bD9TKR7lZEG1MWcG7UdWYrFAU= github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= ))entry(name version.jsonnode(typeregularcontents{ "version": "v0.5.0" } )))))))))))