24.10.2019
Nejprve si vytvoříme databázi se stejnou strukturou jako v předchozím díle, a tentokrát ji naplníme 100 řádky na základě vygenerovaných náhodných čísel. Pro lepší přehlednost a další použití si připravíme samostatné funkce pro vytvoření nové (s přepsáním případné již existující) a otevření existující databáze.
#include <cstdio> #include <stdexcept> #include <unistd.h> #include <pwd.h> #include <sys/stat.h> #include <sys/types.h> #include <assert.h> #include <sqlite3.h> sqlite3* _sqlite3 = nullptr; bool directory_exists(const char* fs_path) { struct stat ss; if (stat(fs_path, &ss) != 0) return false; else return (ss.st_mode & S_IFMT) == S_IFDIR; } bool file_exists(const char* file_path) { struct stat ss; if (stat(file_path, &ss) != 0) return false; else return (ss.st_mode & S_IFMT) == S_IFREG; } std::string get_cesta_db() { std::string db_file = getenv("HOME"); if (!directory_exists(db_file.c_str())) db_file = getpwuid(getuid())->pw_dir; if (!directory_exists(db_file.c_str())) throw std::runtime_error("Nepodařilo se zjistit domovský adresář."); db_file.append("/Dokumenty/test.db"); return db_file; } void vytvorit_databazi() { std::string str_file = get_cesta_db(); if (file_exists(str_file.c_str())) { if (remove(str_file.c_str()) !=0) throw std::runtime_error("Chyba smazání souboru"); } int ires = sqlite3_open(str_file.c_str(), &_sqlite3); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); ires = sqlite3_exec(_sqlite3, "CREATE TABLE IF NOT EXISTS polozky (id INTEGER PRIMARY KEY," " cislo INTEGER DEFAULT 0, nazev TEXT)", nullptr, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); } void otevrit_databazi() { std::string str_file = get_cesta_db(); if (!file_exists(str_file.c_str())) throw std::runtime_error("Soubor databáze nebyl nalezen"); int ires = sqlite3_open(str_file.c_str(), &_sqlite3); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); }
Ve funkci pro naplnění dat budeme generovat náhodné číslo v intervalu 1-100, které nastavíme do sloupce cislo> a do sloupce nazev uložíme jeho zobrazení v desítkové a šestnáctkové podobě:
void naplnit_nahodne(size_t pocet = 100) { assert(_sqlite3); srand(time(nullptr)); char text[255]; int cislo; int ires; for (size_t i = 0; i < pocet; i++) { cislo = rand() % 100 + 1; sprintf(text, "INSERT INTO polozky (cislo, nazev) VALUES (%d, '%d - %X')", cislo, cislo, cislo); ires = sqlite3_exec(_sqlite3, text, nullptr, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); } }
V úvodním díle jsme si ukázali callback funkci pro výpis dat, použitou při volání funkce sqlite3_exec, která musí mít následující formát:
int select_callback(void* p, int count, char** data, char** col_name);
Význam parametrů count, data a col_name již známe z minulého dílu. Prvním (volitelným) parametrem je ukazatel na libovolná data, která chceme do funkce předat. zadáme ho jako 4. parametr funkce sqlite3_exec. Jak ho můžeme využít v praktickém použití? například v aplikaci s uživatelským rozhraním si v něm můžeme předat ukazatel na prvek typu "list-box", do kterého chceme v tom okamžiku data vypsat, nebo cestu k souboru do kterého výpis uložit. V naší ukázce si v tomto parametru předáme text který použijeme jako nadpis výpisu, jak si následně ukážeme. Nyní ještě k významu návratové hodnoty této callback funkce. Tato funkce je samozřejmě volána opakovaně pro každou řádku vrácenou zadaným SELECT příkazem pokud ji ukončíme návratovou hodnotou 0. Pokud z nějakého důvodu chceme procházení vrácených dat ukončit za nějaké podmínky předčasně, vrátíme nenulovou hodnotu. V naší ukázce budeme vypisovat vygenerovaná data tak dlouho dokud nenajdeme ve sloupci cislo hodnotu 78.
int select_callback(void* p, int count, char** data, char** col_name) { for (size_t i = 0; i < count; i++) { if (p && (0==i)) { printf("%s\n", (const char*)p); } printf("%s: %s \t", *(col_name+i), *(data+i)); } printf("\n"); if (strcmp(*(data+1), "78") == 0) { printf("Nalezeno číslo 78 na pozici %s\nKončím výpis\n", (char*)(*data)); return 1; } return 0; }
Mnoho funkcí SQLite, které nevracejí nějaká data indikuje výsledek operace číselnou návratovou hodnotou (typ int). K snadnému vyhodnocení situace existují nadefinované hodnoty s mnemotechnickými názvy. Zatím jsme použili pouze test na hodnotu SQLITE_OK, která jak název napovídá, značí úspěch operace. Jak uvidíme dále, ne všechny ostatní návratové hodnoty musí nutně znamenat chybu. Pro textovou interpretaci návratové hodnoty máme k disposici funkci sqlite3_errstr, kterou jsme také již použili.
Při volání funkce sqlite3_exec máme další možnost získání textové interpretace výsledku. Pokud jako poslední parametr zadáme adresu ukazatele (zdůrazňuji že jde o ukazatel na ukazatel, tedy typ char**, nikoliv "pouze" char*), funkce nám tento ukazatel naalokuje a nakopíruje do něj textový popis, který bude ve většině případů totožný s textem získaným funkcí sqlite3_errstr. POZOR! Pokud na výstupu dostaneme nenulový ukazatel, musíme jej po použití uvolnit funkcí sqlite3_free.
Nyní si již výše uvedené můžeme ukázat na výpisu funkce main ukázkové aplikace používající již uvedené funkce:
int main(int argc, char** argv) { try { //vytvorit_databazi(); //naplnit_nahodne(); otevrit_databazi(); char* err_msg = nullptr; int ires = sqlite3_exec(_sqlite3, "SELECT * FROM polozky", select_callback, (void*)"Ukázka výpisu dat", &err_msg); if (err_msg) { printf("err_msg: %s\n", err_msg); sqlite3_free(err_msg); } printf("sqlite3_errstr: %s\n", sqlite3_errstr(ires)); if (SQLITE_ABORT == ires) { } else { if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); } sqlite3_close(_sqlite3); printf("\nok\n"); return EXIT_SUCCESS; } catch(const std::exception& e) { printf("Došlo k chybě: %s\n", e.what()); if (_sqlite3) sqlite3_close(_sqlite3); return EXIT_FAILURE; } }
Jak je vidět, pokud v callback funkci předčasně ukončíme procházení dat, funkce sqlite3_exec vrátí hodnotu SQLITE_ABORT, tedy jak jsem již zmínil sice různou od SQLITE_OK, ale neznamenající chybu a v našem případě indikující výskyt a nalezení čísla 78 mezi náhodně vygenerovanými daty. Pro demonstraci zde vždy vypisujeme "chybovou" hlášku a v případě výskytu čísla 78 dostaneme na konci výpisu toto:
id: 56 cislo: 89 nazev: 89 - 59 id: 57 cislo: 90 nazev: 90 - 5A id: 58 cislo: 41 nazev: 41 - 29 id: 59 cislo: 78 nazev: 78 - 4E Nalezeno číslo 78 na pozici 59 Končím výpis err_msg: query aborted sqlite3_errstr: query aborted
Celý kód si můžete prohlédnou na mém githubu. Přeložit lze kompilátorem GCC příkazem:
g++ main.cpp -g -lsqlite3 -osqlite-cpp
Školení
Kontakt
739 219 991
live:radekchalupa_1
Nové články