Shift to master. Not done but database abstraction is nearly done. #3
|
@ -22,7 +22,9 @@ SOURCES += model/recipe/instruction.cpp \
|
|||
model/recipe/ingredients/ingredientlistmodel.cpp \
|
||||
model/recipe/ingredients/recipeingredient.cpp \
|
||||
model/recipe/tags/recipetag.cpp \
|
||||
SQLite/sqlite3.c
|
||||
SQLite/sqlite3.c \
|
||||
model/database/resulttable.cpp \
|
||||
model/database/recipedatabase.cpp
|
||||
|
||||
HEADERS += model/recipe/instruction.h \
|
||||
model/recipe/recipe.h \
|
||||
|
@ -34,7 +36,9 @@ HEADERS += model/recipe/instruction.h \
|
|||
model/recipe/ingredients/recipeingredient.h \
|
||||
model/recipe/tags/recipetag.h \
|
||||
SQLite/sqlite3.h \
|
||||
SQLite/sqlite3ext.h
|
||||
SQLite/sqlite3ext.h \
|
||||
model/database/resulttable.h \
|
||||
model/database/recipedatabase.h
|
||||
|
||||
LIBS += -ldl \
|
||||
|
||||
|
|
8
main.cpp
8
main.cpp
|
@ -2,6 +2,7 @@
|
|||
#include <QApplication>
|
||||
|
||||
#include "model/database/database.h"
|
||||
#include "model/database/recipedatabase.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -9,7 +10,14 @@ int main(int argc, char *argv[])
|
|||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
//TESTING CODE
|
||||
Database db("test.db");
|
||||
printf("Table exists: %d\n", db.tableExists("ingredients"));
|
||||
db.executeSQL("SELECT * FROM ingredients;").printData();
|
||||
db.executeSQL("PRAGMA table_info('ingredients');").printData();
|
||||
db.executeSQL("SELECT name FROM ingredients WHERE foodGroup == 'fruit';").printData();
|
||||
|
||||
RecipeDatabase recipeDB("recipes");
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
|
|
@ -3,18 +3,29 @@
|
|||
Database::Database(string filename){
|
||||
this->filename = filename;
|
||||
openConnection();
|
||||
//TESTING CODE
|
||||
if (tableExists("ingredients")){
|
||||
printf("Ingredients table already exists.\n");
|
||||
} else {
|
||||
printf("Couldn't find the ingredients table.\n");
|
||||
}
|
||||
}
|
||||
|
||||
Database::~Database(){
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
ResultTable Database::executeSQL(string statement){
|
||||
sqlite3_stmt* stmt;
|
||||
this->sql = statement;
|
||||
this->returnCode = sqlite3_prepare_v2(this->db, statement.c_str(), -1, &stmt, NULL);
|
||||
ResultTable t;
|
||||
if (this->returnCode != SQLITE_OK){
|
||||
fprintf(stderr, "Unable to successfully prepare SQL statement. Error code: %d\nError Message: %s\n", this->returnCode, sqlite3_errmsg(this->db));
|
||||
return t;
|
||||
}
|
||||
|
||||
t.extractData(stmt);
|
||||
|
||||
this->returnCode = sqlite3_finalize(stmt);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void Database::openConnection(){
|
||||
this->returnCode = sqlite3_open(this->filename.c_str(), &this->db);
|
||||
if (this->returnCode || this->db == NULL){
|
||||
|
@ -32,16 +43,9 @@ void Database::closeConnection(){
|
|||
}
|
||||
|
||||
bool Database::tableExists(string tableName){
|
||||
if (this->dbIsOpen){
|
||||
this->sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';";
|
||||
const char* str = this->sql.c_str();
|
||||
this->returnCode = sqlite3_exec(this->db, str, NULL, 0, &this->errorMsg);
|
||||
if (this->returnCode == SQLITE_ERROR){
|
||||
fprintf(stderr, "Unable to select name from master table list: %s\n", this->errorMsg);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (tableName.empty() || this->db == NULL || !this->dbIsOpen){
|
||||
return false;
|
||||
}
|
||||
ResultTable t = executeSQL("SELECT name FROM sqlite_master WHERE type='table' AND name='"+tableName+"';");
|
||||
return !t.isEmpty();
|
||||
}
|
||||
|
|
|
@ -6,17 +6,25 @@
|
|||
|
||||
#include "SQLite/sqlite3.h"
|
||||
#include "model/recipe/ingredients/ingredient.h"
|
||||
#include "resulttable.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* @brief The Database class is responsible for generic abstraction of commonly used database features.
|
||||
*/
|
||||
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
//Creates and opens a database connection to a file of the given name. If not there, this will generate a database.
|
||||
Database(string filename);
|
||||
~Database();
|
||||
|
||||
void insertIngredient(Ingredient);
|
||||
//Executes an SQL string statement in a safe way and returns the result.
|
||||
ResultTable executeSQL(string statement);
|
||||
|
||||
bool tableExists(string tableName);
|
||||
private:
|
||||
//SQL Instance variables.
|
||||
string filename;
|
||||
|
@ -28,11 +36,6 @@ private:
|
|||
|
||||
void openConnection();
|
||||
void closeConnection();
|
||||
//Guarantees that all tables from the schema exist.
|
||||
void ensureTablesExist();
|
||||
|
||||
//Utility methods.
|
||||
bool tableExists(string tableName);
|
||||
};
|
||||
|
||||
#endif // DATABASE_H
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#include "recipedatabase.h"
|
||||
|
||||
RecipeDatabase::RecipeDatabase(string filename) : Database(filename){
|
||||
this->ensureTablesExist();
|
||||
}
|
||||
|
||||
void RecipeDatabase::ensureTablesExist(){
|
||||
//Make sure that foreign keys are enabled.
|
||||
this->executeSQL("PRAGMA foreign_keys = ON;");
|
||||
//Ingredients table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS ingredient("
|
||||
"ingredientId int,"
|
||||
"foodGroup varchar,"
|
||||
"name varchar,"
|
||||
"PRIMARY KEY (ingredientId));");
|
||||
//Images table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS image("
|
||||
"imageNr int,"
|
||||
"contentURL varchar,"
|
||||
"PRIMARY KEY (imageNr));");
|
||||
//Instructions table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS instruction("
|
||||
"instructionNr int,"
|
||||
"contentURL varchar,"
|
||||
"PRIMARY KEY (instructionNr));");
|
||||
//Recipe table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS recipe("
|
||||
"recipeId int,"
|
||||
"date date,"
|
||||
"name varchar,"
|
||||
"imageNr int,"
|
||||
"cookTime time,"
|
||||
"prepTime time,"
|
||||
"servingCount real,"
|
||||
"PRIMARY KEY (recipeId),"
|
||||
"FOREIGN KEY (imageNr) REFERENCES image(imageNr));");
|
||||
//Recipe tags table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS recipeTag("
|
||||
"recipeId int,"
|
||||
"tagName varchar,"
|
||||
"PRIMARY KEY (recipeId),"
|
||||
"FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));");
|
||||
//RecipeIngredient table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS recipeIngredient("
|
||||
"ingredientId int,"
|
||||
"recipeId int,"
|
||||
"quantity real,"
|
||||
"unitName varchar,"
|
||||
"comment varchar,"
|
||||
"PRIMARY KEY (ingredientId, recipeId),"
|
||||
"FOREIGN KEY (ingredientId) REFERENCES ingredient(ingredientId),"
|
||||
"FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));");
|
||||
//Recipe Instruction mapping table.
|
||||
this->executeSQL("CREATE TABLE IF NOT EXISTS recipeInstruction("
|
||||
"instructionNr int,"
|
||||
"recipeId int,"
|
||||
"PRIMARY KEY (recipeId),"
|
||||
"FOREIGN KEY (instructionNr) REFERENCES instruction(instructionNr),"
|
||||
"FOREIGN KEY (recipeId) REFERENCES recipe(recipeId));");
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef RECIPEDATABASE_H
|
||||
#define RECIPEDATABASE_H
|
||||
|
||||
|
||||
|
||||
#include "database.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* @brief The RecipeDatabase class represents the precise database used for the recipe storage, and is specialized.
|
||||
*/
|
||||
|
||||
class RecipeDatabase : public Database
|
||||
{
|
||||
public:
|
||||
RecipeDatabase(string filename);
|
||||
private:
|
||||
|
||||
//Utility methods.
|
||||
void ensureTablesExist();
|
||||
};
|
||||
|
||||
#endif // RECIPEDATABASE_H
|
|
@ -0,0 +1,75 @@
|
|||
#include "resulttable.h"
|
||||
|
||||
ResultTable::ResultTable(){
|
||||
|
||||
}
|
||||
|
||||
void ResultTable::extractData(sqlite3_stmt *stmt){
|
||||
this->values.clear();
|
||||
int res = sqlite3_step(stmt);
|
||||
while (res == SQLITE_ROW){
|
||||
processRow(stmt);
|
||||
res = sqlite3_step(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
void ResultTable::processRow(sqlite3_stmt *stmt){
|
||||
int colCount = sqlite3_column_count(stmt);
|
||||
vector<string> currentRow;
|
||||
|
||||
for (int i = 0; i < colCount; i++){
|
||||
currentRow.push_back(convertToString(sqlite3_column_value(stmt, i)));
|
||||
}
|
||||
|
||||
this->values.push_back(currentRow);
|
||||
}
|
||||
|
||||
void ResultTable::printData(){
|
||||
printf("Printing table: %d by %d\n", this->rowCount(), this->columnCount());
|
||||
for (unsigned int row = 0; row < this->rowCount(); row++){
|
||||
for (unsigned int col = 0; col < this->columnCount(); col++){
|
||||
printf("| %s \t", this->values[row][col].c_str());
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool ResultTable::isEmpty(){
|
||||
return this->values.empty();
|
||||
}
|
||||
|
||||
string ResultTable::valueAt(unsigned int row, unsigned int col){
|
||||
if (isIndexValid(row, col)){
|
||||
return this->values[row][col];
|
||||
} else {
|
||||
fprintf(stderr, "Out of bounds error while trying to get value in result table at [%d, %d].\n", row, col);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ResultTable::columnCount(){
|
||||
if (this->isEmpty()){
|
||||
return 0;
|
||||
}
|
||||
return this->values[0].size();
|
||||
}
|
||||
|
||||
unsigned int ResultTable::rowCount(){
|
||||
if (this->isEmpty()){
|
||||
return 0;
|
||||
}
|
||||
return this->values.size();
|
||||
}
|
||||
|
||||
string ResultTable::convertToString(sqlite3_value *val){
|
||||
const unsigned char* raw_text = sqlite3_value_text(val);
|
||||
if (raw_text == 0){
|
||||
return "";
|
||||
}
|
||||
string st = (const char*) raw_text;
|
||||
return st;
|
||||
}
|
||||
|
||||
bool ResultTable::isIndexValid(unsigned int row, unsigned int col){
|
||||
return (row < this->values.size()) && (col < this->values[0].size());
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef RESULTTABLE_H
|
||||
#define RESULTTABLE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "SQLite/sqlite3.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* @brief The ResultTable class is an object that contains the results of an SQL query, in string form.
|
||||
*/
|
||||
|
||||
class ResultTable
|
||||
{
|
||||
public:
|
||||
//Constructs an empty table.
|
||||
ResultTable();
|
||||
|
||||
//Gets all the data from the result set and stores it internally as strings.
|
||||
void extractData(sqlite3_stmt* stmt);
|
||||
//Stores the information from one row of a result set.
|
||||
void processRow(sqlite3_stmt* stmt);
|
||||
//Displays the data somewhat legibly.
|
||||
void printData();
|
||||
|
||||
bool isEmpty();
|
||||
string valueAt(unsigned int row, unsigned int col);
|
||||
unsigned int columnCount();
|
||||
unsigned int rowCount();
|
||||
private:
|
||||
vector<vector<string>> values;
|
||||
|
||||
//Utility methods.
|
||||
string convertToString(sqlite3_value* val);
|
||||
bool isIndexValid(unsigned int row, unsigned int col);
|
||||
};
|
||||
|
||||
#endif // RESULTTABLE_H
|
Loading…
Reference in New Issue