Commit | Line | Data |
---|---|---|
7bed5d91 ED |
1 | #include "sqlite.hh" |
2 | #include "util.hh" | |
3 | ||
4 | #include <sqlite3.h> | |
5 | ||
6 | namespace nix { | |
7 | ||
8 | [[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f) | |
9 | { | |
10 | int err = sqlite3_errcode(db); | |
11 | if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) { | |
12 | if (err == SQLITE_PROTOCOL) | |
13 | printMsg(lvlError, "warning: SQLite database is busy (SQLITE_PROTOCOL)"); | |
14 | else { | |
15 | static bool warned = false; | |
16 | if (!warned) { | |
17 | printMsg(lvlError, "warning: SQLite database is busy"); | |
18 | warned = true; | |
19 | } | |
20 | } | |
21 | /* Sleep for a while since retrying the transaction right away | |
22 | is likely to fail again. */ | |
23 | #if HAVE_NANOSLEEP | |
24 | struct timespec t; | |
25 | t.tv_sec = 0; | |
26 | t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */ | |
27 | nanosleep(&t, 0); | |
28 | #else | |
29 | sleep(1); | |
30 | #endif | |
31 | throw SQLiteBusy(format("%1%: %2%") % f.str() % sqlite3_errmsg(db)); | |
32 | } | |
33 | else | |
34 | throw SQLiteError(format("%1%: %2%") % f.str() % sqlite3_errmsg(db)); | |
35 | } | |
36 | ||
37 | SQLite::~SQLite() | |
38 | { | |
39 | try { | |
40 | if (db && sqlite3_close(db) != SQLITE_OK) | |
41 | throwSQLiteError(db, "closing database"); | |
42 | } catch (...) { | |
43 | ignoreException(); | |
44 | } | |
45 | } | |
46 | ||
47 | void SQLiteStmt::create(sqlite3 * db, const string & s) | |
48 | { | |
49 | checkInterrupt(); | |
50 | assert(!stmt); | |
51 | if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK) | |
52 | throwSQLiteError(db, "creating statement"); | |
53 | this->db = db; | |
54 | } | |
55 | ||
7bed5d91 ED |
56 | SQLiteStmt::~SQLiteStmt() |
57 | { | |
58 | try { | |
59 | if (stmt && sqlite3_finalize(stmt) != SQLITE_OK) | |
60 | throwSQLiteError(db, "finalizing statement"); | |
61 | } catch (...) { | |
62 | ignoreException(); | |
63 | } | |
64 | } | |
65 | ||
b1fd0ab7 ED |
66 | SQLiteStmt::Use::Use(SQLiteStmt & stmt) |
67 | : stmt(stmt) | |
68 | { | |
69 | assert(stmt.stmt); | |
70 | /* Note: sqlite3_reset() returns the error code for the most | |
71 | recent call to sqlite3_step(). So ignore it. */ | |
72 | sqlite3_reset(stmt); | |
73 | } | |
74 | ||
75 | SQLiteStmt::Use::~Use() | |
7bed5d91 | 76 | { |
b1fd0ab7 | 77 | sqlite3_reset(stmt); |
7bed5d91 ED |
78 | } |
79 | ||
b1fd0ab7 | 80 | SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool notNull) |
7bed5d91 | 81 | { |
b1fd0ab7 ED |
82 | if (notNull) { |
83 | if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) | |
84 | throwSQLiteError(stmt.db, "binding argument"); | |
85 | } else | |
86 | bind(); | |
87 | return *this; | |
7bed5d91 ED |
88 | } |
89 | ||
b1fd0ab7 | 90 | SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull) |
7bed5d91 | 91 | { |
b1fd0ab7 ED |
92 | if (notNull) { |
93 | if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK) | |
94 | throwSQLiteError(stmt.db, "binding argument"); | |
95 | } else | |
96 | bind(); | |
97 | return *this; | |
7bed5d91 ED |
98 | } |
99 | ||
b1fd0ab7 | 100 | SQLiteStmt::Use & SQLiteStmt::Use::bind() |
7bed5d91 ED |
101 | { |
102 | if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK) | |
b1fd0ab7 ED |
103 | throwSQLiteError(stmt.db, "binding argument"); |
104 | return *this; | |
7bed5d91 ED |
105 | } |
106 | ||
b1fd0ab7 | 107 | int SQLiteStmt::Use::step() |
7bed5d91 | 108 | { |
b1fd0ab7 | 109 | return sqlite3_step(stmt); |
7bed5d91 ED |
110 | } |
111 | ||
b1fd0ab7 | 112 | void SQLiteStmt::Use::exec() |
7bed5d91 | 113 | { |
b1fd0ab7 ED |
114 | int r = step(); |
115 | assert(r != SQLITE_ROW); | |
116 | if (r != SQLITE_DONE) | |
117 | throwSQLiteError(stmt.db, "executing SQLite statement"); | |
118 | } | |
119 | ||
120 | bool SQLiteStmt::Use::next() | |
121 | { | |
122 | int r = step(); | |
123 | if (r != SQLITE_DONE && r != SQLITE_ROW) | |
124 | throwSQLiteError(stmt.db, "executing SQLite query"); | |
125 | return r == SQLITE_ROW; | |
126 | } | |
127 | ||
128 | std::string SQLiteStmt::Use::getStr(int col) | |
129 | { | |
130 | auto s = (const char *) sqlite3_column_text(stmt, col); | |
131 | assert(s); | |
132 | return s; | |
133 | } | |
134 | ||
135 | int64_t SQLiteStmt::Use::getInt(int col) | |
136 | { | |
137 | // FIXME: detect nulls? | |
138 | return sqlite3_column_int64(stmt, col); | |
7bed5d91 ED |
139 | } |
140 | ||
141 | SQLiteTxn::SQLiteTxn(sqlite3 * db) | |
142 | { | |
143 | this->db = db; | |
144 | if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK) | |
145 | throwSQLiteError(db, "starting transaction"); | |
146 | active = true; | |
147 | } | |
148 | ||
149 | void SQLiteTxn::commit() | |
150 | { | |
151 | if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK) | |
152 | throwSQLiteError(db, "committing transaction"); | |
153 | active = false; | |
154 | } | |
155 | ||
156 | SQLiteTxn::~SQLiteTxn() | |
157 | { | |
158 | try { | |
159 | if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK) | |
160 | throwSQLiteError(db, "aborting transaction"); | |
161 | } catch (...) { | |
162 | ignoreException(); | |
163 | } | |
164 | } | |
165 | ||
166 | } |