Error while executing query for Postgresql from Qt C++ - c++

I want to enter enter data from vector of objects into Postgresql table but it's not working. I establish connection to the database successfully, but the output returns an error -
QSqlError("42601", "QPSQL: Unable to create query", "ERROR: syntax error at end of input\nLINE 1: EXECUTE \n
My Code -
void MainWindow::WriteToDatabase()
{
// Establishing a connection to MySQL
database = QSqlDatabase::addDatabase("QPSQL");
database.setHostName("localhost");
database.setUserName("postgres");
database.setPassword("1234");
database.setDatabaseName("nz5_flightdatar");
bool test = QSqlDatabase::isDriverAvailable("QPSQL");
qDebug() << test << Qt::endl;
if(database.open())
{
sort(FlightList.begin(), FlightList.end());
vector<FlightData>::iterator itr;
ui->plainTextEdit->clear();
for (itr = FlightList.begin(); itr != FlightList.end(); itr++)
{
static int i = 0;
QString Time = QString::fromStdString(FlightList[i].getTime());
double Latitude = FlightList[i].getLatitude();
double Longitude = FlightList[i].getLongitude();
int Course = FlightList[i].getCourse();
int KTS = FlightList[i].getKTS();
int MPH = FlightList[i].getMPH();
int AltitudeFeet = FlightList[i].getAltitudeFeet();
QString ReportingFacility = QString::fromStdString(FlightList[i].getReportingFacility());
i++;
QSqlQuery query;
query.prepare("INSERT INTO 'public.flight_data' (Time, Latitude, Longitude, Course, KTS, MPH, AltitudeFeet, ReportingFacility)"
"VALUES (':time', ':latitude', ':longitude', ':course', ':kts', ':mph', ':altitudefeet', ':reportingfacility')");
query.bindValue(":time", Time);
query.bindValue(":latitude", Latitude);
query.bindValue(":longitude", Longitude);
query.bindValue(":course", Course);
query.bindValue(":kts", KTS);
query.bindValue(":mph", MPH);
query.bindValue(":altitudefeet", AltitudeFeet);
query.bindValue(":reportingfacility", ReportingFacility);
if(query.exec())
qDebug() << "success";
else
{
QMessageBox::information(this, "error", "No success");
qDebug() << query.lastError();
exit(0);
}
}
ui->statusbar->showMessage("Writing data to Database successful", 4000);
}
else
{
QMessageBox::warning(this, "Not connected", "Unable to connect to Database");
}
}
and the output -
qt.core.plugin.loader: In C:/Qt_Framework/6.4.1/mingw_64/plugins/sqldrivers/qsqlmysqld.dll:
Plugin uses incompatible Qt library (5.13.0) [debug]
true
QSqlError("42601", "QPSQL: Unable to create query", "ERROR: syntax error at end of input\nLINE 1: EXECUTE \n ^\n(42601)")
I am familiar with MySQL but I went crazy today trying to install drivers but failed miserably (read document and also a lot of StackOverflow threads), so I switched to Postgresql as Qt supports QPSQL.
Kindly help if you can.

Related

How to write a prepared update query?

I've been trying to write an update function that takes the user to be updated(usr), the flag which then translates to the field to be updated and the new value. Here's the function,
bool DBManager::updateDB(User* usr, int flag, QVariant& value)
{
QSqlQuery query;
bool status = false;
QString temp_value = "";
QList<QString> fieldlist = {"First_Name", "Last_Name", "Email", "Username", "Password"};
QString field = fieldlist.at(flag);
if (field == "First_Name") {
usr->setFirstName(value.toString());
temp_value = usr->getFirstName();
} else if (field == "Last_Name") {
usr->setLastName(value.toString());
temp_value = usr->getLastName();
} else {
qDebug() << "invalid option";
}
query.prepare("UPADATE Users SET :field = :newValue WHERE Username = :Username");
query.bindValue(":field",field);
query.bindValue(":newValue",temp_value);
query.bindValue(":Username",usr->getUserName());
qDebug() << query.boundValues();
if (m_db.isOpen()){
if (query.exec()){
status = true;
qDebug() << "DATABASE:: Record update successful";
} else {
qDebug() << "DATABASE:: Record update unsuccessful - " << query.lastError();
}
} else {
qDebug() << "DATABASE:: not open ";
}
return status;
}
When I run the code I get an error. Here is the output:
QMap((":Username", QVariant(QString, "zielinkin1"))(":field", QVariant(QString, "Last_Name"))(":newValue", QVariant(QString, "Thibos")))
DATABASE:: Record update unsuccessful - QSqlError("", "Parameter count mismatch", "") User not updated DATABASE:: database closed
How to fix this?
Cause
First problem
It is not hard to guess, that value binding works only for values, not for field names.
For more info, please read about Inserting, Updating, and Deleting Records.
Second problem
Found by #JimCastro:
You have a typo in your query. UPADATE should be UPDATE.
Solution
Use a string concatenation to form your prepared query.
Example
Instead of
query.prepare("UPDATE Users SET :field = :newValue WHERE Username = :Username");
write
query.prepare("UPDATE Users SET " + field + " = :newValue WHERE Username = :Username");
Delete or comment this line
query.bindValue(":field",field);

Create sqlite table is not work

I have a method that make a connection to SQLITE and work with database by conditions (insert data or create table in my database.)
When I call it in my code, at first open database correctly but can not create tables. Mean that query does not show error but not execute.
this is my method :
void SQL(string cmnd)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");//not dbConnection
QString path = "DBAbsent";
db.setDatabaseName(path);
QSqlQuery q(db);
if(!db.open())
{
printf("Error to open database");
}
else
{
if (cmnd=="createTable")
{
q.prepare("create table IF NOT EXISTS Personel (P_id int primary key, "
"CardID varchar(50), "
"name varchar(50));");
if(!q.exec())
{
qDebug()<<strerror(errno);
qDebug()<<q.lastError(); // show this error : QSqlError("", "Unable to fetch row", "No query")
}
q.prepare("create table IF NOT EXISTS Absent "
"(P_id int , "
"DateTime varchar(50), "
"key1 int,key2 int,key3 int,key4 int);");
if(!q.exec())
qDebug()<<strerror(errno);
db.close();
return;
}
else if (cmnd=="Register")
{
string card=ReadFromCard();
printf("\nCard Accepted : %s\nEnter Your name: ",card.c_str());
string name;
int id;
//string dt=QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
cin>>name;
printf("\n Name Accepted.\nEnter Your Personel ID: ");
cin>>id;
q.prepare( "SELECT count(P_id) FROM Personel WHERE P_id =?;" );
q.bindValue(0,id);
if( !q.exec() )
{
printf("\nThe error occured from P_ID database");
qDebug()<<strerror(errno);
//ShowMainMenu();
//return;
}
// Note: if you don't return in case of an error, put this into the else{} part
while( q.next() )
{
if (q.value(0).toInt()>0)
{
printf("\nThis personel ID was repeated\n");
break;
}
else
{
q.prepare("SELECT CardID FROM Personel WHERE CardID =?;");
q.bindValue(0,card.c_str());
q.exec();
while(q.next())
{
printf("\nThis card is not valid : it was repeated\n");
break;
}
}
}
q.prepare("insert into Personel value(?,?,?);");
q.bindValue(0,id);
q.bindValue(1,card.c_str());
q.bindValue(2,name.c_str());
q.exec();
printf("\nInsert Personel complete\n");
db.close();
return;
}
else
{
string card;
card=cmnd;
QString dt=QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
q.prepare("SELECT P_id FROM Personel WHERE CardID =?;");
q.bindValue(0,card.c_str());
if( !q.exec() )
{
printf("\nThe error occured from database418\n");
ShowMainMenu();
}
string result=("\n CARD NOT VALID =>repeated %s\n");
// Note: if you don't return in case of an error, put this into the else{} part
while( q.next() )
{
int P_id = q.value(0).toInt();
q.prepare("insert into Absent(P_id,DateTime,key1,key2,key3,key4) value(?,?,1,0,0,0);");
q.bindValue(0,P_id);
q.bindValue(1,dt);
q.exec();
result=("\n********WELL COME **********%s\n");
}
printf("%s",result);
db.close();
return;
}
}
}
And this is my output :
0 - Press 0 to Back
1 - Press 1 to Register the card
1
//the below lines when I want to select something from both tables , occurred
No such file or directory
QSqlError("", "Unable to fetch row", "No query")
No such file or directory
//the below line is when the if(cmnd="Register") {..} block is running , occurred.
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
You don't need to close and open db connection every time SQL() function is running. Better approach is to open db once, when your app starts and close it at the end. Then, in SQL(..) you just create query like
QSqlQuery q;
and work with it.
Cause of QSqlError("", "Unable to fetch row", "No query") is that you create QSqlQuery(db); before db is opened. And you need to check q.prepare() for errors too.
It solved. Just do these :
1 - As #Tony said: QSqlQuery should defined after opening database.
2 - q.prepare just uses for SQL query contains bindvalue(), like select() or insert() .

QMYSQL query failing

I'm currently working on my project within C++ using Qt. I have MySQL as database storage and the idea is to make a small messenger like MSN and Skype.
However, my MySQL queries are failing. It simply gets no results or gives me an error. There's loads of code coming up, I'm sorry for that.
This is my mysql.cpp which creates and opens the database connection:
#include "mysql.h"
mysql::mysql()
{
this->db = QSqlDatabase::addDatabase("QMYSQL", "QMYSQL");
this->db.setHostName("localhost");
this->db.setUserName("root");
this->db.setPassword("Eequi4");
this->db.setDatabaseName("test");
this->db.open();
}
mysql::~mysql()
{
}
mysql_result mysql::create_result(QString query)
{
return mysql_result(this->db.exec(query));
}
QSqlError mysql::error()
{
return this->db.lastError();
}
The connection is opened. That works correctly.
This is my mysql_result.cpp, the file I use to add parameters, insert, get results etc:
#include "mysql_result.h"
mysql_result::mysql_result(QSqlQuery query)
{
this->query = query;
}
void mysql_result::add_parameter(QVariant value)
{
this->query.addBindValue(value);
}
void mysql_result::add_parameter(QString key, QVariant value)
{
this->query.bindValue(key, value);
}
int mysql_result::get_last_id()
{
return this->query.lastInsertId().toInt();
}
void mysql_result::execute()
{
this->query.execBatch();
}
QSqlQuery mysql_result::get_query()
{
return this->query;
}
mysql_result::~mysql_result()
{
}
Okay, this should work. If I have the following code, it correctly returns all member first_name's from the database:
mysql_result res = _mysql->create_result("SELECT * FROM members");
QSqlQuery qry = res.get_query();
while (qry.next())
{
qDebug() << qry.value("first_name");
}
In member_controller.cpp (the class I use to retrieve members by name/id), I got this:
member* member_controller::get_member(int id)
{
mysql_result result = engine::get_mysql().create_result("SELECT * FROM members WHERE member_id = :ID");
result.add_parameter(":ID", id);
QSqlQuery query = result.get_query();
if (query.exec() && query.next())
{
return new member(id, query.value("first_name").toString(), query.value("second_name").toString(), query.value("screen_name").toString(), query.value("email").toString(), query.value("status").toString());
}
else
{
qDebug() << engine::get_mysql().error() << "\n";
qDebug() << query.lastError() << "\n";
}
return new member(0, "", "", "", "", "");
}
What it does it will go to the else, and I get the error invalid syntax near :ID. If I replace :ID with #ID (just like in C#), it will go to the else without error code.. I don't know what the problem is.
Two things. The code needs to be optimized a bit and made easier, I'm gonna work on that. Also, is it possible/allowed to put code in a pastebin and paste the URL rather than put the code here?
Try changing your query to this:
"SELECT * FROM members WHERE member_id = ?"
and add your param like this:
result.add_parameter(0, id);
I'd also suspect, if (query.exec() && query.next()) is incorrect, and the check for .next() should be removed as I'd imagine that requires another record to exist in the result set.

Getting a QSqlError("", "", "") but the data is inserted

I am programming a Stock Control Program in Qt but when im inserting data I receive an error of QSqlError("", "", ""). The problem is that the data is being inserted into the SQLITE database but I'm unsure of what error means.
The code that I'm using to insert data into the database is below:
query_Account.prepare("INSERT INTO Customer(Company_Name, City, Phone_Number, Street_Adress, County, BULSTAT, Company_Owner, Account_Since) "
"VALUES (:Company_Name, :City, :Phone_Number, :Street_Adress, :County, :BULSTAT, :Company_Owner, :Account_Since)");
query_Account.bindValue(":Company_Name", ui->lineEdit_Company_Name->text());
query_Account.bindValue(":City", ui->lineEdit_City->text());
query_Account.bindValue(":Phone_Number", (ui->lineEdit_Phone_Num->text()).toInt());
query_Account.bindValue(":Street_Adress", ui->lineEdit_Street_Add->text());
query_Account.bindValue(":County", ui->lineEdit_County->text());
query_Account.bindValue(":BULSTAT", (ui->lineEdit_BULSTAT->text()).toInt());
query_Account.bindValue(":Company_Owner", ui->lineEdit_Company_Owner->text());
query_Account.bindValue(":Account_Since", 1776-07-04);
query_Account.exec();
qDebug() << "SQL query_Account:" << query_Account.executedQuery();
qDebug() << "SQL ERROR:" << query_Account.lastError();
You're not in fact getting an error. You're just unconditionally printing out an error even if you didn't get one.
if (query_Account.exec()) {
// got no error, proceed
qDebug() << "Yay!";
} else {
// got an error, deal with it
qDebug() << query_Account.executedQuery();
qDebug() << query_Account.lastError();
}

net-snmp is not changing auth and priv protocol correctly

I'm using the net-snmp library (version 5.7.1) in a c++ programm under Linux. I have a Web-Frontend where a user can choose a SNMP-Version and configure it. SNMPv1 and SNMPv2 are working just fine, but I got some issues with SNMPv3.
Here is a picture of the frontend: Screenshot of Webinterface (Sorry for not uploading it directly here, but I need at least 10 reputation to do this)
When I start the c++ backend and enter all needed SNMPv3 credentials correctly, everything is working fine and the device is reachable. If I change for example the Auth Protocol from MD5 to SHA, but leave the rest of the credentials the same, I would expect that the device is not reachable anymore. In real it stays reachable. After restarting the backend the device is (as expected) not reachable anymore with the same settings.
After discovering this issue, I ran some tests. For the test I used different users and different settings. They were run with three different devices of different vendors and I got every time the same result. So it can not be device realated issue. The results can be seen here: Test results
My conclusion after testing was, that net-snmp seems to cache the selected auth and priv protocol for one user name. This can be seen very good at Test 2. The first time I use an user name with a specific protocol I get the expected result. After changing the protocol
a different result is expected, but I get still the same result as before.
At the end some information how the SNMP-calls are made:
There is a class called SNMPWrapper, which handels the whole SNMP-communication
Inside the constructor I call init_snmp() to init net-snmp
From the outside I can call only get(), set() and walk(). Every time one of these methods is called, a new SNMP-Session is created (First I create a new Session with snmp_sess_init(), than I set up the things needed and finally I open the session with snmp_sess_open()
After I made the request and received my answer I close the session with snmp_sess_close()
Question: Do I have to do any other clean up before changing a protocol in order to get it work correctly?
Edit: I added some code, that shows the described behaviour
int main(int argc, char** argv) {
struct snmp_session session, session1, *ss, *ss1;
struct snmp_pdu *pdu, *pdu1;
struct snmp_pdu *response, *response1;
oid anOID[MAX_OID_LEN];
size_t anOID_len = MAX_OID_LEN;
struct variable_list *vars;
int status, status1;
init_snmp("snmpapp");
const char* user = "md5";
string authpw = "123123123";
string privpw = "";
string ipString = "192.168.15.32";
char ip[16];
memset(&ip, 0, sizeof (ip));
ipString.copy(ip, sizeof (ip) - 1, 0);
/*
* First request: AuthProto is MD5, no PrivProto is used. The snmp-get
* request is successful
*/
snmp_sess_init(&session); /* set up defaults */
session.peername = ip;
session.version = SNMP_VERSION_3;
/* set the SNMPv3 user name */
session.securityName = strdup(user);
session.securityNameLen = strlen(session.securityName);
// set the authentication method to MD5
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
session.securityAuthKeyLen = USM_AUTH_KU_LEN;;
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) authpw.c_str(), strlen(authpw.c_str()),
session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
//if code reaches here, the creation of the security key was not successful
}
cout << "SecurityAuthProto - session: " << session.securityAuthProto[9] << " / SecurityAuthKey - session: " << session.securityAuthKey << endl;
ss = snmp_open(&session); /* establish the session */
if (!ss) {
cout << "Couldn't open session1 correctly";
exit(2);
}
cout << "SecurityAuthProto - ss: " << ss->securityAuthProto[9] << " / SecurityAuthKey - ss: " << ss->securityAuthKey << endl;
//send message
pdu = snmp_pdu_create(SNMP_MSG_GET);
read_objid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len);
snmp_add_null_var(pdu, anOID, anOID_len);
status = snmp_synch_response(ss, pdu, &response);
/*
* Process the response.
*/
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
cout << "SNMP-read success" << endl;
} else {
cout << "SNMP-read fail" << endl;
}
if (response)
snmp_free_pdu(response);
if (!snmp_close(ss))
cout << "Snmp closing failed" << endl;
/*
* Second request: Only the authProto is changed from MD5 to SHA1. I expect,
* that the snmp-get fails, but it still succeeds.
*/
snmp_sess_init(&session1);
session1.peername = ip;
session1.version = SNMP_VERSION_3;
/* set the SNMPv3 user name */
session1.securityName = strdup(user);
session1.securityNameLen = strlen(session1.securityName);
// set the authentication method to SHA1
session1.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session1.securityAuthProto = usmHMACSHA1AuthProtocol;
session1.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
session1.securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session1.securityAuthProto,
session1.securityAuthProtoLen,
(u_char *) authpw.c_str(), strlen(authpw.c_str()),
session1.securityAuthKey,
&session1.securityAuthKeyLen) != SNMPERR_SUCCESS) {
//if code reaches here, the creation of the security key was not successful
}
cout << "SecurityAuthProto - session1: " << session1.securityAuthProto[9] << " / SecurityAuthKey - session1: " << session1.securityAuthKey << endl;
ss1 = snmp_open(&session1); /* establish the session */
if (!ss1) {
cout << "Couldn't open session1 correctly";
exit(2);
}
cout << "SecurityAuthProto - ss1: " << ss1->securityAuthProto[9] << " / SecurityAuthKey - ss1: " << ss1->securityAuthKey << endl;
//send message
pdu1 = snmp_pdu_create(SNMP_MSG_GET);
read_objid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len);
snmp_add_null_var(pdu1, anOID, anOID_len);
status1 = snmp_synch_response(ss1, pdu1, &response1);
/*
* Process the response.
*/
if (status1 == STAT_SUCCESS && response1->errstat == SNMP_ERR_NOERROR) {
cout << "SNMP-read success" << endl;
} else {
cout << "SNMP-read fail" << endl;
}
if (response1)
snmp_free_pdu(response1);
snmp_close(ss1);
return 0;
}
I found the solution by myself:
net-snmp caches for every EngineId (device) the users. If there is an existing user for an engineID and you try to open a new session with this user, net-snmp will use the cached one. So the solution was to clear the list with cached users.
With this code snippet I could resolve my problem:
usmUser* actUser = usm_get_userList();
while (actUser != NULL) {
usmUser* dummy = actUser;
usm_remove_user(actUser);
actUser = dummy->next;
}
I hope I can help somebody else with this.
You can also update password for an existing user:
for (usmUser* actUser = usm_get_userList(); actUser != NULL; actUser = actUser->next) {
if (strcmp(actUser->secName, user) == 0) {
//this method calls generate_Ku with previous security data but with specified password
usm_set_user_password(actUser, "userSetAuthPass", authpw.c_str());
break;
}
}