QSqlQuery forbid non-SELECT query - c++

I'm looking for a way to forbid non-SELECT query for a QSqlQuery object.
I know QSqlQuery::isSelect is only effective after QSqlQuery::exec (damage
already done here).
So, is there any way to do that or I have to verify the query string explicitly.
Thanks.

From my experience, the QSqlQuery::isSelect function works well even before running the QSqlQuery::exec command. But only when the object is initialized with the query. i.e:
QSqlQuery query("SELECT * FROM someTable");
qDebug() << query.isSelect(); //Outputs True
OR
QString queryString = "SELECT * FROM someTable";
QSqlQuery query(queryString);
qDebug() << query.isSelect(); //Outputs True
If you are using the prepare function and passing the query string to it, then you will have to verify the query string yourself. i.e;
QSqlQuery query;
query.prepare("SELECT * FROM someTable");
qDebug() << query.isSelect(); //Outputs False
If you really want to avoid manual verification of the query string, you will have to avoid using QSqlQuery::prepare and QSqlQuery::bindValue functions and instead create the query yourself and initialize the QSqlQuery object with the QString you create.

Related

QSqlQuery with back slash

I'm trying to retrieve the users from the database their names are 'DOMAIN\name'.
I've checked the query in sql console, simple select like :
select * from users where name='DOMAIN\\name'
it returns correct row if the name in the database looks like 'DOMAIN\user' (single back slash).
however QSqlQuery returns empty :
The code something like:
const QString command = QStringLiteral("select * "
"from %1 where name = '%2'")
.arg(Constants::kUsersTableName).arg(userId);
qCDebug() << "Query:" << command;
QSqlDatabase db = QSqlDatabase::database(m_connection, false);
QSqlQuery query(db);
if (!query.prepare(command) || !query.exec()) {
...
log :
Query: "select * from users where name = 'DOMAIN\\name'"
any idea why QSqlQuery returns empty at the same time when database console return valid record for the user.
dbms: MySQL
MySQL uses \ as its escape character therefore the correct query string you have to escape the slash twice, once for c++ and once for mysql:
"select * from users where name='DOMAIN\\\\name'"
The simpler solution is to use prepared statements and placeholders correctly which as a very important bonus will protect your code from SQLI.
Note that field and table names aren't allowed to be placeholders by most if not all database engines so you will still need to build that part of your string by hand:
const QString usernamePlaceholder = ":username"
const QString command = QStringLiteral("select * "
"from %1 where name = %2")
.arg(Constants::kUsersTableName).arg(usernamePlaceholder);
QSqlDatabase db = QSqlDatabase::database(m_connection, false);
QSqlQuery query(db);
if (!query.prepare(command)) {
qCDebug() << query.lastError();
return;
}
query.bindValue(usernamePlaceholder, userId);
if (!query.exec()) {
qCDebug() << query.lastError();
return;
}
bindValue will take care of whatever quoting and escaping is required for your values.

QSqlDatabase::record(const QString &tablename) returns empty record

I have already read this question. I want to get field names of a table using method QSqlDatabase::record(const QString &tablename). But it always returns an empty record. I can query database tables by QSQLQuery properly. My database is a SQL Server database.
Actually you should get.
As you are saying SQL server, try with "#yourtablename". I mean to say prepend "#" before your tablename in your QString.
if no luck,
Check your table name. (spaces or additional chars etc..)
Type cast to QString (safe side). Or create a QString object with table name and pass it.
Trim the QString, before passing it.
Still for some reason if you are not getting the field names, try below steps.
Your QSqlRecord QSqlDatabase::record(const QString &tablename) const will return a QSqlRecord object
First get the number fields in record
int QSqlRecord::count() const
Loop the count (ex: using for) and get the field name for each index using below function
QString QSqlRecord::fieldName(int index) const
Some pseudo code below: (assuming you have successful connection established, Not compiled and not tested.)
QSqlRecord rec = QSqlDatabase::record("Your table name");
int count = rec.count();
QStringList fieldNames;
For (int i =0; i<count; i++)
{
fieldNames.push_back(rec.fieldName(i);
}
By Select Query:
QSqlQuery select;
select.prepare("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'YourTableName'");
if (select.exec() && select.next()) {
QSqlRecord record = select.record();
}

Qt: Save result of SQL-query in variable, use C++ variable in SQL-query

My project is to program a simple ShopApp. One function is about checking, whether there is enough stock left, so that a customer can buy the desired amount of whatever he wants to buy. The functions looks like (where cart is a std::vector<product> and Pid stands for product id):
bool sqlfunctions::checkStock(){
QSqlQuery query;
int diff, stock;
for(iter cursor = cart.begin();cursor!=cart.end();cursor++){
query.prepare("SELECT stock FROM products WHERE id = cursor->getPid()");
query.exec();
// Need to save result of query into variable stock
stock = ??;
diff = stock - cursor->getAmount;
if(diff < 0){
return false;
}
}
return true;
}
Obviously this function is not functional, because cursor->getPid() is not executed since it is a string. So the question here is how to insert a c++-variable into the sql-query?In regular C++ i would used some swprintf_s function. So is query.prepare(swprintf_s(...)) a good idea?
The second thing is, since both query.exec() and query.prepare() are booleans, which return true or false, depeding on success, how can i store
results of the queries in a c++ variable?
Please note, that I am new to SQL and SQL with Qt. I use QT5.
I already tried to read the documentation about theQSqlQuery class and its functions, BindValue() and addBindValue() seem to be interesting. However i dont really understand how they work.
Edit
So now I've got a minimal example here that does not work yet, despite following the accepted answer. However the compiler does not give me any warnings or errors:
void MainWindow::on_ButtonSQL_clicked()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("...");
db.setDatabaseName("...");
db.setUserName("...");
db.setPassword("...");
db.setPort(3306);
db.open();
QMessageBox msgBox;
if (db.open()) {
msgBox.setText("It works :)");
msgBox.exec();
}
else {
msgBox.setText("No connection.");
msgBox.exec();
}
QSqlQuery query(db);
// This query worked!
query.exec("INSERT INTO users (id, username, balance) VALUES(25, 'someName', 10000)");
// initialize someNumber to check later, whether it was processed correctly.
int id = 2, someNumber = 20;
query.prepare("SELECT stock FROM products WHERE id = :myid");
query.bindValue(":myid", id);
query.exec();
QString idValue = query.boundValue(0).toString();
someNumber = query.value(0).toInt();
msgBox.setText("The stock is: "+QString::number(someNumber)+"\nThe placeholder has the value: "+idValue);
msgBox.exec();
}
Expected msgBox of the last msgBox is:
The stock is: 100
The placeholder value is: 2
Output actually is:
The stock is: 0
The placeholder value is: 2
If I instead try to select a string (e.g. productName), say with QString myProductName = query.value(0).toString() (and respective changes in the code), the return would be an empty string.
** SOLVED: ** See comment from Floris in the accepted answer. I missed query.next().
It is pretty straight forward actually:
QSqlQuery query;
query.prepare("Select stock from products where id = :input");
query.bindValue(":input", cursor->getPid());
query.exec();
You bind the values to the argument in the string. Arguments follow the format: :name. There is also positional binding which binds in the order it sees ?.
QSqlQuery query;
query.prepare("Select stock from products where id = ?");
// No need for an identifier
query.bindValue(cursor->getPid());
query.exec();
To iterate the records you obtained from a query you can do the following:
QSqlQuery query;
query.prepare("SELECT stock FROM employee WHERE id = ?");
query.bindValue(cursor->getPid());
query.exec();
if (query.next()) {
int stock = query.value(0).toInt();
// You could store the information you obtain here in a vector or something
}
You could also put the prepare statement outside the for loop. If you are interested in iterating multiple records (from a select statement) you can replace the if statement with a while statement.
Concerning QSqlQuery::next():
Retrieves the next record in the result, if available, and positions the query on the retrieved record. Note that the result must be in the active state and isSelect() must return true before calling this function or it will do nothing and return false.
As taken from QSqlQuery. You will need to make this call before the record will actually be accessible with .value(int).

QSqlQuery.record() is always empty

I'm editing the database in this manner:
QSqlQuery query;
query.prepare("UPDATE student "
"SET name = ? "
"WHERE id = ?");
QString name = "t";
int id = 3;
query.addBindValue(name);
query.addBindValue(id);
query.exec(); // query exec returns true
QSqlRecord record = query.record(); // but the record is empty!
mTableModel->beforeInsert(record);
The retrieved record is always empty, but the QSqlTableModel still changes! I need the record to be valid because I'm trying to synchronize an sql db with a std::vector.
I'm connecting to the database like this:
mDatabase = QSqlDatabase::addDatabase("QSQLITE");
mDatabase.setDatabaseName("database.db");
mDatabase.open();
I tried calling QSqlQuery::clear(), QSqlQuery::finish() but it didn't help. I also tried to open and close the DB, but it also didn't help. What can I do? :\
Qt is not a pain indeed.
All your code is good. The only wrong assumption is that an update request will automatically give you back the updated record. You have to make a new select request on this id to get the updates data in a QSqlRecord.
//[untested]
QSqlQuery select;
select.prepare("SELECT * from student where id = ?");
select.addBindValue(id);
if (select.exec() && select.next()) {
QSqlRecord record = select.record();
}

QSqlQuery Memory issues. QSqlQuery::exec() and QSqlDatabase::open()/close();

I'm checking the memory usage of an application I've made. It makes numerous calls to read and write values to and from a database (SQLite 3). I've observed the following:
QSqlQuery::exec() uses some KB of RAM to execute a given query, but does not release the memory after it goes out of scope.
QSqlDatabase:: open() & close() do not help free resources as the documentation suggest. If anything, close() causes resources (at least memory) to remain 'trapped' on the heap/stack.
For example, here is a typical segment of code I've been using to access my database.
QStringList values;
db.open();
QString strQuery = "SELECT DISTINCT " + field + " FROM " + table + str;
QSqlQuery query(db);
query.prepare(strQuery);
if(query.exec() == true)
{
while(query.next())
{
values.push_back(query.value(0).toString());
}
}
db.close();
Having experimented with I find the code below 'traps' less memory:
QStringList values;
QString strQuery = "SELECT DISTINCT " + field + " FROM " + table + str;
QSqlQuery query(strQuery, db);
while(query.next())
{
values.push_back(query.value(0).toString());
}
However, a small amount of memory is still not released. Has anyone else experienced anything like this?
Can I some how release this memory?
P.s. Same happens here, some memory is never released:
db.open();
QSqlQuery query(db);
query.exec("DELETE FROM table1");
query.exec("DELETE FROM table2");
query.exec("DELETE FROM table3");
query.exec("DELETE FROM table4");
...
db.close();
It seems that in order to release this memory you must create the QSqlQuery variable as a pointer, and delete this pointer before you close the database:
QStringList values;
db.open();
QString strQuery = "SELECT DISTINCT " + field + " FROM " + table + str;
QSqlQuery *query = new QSqlQuery(db);
query->prepare(strQuery);
if(query->exec() == true)
{
while(query->next())
{
values.push_back(query->value(0).toString());
}
}
delete query;
db.close();
The memory is then released after the database closes.
You have to use QSqlQuery.finish () or QSqlQuery.clear before you close the database. Otherwise residual memory is left out in the Query object. It is mentioned in the document that Query object can be used for multiple query. You will noticed the "memory leak".. when you query for 10,000 records. The memory usage goes up drastically.
From the documentation of QSqlDatabase::addDatabase and QSqlDatabase::database() one can deduce that there is a global variable that manages the database connections. If you look into qsqldatabase.cpp you will find a QConnectionDict.
BTW: Do not construct your SQL queries by concatenating strings, always use prepare and bindValue (SQL injecttion!), if there is any chance that parts of the query come from user input.