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
|
// file : bdep/database.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
#include <bdep/database.hxx>
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/exceptions.hxx>
#include <bdep/diagnostics.hxx>
#include <bdep/database-views.hxx>
#include <bdep/database-views-odb.hxx>
using namespace std;
namespace bdep
{
using namespace odb::sqlite;
using odb::schema_catalog;
database
open (const dir_path& d, tracer& tr, bool create)
{
tracer trace ("open");
path f (d / bdep_file);
if (exists (f))
create = false;
else if (!create)
fail << d << " does not look like an initialized project directory" <<
info << "run 'bdep init' to initialize";
try
{
// We don't need the thread pool.
//
unique_ptr<connection_factory> cf (new single_connection_factory);
database db (f.string (),
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0),
true, // Enable FKs.
"", // Default VFS.
move (cf));
db.tracer (trace);
// Lock the database for as long as the connection is active. First we
// set locking_mode to EXCLUSIVE which instructs SQLite not to release
// any locks until the connection is closed. Then we force SQLite to
// acquire the write lock by starting exclusive transaction. See the
// locking_mode pragma documentation for details. This will also fail if
// the database is inaccessible (e.g., file does not exist, already used
// by another process, etc).
//
try
{
connection_ptr c (db.connection ());
c->execute ("PRAGMA locking_mode = EXCLUSIVE");
transaction t (c->begin_exclusive ());
if (create)
{
// Create the new schema.
//
if (db.schema_version () != 0)
fail << f << ": already has database schema";
schema_catalog::create_schema (db);
// Make path comparison case-insensitive for certain platforms.
//
// For details on this technique see the SQLite's ALTER TABLE
// documentation. Note that here we don't increment SQLite's
// schema_version since we are in the same transaction as where we
// have created the schema.
//
#ifdef _WIN32
db.execute ("PRAGMA writable_schema = ON");
const char* where ("type == 'table' AND name == 'configuration'");
sqlite_master t (db.query_value<sqlite_master> (where));
auto set_nocase = [&t] (const char* name)
{
string n ('\"' + string (name) + '\"');
size_t p (t.sql.find (n));
assert (p != string::npos);
p = t.sql.find_first_of (",)", p);
assert (p != string::npos);
t.sql.insert (p, " COLLATE NOCASE");
};
set_nocase ("path");
set_nocase ("relative_path");
db.execute ("UPDATE sqlite_master"
" SET sql = '" + t.sql + "'"
" WHERE " + where);
db.execute ("PRAGMA writable_schema = OFF");
db.execute ("PRAGMA integrity_check");
#endif
}
else
{
// Migrate the database if necessary.
//
schema_catalog::migrate (db);
}
t.commit ();
}
catch (odb::timeout&)
{
fail << "project " << d << " is already used by another process";
}
db.tracer (tr); // Switch to the caller's tracer.
return db;
}
catch (const database_exception& e)
{
fail << f << ": " << e.message () << endf;
}
}
}
|