aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/flake/eval-cache.cc
blob: 8d01ef0fc0c61470c36bd753bb5eed98dfcc1533 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "eval-cache.hh"
#include "sqlite.hh"
#include "eval.hh"

#include <set>

namespace nix::flake {

static const char * schema = R"sql(

create table if not exists Fingerprints (
    fingerprint blob primary key not null,
    timestamp   integer not null
);

create table if not exists Attributes (
    fingerprint blob not null,
    attrPath    text not null,
    type        integer,
    value       text,
    primary key (fingerprint, attrPath),
    foreign key (fingerprint) references Fingerprints(fingerprint) on delete cascade
);
)sql";

struct EvalCache::State
{
    SQLite db;
    SQLiteStmt insertFingerprint;
    SQLiteStmt insertAttribute;
    SQLiteStmt queryAttribute;
    std::set<Fingerprint> fingerprints;
};

EvalCache::EvalCache()
    : _state(std::make_unique<Sync<State>>())
{
    auto state(_state->lock());

    Path dbPath = getCacheDir() + "/nix/eval-cache-v1.sqlite";
    createDirs(dirOf(dbPath));

    state->db = SQLite(dbPath);
    state->db.isCache();
    state->db.exec(schema);

    state->insertFingerprint.create(state->db,
        "insert or ignore into Fingerprints(fingerprint, timestamp) values (?, ?)");

    state->insertAttribute.create(state->db,
        "insert or replace into Attributes(fingerprint, attrPath, type, value) values (?, ?, ?, ?)");

    state->queryAttribute.create(state->db,
        "select type, value from Attributes where fingerprint = ? and attrPath = ?");
}

enum ValueType {
    Derivation = 1,
};

void EvalCache::addDerivation(
    const Fingerprint & fingerprint,
    const std::string & attrPath,
    const Derivation & drv)
{
    if (!evalSettings.pureEval) return;

    auto state(_state->lock());

    if (state->fingerprints.insert(fingerprint).second)
        // FIXME: update timestamp
        state->insertFingerprint.use()
            (fingerprint.hash, fingerprint.hashSize)
            (time(0)).exec();

    state->insertAttribute.use()
        (fingerprint.hash, fingerprint.hashSize)
        (attrPath)
        (ValueType::Derivation)
        (std::string(drv.drvPath.to_string()) + " " + std::string(drv.outPath.to_string()) + " " + drv.outputName).exec();
}

std::optional<EvalCache::Derivation> EvalCache::getDerivation(
    const Fingerprint & fingerprint,
    const std::string & attrPath)
{
    if (!evalSettings.pureEval) return {};

    auto state(_state->lock());

    auto queryAttribute(state->queryAttribute.use()
        (fingerprint.hash, fingerprint.hashSize)
        (attrPath));
    if (!queryAttribute.next()) return {};

    // FIXME: handle negative results

    auto type = (ValueType) queryAttribute.getInt(0);
    auto s = queryAttribute.getStr(1);

    if (type != ValueType::Derivation) return {};

    auto ss = tokenizeString<std::vector<std::string>>(s, " ");

    debug("evaluation cache hit for '%s'", attrPath);

    return Derivation { StorePath::fromBaseName(ss[0]), StorePath::fromBaseName(ss[1]), ss[2] };
}

EvalCache & EvalCache::singleton()
{
    static std::unique_ptr<EvalCache> evalCache(new EvalCache());
    return *evalCache;
}

}