aboutsummaryrefslogtreecommitdiff
path: root/nix/libstore/sqlite.cc
blob: e08c67f40ed296cb5766ded6af1d55e20e9f6957 (about) (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "sqlite.hh"
#include "util.hh"

#include <sqlite3.h>

namespace nix {

[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f)
{
    int err = sqlite3_errcode(db);
    if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
        if (err == SQLITE_PROTOCOL)
            printMsg(lvlError, "warning: SQLite database is busy (SQLITE_PROTOCOL)");
        else {
            static bool warned = false;
            if (!warned) {
                printMsg(lvlError, "warning: SQLite database is busy");
                warned = true;
            }
        }
        /* Sleep for a while since retrying the transaction right away
           is likely to fail again. */
#if HAVE_NANOSLEEP
        struct timespec t;
        t.tv_sec = 0;
        t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */
        nanosleep(&t, 0);
#else
        sleep(1);
#endif
        throw SQLiteBusy(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
    }
    else
        throw SQLiteError(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
}

SQLite::~SQLite()
{
    try {
        if (db && sqlite3_close(db) != SQLITE_OK)
            throwSQLiteError(db, "closing database");
    } catch (...) {
        ignoreException();
    }
}

void SQLiteStmt::create(sqlite3 * db, const string & s)
{
    checkInterrupt();
    assert(!stmt);
    if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK)
        throwSQLiteError(db, "creating statement");
    this->db = db;
}

SQLiteStmt::~SQLiteStmt()
{
    try {
        if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
            throwSQLiteError(db, "finalizing statement");
    } catch (...) {
        ignoreException();
    }
}

SQLiteStmt::Use::Use(SQLiteStmt & stmt)
    : stmt(stmt)
{
    assert(stmt.stmt);
    /* Note: sqlite3_reset() returns the error code for the most
       recent call to sqlite3_step().  So ignore it. */
    sqlite3_reset(stmt);
}

SQLiteStmt::Use::~Use()
{
    sqlite3_reset(stmt);
}

SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool notNull)
{
    if (notNull) {
        if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
            throwSQLiteError(stmt.db, "binding argument");
    } else
        bind();
    return *this;
}

SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
{
    if (notNull) {
        if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
            throwSQLiteError(stmt.db, "binding argument");
    } else
        bind();
    return *this;
}

SQLiteStmt::Use & SQLiteStmt::Use::bind()
{
    if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
        throwSQLiteError(stmt.db, "binding argument");
    return *this;
}

int SQLiteStmt::Use::step()
{
    return sqlite3_step(stmt);
}

void SQLiteStmt::Use::exec()
{
    int r = step();
    assert(r != SQLITE_ROW);
    if (r != SQLITE_DONE)
        throwSQLiteError(stmt.db, "executing SQLite statement");
}

bool SQLiteStmt::Use::next()
{
    int r = step();
    if (r != SQLITE_DONE && r != SQLITE_ROW)
        throwSQLiteError(stmt.db, "executing SQLite query");
    return r == SQLITE_ROW;
}

std::string SQLiteStmt::Use::getStr(int col)
{
    auto s = (const char *) sqlite3_column_text(stmt, col);
    assert(s);
    return s;
}

int64_t SQLiteStmt::Use::getInt(int col)
{
    // FIXME: detect nulls?
    return sqlite3_column_int64(stmt, col);
}

SQLiteTxn::SQLiteTxn(sqlite3 * db)
{
    this->db = db;
    if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, "starting transaction");
    active = true;
}

void SQLiteTxn::commit()
{
    if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, "committing transaction");
    active = false;
}

SQLiteTxn::~SQLiteTxn()
{
    try {
        if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
            throwSQLiteError(db, "aborting transaction");
    } catch (...) {
        ignoreException();
    }
}

}
bd2e85ef593231ac9d'>gexp: Add 'with-parameters'....* guix/gexp.scm (<parameterized>): New record type. (with-parameters): New macro. (compile-parameterized): New gexp compiler. * tests/gexp.scm ("with-parameters for %current-system") ("with-parameters for %current-target-system") ("with-parameters + file-append"): New tests. * doc/guix.texi (G-Expressions): Document it. Ludovic Courtès 2020-03-08gexp: Default to current target....* guix/gexp.scm (lower-object): Set target argument to 'current by default and look for the current target system at bind time if needed, (gexp->file): ditto, (gexp->script): ditto, (lower-gexp): make sure lowered extensions are not cross-compiled. * tests/gexp.scm: Add cross-compilation test-cases for gexp->script and gexp->file with a target passed explicitely and with a default target. Mathieu Othacehe 2020-01-04gexp: Add 'raw-derivation-file'....* guix/gexp.scm (<raw-derivation-file>): New record type. (raw-derivation-file-compiler): New gexp compiler. * tests/gexp.scm ("lower-gexp, raw-derivation-file") ("raw-derivation-file"): New tests. Ludovic Courtès 2019-12-18gexp: Allow character literals in GEXP->SEXP....Fixes <https://bugs.gnu.org/38628>. * tests/gexp.scm ("lower-gexp, character literal"): New test. * guix/gexp.scm (gexp->sexp)[self-quoting?]: Add CHAR? to the tested types. * guix/repl.scm (self-quoting?): Likewise. * gnu/tests.scm (marionette-shepherd-service)[self-quoting?]: Likewise. Marius Bakke 2019-11-30gexp: 'local-file' properly resolves non-literal relative file names....* guix/gexp.scm (local-file): Distinguish the case where FILE is a literal string and when it's not. Add a clause for when FILE is not a literal string. * tests/gexp.scm ("local-file, non-literal relative file name"): New test. * doc/guix.texi (G-Expressions): Update accordingly. Ludovic Courtès 2019-09-23gexp: Catch and report non-self-quoting gexp inputs....Previously we would, for example, generate build scripts in the store; when trying to run them, we'd get a 'read' error due to the presence of #<foo> syntax in there. * guix/gexp.scm (gexp->sexp)[self-quoting?]: New procedure. [reference->sexp]: Check whether the argument in a <gexp-input> box is self-quoting. Raise a '&gexp-input-error' condition if it's not. * tests/gexp.scm ("lower-gexp, non-self-quoting input"): New test. Ludovic Courtès 2019-07-26gexp: 'program-file' honors the current system and cross-compilation target....Fixes <https://bugs.gnu.org/36813>. Reported by Jakob L. Kreuze <zerodaysfordays.sdf.org@sdf.org>. * guix/gexp.scm (program-file-compiler): Pass #:system and #:target to 'gexp->script'. (load-path-expression): Add #:system and #:target and honor them. (gexp->script): Likewise. * tests/gexp.scm ("program-file #:system"): New test. * doc/guix.texi (G-Expressions): Adjust accordingly. Ludovic Courtès 2019-07-15gexp: 'lowered-gexp-guile' now returns a <derivation-input>....* guix/derivations.scm (derivation-input-output-path): New procedure. * guix/gexp.scm (lower-gexp): Wrap GUILE in a <derivation-input>. (gexp->derivation): Adjust accordingly. * guix/remote.scm (remote-pipe-for-gexp, remote-eval): Adjust accordingly. * tests/gexp.scm ("lower-gexp"): Adjust accordingly. Ludovic Courtès 2019-07-15gexp: <lowered-gexp> separates sources from derivation inputs....* guix/gexp.scm (lower-inputs): Return either <derivation-input> records or store items. (lower-reference-graphs): Return file/input pairs. (<lowered-gexp>)[sources]: New field. (lower-gexp): Adjust accordingly. (gexp->input-tuple): Remove. (gexp->derivation)[graphs-file-names]: Handle only the 'derivation-input?' and 'string?' cases. Pass #:sources to 'raw-derivation'; ensure #:inputs contains only <derivation-input> records. * guix/remote.scm (remote-eval): Adjust to the new <lowered-gexp> interface. * tests/gexp.scm ("lower-gexp"): Adjust to expect <derivation-input> records instead of <gexp-input> Ludovic Courtès 2019-07-04gexp: Add 'lower-gexp' and express 'gexp->derivation' in terms of it....* guix/gexp.scm (gexp-input-thing, gexp-input-output) (gexp-input-native?): Export. (lower-inputs): Return <gexp-input> records instead of tuples. (lower-reference-graphs): Adjust accordingly. (<lowered-gexp>): New record type. (lower-gexp, gexp-input->tuple): New procedure. (gexp->derivation)[%modules]: Remove. [requested-graft?]: New variable. [add-modules]: New procedure. Rewrite in terms of 'lower-gexp'. (gexp-inputs): Add TODO comment. * tests/gexp.scm ("lower-gexp"): New test. Ludovic Courtès