Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Error "no matching member function for call to bindValue"



  • Goodmorning to all,
    I'm using Qt version 5.14.0 and I'm tring to develop a database with SQL.
    The problem is that when I call the method bindValue, I get the error:

    no matching member function for call to bindValue
    candidate function not viable: requires 3 arguments, but 2 were provided

    The sample code is the shown below:

    #ifndef ALBUMDAO_H
    #define ALBUMDAO_H
    
    class QSqlDatabase;
    
    #include "album.h"
    
    #include <QList>
    
    class AlbumDao
    {
        public:
            AlbumDao(QSqlDatabase& database);
            void init()	const;
    
            void addAlbum(Album& album) const;
            void updateAlbum(const	Album&	album)	const;
            void removeAlbum(int	id)	const;
            QList<Album*>	albums()	const;
    
        private:
            QSqlDatabase& mDatabase;
    };
    
    #endif // ALBUMDAO_H
    
    
    #include "albumdao.h"
    
    #include <QSqlDatabase>
    #include <QSqlQuery>
    #include <QSql>
    #include <QVariant>
    
    #include "databasemanager.h"
    
    AlbumDao::AlbumDao(QSqlDatabase &database) :
        mDatabase(database)
    {
    
    }
    
    void AlbumDao::init() const
    {
        if (!mDatabase.tables().contains("albums"))	{
            QSqlQuery query(mDatabase);
            query.exec("CREATE TABLE albums (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
        }
    }
    
    void AlbumDao::addAlbum(Album &album) const
    {
        QSqlQuery query(mDatabase);
        query.prepare("INSERT INTO albums (name) VALUES (:name)");
        query.bindValue(":name", album.name());
        query.exec();
        album.setId(query.lastInsertId().toInt());
    }
    
    void AlbumDao::updateAlbum(const Album &album) const
    {
        QSqlQuery query(mDatabase);
        query.prepare("UPDATE albums SET name = (:name) WHERE id = (:id)");
        query.bindValue(":name", album.name());
        query.bindValue(":id", album.id());
        query.exec();
        //DatabaseManager::debugQuery(query);
    }
    
    void AlbumDao::removeAlbum(int id) const
    {
        QSqlQuery query(mDatabase);
        query.prepare("DELETE FROM albums WHERE id = (:id)");
        query.bindValue(":id", id);
        query.exec();
    }
    
    QList<Album *> AlbumDao::albums() const
    {
        QSqlQuery query("SELECT * FROM albums", mDatabase);
        query.exec();
        QList<Album*> list;
        while(query.next())	{
            Album* album = new Album();
            album->setId(query.value("id").toInt());
            album->setName(query.value("name").toString());
            list.append(album);
        }
        return list;
    }
    
    

    Have you got any advices?
    Thank you in advanced.


  • Lifetime Qt Champion

    @davidino said in Error "no matching member function for call to bindValue":

    mDatabase(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"))),
    albumDao(*mDatabase)

    This is wrong on so many levels.

    First: don't store a QSqlDatabase member variable like explained in a warning in the QSqlDatabase documentation.
    Second: even less as a pointer
    Third: if you have to deference QSqlDatabase, it means you are already having issues in your design.

    Are you using only the default connection ? If so, then there's no need to pass anything around as it is the connection used when you don't pass any specific parameter to e.g. QSqlQuery.
    If you have several different connections, then pass their name around so the classes concerned can all QSqlDatabase::database to get the correct connection to use.


  • Lifetime Qt Champion

    Hi @davidino,

    query.bindValue(":name", album.name());

    You mean here? So please provide the function signature of Album::name().

    Btw.: Why are you using Qt 5.14? It is not released yet. Have you tried 5.12.x or 5.13.x?

    Regards


  • Lifetime Qt Champion

    @davidino said in Error "no matching member function for call to bindValue":

    no matching member function for call to bindValue

    Hi
    Could you try to include
    #include <QVariant>
    in the albumdao.cpp

    Not that. ( thx jonb )

    also as a note:
    You dont need to keep the database around to be used with QSqlQuery
    as it has a default connection concept.
    Which allows to simply open the database in some part of the application and
    simply do QSqlQuery query; ( anywhere in the app)
    and it will use the default database.
    https://doc.qt.io/Qt-5/qsqldatabase.html

    also Docs says
    "
    Warning: It is highly recommended that you do not keep a copy of the QSqlDatabase around as a member of a class, as this will prevent the instance from being correctly cleaned up on shutdown. If you need to access an existing QSqlDatabase, it should be accessed with database(). If you chose to have a QSqlDatabase member variable, this needs to be deleted before the QCoreApplication instance is deleted, otherwise it may lead to undefined behavior. "

    So make sure you are not jumping into that trap :)



  • @mrjj
    I was indeed tempted by your answer, but the OP's code already shows

    #include <QVariant>
    

    in the albumdao.cpp. Which leaves @aha_1980's

    So please provide the function signature of Album::name().

    though it seems likely that will just be a string... ah, if it is perhaps(?) a std::string that won't work without some coercion.

    Make sure whatever your Qt 5.14 is it has not lost the https://doc.qt.io/qt-5/qsqlquery.html#bindValue overload! Also, I assume you are getting a compile-time error (check the line number and tell us which line it is?), but you should anyway test the run-time return value of your prepare() (plus exec() etc.) statements.



  • Hello,
    Thank you all for your support. I'm surprised that after a few hours, I received these many answers :).
    For info, the error is signed as Semantic Error.

    To @aha_1980, yes it's query.bindValue(":name", album.name()); Thank you. Regarding the Qt version, I've build image and sdk from the last release of meta-boot2qt (open-source version) for a raspberrypi0 board.
    QtVersion.png

    To @mrjj, regarding the fact that QSqlQuey doesn't need the reference to database, how do I set the database name and driver that are meant to be used (MYQSL in my case)? An if I have multiple databases?
    Regarding the class that I post, thank you for your observation. Actually I have a class "databaseManager", where I create the database object, and take care of its deletion. Then I've created other classes like "albumdao" that I posted, where I add queries, otherwise databaseManager would have been very long. Since the database is passed as reference, it's not up to "albumdao" to delete the database but to "databaseManager". Isn't it as safe as getting it using database()? (in both case, the database deletion is perform by database class). Thank you for your comment, I could be wrong.

    To @JonB Yes, the method still has the following signature:
    void bindValue(int pos, const QVariant& val, QSql::ParamType type = QSql::In);, even though I specify the third argument, it doesn't work,
    I could run the project both prepare and exec return true. Thank you,

    Below album code:

    #include "album.h"
    
    Album::Album(const QString &name) :
        mId(-1),
        mName(name)
    {
    
    }
    
    int Album::id() const
    {
        return mId;
    }
    
    void Album::setId(int id)
    {
        mId = id;
    }
    
    QString Album::name() const
    {
        return mName;
    }
    
    void Album::setName(const QString &name)
    {
        mName = name;
    }
    
    
    #ifndef ALBUM_H
    #define ALBUM_H
    
    #include "gallery-core_global.h"
    #include <QString>
    
    class GALLERYCORE_EXPORT Album
    {
    public:
        explicit Album(const QString& name = "");
        int id() const;
        void setId(int id);
        QString	name() const;
        void setName(const QString& name);
    
    private:
        int mId;
        QString mName;
    };
    
    #endif // ALBUM_H
    

  • Lifetime Qt Champion

    Hi,

    QSqlDatabase::addDatabase has a parameter which allows you to have several different connections.

    QSqlDatabase::database allows you to retrieve the connection with the given name.



  • Hello @SGaist,
    thank you for your answer. Yes, in my databaseManager I use, the following and I pass the newly created database to albumdao as reference:

    DatabaseManager::DatabaseManager(const QString &path) :
          mDatabase(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"))),
          albumDao(*mDatabase)
    {}
    

    Now I understand that using QSqlDatabase::database I can retrieve the database by name without providing the reference as I did, I could simply pass the name.
    Do you think that it could be convenient that I open an issue in QtBug?
    Thank you.



  • @davidino
    Sorry, I'm lost as to what "bug" you want to report?

    new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"))

    This creates a copy of the QSqlDatabase returned by addDatabase(). I don't know what the consequences of that might be?


  • Lifetime Qt Champion

    @davidino said in Error "no matching member function for call to bindValue":

    mDatabase(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"))),
    albumDao(*mDatabase)

    This is wrong on so many levels.

    First: don't store a QSqlDatabase member variable like explained in a warning in the QSqlDatabase documentation.
    Second: even less as a pointer
    Third: if you have to deference QSqlDatabase, it means you are already having issues in your design.

    Are you using only the default connection ? If so, then there's no need to pass anything around as it is the connection used when you don't pass any specific parameter to e.g. QSqlQuery.
    If you have several different connections, then pass their name around so the classes concerned can all QSqlDatabase::database to get the correct connection to use.



  • Hello SGaist and jonB,
    thank you for messages. I open this post for the queryBind error (which is the one that I'm going to ask to QtBug), but I received many good advices. I've changed the code as follow:

    databaseManager:

    #ifndef DATABASEMANAGER_H
    #define DATABASEMANAGER_H
    
    #include <QString>
    #include "albumdao.h"
    #include "picturedao.h"
    
    class QSqlDatabase;
    
    const QString DATABASE_FILENAME = "gallery.db";
    
    class DatabaseManager
    {
        public:
            static DatabaseManager& instance();
            ~DatabaseManager();
    
        protected:
            DatabaseManager(const QString& path	= DATABASE_FILENAME);
            DatabaseManager& operator=(const DatabaseManager& rhs);
    
        public:
            const QString mDataBasePath;
            const AlbumDao albumDao;
            const PictureDao pictureDao;
    };
    
    #endif // DATABASEMANAGER_H
    
    
    #include "databasemanager.h"
    #include	<QSqlDatabase>
    
    DatabaseManager &DatabaseManager::instance()
    {
        static DatabaseManager singleton;
        return singleton;
    }
    
    DatabaseManager::~DatabaseManager()
    {
        QSqlDatabase::database(mDataBasePath).close();
    }
    
    DatabaseManager::DatabaseManager(const QString &path) :
        mDataBasePath(path),
        albumDao(path),
        pictureDao(path)
    {
        QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", path);
        database.open();
        // Called after opening database
        albumDao.init();
        pictureDao.init();
    }
    
    

    albumDao:

    #ifndef ALBUMDAO_H
    #define ALBUMDAO_H
    
    class QSqlDatabase;
    
    #include "album.h"
    #include <QList>
    
    class AlbumDao
    {
        public:
            explicit AlbumDao(QString database);
            void init()	const;
    
            void addAlbum(Album& album) const;
            void updateAlbum(const	Album&	album)	const;
            void removeAlbum(int	id)	const;
            QList<Album*>	albums()	const;
    
        private:
            QString mDatabaseName;
    };
    
    #endif // ALBUMDAO_H
    
    
    #include "albumdao.h"
    
    #include <QSqlDatabase>
    #include <QSqlQuery>
    #include <QSql>
    #include <QVariant>
    #include <QDebug>
    
    #include "databasemanager.h"
    
    AlbumDao::AlbumDao(QString database) :
        mDatabaseName(database)
    {
    
    }
    
    void AlbumDao::init() const
    {
        QSqlDatabase database = QSqlDatabase::database(mDatabaseName);
        if (!database.tables().contains("albums"))	{
            QSqlQuery query(database);
            query.exec("CREATE TABLE albums (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
        }
    }
    
    void AlbumDao::addAlbum(Album &album) const
    {
        bool tempBool;
        QSqlDatabase database = QSqlDatabase::database(mDatabaseName);
        QSqlQuery query(database);
        tempBool=query.prepare("INSERT INTO albums (name) VALUES (:name)");
        qDebug() << "prepare: " << tempBool << endl;
        query.bindValue(":name", album.name());
        tempBool=query.exec();
        qDebug() << "exec: " << tempBool << endl;
        album.setId(query.lastInsertId().toInt());
    }
    
    void AlbumDao::updateAlbum(const Album &album) const
    {
        QSqlDatabase database = QSqlDatabase::database(mDatabaseName);
        QSqlQuery query(database);
        query.prepare("UPDATE albums SET name = (:name) WHERE id = (:id)");
        query.bindValue(":name", album.name());
        query.bindValue(":id", album.id());
        query.exec();
        //DatabaseManager::debugQuery(query);
    }
    
    void AlbumDao::removeAlbum(int id) const
    {
        QSqlDatabase database = QSqlDatabase::database(mDatabaseName);
        QSqlQuery query(database);
        query.prepare("DELETE FROM albums WHERE id = (:id)");
        query.bindValue(":id", id);
        query.exec();
    }
    
    QList<Album *> AlbumDao::albums() const
    {
        QSqlDatabase database = QSqlDatabase::database(mDatabaseName);
        QSqlQuery query("SELECT * FROM albums", database);
        query.exec();
        QList<Album*> list;
        while(query.next())	{
            Album* album = new Album();
            album->setId(query.value("id").toInt());
            album->setName(query.value("name").toString());
            list.append(album);
        }
        return list;
    }
    

    Is it correct in this way?
    Thank you.


  • Lifetime Qt Champion

    No, the second parameter is not the path to your database, it's the name of the connection.



  • Hello @SGaist,
    thank you for your post I've changed as follow.

    DatabaseManager::DatabaseManager(const QString &connectionName) :
        mDataBaseName(connectionName),
        albumDao(connectionName),
        pictureDao(connectionName)
    {
        QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
        // Database path
        database.setDatabaseName(connectionName + ".dB");
        database.open();
        // Called after opening database
        albumDao.init();
        pictureDao.init();
    }
    

    Thank you again for your always helpful advice. I'll ask to QtBug regarding the Semantic error with "bindValue"


Log in to reply