From fac8edd47aa8f86688e8b7d7d0c6945509954d28 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Sat, 29 Oct 2016 10:21:57 +0200
Subject: [PATCH] db/Interface: migrate visitor methods from class Error to C++
 exceptions

---
 src/Stats.cxx                                 | 15 +--
 src/command/DatabaseCommands.cxx              | 78 ++++++----------
 src/command/PlaylistCommands.cxx              | 20 ++--
 src/command/QueueCommands.cxx                 |  9 +-
 src/db/Count.cxx                              | 13 +--
 src/db/Count.hxx                              |  6 +-
 src/db/DatabasePlaylist.cxx                   |  7 +-
 src/db/DatabasePlaylist.hxx                   |  6 +-
 src/db/DatabasePrint.cxx                      | 29 +++---
 src/db/DatabasePrint.hxx                      | 15 ++-
 src/db/DatabaseQueue.cxx                      |  7 +-
 src/db/DatabaseQueue.hxx                      |  6 +-
 src/db/Helpers.cxx                            | 11 +--
 src/db/Helpers.hxx                            |  6 +-
 src/db/Interface.hxx                          | 29 +++---
 src/db/UniqueTags.cxx                         | 10 +-
 src/db/UniqueTags.hxx                         |  6 +-
 src/db/plugins/ProxyDatabasePlugin.cxx        | 91 +++++++------------
 src/db/plugins/simple/Directory.cxx           | 29 +++---
 src/db/plugins/simple/Directory.hxx           |  6 +-
 src/db/plugins/simple/Mount.cxx               |  8 +-
 src/db/plugins/simple/Mount.hxx               |  6 +-
 .../plugins/simple/SimpleDatabasePlugin.cxx   | 31 +++----
 .../plugins/simple/SimpleDatabasePlugin.hxx   | 20 ++--
 .../plugins/upnp/ContentDirectoryService.cxx  |  1 -
 src/db/plugins/upnp/UpnpDatabasePlugin.cxx    | 48 ++++------
 test/DumpDatabase.cxx                         |  8 +-
 27 files changed, 190 insertions(+), 331 deletions(-)

diff --git a/src/Stats.cxx b/src/Stats.cxx
index 4bf69f643..a0a046b93 100644
--- a/src/Stats.cxx
+++ b/src/Stats.cxx
@@ -26,7 +26,6 @@
 #include "db/Selection.hxx"
 #include "db/Interface.hxx"
 #include "db/Stats.hxx"
-#include "util/Error.hxx"
 #include "system/Clock.hxx"
 #include "Log.hxx"
 
@@ -79,20 +78,12 @@ stats_update(const Database &db)
 		return false;
 	}
 
-	Error error;
-
 	const DatabaseSelection selection("", true);
 
 	try {
-		if (db.GetStats(selection, stats, error)) {
-			stats_validity = StatsValidity::VALID;
-			return true;
-		} else {
-			LogError(error);
-
-			stats_validity = StatsValidity::FAILED;
-			return false;
-		}
+		stats = db.GetStats(selection);
+		stats_validity = StatsValidity::VALID;
+		return true;
 	} catch (const std::runtime_error &e) {
 		LogError(e);
 		stats_validity = StatsValidity::FAILED;
diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx
index a4d3da179..66d4e580c 100644
--- a/src/command/DatabaseCommands.cxx
+++ b/src/command/DatabaseCommands.cxx
@@ -30,7 +30,6 @@
 #include "client/Response.hxx"
 #include "tag/Tag.hxx"
 #include "util/ConstBuffer.hxx"
-#include "util/Error.hxx"
 #include "util/StringAPI.hxx"
 #include "SongFilter.hxx"
 #include "BulkEdit.hxx"
@@ -41,12 +40,8 @@ CommandResult
 handle_listfiles_db(Client &client, Response &r, const char *uri)
 {
 	const DatabaseSelection selection(uri, false);
-
-	Error error;
-	if (!db_selection_print(r, client.partition,
-				selection, false, true, error))
-		return print_error(r, error);
-
+	db_selection_print(r, client.partition,
+			   selection, false, true);
 	return CommandResult::OK;
 }
 
@@ -54,12 +49,8 @@ CommandResult
 handle_lsinfo2(Client &client, const char *uri, Response &r)
 {
 	const DatabaseSelection selection(uri, false);
-
-	Error error;
-	if (!db_selection_print(r, client.partition,
-				selection, true, false, error))
-		return print_error(r, error);
-
+	db_selection_print(r, client.partition,
+			   selection, true, false);
 	return CommandResult::OK;
 }
 
@@ -83,12 +74,10 @@ handle_match(Client &client, Request args, Response &r, bool fold_case)
 
 	const DatabaseSelection selection("", true, &filter);
 
-	Error error;
-	return db_selection_print(r, client.partition,
-				  selection, true, false,
-				  window.start, window.end, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	db_selection_print(r, client.partition,
+			   selection, true, false,
+			   window.start, window.end);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -115,10 +104,8 @@ handle_match_add(Client &client, Request args, Response &r, bool fold_case)
 	const ScopeBulkEdit bulk_edit(client.partition);
 
 	const DatabaseSelection selection("", true, &filter);
-	Error error;
-	return AddFromDatabase(client.partition, selection, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	AddFromDatabase(client.partition, selection);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -144,13 +131,11 @@ handle_searchaddpl(Client &client, Request args, Response &r)
 		return CommandResult::ERROR;
 	}
 
-	Error error;
 	const Database &db = client.GetDatabaseOrThrow();
 
-	return search_add_to_playlist(db, *client.GetStorage(),
-				      "", playlist, &filter, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	search_add_to_playlist(db, *client.GetStorage(),
+			       "", playlist, &filter);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -176,10 +161,8 @@ handle_count(Client &client, Request args, Response &r)
 		return CommandResult::ERROR;
 	}
 
-	Error error;
-	return PrintSongCount(r, client.partition, "", &filter, group, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	PrintSongCount(r, client.partition, "", &filter, group);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -188,12 +171,10 @@ handle_listall(Client &client, Request args, Response &r)
 	/* default is root directory */
 	const auto uri = args.GetOptional(0, "");
 
-	Error error;
-	return db_selection_print(r, client.partition,
-				  DatabaseSelection(uri, true),
-				  false, false, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	db_selection_print(r, client.partition,
+			   DatabaseSelection(uri, true),
+			   false, false);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -255,14 +236,9 @@ handle_list(Client &client, Request args, Response &r)
 		return CommandResult::ERROR;
 	}
 
-	Error error;
-	CommandResult ret =
-		PrintUniqueTags(r, client.partition,
-				tagType, group_mask, filter.get(), error)
-		? CommandResult::OK
-		: print_error(r, error);
-
-	return ret;
+	PrintUniqueTags(r, client.partition,
+			tagType, group_mask, filter.get());
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -271,10 +247,8 @@ handle_listallinfo(Client &client, Request args, Response &r)
 	/* default is root directory */
 	const auto uri = args.GetOptional(0, "");
 
-	Error error;
-	return db_selection_print(r, client.partition,
-				  DatabaseSelection(uri, true),
-				  true, false, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	db_selection_print(r, client.partition,
+			   DatabaseSelection(uri, true),
+			   true, false);
+	return CommandResult::OK;
 }
diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx
index 1974d7b83..a008809ea 100644
--- a/src/command/PlaylistCommands.cxx
+++ b/src/command/PlaylistCommands.cxx
@@ -156,35 +156,27 @@ handle_playlistclear(gcc_unused Client &client,
 }
 
 CommandResult
-handle_playlistadd(Client &client, Request args, Response &r)
+handle_playlistadd(Client &client, Request args, gcc_unused Response &r)
 {
 	const char *const playlist = args[0];
 	const char *const uri = args[1];
 
-	bool success;
-	Error error;
 	if (uri_has_scheme(uri)) {
 		const SongLoader loader(client);
 		spl_append_uri(playlist, loader, uri);
-		success = true;
 	} else {
 #ifdef ENABLE_DATABASE
 		const Database &db = client.GetDatabaseOrThrow();
 
-		success = search_add_to_playlist(db, *client.GetStorage(),
-						 uri, playlist, nullptr,
-						 error);
+		search_add_to_playlist(db, *client.GetStorage(),
+				       uri, playlist, nullptr);
 #else
-		success = false;
+		r.Error(ACK_ERROR_NO_EXIST, "directory or file not found");
+		return CommandResult::ERROR;
 #endif
 	}
 
-	if (!success && !error.IsDefined()) {
-		r.Error(ACK_ERROR_NO_EXIST, "directory or file not found");
-		return CommandResult::ERROR;
-	}
-
-	return success ? CommandResult::OK : print_error(r, error);
+	return CommandResult::OK;
 }
 
 CommandResult
diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx
index f26b34f7c..911b21304 100644
--- a/src/command/QueueCommands.cxx
+++ b/src/command/QueueCommands.cxx
@@ -52,16 +52,15 @@ AddUri(Client &client, const LocatedUri &uri)
 }
 
 static CommandResult
-AddDatabaseSelection(Client &client, const char *uri, Response &r)
+AddDatabaseSelection(Client &client, const char *uri,
+		     gcc_unused Response &r)
 {
 #ifdef ENABLE_DATABASE
 	const ScopeBulkEdit bulk_edit(client.partition);
 
 	const DatabaseSelection selection(uri, true);
-	Error error;
-	return AddFromDatabase(client.partition, selection, error)
-		? CommandResult::OK
-		: print_error(r, error);
+	AddFromDatabase(client.partition, selection);
+	return CommandResult::OK;
 #else
 	(void)client;
 	(void)uri;
diff --git a/src/db/Count.cxx b/src/db/Count.cxx
index 1bd75448b..99e31fce8 100644
--- a/src/db/Count.cxx
+++ b/src/db/Count.cxx
@@ -107,11 +107,10 @@ GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
 	return true;
 }
 
-bool
+void
 PrintSongCount(Response &r, const Partition &partition, const char *name,
 	       const SongFilter *filter,
-	       TagType group,
-	       Error &error)
+	       TagType group)
 {
 	const Database &db = partition.GetDatabaseOrThrow();
 
@@ -125,8 +124,7 @@ PrintSongCount(Response &r, const Partition &partition, const char *name,
 		using namespace std::placeholders;
 		const auto f = std::bind(stats_visitor_song, std::ref(stats),
 					 _1);
-		if (!db.Visit(selection, f, error))
-			return false;
+		db.Visit(selection, f);
 
 		PrintSearchStats(r, stats);
 	} else {
@@ -138,11 +136,8 @@ PrintSongCount(Response &r, const Partition &partition, const char *name,
 		using namespace std::placeholders;
 		const auto f = std::bind(GroupCountVisitor, std::ref(map),
 					 group, _1);
-		if (!db.Visit(selection, f, error))
-			return false;
+		db.Visit(selection, f);
 
 		Print(r, group, map);
 	}
-
-	return true;
 }
diff --git a/src/db/Count.hxx b/src/db/Count.hxx
index 7ba189706..188fe1053 100644
--- a/src/db/Count.hxx
+++ b/src/db/Count.hxx
@@ -28,13 +28,11 @@ enum TagType : uint8_t;
 struct Partition;
 class Response;
 class SongFilter;
-class Error;
 
 gcc_nonnull(3)
-bool
+void
 PrintSongCount(Response &r, const Partition &partition, const char *name,
 	       const SongFilter *filter,
-	       TagType group,
-	       Error &error);
+	       TagType group);
 
 #endif
diff --git a/src/db/DatabasePlaylist.cxx b/src/db/DatabasePlaylist.cxx
index a052fdfbf..d17211491 100644
--- a/src/db/DatabasePlaylist.cxx
+++ b/src/db/DatabasePlaylist.cxx
@@ -36,16 +36,15 @@ AddSong(const Storage &storage, const char *playlist_path_utf8,
 	return true;
 }
 
-bool
+void
 search_add_to_playlist(const Database &db, const Storage &storage,
 		       const char *uri, const char *playlist_path_utf8,
-		       const SongFilter *filter,
-		       Error &error)
+		       const SongFilter *filter)
 {
 	const DatabaseSelection selection(uri, true, filter);
 
 	using namespace std::placeholders;
 	const auto f = std::bind(AddSong, std::ref(storage),
 				 playlist_path_utf8, _1);
-	return db.Visit(selection, f, error);
+	db.Visit(selection, f);
 }
diff --git a/src/db/DatabasePlaylist.hxx b/src/db/DatabasePlaylist.hxx
index 0b3534fc1..d219ac2b1 100644
--- a/src/db/DatabasePlaylist.hxx
+++ b/src/db/DatabasePlaylist.hxx
@@ -25,13 +25,11 @@
 class Database;
 class Storage;
 class SongFilter;
-class Error;
 
 gcc_nonnull(3,4)
-bool
+void
 search_add_to_playlist(const Database &db, const Storage &storage,
 		       const char *uri, const char *path_utf8,
-		       const SongFilter *filter,
-		       Error &error);
+		       const SongFilter *filter);
 
 #endif
diff --git a/src/db/DatabasePrint.cxx b/src/db/DatabasePrint.cxx
index 024573713..14725d4ce 100644
--- a/src/db/DatabasePrint.cxx
+++ b/src/db/DatabasePrint.cxx
@@ -148,12 +148,11 @@ PrintPlaylistFull(Response &r, bool base,
 	return true;
 }
 
-bool
+void
 db_selection_print(Response &r, Partition &partition,
 		   const DatabaseSelection &selection,
 		   bool full, bool base,
-		   unsigned window_start, unsigned window_end,
-		   Error &error)
+		   unsigned window_start, unsigned window_end)
 {
 	const Database &db = partition.GetDatabaseOrThrow();
 
@@ -180,18 +179,16 @@ db_selection_print(Response &r, Partition &partition,
 				s(song);
 		};
 
-	return db.Visit(selection, d, s, p, error);
+	db.Visit(selection, d, s, p);
 }
 
-bool
+void
 db_selection_print(Response &r, Partition &partition,
 		   const DatabaseSelection &selection,
-		   bool full, bool base,
-		   Error &error)
+		   bool full, bool base)
 {
-	return db_selection_print(r, partition, selection, full, base,
-				  0, std::numeric_limits<int>::max(),
-				  error);
+	db_selection_print(r, partition, selection, full, base,
+			   0, std::numeric_limits<int>::max());
 }
 
 static bool
@@ -218,11 +215,10 @@ PrintUniqueTag(Response &r, TagType tag_type,
 	return true;
 }
 
-bool
+void
 PrintUniqueTags(Response &r, Partition &partition,
 		unsigned type, tag_mask_t group_mask,
-		const SongFilter *filter,
-		Error &error)
+		const SongFilter *filter)
 {
 	const Database &db = partition.GetDatabaseOrThrow();
 
@@ -232,15 +228,14 @@ PrintUniqueTags(Response &r, Partition &partition,
 		using namespace std::placeholders;
 		const auto f = std::bind(PrintSongURIVisitor,
 					 std::ref(r), std::ref(partition), _1);
-		return db.Visit(selection, f, error);
+		db.Visit(selection, f);
 	} else {
 		assert(type < TAG_NUM_OF_ITEM_TYPES);
 
 		using namespace std::placeholders;
 		const auto f = std::bind(PrintUniqueTag, std::ref(r),
 					 (TagType)type, _1);
-		return db.VisitUniqueTags(selection, (TagType)type,
-					  group_mask,
-					  f, error);
+		db.VisitUniqueTags(selection, (TagType)type,
+				   group_mask, f);
 	}
 }
diff --git a/src/db/DatabasePrint.hxx b/src/db/DatabasePrint.hxx
index 2c85f7c06..db435000f 100644
--- a/src/db/DatabasePrint.hxx
+++ b/src/db/DatabasePrint.hxx
@@ -26,28 +26,25 @@ class SongFilter;
 struct DatabaseSelection;
 struct Partition;
 class Response;
-class Error;
 
 /**
  * @param full print attributes/tags
  * @param base print only base name of songs/directories?
  */
-bool
+void
 db_selection_print(Response &r, Partition &partition,
 		   const DatabaseSelection &selection,
-		   bool full, bool base, Error &error);
+		   bool full, bool base);
 
-bool
+void
 db_selection_print(Response &r, Partition &partition,
 		   const DatabaseSelection &selection,
 		   bool full, bool base,
-		   unsigned window_start, unsigned window_end,
-		   Error &error);
+		   unsigned window_start, unsigned window_end);
 
-bool
+void
 PrintUniqueTags(Response &r, Partition &partition,
 		unsigned type, tag_mask_t group_mask,
-		const SongFilter *filter,
-		Error &error);
+		const SongFilter *filter);
 
 #endif
diff --git a/src/db/DatabaseQueue.cxx b/src/db/DatabaseQueue.cxx
index 07869c032..f5c644609 100644
--- a/src/db/DatabaseQueue.cxx
+++ b/src/db/DatabaseQueue.cxx
@@ -37,13 +37,12 @@ AddToQueue(Partition &partition, const LightSong &song)
 	return true;
 }
 
-bool
-AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
-		Error &error)
+void
+AddFromDatabase(Partition &partition, const DatabaseSelection &selection)
 {
 	const Database &db = partition.instance.GetDatabaseOrThrow();
 
 	using namespace std::placeholders;
 	const auto f = std::bind(AddToQueue, std::ref(partition), _1);
-	return db.Visit(selection, f, error);
+	db.Visit(selection, f);
 }
diff --git a/src/db/DatabaseQueue.hxx b/src/db/DatabaseQueue.hxx
index ac3a2713c..bec65f986 100644
--- a/src/db/DatabaseQueue.hxx
+++ b/src/db/DatabaseQueue.hxx
@@ -22,10 +22,8 @@
 
 struct Partition;
 struct DatabaseSelection;
-class Error;
 
-bool
-AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
-		Error &error);
+void
+AddFromDatabase(Partition &partition, const DatabaseSelection &selection);
 
 #endif
diff --git a/src/db/Helpers.cxx b/src/db/Helpers.cxx
index 2eaa37c28..2ae89663e 100644
--- a/src/db/Helpers.cxx
+++ b/src/db/Helpers.cxx
@@ -78,10 +78,10 @@ StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
 	return true;
 }
 
-bool
-GetStats(const Database &db, const DatabaseSelection &selection,
-	 DatabaseStats &stats, Error &error)
+DatabaseStats
+GetStats(const Database &db, const DatabaseSelection &selection)
 {
+	DatabaseStats stats;
 	stats.Clear();
 
 	StringSet artists, albums;
@@ -89,10 +89,9 @@ GetStats(const Database &db, const DatabaseSelection &selection,
 	const auto f = std::bind(StatsVisitSong,
 				 std::ref(stats), std::ref(artists),
 				 std::ref(albums), _1);
-	if (!db.Visit(selection, f, error))
-		return false;
+	db.Visit(selection, f);
 
 	stats.artist_count = artists.size();
 	stats.album_count = albums.size();
-	return true;
+	return stats;
 }
diff --git a/src/db/Helpers.hxx b/src/db/Helpers.hxx
index 86a4f7034..b9855eb5f 100644
--- a/src/db/Helpers.hxx
+++ b/src/db/Helpers.hxx
@@ -20,13 +20,11 @@
 #ifndef MPD_MEMORY_DATABASE_PLUGIN_HXX
 #define MPD_MEMORY_DATABASE_PLUGIN_HXX
 
-class Error;
 class Database;
 struct DatabaseSelection;
 struct DatabaseStats;
 
-bool
-GetStats(const Database &db, const DatabaseSelection &selection,
-	 DatabaseStats &stats, Error &error);
+DatabaseStats
+GetStats(const Database &db, const DatabaseSelection &selection);
 
 #endif
diff --git a/src/db/Interface.hxx b/src/db/Interface.hxx
index 625328b42..6ad9560d6 100644
--- a/src/db/Interface.hxx
+++ b/src/db/Interface.hxx
@@ -84,36 +84,31 @@ public:
 	/**
 	 * Visit the selected entities.
 	 */
-	virtual bool Visit(const DatabaseSelection &selection,
+	virtual void Visit(const DatabaseSelection &selection,
 			   VisitDirectory visit_directory,
 			   VisitSong visit_song,
-			   VisitPlaylist visit_playlist,
-			   Error &error) const = 0;
+			   VisitPlaylist visit_playlist) const = 0;
 
-	bool Visit(const DatabaseSelection &selection,
+	void Visit(const DatabaseSelection &selection,
 		   VisitDirectory visit_directory,
-		   VisitSong visit_song,
-		   Error &error) const {
-		return Visit(selection, visit_directory, visit_song,
-			     VisitPlaylist(), error);
+		   VisitSong visit_song) const {
+		Visit(selection, visit_directory, visit_song, VisitPlaylist());
 	}
 
-	bool Visit(const DatabaseSelection &selection, VisitSong visit_song,
-		   Error &error) const {
-		return Visit(selection, VisitDirectory(), visit_song, error);
+	void Visit(const DatabaseSelection &selection,
+		   VisitSong visit_song) const {
+		return Visit(selection, VisitDirectory(), visit_song);
 	}
 
 	/**
 	 * Visit all unique tag values.
 	 */
-	virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+	virtual void VisitUniqueTags(const DatabaseSelection &selection,
 				     TagType tag_type, tag_mask_t group_mask,
-				     VisitTag visit_tag,
-				     Error &error) const = 0;
+				     VisitTag visit_tag) const = 0;
 
-	virtual bool GetStats(const DatabaseSelection &selection,
-			      DatabaseStats &stats,
-			      Error &error) const = 0;
+	gcc_pure
+	virtual DatabaseStats GetStats(const DatabaseSelection &selection) const = 0;
 
 	/**
 	 * Update the database.  Returns the job id on success, 0 on
diff --git a/src/db/UniqueTags.cxx b/src/db/UniqueTags.cxx
index 76a60e674..8bd4a7bdc 100644
--- a/src/db/UniqueTags.cxx
+++ b/src/db/UniqueTags.cxx
@@ -36,22 +36,18 @@ CollectTags(TagSet &set, TagType tag_type, tag_mask_t group_mask,
 	set.InsertUnique(tag, tag_type, group_mask);
 }
 
-bool
+void
 VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
 		TagType tag_type, tag_mask_t group_mask,
-		VisitTag visit_tag,
-		Error &error)
+		VisitTag visit_tag)
 {
 	TagSet set;
 
 	using namespace std::placeholders;
 	const auto f = std::bind(CollectTags, std::ref(set),
 				 tag_type, group_mask, _1);
-	if (!db.Visit(selection, f, error))
-		return false;
+	db.Visit(selection, f);
 
 	for (const auto &value : set)
 		visit_tag(value);
-
-	return true;
 }
diff --git a/src/db/UniqueTags.hxx b/src/db/UniqueTags.hxx
index f841c556a..cc581d0ea 100644
--- a/src/db/UniqueTags.hxx
+++ b/src/db/UniqueTags.hxx
@@ -24,14 +24,12 @@
 #include "tag/TagType.h"
 #include "tag/Mask.hxx"
 
-class Error;
 class Database;
 struct DatabaseSelection;
 
-bool
+void
 VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
 		TagType tag_type, tag_mask_t group_mask,
-		VisitTag visit_tag,
-		Error &error);
+		VisitTag visit_tag);
 
 #endif
diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx
index a865343b8..2493569e2 100644
--- a/src/db/plugins/ProxyDatabasePlugin.cxx
+++ b/src/db/plugins/ProxyDatabasePlugin.cxx
@@ -33,8 +33,6 @@
 #include "config/Block.hxx"
 #include "tag/TagBuilder.hxx"
 #include "tag/Tag.hxx"
-#include "util/Error.hxx"
-#include "util/Domain.hxx"
 #include "util/ScopeExit.hxx"
 #include "protocol/Ack.hxx"
 #include "event/SocketMonitor.hxx"
@@ -115,20 +113,16 @@ public:
 	const LightSong *GetSong(const char *uri_utf8) const override;
 	void ReturnSong(const LightSong *song) const override;
 
-	virtual bool Visit(const DatabaseSelection &selection,
-			   VisitDirectory visit_directory,
-			   VisitSong visit_song,
-			   VisitPlaylist visit_playlist,
-			   Error &error) const override;
+	void Visit(const DatabaseSelection &selection,
+		   VisitDirectory visit_directory,
+		   VisitSong visit_song,
+		   VisitPlaylist visit_playlist) const override;
 
-	virtual bool VisitUniqueTags(const DatabaseSelection &selection,
-				     TagType tag_type, tag_mask_t group_mask,
-				     VisitTag visit_tag,
-				     Error &error) const override;
+	void VisitUniqueTags(const DatabaseSelection &selection,
+			     TagType tag_type, tag_mask_t group_mask,
+			     VisitTag visit_tag) const override;
 
-	virtual bool GetStats(const DatabaseSelection &selection,
-			      DatabaseStats &stats,
-			      Error &error) const override;
+	DatabaseStats GetStats(const DatabaseSelection &selection) const override;
 
 	virtual unsigned Update(const char *uri_utf8, bool discard,
 				Error &error) override;
@@ -151,8 +145,6 @@ private:
 	virtual void OnIdle() override;
 };
 
-static constexpr Domain libmpdclient_domain("libmpdclient");
-
 static constexpr struct {
 	TagType d;
 	enum mpd_tag_type s;
@@ -543,18 +535,18 @@ ProxyDatabase::ReturnSong(const LightSong *_song) const
 	delete song;
 }
 
-static bool
+static void
 Visit(struct mpd_connection *connection, const char *uri,
       bool recursive, const SongFilter *filter,
       VisitDirectory visit_directory, VisitSong visit_song,
-      VisitPlaylist visit_playlist, Error &error);
+      VisitPlaylist visit_playlist);
 
-static bool
+static void
 Visit(struct mpd_connection *connection,
       bool recursive, const SongFilter *filter,
       const struct mpd_directory *directory,
       VisitDirectory visit_directory, VisitSong visit_song,
-      VisitPlaylist visit_playlist, Error &error)
+      VisitPlaylist visit_playlist)
 {
 	const char *path = mpd_directory_get_path(directory);
 #if LIBMPDCLIENT_CHECK_VERSION(2,9,0)
@@ -566,12 +558,9 @@ Visit(struct mpd_connection *connection,
 	if (visit_directory)
 		visit_directory(LightDirectory(path, mtime));
 
-	if (recursive &&
-	    !Visit(connection, path, recursive, filter,
-		   visit_directory, visit_song, visit_playlist, error))
-		return false;
-
-	return true;
+	if (recursive)
+		Visit(connection, path, recursive, filter,
+		      visit_directory, visit_song, visit_playlist);
 }
 
 gcc_pure
@@ -645,11 +634,11 @@ ReceiveEntities(struct mpd_connection *connection)
 	return entities;
 }
 
-static bool
+static void
 Visit(struct mpd_connection *connection, const char *uri,
       bool recursive, const SongFilter *filter,
       VisitDirectory visit_directory, VisitSong visit_song,
-      VisitPlaylist visit_playlist, Error &error)
+      VisitPlaylist visit_playlist)
 {
 	if (!mpd_send_list_meta(connection, uri))
 		ThrowError(connection);
@@ -663,11 +652,9 @@ Visit(struct mpd_connection *connection, const char *uri,
 			break;
 
 		case MPD_ENTITY_TYPE_DIRECTORY:
-			if (!Visit(connection, recursive, filter,
-				   mpd_entity_get_directory(entity),
-				   visit_directory, visit_song, visit_playlist,
-				   error))
-				return false;
+			Visit(connection, recursive, filter,
+			      mpd_entity_get_directory(entity),
+			      visit_directory, visit_song, visit_playlist);
 			break;
 
 		case MPD_ENTITY_TYPE_SONG:
@@ -680,8 +667,6 @@ Visit(struct mpd_connection *connection, const char *uri,
 			break;
 		}
 	}
-
-	return true;
 }
 
 static void
@@ -734,12 +719,11 @@ ServerSupportsSearchBase(const struct mpd_connection *connection)
 #endif
 }
 
-bool
+void
 ProxyDatabase::Visit(const DatabaseSelection &selection,
 		     VisitDirectory visit_directory,
 		     VisitSong visit_song,
-		     VisitPlaylist visit_playlist,
-		     Error &error) const
+		     VisitPlaylist visit_playlist) const
 {
 	// TODO: eliminate the const_cast
 	const_cast<ProxyDatabase *>(this)->EnsureConnected();
@@ -751,31 +735,27 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
 		/* this optimized code path can only be used under
 		   certain conditions */
 		::SearchSongs(connection, selection, visit_song);
-		return true;
+		return;
 	}
 
 	/* fall back to recursive walk (slow!) */
-	return ::Visit(connection, selection.uri.c_str(),
-		       selection.recursive, selection.filter,
-		       visit_directory, visit_song, visit_playlist,
-		       error);
+	::Visit(connection, selection.uri.c_str(),
+		selection.recursive, selection.filter,
+		visit_directory, visit_song, visit_playlist);
 }
 
-bool
+void
 ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 			       TagType tag_type,
 			       gcc_unused tag_mask_t group_mask,
-			       VisitTag visit_tag,
-			       Error &error) const
+			       VisitTag visit_tag) const
 {
 	// TODO: eliminate the const_cast
 	const_cast<ProxyDatabase *>(this)->EnsureConnected();
 
 	enum mpd_tag_type tag_type2 = Convert(tag_type);
-	if (tag_type2 == MPD_TAG_COUNT) {
-		error.Set(libmpdclient_domain, "Unsupported tag");
-		return false;
-	}
+	if (tag_type2 == MPD_TAG_COUNT)
+		throw std::runtime_error("Unsupported tag");
 
 	if (!mpd_search_db_tags(connection, tag_type2) ||
 	    !SendConstraints(connection, selection))
@@ -812,13 +792,10 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 
 	if (!mpd_response_finish(connection))
 		ThrowError(connection);
-
-	return true;
 }
 
-bool
-ProxyDatabase::GetStats(const DatabaseSelection &selection,
-			DatabaseStats &stats, gcc_unused Error &error) const
+DatabaseStats
+ProxyDatabase::GetStats(const DatabaseSelection &selection) const
 {
 	// TODO: match
 	(void)selection;
@@ -833,13 +810,13 @@ ProxyDatabase::GetStats(const DatabaseSelection &selection,
 
 	update_stamp = (time_t)mpd_stats_get_db_update_time(stats2);
 
+	DatabaseStats stats;
 	stats.song_count = mpd_stats_get_number_of_songs(stats2);
 	stats.total_duration = std::chrono::seconds(mpd_stats_get_db_play_time(stats2));
 	stats.artist_count = mpd_stats_get_number_of_artists(stats2);
 	stats.album_count = mpd_stats_get_number_of_albums(stats2);
 	mpd_stats_free(stats2);
-
-	return true;
+	return stats;
 }
 
 unsigned
diff --git a/src/db/plugins/simple/Directory.cxx b/src/db/plugins/simple/Directory.cxx
index 4aa5c8aa5..9fc6e2dc2 100644
--- a/src/db/plugins/simple/Directory.cxx
+++ b/src/db/plugins/simple/Directory.cxx
@@ -32,7 +32,6 @@
 #include "fs/Traits.hxx"
 #include "util/Alloc.hxx"
 #include "util/DeleteDisposer.hxx"
-#include "util/Error.hxx"
 
 #include <assert.h>
 #include <string.h>
@@ -218,14 +217,11 @@ Directory::Sort()
 		child.Sort();
 }
 
-bool
+void
 Directory::Walk(bool recursive, const SongFilter *filter,
 		VisitDirectory visit_directory, VisitSong visit_song,
-		VisitPlaylist visit_playlist,
-		Error &error) const
+		VisitPlaylist visit_playlist) const
 {
-	assert(!error.IsDefined());
-
 	if (IsMount()) {
 		assert(IsEmpty());
 
@@ -233,11 +229,11 @@ Directory::Walk(bool recursive, const SongFilter *filter,
 		   because the child's SimpleDatabasePlugin::Visit()
 		   call will lock it again */
 		const ScopeDatabaseUnlock unlock;
-		return WalkMount(GetPath(), *mounted_database,
-				 recursive, filter,
-				 visit_directory, visit_song,
-				 visit_playlist,
-				 error);
+		WalkMount(GetPath(), *mounted_database,
+			  recursive, filter,
+			  visit_directory, visit_song,
+			  visit_playlist);
+		return;
 	}
 
 	if (visit_song) {
@@ -257,14 +253,11 @@ Directory::Walk(bool recursive, const SongFilter *filter,
 		if (visit_directory)
 			visit_directory(child.Export());
 
-		if (recursive &&
-		    !child.Walk(recursive, filter,
-				visit_directory, visit_song, visit_playlist,
-				error))
-			return false;
+		if (recursive)
+			child.Walk(recursive, filter,
+				   visit_directory, visit_song,
+				   visit_playlist);
 	}
-
-	return true;
 }
 
 LightDirectory
diff --git a/src/db/plugins/simple/Directory.hxx b/src/db/plugins/simple/Directory.hxx
index 2eda91c46..29618130b 100644
--- a/src/db/plugins/simple/Directory.hxx
+++ b/src/db/plugins/simple/Directory.hxx
@@ -44,7 +44,6 @@ static constexpr unsigned DEVICE_INARCHIVE = -1;
 static constexpr unsigned DEVICE_CONTAINER = -2;
 
 class SongFilter;
-class Error;
 class Database;
 
 struct Directory {
@@ -266,10 +265,9 @@ public:
 	/**
 	 * Caller must lock #db_mutex.
 	 */
-	bool Walk(bool recursive, const SongFilter *match,
+	void Walk(bool recursive, const SongFilter *match,
 		  VisitDirectory visit_directory, VisitSong visit_song,
-		  VisitPlaylist visit_playlist,
-		  Error &error) const;
+		  VisitPlaylist visit_playlist) const;
 
 	gcc_pure
 	LightDirectory Export() const;
diff --git a/src/db/plugins/simple/Mount.cxx b/src/db/plugins/simple/Mount.cxx
index 9708780a1..e55bba03d 100644
--- a/src/db/plugins/simple/Mount.cxx
+++ b/src/db/plugins/simple/Mount.cxx
@@ -63,12 +63,11 @@ PrefixVisitPlaylist(const char *base, const VisitPlaylist &visit_playlist,
 		       PrefixedLightDirectory(directory, base));
 }
 
-bool
+void
 WalkMount(const char *base, const Database &db,
 	  bool recursive, const SongFilter *filter,
 	  const VisitDirectory &visit_directory, const VisitSong &visit_song,
-	  const VisitPlaylist &visit_playlist,
-	  Error &error)
+	  const VisitPlaylist &visit_playlist)
 {
 	using namespace std::placeholders;
 
@@ -87,6 +86,5 @@ WalkMount(const char *base, const Database &db,
 		vp = std::bind(PrefixVisitPlaylist,
 			       base, std::ref(visit_playlist), _1, _2);
 
-	return db.Visit(DatabaseSelection("", recursive, filter),
-			vd, vs, vp, error);
+	db.Visit(DatabaseSelection("", recursive, filter), vd, vs, vp);
 }
diff --git a/src/db/plugins/simple/Mount.hxx b/src/db/plugins/simple/Mount.hxx
index f6bea4e18..5aff9533a 100644
--- a/src/db/plugins/simple/Mount.hxx
+++ b/src/db/plugins/simple/Mount.hxx
@@ -24,13 +24,11 @@
 
 class Database;
 class SongFilter;
-class Error;
 
-bool
+void
 WalkMount(const char *base, const Database &db,
 	  bool recursive, const SongFilter *filter,
 	  const VisitDirectory &visit_directory, const VisitSong &visit_song,
-	  const VisitPlaylist &visit_playlist,
-	  Error &error);
+	  const VisitPlaylist &visit_playlist);
 
 #endif
diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx
index 2260e8aca..075f33f13 100644
--- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx
+++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx
@@ -23,6 +23,7 @@
 #include "db/DatabasePlugin.hxx"
 #include "db/Selection.hxx"
 #include "db/Helpers.hxx"
+#include "db/Stats.hxx"
 #include "db/UniqueTags.hxx"
 #include "db/LightDirectory.hxx"
 #include "Directory.hxx"
@@ -37,7 +38,6 @@
 #include "config/Block.hxx"
 #include "fs/FileSystem.hxx"
 #include "util/CharUtil.hxx"
-#include "util/Error.hxx"
 #include "util/Domain.hxx"
 #include "Log.hxx"
 
@@ -261,12 +261,11 @@ SimpleDatabase::ReturnSong(gcc_unused const LightSong *song) const
 #endif
 }
 
-bool
+void
 SimpleDatabase::Visit(const DatabaseSelection &selection,
 		      VisitDirectory visit_directory,
 		      VisitSong visit_song,
-		      VisitPlaylist visit_playlist,
-		      Error &error) const
+		      VisitPlaylist visit_playlist) const
 {
 	ScopeDatabaseLock protect;
 
@@ -277,10 +276,10 @@ SimpleDatabase::Visit(const DatabaseSelection &selection,
 		if (selection.recursive && visit_directory)
 			visit_directory(r.directory->Export());
 
-		return r.directory->Walk(selection.recursive, selection.filter,
-					 visit_directory, visit_song,
-					 visit_playlist,
-					 error);
+		r.directory->Walk(selection.recursive, selection.filter,
+				  visit_directory, visit_song,
+				  visit_playlist);
+		return;
 	}
 
 	if (strchr(r.uri, '/') == nullptr) {
@@ -298,22 +297,18 @@ SimpleDatabase::Visit(const DatabaseSelection &selection,
 			    "No such directory");
 }
 
-bool
+void
 SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 				TagType tag_type, tag_mask_t group_mask,
-				VisitTag visit_tag,
-				Error &error) const
+				VisitTag visit_tag) const
 {
-	return ::VisitUniqueTags(*this, selection, tag_type, group_mask,
-				 visit_tag,
-				 error);
+	::VisitUniqueTags(*this, selection, tag_type, group_mask, visit_tag);
 }
 
-bool
-SimpleDatabase::GetStats(const DatabaseSelection &selection,
-			 DatabaseStats &stats, Error &error) const
+DatabaseStats
+SimpleDatabase::GetStats(const DatabaseSelection &selection) const
 {
-	return ::GetStats(*this, selection, stats, error);
+	return ::GetStats(*this, selection);
 }
 
 void
diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.hxx b/src/db/plugins/simple/SimpleDatabasePlugin.hxx
index 0d96fdbba..d01ae498f 100644
--- a/src/db/plugins/simple/SimpleDatabasePlugin.hxx
+++ b/src/db/plugins/simple/SimpleDatabasePlugin.hxx
@@ -112,20 +112,16 @@ public:
 	const LightSong *GetSong(const char *uri_utf8) const override;
 	void ReturnSong(const LightSong *song) const override;
 
-	virtual bool Visit(const DatabaseSelection &selection,
-			   VisitDirectory visit_directory,
-			   VisitSong visit_song,
-			   VisitPlaylist visit_playlist,
-			   Error &error) const override;
+	void Visit(const DatabaseSelection &selection,
+		   VisitDirectory visit_directory,
+		   VisitSong visit_song,
+		   VisitPlaylist visit_playlist) const override;
 
-	virtual bool VisitUniqueTags(const DatabaseSelection &selection,
-				     TagType tag_type, tag_mask_t group_mask,
-				     VisitTag visit_tag,
-				     Error &error) const override;
+	void VisitUniqueTags(const DatabaseSelection &selection,
+			     TagType tag_type, tag_mask_t group_mask,
+			     VisitTag visit_tag) const override;
 
-	virtual bool GetStats(const DatabaseSelection &selection,
-			      DatabaseStats &stats,
-			      Error &error) const override;
+	DatabaseStats GetStats(const DatabaseSelection &selection) const override;
 
 	virtual time_t GetUpdateStamp() const override {
 		return mtime;
diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx
index 6754b48b7..6df0b6850 100644
--- a/src/db/plugins/upnp/ContentDirectoryService.cxx
+++ b/src/db/plugins/upnp/ContentDirectoryService.cxx
@@ -26,7 +26,6 @@
 #include "util/NumberParser.hxx"
 #include "util/UriUtil.hxx"
 #include "util/RuntimeError.hxx"
-#include "util/Error.hxx"
 #include "util/ScopeExit.hxx"
 
 #include <stdio.h>
diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx
index 702a37602..3be615d3a 100644
--- a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx
+++ b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx
@@ -35,8 +35,6 @@
 #include "config/Block.hxx"
 #include "tag/TagBuilder.hxx"
 #include "tag/TagTable.hxx"
-#include "util/Error.hxx"
-#include "util/Domain.hxx"
 #include "fs/Traits.hxx"
 #include "Log.hxx"
 #include "SongFilter.hxx"
@@ -84,20 +82,17 @@ public:
 	virtual const LightSong *GetSong(const char *uri_utf8) const override;
 	void ReturnSong(const LightSong *song) const override;
 
-	virtual bool Visit(const DatabaseSelection &selection,
-			   VisitDirectory visit_directory,
-			   VisitSong visit_song,
-			   VisitPlaylist visit_playlist,
-			   Error &error) const override;
+	void Visit(const DatabaseSelection &selection,
+		   VisitDirectory visit_directory,
+		   VisitSong visit_song,
+		   VisitPlaylist visit_playlist) const override;
 
-	virtual bool VisitUniqueTags(const DatabaseSelection &selection,
-				     TagType tag_type, tag_mask_t group_mask,
-				     VisitTag visit_tag,
-				     Error &error) const override;
+	void VisitUniqueTags(const DatabaseSelection &selection,
+			     TagType tag_type, tag_mask_t group_mask,
+			     VisitTag visit_tag) const override;
+
+	DatabaseStats GetStats(const DatabaseSelection &selection) const override;
 
-	virtual bool GetStats(const DatabaseSelection &selection,
-			      DatabaseStats &stats,
-			      Error &error) const override;
 	time_t GetUpdateStamp() const override {
 		return 0;
 	}
@@ -576,12 +571,11 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server,
 }
 
 // Deal with the possibly multiple servers, call VisitServer if needed.
-bool
+void
 UpnpDatabase::Visit(const DatabaseSelection &selection,
 		    VisitDirectory visit_directory,
 		    VisitSong visit_song,
-		    VisitPlaylist visit_playlist,
-		    Error &) const
+		    VisitPlaylist visit_playlist) const
 {
 	auto vpath = stringToTokens(selection.uri, "/", true);
 	if (vpath.empty()) {
@@ -597,7 +591,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
 					    visit_playlist);
 		}
 
-		return true;
+		return;
 	}
 
 	// We do have a path: the first element selects the server
@@ -607,19 +601,17 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
 	auto server = discovery->GetServer(servername.c_str());
 	VisitServer(server, vpath, selection,
 		    visit_directory, visit_song, visit_playlist);
-	return true;
 }
 
-bool
+void
 UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 			      TagType tag, gcc_unused tag_mask_t group_mask,
-			      VisitTag visit_tag,
-			      Error &) const
+			      VisitTag visit_tag) const
 {
 	// TODO: use group_mask
 
 	if (!visit_tag)
-		return true;
+		return;
 
 	std::set<std::string> values;
 	for (auto& server : discovery->GetDirectories()) {
@@ -646,18 +638,16 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 		builder.AddItem(tag, value.c_str());
 		visit_tag(builder.Commit());
 	}
-
-	return true;
 }
 
-bool
-UpnpDatabase::GetStats(const DatabaseSelection &,
-		       DatabaseStats &stats, Error &) const
+DatabaseStats
+UpnpDatabase::GetStats(const DatabaseSelection &) const
 {
 	/* Note: this gets called before the daemonizing so we can't
 	   reallyopen this would be a problem if we had real stats */
+	DatabaseStats stats;
 	stats.Clear();
-	return true;
+	return stats;
 }
 
 const DatabasePlugin upnp_db_plugin = {
diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx
index 4324b2088..e6a5030dd 100644
--- a/test/DumpDatabase.cxx
+++ b/test/DumpDatabase.cxx
@@ -33,7 +33,6 @@
 #include "fs/Path.hxx"
 #include "event/Loop.hxx"
 #include "Log.hxx"
-#include "util/Error.hxx"
 #include "util/ScopeExit.hxx"
 
 #include <stdexcept>
@@ -108,7 +107,6 @@ try {
 	config_global_init();
 	AtScopeExit() { config_global_finish(); };
 
-	Error error;
 	ReadConfigFile(config_path);
 
 	TagLoadConfig();
@@ -133,11 +131,7 @@ try {
 
 	const DatabaseSelection selection("", true);
 
-	if (!db->Visit(selection, DumpDirectory, DumpSong, DumpPlaylist,
-		       error)) {
-		cerr << error.GetMessage() << endl;
-		return EXIT_FAILURE;
-	}
+	db->Visit(selection, DumpDirectory, DumpSong, DumpPlaylist);
 
 	return EXIT_SUCCESS;
  } catch (const std::exception &e) {