src/database/sql_database.h
Rama master · réplica de lectura del repositorio original.
/*MT*
MediaTomb - http://www.mediatomb.cc/
sql_database.h - this file is part of MediaTomb.
Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
Copyright (C) 2006-2010 Gena Batyan <bgeradz@mediatomb.cc>,
Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
Leonhard Wimmer <leo@mediatomb.cc>
Copyright (C) 2016-2026 Gerbera Contributors
MediaTomb is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
MediaTomb is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
version 2 along with MediaTomb; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
$Id$
*/
/// @file database/sql_database.h
#ifndef __SQL_STORAGE_H__
#define __SQL_STORAGE_H__
#include "config/config.h"
#include "config/config_val.h"
#include "database.h"
#include "sql_format.h"
#include <array>
#include <mutex>
#include <unordered_set>
#include <utility>
// forward declarations
template <class Item>
class AddUpdateTable;
class CdsContainer;
class CdsResource;
class SQLEmitter;
class SQLResult;
class SQLRow;
template <class Col>
class EnumColumnMapper;
enum class BrowseColumn;
enum class ResourceDataType;
enum class ObjectSource;
enum class Operation;
#define DBVERSION 30
#define STRING_LIMIT "GRBMAX"
#define INTERNAL_SETTINGS_TABLE "mt_internal_setting"
class SQLDatabase
: public Database,
public std::enable_shared_from_this<SQLDatabase> {
public:
virtual std::string quote(const std::string& value) const override = 0;
std::string quote(const std::string& value, std::size_t len) const;
/// @brief ensure correct string quoting for SQL statement
std::string quote(const char* str, std::size_t len = 0) const { return quote(std::string(str), len); }
/* wrapper functions for different types */
std::string quote(char val) const { return quote(fmt::to_string(val)); }
static std::string quote(int val) { return fmt::to_string(val); }
static std::string quote(unsigned int val) { return fmt::to_string(val); }
static std::string quote(long val) { return fmt::to_string(val); }
static std::string quote(unsigned long val) { return fmt::to_string(val); }
static std::string quote(bool val) { return val ? "1" : "0"; }
static std::string quote(long long val) { return fmt::to_string(val); }
/// @brief returns a fmt-printable identifier name
SQLIdentifier identifier(const std::string& name) const { return { name, table_quote_begin, table_quote_end }; }
// hooks for transactions
virtual void beginTransaction(std::string_view tName) { }
virtual void rollback(std::string_view tName) { }
virtual void commit(std::string_view tName) { }
virtual void del(std::string_view tableName, const std::string& clause, const std::vector<int>& ids) = 0;
virtual void execOnTable(std::string_view tableName, const std::string& query, int objId) = 0;
virtual int exec(const std::string& query, const std::string& getLastInsertId = "") = 0;
virtual void execOnly(const std::string& query) = 0;
virtual std::shared_ptr<SQLResult> select(const std::string& query) = 0;
void addObject(const std::shared_ptr<CdsObject>& obj, int* changedContainer) override;
void updateObject(const std::shared_ptr<CdsObject>& obj, int* changedContainer) override;
std::shared_ptr<CdsObject> loadObject(
int objectID,
const std::string& group = UNUSED_CLIENT_GROUP) override;
std::map<int, int> getChildCounts(
const std::vector<int>& contId,
bool containers,
bool items,
bool hideFsRoot) override;
std::size_t getObjects(
int parentID,
bool withoutContainer,
std::unordered_set<int>& ret,
bool full,
int refID) override;
std::vector<int> getRefObjects(
int objectId,
CdsEntryType entryType) override;
std::unordered_set<int> getUnreferencedObjects() override;
std::unique_ptr<ChangedContainers> removeObject(int objectID, const fs::path& path, bool all) override;
std::unique_ptr<ChangedContainers> removeObjects(const std::unordered_set<int>& list, bool all = false) override;
std::shared_ptr<CdsObject> loadObjectByServiceID(const std::string& serviceID, const std::string& group) override;
std::vector<int> getServiceObjectIDs(char servicePrefix) override;
std::vector<std::shared_ptr<CdsObject>> browse(BrowseParam& param) override;
std::vector<std::shared_ptr<CdsObject>> search(SearchParam& param) override;
std::vector<std::string> getMimeTypes() override;
std::map<std::string, std::shared_ptr<CdsContainer>> getShortcuts() override;
std::vector<std::shared_ptr<CdsObject>> findObjectByContentClass(
const std::string& contentClass,
int startIndex,
int count,
const std::string& group) override;
std::shared_ptr<CdsObject> findObjectByPath(
const fs::path& fullpath,
const std::string& group,
DbFileType fileType = DbFileType::Auto) override;
std::string incrementUpdateIDs(const std::unordered_set<int>& ids) override;
fs::path buildContainerPath(int parentID, const std::string& title) override;
bool addContainer(
int parentContainerId,
std::string virtualPath,
const std::shared_ptr<CdsContainer>& cont,
int* containerID) override;
/* accounting methods */
long long getFileStats(const StatsParam& stats) override;
std::map<std::string, long long> getGroupStats(const StatsParam& stats) override;
std::string getInternalSetting(const std::string& key) override;
void storeInternalSetting(const std::string& key, const std::string& value) override = 0;
std::shared_ptr<AutoscanList> getAutoscanList(AutoscanScanMode scanmode) override;
void updateAutoscanList(AutoscanScanMode scanmode, const std::shared_ptr<AutoscanList>& list) override;
std::shared_ptr<AutoscanDirectory> getAutoscanDirectory(int objectID) override;
void addAutoscanDirectory(const std::shared_ptr<AutoscanDirectory>& adir) override;
void updateAutoscanDirectory(const std::shared_ptr<AutoscanDirectory>& adir) override;
void removeAutoscanDirectory(const std::shared_ptr<AutoscanDirectory>& adir) override;
void checkOverlappingAutoscans(const std::shared_ptr<AutoscanDirectory>& adir) override;
/* config methods */
std::vector<ConfigValue> getConfigValues() override;
void removeConfigValue(const std::string& item) override;
void updateConfigValue(const std::string& key, const std::string& item, const std::string& value, const std::string& status = "unchanged") override;
/* clients methods */
std::vector<ClientObservation> getClients() override;
void saveClients(const std::vector<ClientObservation>& cache) override;
std::shared_ptr<ClientStatusDetail> getPlayStatus(const std::string& group, int objectId) override;
std::vector<std::shared_ptr<ClientStatusDetail>> getPlayStatusList(int objectId) override;
void savePlayStatus(const std::shared_ptr<ClientStatusDetail>& detail) override;
std::vector<std::map<std::string, std::string>> getClientGroupStats() override;
std::vector<int> getPathIDs(int objectID) override;
void shutdown() override;
virtual void shutdownDriver() = 0;
int ensurePathExistence(const fs::path& path, int* changedContainer) override;
static std::string getSortCapabilities();
static std::string getSearchCapabilities();
unsigned int getHash(int index) const { return index < DBVERSION ? hashies.at(index) : 0; }
void deleteAll(std::string_view tableName);
template <typename T>
void deleteRow(const std::string& tableName, const std::string& key, const T& value);
void deleteRows(std::string_view tableName, const std::string& key, const std::vector<int>& values);
int getFirstVersion() const override { return firstDBVersion; };
protected:
std::shared_ptr<Mime> mime;
std::shared_ptr<ConverterManager> converterManager;
char table_quote_begin { '\0' };
char table_quote_end { '\0' };
std::map<int, unsigned int> hashies;
/// @brief Initial db version with gerbera
std::size_t firstDBVersion = 1;
/// @brief Maximum length designated as GRBMAX in ddl statement
unsigned int stringLimit;
/// @brief lock for special sql commands
mutable std::recursive_mutex sqlMutex;
using SqlAutoLock = std::scoped_lock<decltype(sqlMutex)>;
std::map<int, std::shared_ptr<CdsContainer>> dynamicContainers;
std::shared_ptr<EnumColumnMapper<BrowseColumn>> browseColumnMapper;
/// @brief constructor for derived classes
explicit SQLDatabase(const std::shared_ptr<Config>& config, std::shared_ptr<Mime> mime, std::shared_ptr<ConverterManager> converterManager);
void init() override;
/// @brief build up runtime data for dynamic (search based) containers
void initDynContainers(const std::shared_ptr<CdsObject>& sParent = {});
/// @brief create core entries in fresh database
void fillDatabase();
/// @brief upgrade database version by applying migration commands
void upgradeDatabase(
unsigned int dbVersion,
const std::map<int, unsigned int>& hashies,
ConfigVal upgradeOption,
std::string_view updateVersionCommand,
const std::map<ResourceDataType, std::string_view>& addResourceColumnCmd);
/// @brief internal version of sql exec command
virtual void _exec(const std::string& query) = 0;
/// @brief Get query for unreferenced objects depending on database
virtual std::string getUnreferencedQuery(const std::string& table);
private:
std::string sql_browse_columns;
std::string sql_browse_query;
std::string sql_search_columns;
std::string sql_search_container_query_format;
std::string sql_search_query;
std::string sql_meta_query;
std::string sql_autoscan_query;
std::string sql_resource_query;
/// @brief Configuration content for dynamic folders
std::shared_ptr<DynamicContentList> dynamicContentList;
/// @brief Are dynamic search folders enabled in config
bool dynamicContentEnabled;
/// @brief Is sorting by sort_key enabled in config
bool sortKeyEnabled;
/// @brief Default sort statement applied for all types
std::string sortDefault;
std::shared_ptr<CdsObject> createObjectFromRow(const std::string& group, const std::unique_ptr<SQLRow>& row);
std::shared_ptr<CdsObject> createObjectFromSearchRow(const std::string& group, const std::unique_ptr<SQLRow>& row);
std::vector<std::pair<std::string, std::string>> retrieveMetaDataForObject(int objectId);
std::vector<std::shared_ptr<CdsResource>> retrieveResourcesForObject(int objectId);
std::vector<std::shared_ptr<AddUpdateTable<CdsObject>>> _addUpdateObject(
const std::shared_ptr<CdsObject>& obj,
Operation op,
int* changedContainer);
void generateMetaDataDBOperations(
const std::shared_ptr<CdsObject>& obj,
Operation op,
std::vector<std::shared_ptr<AddUpdateTable<CdsObject>>>& operations) const;
void generateResourceDBOperations(
const std::shared_ptr<CdsObject>& obj,
Operation op,
std::vector<std::shared_ptr<AddUpdateTable<CdsObject>>>& operations);
/* helper for removeObject(s) */
void _removeObjects(const std::vector<std::int32_t>& objectIDs);
ChangedContainers _recursiveRemove(
const std::vector<std::int32_t>& items,
const std::vector<std::int32_t>& containers, bool all);
std::unique_ptr<ChangedContainers> _purgeEmptyContainers(const ChangedContainers& maybeEmpty);
/* helpers for autoscan */
void _removeAutoscanDirectory(int autoscanID);
int _getAutoscanObjectID(int autoscanID);
void _autoscanChangePersistentFlag(int objectID, bool persistent);
static std::shared_ptr<AutoscanDirectory> _fillAutoscanDirectory(const std::unique_ptr<SQLRow>& row);
std::vector<int> _checkOverlappingAutoscans(const std::shared_ptr<AutoscanDirectory>& adir);
std::shared_ptr<CdsObject> checkRefID(const std::shared_ptr<CdsObject>& obj);
int createContainer(
int parentID,
const std::string& name,
const std::string& sortKey,
const std::string& virtualPath,
int flags,
bool isVirtual,
const std::string& upnpClass,
int refID,
ObjectSource source,
const std::vector<std::pair<std::string, std::string>>& itemMetadata,
const std::vector<std::shared_ptr<CdsResource>>& itemResources = {});
static bool remapBool(const std::string& field) { return field == "1"; }
static bool remapBool(int field) { return field == 1; }
std::shared_ptr<SQLEmitter> sqlEmitter;
using AutoLock = std::scoped_lock<std::mutex>;
friend class SQLMigration;
};
template <typename T>
void SQLDatabase::deleteRow(const std::string& tableName, const std::string& key, const T& value)
{
exec(fmt::format("DELETE FROM {} WHERE {} = {}", identifier(tableName), identifier(key), quote(value)));
}
class SqlWithTransactions {
protected:
explicit SqlWithTransactions(const std::shared_ptr<Config>& config)
: use_transaction(config->getBoolOption(ConfigVal::SERVER_STORAGE_USE_TRANSACTIONS))
{
}
bool use_transaction;
bool inTransaction {};
};
#endif // __SQL_STORAGE_H__