22.10.2019
Nejjednodušší cestou jak získat vše potřebné pro psaní aplikací nad SQLite v C/C++ je v Linux nainstalovat příslušný vývojářský balíček. V Debianu a jeho derivátech jde o balíček libsqlite3-dev. Při kompilaci a sestavování projektu pak do zdrojového kódu přidáme (direktivou #include) hlavičkový soubor sqlite3.h a do sestavení knihovnu sqlite3, tj. při použití kompilátoru GCC přidáme do parametrů -lsqlite3. Doporučuji si nainstalovat také balíček sqlitebrowser, což je nástroj DB Browser for SQLite, umožňující prohlížet a upravovat databázové soubory SQLite, samozřejmě jak data tak struktury tabulek.
Další možností je stažení zdrojových souborů z webu SQLite. V archivu sqlite-amalgamation-xxx.zip (kde xxx je číslo aktuální verze) najdeme soubory sqlite3.h a sqlite3.c, které přidáme do sestavení projektu. Jak je zřejmé, celý zdrojový kód databáze je v souboru sqlite3.c.
Nyní si již můžeme vytvořit jednoduchý ukázkový program, který vytvoří databázový soubor SQLite, v něm vytvoří tabulku s položkami typu číslo a text, do které přidá data. Následné pak data zobrazí textově v konzoli.
Vše bude v jednom zdrojovém souboru (main.cpp) který přeložíme a sestavíme příkazem:
g++ main.cpp -g -lsqlite3 -osqlite-cpp
Do zdrojového kódu si přidáme potřebné hlavičkové soubory. Kromě běžných céčkovských hlaviček a výše zmíněné hlavičce sqlite3.h si přidáme také specifické Linuxové hlavičky obsahující deklarace funkcí které využijeme ve vlastních pomocných funkcích pro dotazování na souborový systém.
#include <cstdio> #include <stdexcept> #include <unistd.h> #include <pwd.h> #include <sys/stat.h> #include <sys/types.h> #include <assert.h> #include <sqlite3.h>
Nejprve si napíšeme jednoduché pomocné funkce pro zjištění existence zadaného souboru a adresáře:
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; }
Nyní již můžeme přikročit k vytvoření databázového souboru pomocí funkce sqlite3_open, která vytvoří zadaný soubor nebo pokud již existuje, otevře ho a v případě úspěchu naplní ukazatel zadaný jako 2. parametr. Ten je typu sqlite3* a představuje handle na otevřenou databázi. Pokud máme tento platný handle, můžeme použít některou z mnoha funkcí sqlite, například sqlite3_exec, který provede nad databází zadaný SQL příkaz. V našem případě po otevření a vytvoření tabulky přidáme do databáze pro ukázku 2 položky.
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"); printf("Soubor databáze: %s\n", db_file.c_str()); int ires = sqlite3_open(db_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)); ires = sqlite3_exec(_sqlite3, "INSERT INTO polozky (cislo, nazev) VALUES (1, 'první položka')", nullptr, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); ires = sqlite3_exec(_sqlite3, "INSERT INTO polozky (cislo, nazev) VALUES (2, 'druhá položka')", nullptr, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires));
V kódu jak vidno používám výjimky při zjištění chyby, proto celý kód z něhož je uvedená část je uvnitř bloku try-catch.
Nyní si ještě ukážeme jeden ze způsobů jak procházet tabulku a vypsat všechny položky, resp. ty které nadefinujeme v SQL příkazu SELECT. Opět použijeme funkci sqlite3_exec s tím, že jako 3. parametr uvedeme vlastní callback funkci, která bude volaná pro každý výsledný řádek příkazu select. V této funkci máme v parametrech vše potřebné, tj. počet sloupců a pole ukazatelů na data v jednotlivých sloupcích a také názvy sloupců. V našem případě může tato funkce vypadat například takto:
int select_callback(void* p, int count, char** data, char** col_name) { for (size_t i = 0; i < count; i++) { printf("%s: %s \t", *(col_name+i), *(data+i)); } printf("\n"); return 0; }
A volání příkazu SELECT pak vypadá takto:
ires = sqlite3_exec(_sqlite3, "SELECT * FROM polozky", select_callback, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires));
Po použití databázi zavřeme funkcí:
sqlite3_close(_sqlite3);
Ukázkový program najdete také na mém githubu a kompletní kód vypadá takto:
#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; } int select_callback(void* p, int count, char** data, char** col_name) { for (size_t i = 0; i < count; i++) { printf("%s: %s \t", *(col_name+i), *(data+i)); } printf("\n"); return 0; } int main(int argc, char** argv) { try { 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"); printf("Soubor databáze: %s\n", db_file.c_str()); int ires = sqlite3_open(db_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)); ires = sqlite3_exec(_sqlite3, "INSERT INTO polozky (cislo, nazev) VALUES (1, 'první položka')", nullptr, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); ires = sqlite3_exec(_sqlite3, "INSERT INTO polozky (cislo, nazev) VALUES (2, 'druhá položka')", nullptr, nullptr, nullptr); if (ires != SQLITE_OK) throw std::runtime_error(sqlite3_errstr(ires)); ires = sqlite3_exec(_sqlite3, "SELECT * FROM polozky", select_callback, nullptr, nullptr); 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(e.what()); if (_sqlite3) sqlite3_close(_sqlite3); return EXIT_FAILURE; } }
Školení
Kontakt
739 219 991
live:radekchalupa_1
Nové články