about summary refs log tree commit diff
path: root/third_party/nix/src/libstore/sqlite.hh
blob: cad78aed45beb5950c9179ce0c4f67423ce5a8bb (plain) (blame)
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
#pragma once

#include <functional>
#include <string>

#include "libutil/types.hh"

class sqlite3;
class sqlite3_stmt;

namespace nix {

/* RAII wrapper to close a SQLite database automatically. */
struct SQLite {
  sqlite3* db = 0;
  SQLite() {}
  SQLite(const Path& path);
  SQLite(const SQLite& from) = delete;
  SQLite& operator=(const SQLite& from) = delete;
  SQLite& operator=(SQLite&& from) {
    db = from.db;
    from.db = 0;
    return *this;
  }
  ~SQLite();
  operator sqlite3*() { return db; }

  void exec(const std::string& stmt);
};

/* RAII wrapper to create and destroy SQLite prepared statements. */
struct SQLiteStmt {
  sqlite3* db = 0;
  sqlite3_stmt* stmt = 0;
  std::string sql;
  SQLiteStmt() {}
  SQLiteStmt(sqlite3* db, const std::string& sql) { create(db, sql); }
  void create(sqlite3* db, const std::string& s);
  ~SQLiteStmt();
  operator sqlite3_stmt*() { return stmt; }

  /* Helper for binding / executing statements. */
  class Use {
    friend struct SQLiteStmt;

   private:
    SQLiteStmt& stmt;
    int curArg = 1;
    Use(SQLiteStmt& stmt);

   public:
    ~Use();

    /* Bind the next parameter. */
    Use& operator()(const std::string& value, bool notNull = true);
    Use& operator()(int64_t value, bool notNull = true);
    Use& bind();  // null

    int step();

    /* Execute a statement that does not return rows. */
    void exec();

    /* For statements that return 0 or more rows. Returns true iff
       a row is available. */
    bool next();

    std::string getStr(int col);
    int64_t getInt(int col);
    bool isNull(int col);
  };

  Use use() { return Use(*this); }
};

/* RAII helper that ensures transactions are aborted unless explicitly
   committed. */
struct SQLiteTxn {
  bool active = false;
  sqlite3* db;

  SQLiteTxn(sqlite3* db);

  void commit();

  ~SQLiteTxn();
};

MakeError(SQLiteError, Error);
MakeError(SQLiteBusy, SQLiteError);

[[noreturn]] void throwSQLiteError(sqlite3* db, const FormatOrString& fs);

void handleSQLiteBusy(const SQLiteBusy& e);

/* Convenience function for retrying a SQLite transaction when the
   database is busy. */
template <typename T>
T retrySQLite(std::function<T()> fun) {
  while (true) {
    try {
      return fun();
    } catch (SQLiteBusy& e) {
      handleSQLiteBusy(e);
    }
  }
}

}  // namespace nix