Skip to content

Commit ef929d0

Browse files
author
Steve Bendiola
committed
add uri support
1 parent 860eded commit ef929d0

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

src/Index.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,8 @@ struct Index::Builder::Impl : LineSink {
471471
addIndexSql(log), addMetaSql(log), saveAllLines_{false} {}
472472

473473
void init() {
474-
if (unlink(indexFilename.c_str()) == 0) {
474+
auto file = db.toFile(indexFilename);
475+
if (unlink(file.c_str()) == 0) {
475476
log.warn("Rebuilding existing index ", indexFilename);
476477
}
477478
db.open(indexFilename, false);

src/Sqlite.cpp

+27-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "SqliteError.h"
66
#include <cstring>
77
#include "Log.h"
8+
#include "RegExp.h"
89

910
Sqlite::Sqlite(Log &log)
1011
: log_(&log), sql_(nullptr) {
@@ -20,7 +21,7 @@ void Sqlite::open(const std::string &filename, bool readOnly) {
2021
readOnly ? "read-only" : "read-write", " mode");
2122
R(sqlite3_open_v2(filename.c_str(), &sql_,
2223
readOnly ? SQLITE_OPEN_READONLY :
23-
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr));
24+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, nullptr));
2425
R(sqlite3_extended_result_codes(sql_, true));
2526
}
2627

@@ -144,3 +145,28 @@ void Sqlite::Statement::R(int result) const {
144145
if (result != SQLITE_OK)
145146
throw SqliteError(sqlite3_errmsg(sqlite3_db_handle(statement_)));
146147
}
148+
149+
std::string Sqlite::toFile(const std::string &uri) {
150+
RegExp uriRegex("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)");
151+
RegExp::Matches matches;
152+
if (!uriRegex.exec(uri, matches)) {
153+
return uri;
154+
}
155+
auto protocol = matches[1];
156+
auto result = std::string(uri.c_str() + protocol.first, protocol.second - protocol.first);
157+
if (result.find("file:") != 0) {
158+
throw std::runtime_error(
159+
"no uri support for '" + result+ "'");
160+
}
161+
auto start = protocol.second;
162+
if (matches.size() > 3) {
163+
auto slashes = matches[3];
164+
start = (slashes.second != 0) ? slashes.second : slashes.first;
165+
}
166+
auto path = std::string(uri.c_str() + start);
167+
auto paramsStart = path.rfind("?");
168+
if (paramsStart != std::string::npos) {
169+
path.resize(paramsStart);
170+
}
171+
return path;
172+
}

src/Sqlite.h

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Sqlite {
3636

3737
void open(const std::string &filename, bool readOnly);
3838
void close();
39+
std::string toFile(const std::string &uri);
3940

4041
class Statement {
4142
Log *log_;

tests/IndexTest.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ TEST_CASE("sparsely indexes files", "[Index]") {
328328
}
329329
}
330330

331+
332+
331333
TEST_CASE("metadata tests", "[Index]") {
332334
TempDir tempDir;
333335
CaptureLog log;

tests/SqliteTest.cpp

+77
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,80 @@ TEST_CASE("handles binds and blobs", "[Sqlite]") {
115115
for (int i = 0; i < byteLen; ++i) REQUIRE(blob2[i] == (0xff ^ (i & 0xff)));
116116
CHECK(select.step() == true);
117117
}
118+
119+
120+
TEST_CASE("handles binds and blobs uri", "[Sqlite]") {
121+
TempDir tempDir;
122+
CaptureLog log;
123+
Sqlite sqlite(log);
124+
auto dbPath = "file:" +tempDir.path + "/db.sqlite?foo=bar";
125+
sqlite.open(dbPath, false);
126+
127+
REQUIRE(sqlite.prepare("create table t(offset integer, data blob)").step() == true);
128+
auto inserter = sqlite.prepare("insert into t values(:id, :data)");
129+
inserter.bindInt64(":id", 1234);
130+
constexpr auto byteLen = 1024;
131+
char bytes[byteLen];
132+
for (int i = 0; i < byteLen; ++i) bytes[i] = i & 0xff;
133+
inserter.bindBlob(":data", bytes, byteLen);
134+
CHECK(inserter.step() == true);
135+
inserter.reset();
136+
inserter.bindInt64(":id", 5678);
137+
for (int i = 0; i < byteLen; ++i) bytes[i] = (i & 0xff) ^ 0xff;
138+
inserter.bindBlob(":data", bytes, byteLen);
139+
CHECK(inserter.step() == true);
140+
141+
auto select = sqlite.prepare("select * from t order by offset");
142+
CHECK(select.columnCount() == 2);
143+
CHECK(select.step() == false);
144+
CHECK(select.columnName(0) == "offset");
145+
CHECK(select.columnName(1) == "data");
146+
CHECK(select.columnInt64(0) == 1234);
147+
auto blob1 = select.columnBlob(1);
148+
REQUIRE(blob1.size() == byteLen);
149+
for (int i = 0; i < byteLen; ++i) REQUIRE(blob1[i] == (i & 0xff));
150+
CHECK(select.step() == false);
151+
CHECK(select.columnInt64(0) == 5678);
152+
auto blob2 = select.columnBlob(1);
153+
REQUIRE(blob2.size() == byteLen);
154+
for (int i = 0; i < byteLen; ++i) REQUIRE(blob2[i] == (0xff ^ (i & 0xff)));
155+
CHECK(select.step() == true);
156+
}
157+
158+
TEST_CASE("uri convert to file", "[Sqlite]") {
159+
TempDir tempDir;
160+
CaptureLog log;
161+
Sqlite sqlite(log);
162+
163+
// file:data.db Open the file "data.db" in the current directory.
164+
REQUIRE(sqlite.toFile("file:data.db") == "data.db");
165+
166+
// file:/home/fred/data.db
167+
// file:///home/fred/data.db
168+
// file://localhost/home/fred/data.db Open the database file "/home/fred/data.db".
169+
REQUIRE(sqlite.toFile("file:/home/fred/data.db") == "/home/fred/data.db");
170+
REQUIRE(sqlite.toFile("file:///home/fred/data.db") == "/home/fred/data.db");
171+
REQUIRE(sqlite.toFile("file://localhost/home/fred/data.db") == "/home/fred/data.db");
172+
//
173+
//
174+
// file://darkstar/home/fred/data.db An error. "darkstar" is not a recognized authority.
175+
//let sqlite handle
176+
REQUIRE(sqlite.toFile("file://darkstar/home/fred/data.db") == "/home/fred/data.db");
177+
//
178+
// file:///C:/Documents%20and%20Settings/fred/Desktop/data.db Windows only: Open the file "data.db" on fred's desktop on drive C:. Note that the %20 escaping in this example is not strictly necessary - space characters can be used literally in URI filenames.
179+
//REQUIRE(sqlite.toFile("file:///C:/Documents%20and%20Settings/fred/Desktop/data.db") == "/home/fred/data.db");
180+
//
181+
// file:data.db?mode=ro&cache=private Open file "data.db" in the current directory for read-only access. Regardless of whether or not shared-cache mode is enabled by default, use a private cache.
182+
REQUIRE(sqlite.toFile("file:data.db?mode=ro&cache=private") == "data.db");
183+
184+
REQUIRE(sqlite.toFile("file:/home/fred/foo/bar/data.db?mode=ro&cache=private") == "/home/fred/foo/bar/data.db");
185+
//
186+
// file:/home/fred/data.db?vfs=unix-dotfile Open file "/home/fred/data.db". Use the special VFS "unix-dotfile" that uses dot-files in place of posix advisory locking.
187+
REQUIRE(sqlite.toFile("file:/home/fred/data.db?vfs=unix-dotfile") == "/home/fred/data.db");
188+
// file:data.db?mode=readonly An error. "readonly" is not a valid option for the "mode" parameter. Use "ro" instead: "file:data.db?mode=ro".
189+
REQUIRE(sqlite.toFile("file:data.db?mode=readonly") == "data.db");
190+
191+
192+
REQUIRE_THROWS(sqlite.toFile("http://data.db?mode=readonly"));
193+
194+
}

0 commit comments

Comments
 (0)