Use prepared query for value replacement allowing to use non escaped characters

This commit is contained in:
Grégory Soutadé 2016-10-08 20:05:01 +02:00
parent 992516d972
commit fe35930273
4 changed files with 146 additions and 45 deletions

View File

@ -1,9 +1,10 @@
v0.6 (05/11/2015) v0.6 (08/10/2016)
** User ** ** User **
Set autofocus attribute to user in index.php Set autofocus attribute to user in index.php
** Dev ** ** Dev **
Use transactions for SQL updates Use transactions for SQL updates
Use prepared query for value replacement allowing to use non escaped characters
** Bugs ** ** Bugs **
Crash when use of a tag in a new operation Crash when use of a tag in a new operation

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2010-2015 Grégory Soutadé Copyright 2010-2016 Grégory Soutadé
This file is part of KissCount. This file is part of KissCount.
@ -171,7 +171,10 @@ bool Database::IsValidUser(const QString& user, const QString& password)
{ {
QSqlQuery query(_db); QSqlQuery query(_db);
EXECUTE_SQL_QUERY(QString("SELECT name FROM user WHERE name='%1' AND password='%2'").arg(user, HashPassword(password)), false); query.prepare("SELECT name FROM user WHERE name=:id AND password=:password");
query.bindValue(":id", user);
query.bindValue(":password", HashPassword(password));
EXECUTE_PREPARED_SQL_QUERY(false);
return query.next(); return query.next();
} }
@ -239,7 +242,9 @@ User* Database::LoadUser(const QString& name)
std::vector<Account>::iterator it; std::vector<Account>::iterator it;
EXECUTE_SQL_QUERY(QString("SELECT * FROM user WHERE name='%1'").arg(name), 0); query.prepare("SELECT * FROM user WHERE name=:name");
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(0);
if (!query.next()) if (!query.next())
return 0; return 0;
@ -570,14 +575,18 @@ void Database::UpdateOperation(User* user, Operation& op, bool checkTransfert)
ESCAPE_CHARS(op.description); ESCAPE_CHARS(op.description);
req = "UPDATE operation SET parent='%1', account='%2', year='%3', month='%4', day='%5', amount='%6', description=\"%7\", category='%8', tag='%9'" ; req = "UPDATE operation SET parent='%1', account='%2', year='%3', month='%4', day='%5', amount='%6', description=:description, category='%8', tag='%9'" ;
req = req.arg((op.parent) ? QString::number(op.parent) : "", QString::number(op.account), QString::number(op.year), QString::number(op.month), req = req.arg((op.parent) ? QString::number(op.parent) : "", QString::number(op.account), QString::number(op.year), QString::number(op.month),
QString::number(op.day), v.sprintf("%d", op.amount), op.description, QString::number(op.category), QString::number(op.tag)); QString::number(op.day), v.sprintf("%d", op.amount), QString::number(op.category), QString::number(op.tag));
req += ", fix_cost='%1', checked='%2', transfert='%3', meta='%4', virtual='%5', formula='%6' WHERE id='%7'"; req += ", fix_cost='%1', checked='%2', transfert='%3', meta='%4', virtual='%5', formula=:formula WHERE id='%7'";
req = req.arg(QString::number(op.fix_cost), QString::number(op.checked), (op.transfert) ? QString::number(op.transfert): "", req = req.arg(QString::number(op.fix_cost), QString::number(op.checked), (op.transfert) ? QString::number(op.transfert): "",
QString::number(op.meta), QString::number(op._virtual), op.formula, QString::number(op.id)); QString::number(op.meta), QString::number(op._virtual), QString::number(op.id));
EXECUTE_SQL_UPDATE(req, ); query.prepare(req);
query.bindValue(":description", op.description);
query.bindValue(":formula", op.formula);
EXECUTE_PREPARED_SQL_UPDATE();
if (checkTransfert) if (checkTransfert)
LinkOrUnlinkOperation(user, op); LinkOrUnlinkOperation(user, op);
@ -592,14 +601,18 @@ int Database::AddOperation(User* user, Operation& op, bool checkTransfert)
ESCAPE_CHARS(op.description); ESCAPE_CHARS(op.description);
req = "INSERT INTO operation ('user', 'parent', 'account', 'year', 'month', 'day', 'amount', 'description', 'category', 'tag', 'fix_cost', 'formula', 'transfert', 'meta', 'virtual', 'checked') VALUES ('%1', '%2', '%3', '%4', '%5', '%6', '%7', \"%8\"" ; req = "INSERT INTO operation ('user', 'parent', 'account', 'year', 'month', 'day', 'amount', 'description', 'category', 'tag', 'fix_cost', 'formula', 'transfert', 'meta', 'virtual', 'checked') VALUES ('%1', '%2', '%3', '%4', '%5', '%6', '%7', :description" ;
req = req.arg(QString::number(user->_id), (op.parent) ? QString::number(op.parent): "", QString::number(op.account), QString::number(op.year), req = req.arg(QString::number(user->_id), (op.parent) ? QString::number(op.parent): "", QString::number(op.account), QString::number(op.year),
QString::number(op.month), QString::number(op.day), v.sprintf("%d", op.amount), op.description); QString::number(op.month), QString::number(op.day), v.sprintf("%d", op.amount));
req += ", '%1', '%2', '%3', '%4', '%5', '%6', '%7', '%8')"; req += ", '%1', '%2', '%3', :formula, '%4', '%5', '%6', '%7')";
req = req.arg(QString::number(op.category), QString::number(op.tag), QString::number(op.fix_cost), op.formula, (op.transfert) ? QString::number(op.transfert): "", req = req.arg(QString::number(op.category), QString::number(op.tag), QString::number(op.fix_cost), (op.transfert) ? QString::number(op.transfert): "",
QString::number(op.meta), QString::number(op._virtual), QString::number(op.checked)); QString::number(op.meta), QString::number(op._virtual), QString::number(op.checked));
if (!query.exec(req)) query.prepare(req);
query.bindValue(":description", op.description);
query.bindValue(":formula", op.formula);
if (!query.exec())
{ {
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req); QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
return 0; return 0;
@ -841,9 +854,12 @@ void Database::AddSharedAccount(Account& ac, const QString& granted)
QSqlRecord set; QSqlRecord set;
QSqlQuery query(_db); QSqlQuery query(_db);
req = QString("SELECT id FROM user WHERE name='%1'").arg(granted); req = QString("SELECT id FROM user WHERE name=:name");
EXECUTE_SQL_QUERY(req, ); query.prepare(req);
query.bindValue(":name", granted);
EXECUTE_PREPARED_SQL_QUERY();
query.next(); query.next();
@ -907,7 +923,7 @@ int Database::AddCategory(User* user, Category& category)
req = "INSERT INTO category ('user', 'parent', 'name', 'backcolor', 'forecolor', 'font', 'fix_cost') VALUES ('" ; req = "INSERT INTO category ('user', 'parent', 'name', 'backcolor', 'forecolor', 'font', 'fix_cost') VALUES ('" ;
req += QString::number(user->_id) + "'"; req += QString::number(user->_id) + "'";
req += ", '" + QString::number(category.parent) + "'"; req += ", '" + QString::number(category.parent) + "'";
req += ", '" + category.name + "'"; req += ", :name";
req += ", '" + backcolor + "'"; req += ", '" + backcolor + "'";
req += ", '" + forecolor + "'"; req += ", '" + forecolor + "'";
req += ", '" + category.font + "'"; req += ", '" + category.font + "'";
@ -917,7 +933,10 @@ int Database::AddCategory(User* user, Category& category)
req += ", '0'"; req += ", '0'";
req += ")"; req += ")";
if (!query.exec(req)) query.prepare(req);
query.bindValue(":name", category.name);
if (!query.exec())
{ {
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req); QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
std::cerr << __FUNCTION__ << "\n" ; std::cerr << __FUNCTION__ << "\n" ;
@ -933,6 +952,7 @@ void Database::UpdateCategory(Category& category)
{ {
QString req, tmp; QString req, tmp;
QString backcolor, forecolor; QString backcolor, forecolor;
QSqlQuery query(_db);
backcolor = "#" ; backcolor = "#" ;
tmp = QString::number(category.backcolor.red(), 16); tmp = QString::number(category.backcolor.red(), 16);
@ -958,7 +978,7 @@ void Database::UpdateCategory(Category& category)
req = "UPDATE category SET" ; req = "UPDATE category SET" ;
req += " parent='" + QString::number(category.parent) + "'"; req += " parent='" + QString::number(category.parent) + "'";
req += ", name='" + category.name + "'"; req += ", name=:name";
req += ", backcolor='" + backcolor + "'"; req += ", backcolor='" + backcolor + "'";
req += ", forecolor='" + forecolor + "'"; req += ", forecolor='" + forecolor + "'";
req += ", font='" + category.font + "'"; req += ", font='" + category.font + "'";
@ -968,7 +988,9 @@ void Database::UpdateCategory(Category& category)
req += ", fix_cost='0'"; req += ", fix_cost='0'";
req += " WHERE id='" + QString::number(category.id) + "'"; req += " WHERE id='" + QString::number(category.id) + "'";
EXECUTE_SQL_UPDATE(req, ); query.prepare(req);
query.bindValue(":name", category.name);
EXECUTE_PREPARED_SQL_UPDATE();
} }
void Database::DeleteCategory(User* user, Category& category, int replacement) void Database::DeleteCategory(User* user, Category& category, int replacement)
@ -1004,11 +1026,17 @@ bool Database::LoadCategory(int id, const QString& name, Category& category)
bool ret = false ; bool ret = false ;
if (id) if (id)
{
req = QString("SELECT * FROM category WHERE id='%1'").arg(id); req = QString("SELECT * FROM category WHERE id='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
}
else else
req = QString("SELECT * FROM category WHERE name='%1'").arg(name); {
req = QString("SELECT * FROM category WHERE name=:name");
EXECUTE_SQL_QUERY(req, false); query.prepare(req);
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(false);
}
if (query.next()) if (query.next())
{ {
@ -1030,10 +1058,13 @@ int Database::AddTag(User* user, Tag& tag)
req = "INSERT INTO tag ('user', 'name') VALUES ('" ; req = "INSERT INTO tag ('user', 'name') VALUES ('" ;
req += QString::number(user->_id) + "'"; req += QString::number(user->_id) + "'";
req += ", '" + tag.name + "'"; req += ", :name";
req += ")"; req += ")";
if (!query.exec(req)) query.prepare(req);
query.bindValue(":name", tag.name);
if (!query.exec())
{ {
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req); QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
std::cerr << __FUNCTION__ << "\n" ; std::cerr << __FUNCTION__ << "\n" ;
@ -1048,12 +1079,16 @@ int Database::AddTag(User* user, Tag& tag)
void Database::UpdateTag(Tag& tag) void Database::UpdateTag(Tag& tag)
{ {
QString req; QString req;
QSqlQuery query(_db);
req = "UPDATE tag SET" ; req = "UPDATE tag SET" ;
req += "name='" + tag.name + "'"; req += "name=:name";
req += " WHERE id='" + QString::number(tag.id) + "'"; req += " WHERE id='" + QString::number(tag.id) + "'";
EXECUTE_SQL_UPDATE(req, ); query.prepare(req);
query.bindValue(":name", tag.name);
EXECUTE_PREPARED_SQL_UPDATE();
} }
void Database::DeleteTag(User* user, Tag& tag, int replacement) void Database::DeleteTag(User* user, Tag& tag, int replacement)
@ -1078,11 +1113,17 @@ bool Database::LoadTag(int id, const QString& name, Tag& tag)
bool ret = false ; bool ret = false ;
if (id) if (id)
{
req = QString("SELECT * FROM tag WHERE id='%1'").arg(id); req = QString("SELECT * FROM tag WHERE id='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
}
else else
req = QString("SELECT * FROM tag WHERE name='%1'").arg(name); {
req = QString("SELECT * FROM tag WHERE name=:name");
EXECUTE_SQL_QUERY(req, false); query.prepare(req);
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(false);
}
if (query.next()) if (query.next())
{ {
@ -1225,10 +1266,15 @@ void Database::GenerateMonth(User* user, int monthFrom, int yearFrom, int monthT
void Database::ChangePassword(User* user, const QString& password) void Database::ChangePassword(User* user, const QString& password)
{ {
QString req; QString req;
QSqlQuery query(_db);
req = QString("UPDATE user SET password='%1' WHERE name='%2'").arg(HashPassword(password), user->_name) ; req = QString("UPDATE user SET password=:password WHERE name=:name");
EXECUTE_SQL_UPDATE(req, ); query.prepare(req);
query.bindValue(":name", user->_name);
query.bindValue(":password", HashPassword(password));
EXECUTE_PREPARED_SQL_UPDATE();
} }
bool Database::UserExists(const QString& name) bool Database::UserExists(const QString& name)
@ -1238,9 +1284,12 @@ bool Database::UserExists(const QString& name)
QString req; QString req;
bool res=false; bool res=false;
req = QString("SELECT name FROM user WHERE name='%1'").arg(name); req = QString("SELECT name FROM user WHERE name=:name");
EXECUTE_SQL_QUERY(req, false); query.prepare(req);
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(false);
if (query.next()) if (query.next())
res = true; res = true;
@ -1255,19 +1304,29 @@ bool Database::UserExists(const QString& name)
void Database::ChangeName(User* user, const QString& name) void Database::ChangeName(User* user, const QString& name)
{ {
QString req; QString req;
QSqlQuery query(_db);
req = QString("UPDATE user SET name='%1' WHERE name='%2'").arg(name, user->_name) ; req = QString("UPDATE user SET name=:name WHERE name=:name2");
EXECUTE_SQL_UPDATE(req, ); query.prepare(req);
query.bindValue(":name", name);
query.bindValue(":name2", user->_name);
EXECUTE_PREPARED_SQL_UPDATE();
} }
void Database::NewUser(const QString& name) void Database::NewUser(const QString& name)
{ {
QString req; QString req;
QSqlQuery query(_db);
req = QString("INSERT INTO user ('name', 'password') VALUES ('%1', '%2')").arg(name, HashPassword("")) ; req = QString("INSERT INTO user ('name', 'password') VALUES (:name, :password)");
EXECUTE_SQL_UPDATE(req, ); query.prepare(req);
query.bindValue(":name", name);
query.bindValue(":password", HashPassword(""));
EXECUTE_PREPARED_SQL_UPDATE();
} }
/* /*

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2010-2012 Grégory Soutadé Copyright 2010-2016 Grégory Soutadé
This file is part of KissCount. This file is part of KissCount.
@ -64,6 +64,22 @@
_db.commit(); \ _db.commit(); \
} while(0); } while(0);
#define EXECUTE_PREPARED_SQL_UPDATE_WITH_CODE(return_value, code_if_fail, code_if_syntax_fail) \
do { \
_db.transaction(); \
if (!query.exec()) \
{ \
QMessageBox::critical(0, _("Error"), _("Update failed !\n")); \
std::cerr << __FUNCTION__ << " " << __FILE__ << " " << __LINE__ << "\n" ; \
std::cerr << query.lastQuery().toStdString() << "\n" ; \
std::cerr << query.lastError().text().toStdString() << "\n" ; \
_db.rollback(); \
code_if_fail; \
return return_value; \
} \
_db.commit(); \
} while(0);
#define EXECUTE_SQL_QUERY_WITH_CODE(req, return_value, code_if_fail, code_if_syntax_fail) \ #define EXECUTE_SQL_QUERY_WITH_CODE(req, return_value, code_if_fail, code_if_syntax_fail) \
do { \ do { \
if (!query.exec(req)) \ if (!query.exec(req)) \
@ -77,10 +93,27 @@
} \ } \
} while(0); } while(0);
#define EXECUTE_PREPARED_SQL_QUERY_WITH_CODE(return_value, code_if_fail, code_if_syntax_fail) \
do { \
if (!query.exec()) \
{ \
QMessageBox::critical(0, _("Error"), _("Query failed !\n")); \
std::cerr << __FUNCTION__ << "\n" ; \
std::cerr << query.lastQuery().toStdString() << "\n" ; \
std::cerr << query.lastError().text().toStdString() << "\n" ; \
code_if_fail; \
return return_value; \
} \
} while(0);
#define EXECUTE_SQL_QUERY(req, return_value) EXECUTE_SQL_QUERY_WITH_CODE(req, return_value, {}, {}) #define EXECUTE_SQL_QUERY(req, return_value) EXECUTE_SQL_QUERY_WITH_CODE(req, return_value, {}, {})
#define EXECUTE_SQL_UPDATE(req, return_value) EXECUTE_SQL_UPDATE_WITH_CODE(req, return_value, {}, {}) #define EXECUTE_SQL_UPDATE(req, return_value) EXECUTE_SQL_UPDATE_WITH_CODE(req, return_value, {}, {})
#define EXECUTE_PREPARED_SQL_UPDATE(return_value) EXECUTE_PREPARED_SQL_UPDATE_WITH_CODE(return_value, {}, {})
#define EXECUTE_PREPARED_SQL_QUERY(return_value) EXECUTE_PREPARED_SQL_QUERY_WITH_CODE(return_value, {}, {})
class KissCount; class KissCount;
class User; class User;

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2010-2012 Grégory Soutadé Copyright 2010-2016 Grégory Soutadé
This file is part of KissCount. This file is part of KissCount.
@ -913,7 +913,7 @@ void GridAccount::OnOperationModified(int row, int col)
{ {
User* user = _kiss->GetUser(); User* user = _kiss->GetUser();
Operation new_op, cur_op, op_tmp, op_tmp2; Operation new_op, cur_op, op_tmp, op_tmp2;
int op_complete = 6; int op_complete = 6, new_op_id;
QString value, v ; QString value, v ;
QDate date; QDate date;
bool need_insertion = false, transfertCompleted = false; bool need_insertion = false, transfertCompleted = false;
@ -1111,11 +1111,19 @@ void GridAccount::OnOperationModified(int row, int col)
new_op._virtual = false; new_op._virtual = false;
new_op.parent = 0; new_op.parent = 0;
new_op_id = _kiss->AddOperation(new_op);
if (!new_op_id)
{
_inModification = false ;
return;
}
RemoveRow(new_op, row, false); RemoveRow(new_op, row, false);
NULLop.id = 0; NULLop.id = 0;
InsertOperation(user, NULLop, row, new_op.fix_cost, _curMonth, _curYear); InsertOperation(user, NULLop, row, new_op.fix_cost, _curMonth, _curYear);
new_op.id = _kiss->AddOperation(new_op); new_op.id = new_op_id;
if (transfertCompleted) if (transfertCompleted)
_transfertCompletionIndex = (_transfertCompletionIndex + 1) % 2; _transfertCompletionIndex = (_transfertCompletionIndex + 1) % 2;