Added convenience select method, improved security of recipe storage.
This commit is contained in:
parent
cacc75f07d
commit
238d7edee3
|
@ -34,8 +34,16 @@ bool Database::insertInto(string tableName, vector<string> columnNames, vector<s
|
||||||
string cols = combineVector(columnNames, ", ");
|
string cols = combineVector(columnNames, ", ");
|
||||||
string vals = combineVector(values, ", ");
|
string vals = combineVector(values, ", ");
|
||||||
query += cols + ") VALUES (" + vals + ");";
|
query += cols + ") VALUES (" + vals + ");";
|
||||||
this->executeSQL(query);
|
ResultTable t = this->executeSQL(query);
|
||||||
return true;
|
return (t.getReturnCode() == SQLITE_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultTable Database::selectFrom(string tableName, string columnNames, string conditions){
|
||||||
|
if (columnNames.size() == 0 || tableName.empty()){
|
||||||
|
return ResultTable();
|
||||||
|
}
|
||||||
|
string query = "SELECT " + columnNames + " FROM " + tableName + " WHERE " + conditions + ";";
|
||||||
|
return this->executeSQL(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::openConnection(){
|
void Database::openConnection(){
|
||||||
|
@ -58,18 +66,23 @@ string Database::combineVector(std::vector<string> strings, string mid){
|
||||||
if (strings.empty()){
|
if (strings.empty()){
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::string result = "'" + strings[0] + "'";
|
std::string result = surroundString(strings[0], "'");
|
||||||
for (std::vector<std::string>::iterator it = strings.begin() + 1; it != strings.end(); ++it){
|
for (std::vector<std::string>::iterator it = strings.begin() + 1; it != strings.end(); ++it){
|
||||||
result += mid + "'" + (*it) + "'";
|
result += mid + surroundString((*it), "'");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string Database::surroundString(string s, string surround){
|
||||||
|
return surround+s+surround;
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::tableExists(string tableName){
|
bool Database::tableExists(string tableName){
|
||||||
if (tableName.empty() || this->db == NULL || !this->dbIsOpen){
|
if (tableName.empty() || this->db == NULL || !this->dbIsOpen){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';");
|
ResultTable t = this->selectFrom("sqlite_master", "name", "type='table' AND name='"+tableName+"'");
|
||||||
|
//ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';");
|
||||||
return !t.isEmpty();
|
return !t.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,14 @@ public:
|
||||||
//Executes an SQL string statement in a safe way and returns the result.
|
//Executes an SQL string statement in a safe way and returns the result.
|
||||||
ResultTable executeSQL(string statement);
|
ResultTable executeSQL(string statement);
|
||||||
bool insertInto(string tableName, vector<string> columnNames, vector<string> values);
|
bool insertInto(string tableName, vector<string> columnNames, vector<string> values);
|
||||||
|
ResultTable selectFrom(string tableName, string columnNames, string conditions);
|
||||||
|
|
||||||
bool tableExists(string tableName);
|
bool tableExists(string tableName);
|
||||||
int getLastInsertedRowId();
|
int getLastInsertedRowId();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
string surroundString(string s, string surround);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//SQL Instance variables.
|
//SQL Instance variables.
|
||||||
string filename;
|
string filename;
|
||||||
|
|
|
@ -6,10 +6,11 @@ RecipeDatabase::RecipeDatabase(string filename) : Database(filename){
|
||||||
|
|
||||||
bool RecipeDatabase::storeRecipe(Recipe recipe){
|
bool RecipeDatabase::storeRecipe(Recipe recipe){
|
||||||
///TODO: Implement this in a smart way using transaction.
|
///TODO: Implement this in a smart way using transaction.
|
||||||
ResultTable t = this->executeSQL("SELECT * FROM recipe WHERE name='"+recipe.getName()+"';");
|
this->executeSQL("BEGIN;");
|
||||||
|
ResultTable t = this->selectFrom("recipe", "*", "name="+surroundString(recipe.getName(), "'"));
|
||||||
|
//ResultTable t = this->executeSQL("SELECT * FROM recipe WHERE name='"+recipe.getName()+"';");
|
||||||
if (!t.isEmpty()){
|
if (!t.isEmpty()){
|
||||||
fprintf(stderr, "Error storing recipe: Recipe with name %s already exists.\n", recipe.getName().c_str());
|
fprintf(stderr, "Error storing recipe: Recipe with name %s already exists.\n", recipe.getName().c_str());
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
bool success = this->insertInto("recipe",
|
bool success = this->insertInto("recipe",
|
||||||
vector<string>({
|
vector<string>({
|
||||||
|
@ -29,27 +30,27 @@ bool RecipeDatabase::storeRecipe(Recipe recipe){
|
||||||
if (success){
|
if (success){
|
||||||
//If successful, proceed to insert instructions, image, and ingredients.
|
//If successful, proceed to insert instructions, image, and ingredients.
|
||||||
int recipeId = this->getLastInsertedRowId();
|
int recipeId = this->getLastInsertedRowId();
|
||||||
|
bool ingredientSuccess = true;
|
||||||
for (unsigned int i = 0; i < recipe.getIngredients().size(); i++){
|
for (unsigned int i = 0; i < recipe.getIngredients().size(); i++){
|
||||||
if (!this->storeRecipeIngredient(recipe.getIngredients()[i], recipeId)){
|
if (!this->storeRecipeIngredient(recipe.getIngredients()[i], recipeId)){
|
||||||
return false;
|
ingredientSuccess = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this->storeInstruction(recipe.getInstruction(), recipeId)){
|
if (ingredientSuccess && this->storeInstruction(recipe.getInstruction(), recipeId) && this->storeImage(recipe.getImage(), recipeId)){
|
||||||
return false;
|
this->executeSQL("COMMIT;");
|
||||||
}
|
|
||||||
if (!this->storeImage(recipe.getImage(), recipeId)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->executeSQL("ROLLBACK;");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){
|
bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){
|
||||||
//First check if the base ingredient has been added to the database. This is done within storeIngredient().
|
//First check if the base ingredient has been added to the database. This is done within storeIngredient().
|
||||||
ResultTable t = this->executeSQL("SELECT ingredientId FROM ingredient WHERE name='"+ri.getName()+"';");
|
ResultTable t = this->selectFrom("ingredient", "ingredientId", "name="+surroundString(ri.getName(), "'"));
|
||||||
|
//ResultTable t = this->executeSQL("SELECT ingredientId FROM ingredient WHERE name='"+ri.getName()+"';");
|
||||||
int ingId = 0;
|
int ingId = 0;
|
||||||
if (t.isEmpty()){
|
if (t.isEmpty()){
|
||||||
if (!this->insertInto("ingredient", vector<string>({"foodGroup", "name"}), vector<string>({ri.getFoodGroup(), ri.getName()}))){
|
if (!this->insertInto("ingredient", vector<string>({"foodGroup", "name"}), vector<string>({ri.getFoodGroup(), ri.getName()}))){
|
||||||
|
@ -77,15 +78,15 @@ bool RecipeDatabase::storeRecipeIngredient(RecipeIngredient ri, int recipeId){
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecipeDatabase::storeIngredient(Ingredient ingredient){
|
void RecipeDatabase::storeIngredient(Ingredient ingredient){
|
||||||
ResultTable t = this->executeSQL("SELECT * FROM ingredient WHERE name='"+ingredient.getName()+"';");
|
ResultTable t = this->selectFrom("ingredient", "*", "name="+surroundString(ingredient.getName(), "'"));
|
||||||
|
//ResultTable t = this->executeSQL("SELECT * FROM ingredient WHERE name='"+ingredient.getName()+"';");
|
||||||
if (t.isEmpty()){
|
if (t.isEmpty()){
|
||||||
this->insertInto("ingredient", vector<string>({"foodGroup", "name"}), vector<string>({ingredient.getFoodGroup(), ingredient.getName()}));
|
this->insertInto("ingredient", vector<string>({"foodGroup", "name"}), vector<string>({ingredient.getFoodGroup(), ingredient.getName()}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecipeDatabase::storeInstruction(Instruction instruction, int recipeId){
|
bool RecipeDatabase::storeInstruction(Instruction instruction, int recipeId){
|
||||||
bool success = FileUtils::saveInstruction(recipeId, instruction);
|
return FileUtils::saveInstruction(recipeId, instruction);
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecipeDatabase::storeImage(QImage image, int recipeId){
|
bool RecipeDatabase::storeImage(QImage image, int recipeId){
|
||||||
|
@ -99,12 +100,12 @@ void RecipeDatabase::ensureTablesExist(){
|
||||||
this->executeSQL("CREATE TABLE IF NOT EXISTS ingredient("
|
this->executeSQL("CREATE TABLE IF NOT EXISTS ingredient("
|
||||||
"ingredientId INTEGER PRIMARY KEY,"
|
"ingredientId INTEGER PRIMARY KEY,"
|
||||||
"foodGroup varchar,"
|
"foodGroup varchar,"
|
||||||
"name varchar);");
|
"name varchar UNIQUE);");
|
||||||
//Recipe table. Each recipe can have at most one instruction, and one image.
|
//Recipe table. Each recipe can have at most one instruction, and one image.
|
||||||
this->executeSQL("CREATE TABLE IF NOT EXISTS recipe("
|
this->executeSQL("CREATE TABLE IF NOT EXISTS recipe("
|
||||||
"recipeId INTEGER PRIMARY KEY,"
|
"recipeId INTEGER PRIMARY KEY,"
|
||||||
"createdDate date,"
|
"createdDate date,"
|
||||||
"name varchar,"
|
"name varchar UNIQUE,"
|
||||||
"cookTime time,"
|
"cookTime time,"
|
||||||
"prepTime time,"
|
"prepTime time,"
|
||||||
"servingCount real);");
|
"servingCount real);");
|
||||||
|
|
|
@ -26,6 +26,8 @@ class RecipeDatabase : public Database
|
||||||
void storeIngredient(Ingredient ingredient);
|
void storeIngredient(Ingredient ingredient);
|
||||||
bool storeInstruction(Instruction instruction, int recipeId);
|
bool storeInstruction(Instruction instruction, int recipeId);
|
||||||
bool storeImage(QImage image, int recipeId);
|
bool storeImage(QImage image, int recipeId);
|
||||||
|
|
||||||
|
vector<Recipe> retrieveRecipe(string name);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
//Utility methods.
|
//Utility methods.
|
||||||
|
|
|
@ -11,6 +11,7 @@ void ResultTable::extractData(sqlite3_stmt *stmt){
|
||||||
processRow(stmt);
|
processRow(stmt);
|
||||||
res = sqlite3_step(stmt);
|
res = sqlite3_step(stmt);
|
||||||
}
|
}
|
||||||
|
this->queryCode = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResultTable::processRow(sqlite3_stmt *stmt){
|
void ResultTable::processRow(sqlite3_stmt *stmt){
|
||||||
|
@ -51,6 +52,10 @@ string ResultTable::valueAt(unsigned int row, unsigned int col){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ResultTable::getReturnCode(){
|
||||||
|
return this->queryCode;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int ResultTable::columnCount(){
|
unsigned int ResultTable::columnCount(){
|
||||||
if (this->isEmpty()){
|
if (this->isEmpty()){
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -27,10 +27,12 @@ class ResultTable
|
||||||
|
|
||||||
bool isEmpty();
|
bool isEmpty();
|
||||||
string valueAt(unsigned int row, unsigned int col);
|
string valueAt(unsigned int row, unsigned int col);
|
||||||
|
int getReturnCode();
|
||||||
unsigned int columnCount();
|
unsigned int columnCount();
|
||||||
unsigned int rowCount();
|
unsigned int rowCount();
|
||||||
private:
|
private:
|
||||||
vector<vector<string>> values;
|
vector<vector<string>> values;
|
||||||
|
int queryCode;
|
||||||
|
|
||||||
//Utility methods.
|
//Utility methods.
|
||||||
string convertToString(sqlite3_value* val);
|
string convertToString(sqlite3_value* val);
|
||||||
|
|
Loading…
Reference in New Issue