/* * This file is part of RuneOptimizer. * * RuneOptimizer is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * RuneOptimizer. If not, see . */ /** * @file db.c * * Implementation of the database functions. * * This file implements all the database related functions defined in * {@link db.h}. It also defines and implements some static functions used by * them. */ #include #include #include #include #include #include "../runeoptimizer.h" #include "../error/error.h" #include "db.h" /** * Ensures that the directory structure for the database exist. * * Creates the required directories for the file in the glovan variable * {@link db_location}. It doesn't create the database file itself. It doesn't * have return values, so the caller won't know if the folder structure was * succesfully created, but {@link db_open} will throw an error when called * if they werent created. */ static void db_create_directories(); /** * Creates all database tables. * * Runs CREATE TABLE stetements against the database to set up all the tables * the application uses. It is called from {@link db_open} if it detects that * the database has just been created. All the statements contains the IF NOT * EXIST clause, so there will be no errors if they actually exists. * * In case of error, it will print a message to stderr. * * @return {@link SUCCESS} if all the tables were created, or an error defined * in {@link error.h} if one or more tables could not be created. */ static int db_create_tables(); extern int db_close(){ if (db != NULL){ int result = sqlite3_close_v2(db); if (result != SQLITE_OK){ fprintf( stderr, "Error closing database connection: %s", sqlite3_errmsg(db) ); return(result); } } db = NULL; return(SUCCESS); } extern int db_open(char *path){ db_close(); char exists = TRUE; if (access( db_location, F_OK ) != 0) { exists = FALSE; db_create_directories(); } // Get the path, relative to the program char location[sizeof(db_location)]; // If db specified as argument, use it, else use the default location. if (path == NULL) strcpy(location, db_location); else strcpy(location, path); if ( SUCCESS != sqlite3_open_v2( location, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL ) ){ fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); db_close(); return(ERROR_DB_CANT_OPEN); } // If the database was just created, create the tables if (exists == FALSE && access( db_location, F_OK ) == 0){ if (SUCCESS != db_create_tables()){ return ERROR_DB_CANT_OPEN; } } return(SUCCESS); } extern int db_query(sqlite3_stmt **stmt, char query[], char *parameters[]){ // If the connection has not been initialized, do it now if (NULL == db && SUCCESS != db_open(NULL)){ return ERROR_DB_CANT_OPEN; } if (SQLITE_OK != sqlite3_prepare_v2(db, query, -1, stmt, 0)) { fprintf( stderr, "ERROR executing query '%s': %s\n", query, sqlite3_errmsg(db) ); sqlite3_finalize(*stmt); return(ERROR_DB_QUERY); } // Loop and bind parameters char total_parameters = sqlite3_bind_parameter_count(*stmt); if (total_parameters > 0 && parameters == NULL){ return(ERROR_DB_QUERY_NO_PARAMETERS); } for (int i = 0; i < total_parameters; i ++){ sqlite3_bind_text( *stmt, i + 1, parameters[i], strlen(parameters[i]), NULL ); } //printf("PREP QUERY: %s\n", sqlite3_expanded_sql(*stmt)); return(SUCCESS); } extern int db_execute(char query[], char *parameters[]){ // If the connection has not been initialized, do it now if (NULL == db && SUCCESS != db_open(NULL)){ return ERROR_DB_CANT_OPEN; } sqlite3_stmt *stmt; if (SQLITE_OK != sqlite3_prepare_v2(db, query, -1, &stmt, 0)) { fprintf( stderr, "ERROR executing statement '%s': %s\n", query, sqlite3_errmsg(db) ); sqlite3_finalize(stmt); return(ERROR_DB_EXECUTE); } // Loop and bind parameters char total_parameters = sqlite3_bind_parameter_count(stmt); if (total_parameters > 0 && parameters == NULL){ return(ERROR_DB_EXECUTE_NO_PARAMETERS); } for (int i = 0; i < total_parameters; i ++){ sqlite3_bind_text( stmt, i + 1, parameters[i], strlen(parameters[i]), NULL ); } //printf("PREP QUERY: %s\n", sqlite3_expanded_sql(stmt)); if (SQLITE_DONE != sqlite3_step(stmt)){ fprintf( stderr, "ERROR Executing statement '%s': %s\n", query, sqlite3_errmsg(db) ); sqlite3_finalize(stmt); return(ERROR_DB_EXECUTE); } sqlite3_finalize(stmt); return(SUCCESS); } static void db_create_directories(){ char dir_to_make[sizeof(db_location)]; int last_path_separator = -1; for(int i = 0; i < (int) strlen(db_location); i ++){ if(db_location[i] == '/' || db_location[i] == '\\'){ last_path_separator = i + 1; } } if (last_path_separator != -1){ strncpy(dir_to_make, db_location, last_path_separator); dir_to_make[last_path_separator] = '\0'; } #ifdef _WIN32 mkdir(dir_to_make); #else mkdir(dir_to_make, 0777); #endif return; } static int db_create_tables(){ // Table rune_stats if ( SUCCESS != db_execute( "CREATE TABLE IF NOT EXISTS rune_stats(" " rune CHAR(12), slot INT, stat INT, value INT, grind INT, enchant INT" ");", NULL ) ){ fprintf( stderr, "Unable to create table rune_stats: %s\n", sqlite3_errmsg(db) ); return ERROR_DB_CREATE_RUNE_STATS; } // Table runes if ( SUCCESS != db_execute( "CREATE TABLE IF NOT EXISTS runes(" " id CHAR(12)," " unit CHAR(12)," " type INT," " slot INT," " stars INT," " level INT," " quality INT," " efficiency FLOAT," " max_efficiency FLOAT," " main_stat INT," " current_hp_percent INT," " current_atk_percent INT," " current_def_percent INT," " current_hp_flat INT," " current_atk_flat INT," " current_def_flat INT," " current_spd INT," " current_crr INT," " current_crd INT," " current_acc INT," " current_res INT," " lv12_hp_percent INT," " lv12_atk_percent INT," " lv12_def_percent INT," " lv12_hp_flat INT," " lv12_atk_flat INT," " lv12_def_flat INT," " lv12_spd INT," " lv12_crr INT," " lv12_crd INT," " lv12_acc INT," " lv12_res INT," " lv15_hp_percent INT," " lv15_atk_percent INT," " lv15_def_percent INT," " lv15_hp_flat INT," " lv15_atk_flat INT," " lv15_def_flat INT," " lv15_spd INT," " lv15_crr INT," " lv15_crd INT," " lv15_acc INT," " lv15_res INT" ");", NULL ) ){ fprintf( stderr, "Unable to create table runes: %s\n", sqlite3_errmsg(db) ); return ERROR_DB_CREATE_RUNES; } // Table units if ( SUCCESS != db_execute( "CREATE TABLE IF NOT EXISTS units(" " id CHAR(12)," " monster INT," " name CHAR(128)," " stars INT," " level INT," " storage INT," " base_hp INT," " base_atk INT," " base_def INT," " base_spd INT," " base_crr INT," " base_crd INT," " base_res INT," " base_acc INT," " current_hp INT," " current_atk INT," " current_def INT," " current_spd INT," " current_crr INT," " current_crd INT," " current_res INT," " current_acc INT" ");", NULL ) ){ fprintf( stderr, "Unable to create table units: %s\n", sqlite3_errmsg(db) ); return ERROR_DB_CREATE_UNITS; } // Table info if ( SUCCESS != db_execute( "CREATE TABLE IF NOT EXISTS info(" " player_id CHAR(12)," " player_name CHAR(64)," " player_level INT," " ts DATETIME," " modified INT" ");", NULL ) ){ fprintf( stderr, "Unable to create table info: %s\n", sqlite3_errmsg(db) ); return ERROR_DB_CREATE_INFO; } // Tables teams and units_teams if ( SUCCESS != db_execute( "CREATE TABLE IF NOT EXISTS " "units_teams(unit CHAR(12), team CHAR(12));", NULL ) ){ fprintf( stderr, "Unable to create table units_teams: %s\n", sqlite3_errmsg(db) ); return ERROR_DB_CREATE_UNITS_TEAMS; } if ( SUCCESS != db_execute( "CREATE TABLE IF NOT EXISTS " "teams(id CHAR(12), name CHAR(50), priority INT);", NULL ) ){ fprintf( stderr, "Unable to create table teams: %s\n", sqlite3_errmsg(db) ); return ERROR_DB_CREATE_TEAMS; } return SUCCESS; }