db/simple/Save: migrate from class Error to C++ exceptions

This commit is contained in:
Max Kellermann 2016-10-29 09:45:34 +02:00
parent dea46e8d5a
commit 90a14e14f4
11 changed files with 95 additions and 166 deletions

View File

@ -23,14 +23,11 @@
#include "fs/io/TextFile.hxx" #include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx"
#include "util/StringUtil.hxx" #include "util/StringUtil.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
static constexpr Domain playlist_database_domain("playlist_database");
void void
playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv) playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv)
{ {
@ -41,9 +38,8 @@ playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv)
pi.name.c_str(), (long)pi.mtime); pi.name.c_str(), (long)pi.mtime);
} }
bool void
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name, playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name)
Error &error)
{ {
PlaylistInfo pm(name, 0); PlaylistInfo pm(name, 0);
@ -53,24 +49,19 @@ playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name,
while ((line = file.ReadLine()) != nullptr && while ((line = file.ReadLine()) != nullptr &&
strcmp(line, "playlist_end") != 0) { strcmp(line, "playlist_end") != 0) {
colon = strchr(line, ':'); colon = strchr(line, ':');
if (colon == nullptr || colon == line) { if (colon == nullptr || colon == line)
error.Format(playlist_database_domain, throw FormatRuntimeError("unknown line in db: %s",
"unknown line in db: %s", line); line);
return false;
}
*colon++ = 0; *colon++ = 0;
value = StripLeft(colon); value = StripLeft(colon);
if (strcmp(line, "mtime") == 0) if (strcmp(line, "mtime") == 0)
pm.mtime = strtol(value, nullptr, 10); pm.mtime = strtol(value, nullptr, 10);
else { else
error.Format(playlist_database_domain, throw FormatRuntimeError("unknown line in db: %s",
"unknown line in db: %s", line); line);
return false;
}
} }
pv.UpdateOrInsert(std::move(pm)); pv.UpdateOrInsert(std::move(pm));
return true;
} }

View File

@ -32,8 +32,10 @@ class Error;
void void
playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv); playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv);
bool /**
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name, * Throws #std::runtime_error on error.
Error &error); */
void
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name);
#endif #endif

View File

@ -27,8 +27,7 @@
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/StringUtil.hxx" #include "util/StringUtil.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -36,8 +35,6 @@
#define SONG_MTIME "mtime" #define SONG_MTIME "mtime"
#define SONG_END "song_end" #define SONG_END "song_end"
static constexpr Domain song_save_domain("song_save");
static void static void
range_save(BufferedOutputStream &os, unsigned start_ms, unsigned end_ms) range_save(BufferedOutputStream &os, unsigned start_ms, unsigned end_ms)
{ {
@ -74,8 +71,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
} }
DetachedSong * DetachedSong *
song_load(TextFile &file, const char *uri, song_load(TextFile &file, const char *uri)
Error &error)
{ {
DetachedSong *song = new DetachedSong(uri); DetachedSong *song = new DetachedSong(uri);
@ -88,9 +84,7 @@ song_load(TextFile &file, const char *uri,
if (colon == nullptr || colon == line) { if (colon == nullptr || colon == line) {
delete song; delete song;
error.Format(song_save_domain, throw FormatRuntimeError("unknown line in db: %s", line);
"unknown line in db: %s", line);
return nullptr;
} }
*colon++ = 0; *colon++ = 0;
@ -118,9 +112,7 @@ song_load(TextFile &file, const char *uri,
} else { } else {
delete song; delete song;
error.Format(song_save_domain, throw FormatRuntimeError("unknown line in db: %s", line);
"unknown line in db: %s", line);
return nullptr;
} }
} }

View File

@ -38,11 +38,9 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
* Loads a song from the input file. Reading stops after the * Loads a song from the input file. Reading stops after the
* "song_end" line. * "song_end" line.
* *
* @param error location to store the error occurring * Throws #std::runtime_error on error.
* @return true on success, false on error
*/ */
DetachedSong * DetachedSong *
song_load(TextFile &file, const char *uri, song_load(TextFile &file, const char *uri);
Error &error);
#endif #endif

View File

@ -29,7 +29,7 @@
#include "tag/Settings.hxx" #include "tag/Settings.hxx"
#include "fs/Charset.hxx" #include "fs/Charset.hxx"
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <string.h> #include <string.h>
@ -66,8 +66,8 @@ db_save_internal(BufferedOutputStream &os, const Directory &music_root)
directory_save(os, music_root); directory_save(os, music_root);
} }
bool void
db_load_internal(TextFile &file, Directory &music_root, Error &error) db_load_internal(TextFile &file, Directory &music_root)
{ {
char *line; char *line;
unsigned format = 0; unsigned format = 0;
@ -76,10 +76,8 @@ db_load_internal(TextFile &file, Directory &music_root, Error &error)
/* get initial info */ /* get initial info */
line = file.ReadLine(); line = file.ReadLine();
if (line == nullptr || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) { if (line == nullptr || strcmp(DIRECTORY_INFO_BEGIN, line) != 0)
error.Set(db_domain, "Database corrupted"); throw std::runtime_error("Database corrupted");
return false;
}
memset(tags, false, sizeof(tags)); memset(tags, false, sizeof(tags));
@ -90,67 +88,49 @@ db_load_internal(TextFile &file, Directory &music_root, Error &error)
if ((p = StringAfterPrefix(line, DB_FORMAT_PREFIX))) { if ((p = StringAfterPrefix(line, DB_FORMAT_PREFIX))) {
format = atoi(p); format = atoi(p);
} else if (StringStartsWith(line, DIRECTORY_MPD_VERSION)) { } else if (StringStartsWith(line, DIRECTORY_MPD_VERSION)) {
if (found_version) { if (found_version)
error.Set(db_domain, "Duplicate version line"); throw std::runtime_error("Duplicate version line");
return false;
}
found_version = true; found_version = true;
} else if ((p = StringAfterPrefix(line, DIRECTORY_FS_CHARSET))) { } else if ((p = StringAfterPrefix(line, DIRECTORY_FS_CHARSET))) {
if (found_charset) { if (found_charset)
error.Set(db_domain, "Duplicate charset line"); throw std::runtime_error("Duplicate charset line");
return false;
}
found_charset = true; found_charset = true;
const char *new_charset = p; const char *new_charset = p;
const char *const old_charset = GetFSCharset(); const char *const old_charset = GetFSCharset();
if (*old_charset != 0 if (*old_charset != 0
&& strcmp(new_charset, old_charset) != 0) { && strcmp(new_charset, old_charset) != 0)
error.Format(db_domain, throw FormatRuntimeError("Existing database has charset "
"Existing database has charset " "\"%s\" instead of \"%s\"; "
"\"%s\" instead of \"%s\"; " "discarding database file",
"discarding database file", new_charset, old_charset);
new_charset, old_charset);
return false;
}
} else if ((p = StringAfterPrefix(line, DB_TAG_PREFIX))) { } else if ((p = StringAfterPrefix(line, DB_TAG_PREFIX))) {
const char *name = p; const char *name = p;
TagType tag = tag_name_parse(name); TagType tag = tag_name_parse(name);
if (tag == TAG_NUM_OF_ITEM_TYPES) { if (tag == TAG_NUM_OF_ITEM_TYPES)
error.Format(db_domain, throw FormatRuntimeError("Unrecognized tag '%s', "
"Unrecognized tag '%s', " "discarding database file",
"discarding database file", name);
name);
return false;
}
tags[tag] = true; tags[tag] = true;
} else { } else {
error.Format(db_domain, "Malformed line: %s", line); throw FormatRuntimeError("Malformed line: %s", line);
return false;
} }
} }
if (format < OLDEST_DB_FORMAT || format > DB_FORMAT) { if (format < OLDEST_DB_FORMAT || format > DB_FORMAT)
error.Set(db_domain, throw std::runtime_error("Database format mismatch, "
"Database format mismatch, " "discarding database file");
"discarding database file");
return false;
}
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) { for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (IsTagEnabled(i) && !tags[i]) { if (IsTagEnabled(i) && !tags[i])
error.Set(db_domain, throw std::runtime_error("Tag list mismatch, "
"Tag list mismatch, " "discarding database file");
"discarding database file");
return false;
}
}
LogDebug(db_domain, "reading DB"); LogDebug(db_domain, "reading DB");
const ScopeDatabaseLock protect; const ScopeDatabaseLock protect;
return directory_load(file, music_root, error); directory_load(file, music_root);
} }

View File

@ -28,7 +28,10 @@ class Error;
void void
db_save_internal(BufferedOutputStream &os, const Directory &root); db_save_internal(BufferedOutputStream &os, const Directory &root);
bool /**
db_load_internal(TextFile &file, Directory &root, Error &error); * Throws #std::runtime_error on error.
*/
void
db_load_internal(TextFile &file, Directory &root);
#endif #endif

View File

@ -28,8 +28,7 @@
#include "fs/io/BufferedOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx"
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
#include "util/NumberParser.hxx" #include "util/NumberParser.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include <string.h> #include <string.h>
@ -39,8 +38,6 @@
#define DIRECTORY_BEGIN "begin: " #define DIRECTORY_BEGIN "begin: "
#define DIRECTORY_END "end: " #define DIRECTORY_END "end: "
static constexpr Domain directory_domain("directory");
gcc_const gcc_const
static const char * static const char *
DeviceToTypeString(unsigned device) DeviceToTypeString(unsigned device)
@ -115,49 +112,37 @@ ParseLine(Directory &directory, const char *line)
} }
static Directory * static Directory *
directory_load_subdir(TextFile &file, Directory &parent, const char *name, directory_load_subdir(TextFile &file, Directory &parent, const char *name)
Error &error)
{ {
bool success; if (parent.FindChild(name) != nullptr)
throw FormatRuntimeError("Duplicate subdirectory '%s'", name);
if (parent.FindChild(name) != nullptr) {
error.Format(directory_domain,
"Duplicate subdirectory '%s'", name);
return nullptr;
}
Directory *directory = parent.CreateChild(name); Directory *directory = parent.CreateChild(name);
while (true) { try {
const char *line = file.ReadLine(); while (true) {
if (line == nullptr) { const char *line = file.ReadLine();
error.Set(directory_domain, "Unexpected end of file"); if (line == nullptr)
directory->Delete(); throw std::runtime_error("Unexpected end of file");
return nullptr;
if (StringStartsWith(line, DIRECTORY_BEGIN))
break;
if (!ParseLine(*directory, line))
throw FormatRuntimeError("Malformed line: %s", line);
} }
if (StringStartsWith(line, DIRECTORY_BEGIN)) directory_load(file, *directory);
break; } catch (...) {
if (!ParseLine(*directory, line)) {
error.Format(directory_domain,
"Malformed line: %s", line);
directory->Delete();
return nullptr;
}
}
success = directory_load(file, *directory, error);
if (!success) {
directory->Delete(); directory->Delete();
return nullptr; throw;
} }
return directory; return directory;
} }
bool void
directory_load(TextFile &file, Directory &directory, Error &error) directory_load(TextFile &file, Directory &directory)
{ {
const char *line; const char *line;
@ -165,38 +150,23 @@ directory_load(TextFile &file, Directory &directory, Error &error)
!StringStartsWith(line, DIRECTORY_END)) { !StringStartsWith(line, DIRECTORY_END)) {
const char *p; const char *p;
if ((p = StringAfterPrefix(line, DIRECTORY_DIR))) { if ((p = StringAfterPrefix(line, DIRECTORY_DIR))) {
Directory *subdir = directory_load_subdir(file, directory, p);
directory_load_subdir(file, directory,
p, error);
if (subdir == nullptr)
return false;
} else if ((p = StringAfterPrefix(line, SONG_BEGIN))) { } else if ((p = StringAfterPrefix(line, SONG_BEGIN))) {
const char *name = p; const char *name = p;
if (directory.FindSong(name) != nullptr) { if (directory.FindSong(name) != nullptr)
error.Format(directory_domain, throw FormatRuntimeError("Duplicate song '%s'", name);
"Duplicate song '%s'", name);
return false;
}
DetachedSong *song = song_load(file, name, error); DetachedSong *song = song_load(file, name);
if (song == nullptr)
return false;
directory.AddSong(Song::NewFrom(std::move(*song), directory.AddSong(Song::NewFrom(std::move(*song),
directory)); directory));
delete song; delete song;
} else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) { } else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) {
const char *name = p; const char *name = p;
if (!playlist_metadata_load(file, directory.playlists, playlist_metadata_load(file, directory.playlists, name);
name, error))
return false;
} else { } else {
error.Format(directory_domain, throw FormatRuntimeError("Malformed line: %s", line);
"Malformed line: %s", line);
return false;
} }
} }
return true;
} }

View File

@ -28,7 +28,10 @@ class Error;
void void
directory_save(BufferedOutputStream &os, const Directory &directory); directory_save(BufferedOutputStream &os, const Directory &directory);
bool /**
directory_load(TextFile &file, Directory &directory, Error &error); * Throws #std::runtime_error on error.
*/
void
directory_load(TextFile &file, Directory &directory);
#endif #endif

View File

@ -142,22 +142,19 @@ SimpleDatabase::Check() const
#endif #endif
} }
bool void
SimpleDatabase::Load(Error &error) SimpleDatabase::Load()
{ {
assert(!path.IsNull()); assert(!path.IsNull());
assert(root != nullptr); assert(root != nullptr);
TextFile file(path); TextFile file(path);
if (!db_load_internal(file, *root, error)) db_load_internal(file, *root);
return false;
FileInfo fi; FileInfo fi;
if (GetFileInfo(path, fi)) if (GetFileInfo(path, fi))
mtime = fi.GetModificationTime(); mtime = fi.GetModificationTime();
return true;
} }
void void
@ -173,16 +170,7 @@ SimpleDatabase::Open()
#endif #endif
try { try {
Error error2; Load();
if (!Load(error2)) {
LogError(error2);
delete root;
Check();
root = Directory::NewRoot();
}
} catch (const std::exception &e) { } catch (const std::exception &e) {
LogError(e); LogError(e);

View File

@ -136,7 +136,10 @@ private:
void Check() const; void Check() const;
bool Load(Error &error); /**
* Throws #std::runtime_error on error.
*/
void Load();
Database *LockUmountSteal(const char *uri); Database *LockUmountSteal(const char *uri);
}; };

View File

@ -27,7 +27,6 @@
#include "fs/io/TextFile.hxx" #include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx"
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
#include "util/Error.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <stdlib.h> #include <stdlib.h>
@ -95,10 +94,10 @@ queue_load_song(TextFile &file, const SongLoader &loader,
if ((p = StringAfterPrefix(line, SONG_BEGIN))) { if ((p = StringAfterPrefix(line, SONG_BEGIN))) {
const char *uri = p; const char *uri = p;
Error error; try {
song = song_load(file, uri, error); song = song_load(file, uri);
if (song == nullptr) { } catch (const std::runtime_error &e) {
LogError(error); LogError(e);
return; return;
} }
} else { } else {