Commit 62fddf11 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

sqlite: Introduce a "Row" helper

This makes it easier to scan a row and extract values to variables,
without having to manually pass the column index.
In the likely event we add/remove columns, this will help prevent
regressions as well.
parent be265d17
......@@ -31,15 +31,15 @@ const std::string policy::AlbumTable::Name = "Album";
const std::string policy::AlbumTable::CacheColumn = "id_album";
unsigned int Album::* const policy::AlbumTable::PrimaryKey = &Album::m_id;
Album::Album(DBConnection dbConnection, sqlite3_stmt* stmt)
Album::Album(DBConnection dbConnection, sqlite::Row& row)
: m_dbConnection( dbConnection )
{
m_id = sqlite3_column_int( stmt, 0 );
m_title = sqlite::Traits<std::string>::Load( stmt, 1 );
m_releaseDate = sqlite3_column_int( stmt, 2 );
m_shortSummary = sqlite::Traits<std::string>::Load( stmt, 3 );
m_artworkUrl = sqlite::Traits<std::string>::Load( stmt, 4 );
m_lastSyncDate = sqlite3_column_int( stmt, 5 );
row >> m_id
>> m_title
>> m_releaseDate
>> m_shortSummary
>> m_artworkUrl
>> m_lastSyncDate;
}
Album::Album(const std::string& title )
......
......@@ -51,7 +51,7 @@ class Album : public IAlbum, public Cache<Album, IAlbum, policy::AlbumTable>
private:
typedef Cache<Album, IAlbum, policy::AlbumTable> _Cache;
public:
Album( DBConnection dbConnection, sqlite3_stmt* stmt );
Album( DBConnection dbConnection, sqlite::Row& row );
Album( const std::string& title );
virtual unsigned int id() const override;
......
......@@ -30,15 +30,15 @@ const std::string policy::AlbumTrackTable::Name = "AlbumTrack";
const std::string policy::AlbumTrackTable::CacheColumn = "id_track";
unsigned int AlbumTrack::* const policy::AlbumTrackTable::PrimaryKey = &AlbumTrack::m_id;
AlbumTrack::AlbumTrack( DBConnection dbConnection, sqlite3_stmt* stmt )
AlbumTrack::AlbumTrack(DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
, m_album( nullptr )
{
m_id = sqlite::Traits<unsigned int>::Load( stmt, 0 );
m_mediaId = sqlite::Traits<unsigned int>::Load( stmt, 1 );
m_genre = sqlite::Traits<std::string>::Load( stmt, 2 );
m_trackNumber = sqlite::Traits<unsigned int>::Load( stmt, 3 );
m_albumId = sqlite::Traits<unsigned int>::Load( stmt, 4 );
row >> m_id
>> m_mediaId
>> m_genre
>> m_trackNumber
>> m_albumId;
}
//FIXME: constify media
......
......@@ -49,7 +49,7 @@ class AlbumTrack : public IAlbumTrack, public Cache<AlbumTrack, IAlbumTrack, pol
private:
typedef Cache<AlbumTrack, IAlbumTrack, policy::AlbumTrackTable> _Cache;
public:
AlbumTrack( DBConnection dbConnection, sqlite3_stmt* stmt );
AlbumTrack( DBConnection dbConnection, sqlite::Row& row );
AlbumTrack(Media* media, unsigned int trackNumber, unsigned int albumId );
virtual unsigned int id() const override;
......
......@@ -32,12 +32,12 @@ const std::string policy::ArtistTable::CacheColumn = "id_artist";
unsigned int Artist::*const policy::ArtistTable::PrimaryKey = &Artist::m_id;
Artist::Artist( DBConnection dbConnection, sqlite3_stmt *stmt )
Artist::Artist( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
m_id = sqlite::Traits<unsigned int>::Load( stmt, 0 );
m_name = sqlite::Traits<std::string>::Load( stmt, 1 );
m_shortBio = sqlite::Traits<std::string>::Load( stmt, 2 );
row >> m_id
>> m_name
>> m_shortBio;
}
Artist::Artist( const std::string& name )
......
......@@ -44,7 +44,7 @@ private:
using _Cache = Cache<Artist, IArtist, policy::ArtistTable>;
public:
Artist( DBConnection dbConnection, sqlite3_stmt* stmt );
Artist( DBConnection dbConnection, sqlite::Row& row );
Artist( const std::string& name );
/**
* @brief Artist Construct an empty artist, with a DB connection.
......
......@@ -26,14 +26,14 @@ const std::string policy::AudioTrackTable::Name = "AudioTrack";
const std::string policy::AudioTrackTable::CacheColumn = "id_track";
unsigned int AudioTrack::* const policy::AudioTrackTable::PrimaryKey = &AudioTrack::m_id;
AudioTrack::AudioTrack( DBConnection dbConnection, sqlite3_stmt* stmt )
AudioTrack::AudioTrack( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
, m_id( sqlite::Traits<unsigned int>::Load( stmt, 0 ) )
, m_codec( sqlite::Traits<std::string>::Load( stmt, 1 ) )
, m_bitrate( sqlite::Traits<unsigned int>::Load( stmt, 2 ) )
, m_sampleRate( sqlite::Traits<unsigned int>::Load( stmt, 3 ) )
, m_nbChannels( sqlite::Traits<unsigned int>::Load( stmt, 4 ) )
{
row >> m_id
>> m_codec
>> m_bitrate
>> m_sampleRate
>> m_nbChannels;
}
AudioTrack::AudioTrack(const std::string& codec, unsigned int bitrate , unsigned int sampleRate, unsigned int nbChannels )
......
......@@ -42,7 +42,7 @@ struct AudioTrackTable
class AudioTrack : public IAudioTrack, public Cache<AudioTrack, IAudioTrack, policy::AudioTrackTable>
{
public:
AudioTrack( DBConnection dbConnection, sqlite3_stmt* stmt );
AudioTrack( DBConnection dbConnection, sqlite::Row& row );
AudioTrack( const std::string& codec, unsigned int bitrate, unsigned int sampleRate, unsigned int nbChannels );
virtual unsigned int id() const override;
......@@ -59,10 +59,10 @@ class AudioTrack : public IAudioTrack, public Cache<AudioTrack, IAudioTrack, pol
private:
DBConnection m_dbConnection;
unsigned int m_id;
const std::string m_codec;
const unsigned int m_bitrate;
const unsigned int m_sampleRate;
const unsigned int m_nbChannels;
std::string m_codec;
unsigned int m_bitrate;
unsigned int m_sampleRate;
unsigned int m_nbChannels;
private:
......
......@@ -37,21 +37,21 @@ namespace policy
return self->path();
}
FolderCache::KeyType FolderCache::key( sqlite3_stmt* stmt )
FolderCache::KeyType FolderCache::key(sqlite::Row& row )
{
return sqlite::Traits<FolderCache::KeyType>::Load( stmt, 1 );
return row.load<FolderCache::KeyType>( 1 );
}
}
Folder::Folder( DBConnection dbConnection, sqlite3_stmt* stmt )
Folder::Folder(DBConnection dbConnection, sqlite::Row& row )
: m_dbConection( dbConnection )
{
m_id = sqlite::Traits<unsigned int>::Load( stmt, 0 );
m_path = sqlite::Traits<std::string>::Load( stmt, 1 );
m_parent = sqlite::Traits<unsigned int>::Load( stmt, 2 );
m_lastModificationDate = sqlite::Traits<unsigned int>::Load( stmt, 3 );
m_isRemovable = sqlite::Traits<bool>::Load( stmt, 4 );
row >> m_id
>> m_path
>> m_parent
>> m_lastModificationDate
>> m_isRemovable;
}
Folder::Folder( const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parent )
......
......@@ -47,7 +47,7 @@ struct FolderCache
{
using KeyType = std::string;
static const KeyType& key( const std::shared_ptr<Folder>& self );
static KeyType key( sqlite3_stmt* stmt );
static KeyType key( sqlite::Row& row );
};
}
......@@ -57,7 +57,7 @@ class Folder : public IFolder, public Cache<Folder, IFolder, policy::FolderTable
using _Cache = Cache<Folder, IFolder, policy::FolderTable, policy::FolderCache>;
public:
Folder( DBConnection dbConnection, sqlite3_stmt* stmt );
Folder( DBConnection dbConnection, sqlite::Row& row );
Folder( const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parent );
static bool createTable( DBConnection connection );
......
......@@ -32,11 +32,11 @@ const std::string policy::LabelTable::Name = "Label";
const std::string policy::LabelTable::CacheColumn = "name";
unsigned int Label::* const policy::LabelTable::PrimaryKey = &Label::m_id;
Label::Label( DBConnection dbConnection, sqlite3_stmt* stmt )
Label::Label( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
m_id = sqlite3_column_int( stmt, 0 );
m_name = (const char*)sqlite3_column_text( stmt, 1 );
row >> m_id
>> m_name;
}
Label::Label( const std::string& name )
......@@ -95,7 +95,7 @@ const std::string&policy::LabelCachePolicy::key( const std::shared_ptr<ILabel> s
return self->name();
}
std::string policy::LabelCachePolicy::key(sqlite3_stmt* stmt)
std::string policy::LabelCachePolicy::key(sqlite::Row& row)
{
return sqlite::Traits<KeyType>::Load( stmt, 1 );
return row.load<KeyType>( 1 );
}
......@@ -45,7 +45,7 @@ struct LabelCachePolicy
{
typedef std::string KeyType;
static const std::string& key(const std::shared_ptr<ILabel> self );
static std::string key( sqlite3_stmt* stmt );
static std::string key( sqlite::Row& row );
};
}
......@@ -55,7 +55,7 @@ class Label : public ILabel, public Cache<Label, ILabel, policy::LabelTable, pol
private:
typedef Cache<Label, ILabel, policy::LabelTable, policy::LabelCachePolicy> _Cache;
public:
Label( DBConnection dbConnection, sqlite3_stmt* stmt);
Label( DBConnection dbConnection, sqlite::Row& row );
Label( const std::string& name );
public:
......
......@@ -43,21 +43,21 @@ const std::string policy::MediaTable::Name = "Media";
const std::string policy::MediaTable::CacheColumn = "mrl";
unsigned int Media::* const policy::MediaTable::PrimaryKey = &Media::m_id;
Media::Media( DBConnection dbConnection, sqlite3_stmt* stmt )
Media::Media( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
m_id = sqlite3_column_int( stmt, 0 );
m_type = (Type)sqlite3_column_int( stmt, 1 );
m_duration = sqlite::Traits<int64_t>::Load( stmt, 2 );
m_playCount = sqlite3_column_int( stmt, 3 );
m_showEpisodeId = sqlite3_column_int( stmt, 4 );
m_mrl = (const char*)sqlite3_column_text( stmt, 5 );
m_movieId = sqlite::Traits<unsigned int>::Load( stmt, 6 );
m_folderId = sqlite::Traits<unsigned int>::Load( stmt, 7 );
m_lastModificationDate = sqlite::Traits<unsigned int>::Load( stmt, 8 );
m_snapshot = sqlite::Traits<std::string>::Load( stmt, 9 );
m_isParsed = sqlite::Traits<bool>::Load( stmt, 10 );
m_title = sqlite::Traits<std::string>::Load( stmt, 11 );
row >> m_id
>> m_type
>> m_duration
>> m_playCount
>> m_showEpisodeId
>> m_mrl
>> m_movieId
>> m_folderId
>> m_lastModificationDate
>> m_snapshot
>> m_isParsed
>> m_title;
}
Media::Media( const fs::IFile* file, unsigned int folderId, const std::string& title, Type type )
......@@ -391,7 +391,7 @@ const std::string& policy::MediaCache::key(const std::shared_ptr<Media> self )
return self->mrl();
}
std::string policy::MediaCache::key(sqlite3_stmt* stmt)
std::string policy::MediaCache::key(sqlite::Row& row)
{
return sqlite::Traits<std::string>::Load( stmt, 5 );
return row.load<std::string>( 5 );
}
......@@ -29,12 +29,18 @@
#include "IMedia.h"
#include "database/Cache.h"
class Album;
class ShowEpisode;
class AlbumTrack;
class Media;
namespace sqlite
{
class Row;
}
namespace policy
{
struct MediaTable
......@@ -47,7 +53,7 @@ struct MediaCache
{
typedef std::string KeyType;
static const std::string& key(const std::shared_ptr<Media> self);
static std::string key( sqlite3_stmt* stmt );
static std::string key( sqlite::Row& row );
};
}
......@@ -61,7 +67,7 @@ class Media : public IMedia, public Cache<Media, IMedia, policy::MediaTable, pol
// ::new (pv) T(std::forward(args)...)
// shall be well-formed, and private constructor would prevent that.
// There might be a way with a user-defined allocator, but we'll see that later...
Media(DBConnection dbConnection , sqlite3_stmt* stmt);
Media(DBConnection dbConnection , sqlite::Row& row);
Media(const fs::IFile* file, unsigned int folderId, const std::string &title, Type type);
static std::shared_ptr<Media> create(DBConnection dbConnection, Type type, const fs::IFile* file , unsigned int folderId);
......
......@@ -28,15 +28,15 @@ const std::string policy::MovieTable::Name = "Movie";
const std::string policy::MovieTable::CacheColumn = "id_movie";
unsigned int Movie::* const policy::MovieTable::PrimaryKey = &Movie::m_id;
Movie::Movie( DBConnection dbConnection, sqlite3_stmt* stmt )
Movie::Movie(DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
m_id = sqlite::Traits<unsigned int>::Load( stmt, 0 );
m_title = sqlite::Traits<std::string>::Load( stmt, 1 );
m_releaseDate = sqlite::Traits<time_t>::Load( stmt, 2 );
m_summary = sqlite::Traits<std::string>::Load( stmt, 3 );
m_artworkUrl = sqlite::Traits<std::string>::Load( stmt, 4 );
m_imdbId = sqlite::Traits<std::string>::Load( stmt, 5 );
row >> m_id
>> m_title
>> m_releaseDate
>> m_summary
>> m_artworkUrl
>> m_imdbId;
}
Movie::Movie( const std::string& title )
......
......@@ -42,7 +42,7 @@ struct MovieTable
class Movie : public IMovie, public Cache<Movie, IMovie, policy::MovieTable>
{
public:
Movie( DBConnection dbConnection, sqlite3_stmt* stmt );
Movie( DBConnection dbConnection, sqlite::Row& row );
Movie( const std::string& title );
virtual unsigned int id() const override;
......
......@@ -28,16 +28,16 @@ const std::string policy::ShowTable::Name = "Show";
const std::string policy::ShowTable::CacheColumn = "id_show";
unsigned int Show::* const policy::ShowTable::PrimaryKey = &Show::m_id;
Show::Show(DBConnection dbConnection, sqlite3_stmt* stmt)
Show::Show( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
m_id = sqlite::Traits<unsigned int>::Load( stmt, 0 );
m_name = sqlite::Traits<std::string>::Load( stmt, 1 );
m_releaseDate = sqlite::Traits<unsigned int>::Load( stmt, 2 );
m_shortSummary = sqlite::Traits<std::string>::Load( stmt, 3 );
m_artworkUrl = sqlite::Traits<std::string>::Load( stmt, 4 );
m_lastSyncDate = sqlite::Traits<unsigned int>::Load( stmt, 5 );
m_tvdbId = sqlite::Traits<std::string>::Load( stmt, 6 );
row >> m_id
>> m_name
>> m_releaseDate
>> m_shortSummary
>> m_artworkUrl
>> m_lastSyncDate
>> m_tvdbId;
}
Show::Show( const std::string& name )
......
......@@ -45,7 +45,7 @@ struct ShowTable
class Show : public IShow, public Cache<Show, IShow, policy::ShowTable>
{
public:
Show( DBConnection dbConnection, sqlite3_stmt* stmt );
Show( DBConnection dbConnection, sqlite::Row& row );
Show( const std::string& name );
virtual unsigned int id() const override;
......
......@@ -29,18 +29,18 @@ const std::string policy::ShowEpisodeTable::Name = "ShowEpisode";
const std::string policy::ShowEpisodeTable::CacheColumn = "show_id";
unsigned int ShowEpisode::* const policy::ShowEpisodeTable::PrimaryKey = &ShowEpisode::m_id;
ShowEpisode::ShowEpisode( DBConnection dbConnection, sqlite3_stmt* stmt )
ShowEpisode::ShowEpisode(DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
m_id = sqlite::Traits<unsigned int>::Load( stmt, 0 );
m_artworkUrl = sqlite::Traits<std::string>::Load( stmt, 1 );
m_episodeNumber = sqlite::Traits<unsigned int>::Load( stmt, 2 );
m_lastSyncDate = sqlite::Traits<time_t>::Load( stmt, 3 );
m_name = sqlite::Traits<std::string>::Load( stmt, 4 );
m_seasonNumber = sqlite::Traits<unsigned int>::Load( stmt, 5 );
m_shortSummary = sqlite::Traits<std::string>::Load( stmt, 6 );
m_tvdbId = sqlite::Traits<std::string>::Load( stmt, 7 );
m_showId = sqlite::Traits<unsigned int>::Load( stmt, 8 );
row >> m_id
>> m_artworkUrl
>> m_episodeNumber
>> m_lastSyncDate
>> m_name
>> m_seasonNumber
>> m_shortSummary
>> m_tvdbId
>> m_showId;
}
ShowEpisode::ShowEpisode( const std::string& name, unsigned int episodeNumber, unsigned int showId )
......
......@@ -46,7 +46,7 @@ struct ShowEpisodeTable
class ShowEpisode : public IShowEpisode, public Cache<ShowEpisode, IShowEpisode, policy::ShowEpisodeTable>
{
public:
ShowEpisode( DBConnection dbConnection, sqlite3_stmt* stmt );
ShowEpisode( DBConnection dbConnection, sqlite::Row& row );
ShowEpisode(const std::string& name, unsigned int episodeNumber, unsigned int showId );
virtual unsigned int id() const override;
......
......@@ -26,14 +26,14 @@ const std::string policy::VideoTrackTable::Name = "VideoTrack";
const std::string policy::VideoTrackTable::CacheColumn = "id_track";
unsigned int VideoTrack::* const policy::VideoTrackTable::PrimaryKey = &VideoTrack::m_id;
VideoTrack::VideoTrack(DBConnection dbConnection, sqlite3_stmt* stmt)
VideoTrack::VideoTrack( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
, m_id( sqlite::Traits<unsigned int>::Load( stmt, 0 ) )
, m_codec( sqlite::Traits<std::string>::Load( stmt, 1 ) )
, m_width( sqlite::Traits<unsigned int>::Load( stmt, 2 ) )
, m_height( sqlite::Traits<unsigned int>::Load( stmt, 3 ) )
, m_fps( sqlite::Traits<float>::Load( stmt, 4 ) )
{
row >> m_id
>> m_codec
>> m_width
>> m_height
>> m_fps;
}
VideoTrack::VideoTrack( const std::string& codec, unsigned int width, unsigned int height, float fps )
......
......@@ -43,7 +43,7 @@ struct VideoTrackTable
class VideoTrack : public IVideoTrack, public Cache<VideoTrack, IVideoTrack, policy::VideoTrackTable>
{
public:
VideoTrack( DBConnection dbConnection, sqlite3_stmt* stmt );
VideoTrack( DBConnection dbConnection, sqlite::Row& row );
VideoTrack( const std::string& codec, unsigned int width, unsigned int height, float fps );
virtual unsigned int id() const override;
......@@ -61,10 +61,10 @@ class VideoTrack : public IVideoTrack, public Cache<VideoTrack, IVideoTrack, pol
private:
DBConnection m_dbConnection;
unsigned int m_id;
const std::string m_codec;
const unsigned int m_width;
const unsigned int m_height;
const float m_fps;
std::string m_codec;
unsigned int m_width;
unsigned int m_height;
float m_fps;
private:
typedef Cache<VideoTrack, IVideoTrack, policy::VideoTrackTable> _Cache;
......
......@@ -43,9 +43,9 @@ class PrimaryKeyCacheKeyPolicy
{
return self->id();
}
static unsigned int key( sqlite3_stmt* stmt )
static unsigned int key( sqlite::Row& row )
{
return sqlite::Traits<unsigned int>::Load( stmt, 0 );
return row.load<unsigned int>( 0 );
}
};
......@@ -57,7 +57,7 @@ class PrimaryKeyCacheKeyPolicy
* - Name: the table name
* - CacheColumn: The column used to cache records.
* - CACHEPOLICY describes which column to use for caching by providing two "key" methods.
* One that takes a sqlite3_stmt and one that takes an instance of the type being cached
* One that takes a sqlite::Row and one that takes an instance of the type being cached
*
* The default CACHEPOLICY implementation bases itself on an unsigned int column, assumed
* to be the primary key, at index 0 of a fetch statement.
......@@ -116,15 +116,15 @@ class Cache
return sqlite::Tools::fetchAll<IMPL, INTF>( dbConnection, req, std::forward<Args>( args )... );
}
static std::shared_ptr<IMPL> load( DBConnection dbConnection, sqlite3_stmt* stmt )
static std::shared_ptr<IMPL> load( DBConnection dbConnection, sqlite::Row& row )
{
auto cacheKey = CACHEPOLICY::key( stmt );
auto cacheKey = CACHEPOLICY::key( row );
Lock lock( Mutex );
auto it = Store.find( cacheKey );
if ( it != Store.end() )
return it->second;
auto inst = std::make_shared<IMPL>( dbConnection, stmt );
auto inst = std::make_shared<IMPL>( dbConnection, row );
Store[cacheKey] = inst;
return inst;
}
......
......@@ -141,6 +141,40 @@ struct Traits<T, typename std::enable_if<IsSameDecay<T, int64_t>::value>::type>
(*Load)(sqlite3_stmt *, int) = &sqlite3_column_int64;
};
class Row
{
public:
Row( sqlite3_stmt* stmt )
: m_stmt( stmt )
, m_idx( 0 )
{
}
/**
* @brief operator >> Extracts the next column from this result row.
*/
template <typename T>
Row& operator>>(T& t)
{
t = sqlite::Traits<T>::Load( m_stmt, m_idx );
m_idx++;
return *this;
}
/**
* @brief Returns the value in column idx, but doesn't advance to the next column
*/
template <typename T>
T load(unsigned int idx)
{
return sqlite::Traits<T>::Load( m_stmt, idx );
}
private:
sqlite3_stmt* m_stmt;
unsigned int m_idx;
};
class Tools
{
private:
......@@ -165,7 +199,8 @@ class Tools
int res = sqlite3_step( stmt.get() );
while ( res == SQLITE_ROW )
{
auto row = IMPL::load( dbConnection, stmt.get() );
sqlite::Row sqliteRow( stmt.get() );
auto row = IMPL::load( dbConnection, sqliteRow );
results.push_back( row );
res = sqlite3_step( stmt.get() );
}
......@@ -183,7 +218,8 @@ class Tools
int res = sqlite3_step( stmt.get() );
if ( res != SQLITE_ROW )
return nullptr;
return T::load( dbConnection, stmt.get() );
sqlite::Row sqliteRow( stmt.get() );
return T::load( dbConnection, sqliteRow );
}
template <typename... Args>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment