Advertisement
microwerx

SQLite Example in C++

Apr 16th, 2020
761
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.45 KB | None | 0 0
  1. #ifdef _MSC_VER
  2. #include <winsqlite/winsqlite3.h>
  3. #else
  4. // On UNIX systems, make sure libsqlite3 is installed
  5. // Then compile and link the sqlite3 library with your source code
  6. // $ c++ labsqlite.cpp -lsqlite3
  7. #include <sqlite3.h>
  8. #endif
  9. #include <iostream>
  10. #include <iomanip>
  11. #include <string>
  12.  
  13. #pragma comment(lib, "winsqlite3.lib")
  14.  
  15. class PreparedStmt {
  16. public:
  17.     // Initialize the prepared statement and reset it prepare it for use
  18.     PreparedStmt(sqlite3_stmt* stmt) : stmt(stmt) { reset(); }
  19.  
  20.     // Finalize the prepared statement upon completion
  21.     ~PreparedStmt() { if (stmt) finalize(); }
  22.  
  23.     // Overload the bool conversion operator to return whether the query can be used
  24.     operator bool() const { return (stmt != nullptr && readyToStep); }
  25.  
  26.     // Finalize the prepared statement which makes it unusable for any future use.
  27.     void finalize() {
  28.         if (!stmt) {
  29.             std::cerr << "the program is trying to finalize an already finalized prepared statement!\n";
  30.             return;
  31.         }
  32.         sqlite3_finalize(stmt);
  33.         stmt = nullptr;
  34.     }
  35.  
  36.     // Reset the statement so that the SQL query can be executed again.
  37.     bool reset() {
  38.         if (!stmt) return false;
  39.         if (stmt) readyToStep = true;
  40.         if (firstRun) {
  41.             firstRun = true;
  42.             return true;
  43.         }
  44.         sqlite3_reset(stmt);
  45.         return readyToStep;
  46.     }
  47.  
  48.     // Execute the statement -- returns true if a row of information is ready to be processed
  49.     bool step() {
  50.         if (!readyToStep) return false;
  51.         int rc = sqlite3_step(stmt);
  52.         if (rc == SQLITE_ROW) return true;
  53.         // If an error occured or step returned DONE, we need to call reset next time
  54.         readyToStep = false;
  55.         if (rc == SQLITE_DONE) return true;
  56.         return false;
  57.     }
  58.  
  59.     // Return an integer from column index. Index is 0-based.
  60.     int columnInt(int index) {
  61.         if (!stmt) return 0;
  62.         return sqlite3_column_int(stmt, index);
  63.     }
  64.  
  65.     // Return an integer from column index. Index is 0-based.
  66.     long long columnInt64(int index) {
  67.         if (!stmt) return 0;
  68.         return sqlite3_column_int64(stmt, index);
  69.     }
  70.  
  71.     // Return an double from column index. Index is 0-based.
  72.     double columnReal(int index) {
  73.         if (!stmt) return 0.0;
  74.         return sqlite3_column_double(stmt, index);
  75.     }
  76.  
  77.     // Return an string from column index. Index is 0-based.
  78.     std::string columnText(int index) {
  79.         std::string text;
  80.         if (!stmt) return text;
  81.         const char* textptr = (const char*)sqlite3_column_text(stmt, index);
  82.         if (textptr) text = textptr;
  83.         return text;
  84.     }
  85.  
  86.     // Sets query element to an integer. Index is 1-based.
  87.     void bindInt(int index, int value) {
  88.         sqlite3_bind_int(stmt, index, value);
  89.     }
  90.  
  91.     // Sets query element to an integer. Index is 1-based.
  92.     void bindInt64(int index, long long value) {
  93.         sqlite3_bind_int64(stmt, index, value);
  94.     }
  95.  
  96.     // Sets query element to a double. Index is 1-based.
  97.     void bindReal(int index, double value) {
  98.         sqlite3_bind_double(stmt, index, value);
  99.     }
  100.  
  101.     // Sets query element to a string. Index is 1-based.
  102.     void bindText(int index, const std::string& value) {
  103.         sqlite3_bind_text(stmt, index, value.c_str(), (int)value.size(), nullptr);
  104.     }
  105.  
  106. private:
  107.     bool firstRun{ true };
  108.     bool readyToStep{ false };
  109.     sqlite3_stmt* stmt{ nullptr };
  110. };
  111.  
  112.  
  113. class Database {
  114. public:
  115.     Database();
  116.     ~Database();
  117.  
  118.     // Overload the bool conversion operator to return whether the database can be used
  119.     operator bool() { return db_ != nullptr; }
  120.  
  121.     // Open the database file. If database = ":memory:", creates a memory only database. If readonly is true, the file database is loaded as readonly.
  122.     bool open(const std::string& database, bool readonly = false);
  123.  
  124.     // Close the database file
  125.     void close();
  126.  
  127.     // Construct a PreparedStmt
  128.     PreparedStmt prepare(const std::string& sql);
  129.  
  130.     // Execute a one off SQL query -- the internal callback is used to process the rows
  131.     bool exec(const std::string& sql);
  132.  
  133.     // get last row inserted
  134.     long long lastRowID();
  135.  
  136. private:
  137.     // Handle an error by SQLite
  138.     bool _handleError();
  139.  
  140.     // Callback just prints out the rows and columns for now
  141.     static int SQLITE_CALLBACK _callback(void* pDatabase, int argc, char** argv, char** colname);
  142.  
  143.     sqlite3* db_{ nullptr };
  144. };
  145.  
  146.  
  147. Database::Database() {}
  148.  
  149.  
  150. Database::~Database() { close(); }
  151.  
  152.  
  153. bool Database::open(const std::string& database, bool readonly) {
  154.     int flags = 0;
  155.     if (database == ":memory:") {
  156.         flags |= SQLITE_OPEN_MEMORY;
  157.     }
  158.    
  159.     if (readonly) {
  160.         flags |= SQLITE_OPEN_READONLY;
  161.     }
  162.     else {
  163.         flags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
  164.     }
  165.     sqlite3_open_v2(database.c_str(), &db_, flags, nullptr);
  166.     return _handleError();
  167. }
  168.  
  169.  
  170. void Database::close() {
  171.     if (!db_) return;
  172.  
  173.     int rc = sqlite3_close_v2(db_);
  174.     if (rc) _handleError();
  175. }
  176.  
  177.  
  178. bool Database::exec(const std::string& sql) {
  179.     if (!db_) throw std::runtime_error("Database not open");
  180.     char* errorMessage{ nullptr };
  181.     sqlite3_exec(db_, sql.c_str(), Database::_callback, (void*)this, &errorMessage);
  182.     if (errorMessage) {
  183.         std::cerr << "SQLIte3 Error: " << errorMessage << "\n";
  184.         sqlite3_free(errorMessage);
  185.         return false;
  186.     }
  187.     return true;
  188. }
  189.  
  190.  
  191. long long Database::lastRowID() {
  192.     return sqlite3_last_insert_rowid(db_);
  193. }
  194.  
  195.  
  196. PreparedStmt Database::prepare(const std::string& sql) {
  197.     if (!db_) throw std::runtime_error("Database not open");
  198.     sqlite3_stmt* stmt;
  199.     int rc = sqlite3_prepare_v3(db_, sql.c_str(), sql.size(), 0, &stmt, nullptr);
  200.     _handleError();
  201.     return PreparedStmt(stmt);
  202. }
  203.  
  204.  
  205. int SQLITE_CALLBACK Database::_callback(void* pDatabase, int argc, char** argv, char** colname) {
  206.     Database* db = (Database*)pDatabase;
  207.  
  208.     for (int i = 0; i < argc; i++) {
  209.         if (!argv[i]) std::cout << "  --  NULL  --  ";
  210.         else std::cout << std::setw(16) << argv[i] << " ";
  211.     }
  212.     std::cout << "\n";
  213.     // returning non-0 results in SQLITE_ABORT
  214.     return 0;
  215. }
  216.  
  217.  
  218. bool Database::_handleError() {
  219.     if (sqlite3_errcode(db_) != SQLITE_OK) {
  220.         std::cerr << "SQLite error: " << sqlite3_errmsg(db_) << "\n";
  221.         return false;
  222.     }
  223.     return true;
  224. }
  225.  
  226.  
  227. bool UpdateRecord(Database& db, long long rowID, const std::string firstName, const std::string lastName) {
  228.     if (!rowID) return false;
  229.     {auto stmt = db.prepare("UPDATE Customers SET FirstName=?2 WHERE ID=?1");
  230.     stmt.bindInt64(1, rowID);
  231.     stmt.bindText(2, firstName);
  232.     if (!stmt.step()) return false;
  233.     }
  234.     {auto stmt = db.prepare("UPDATE Customers SET LastName=?2 WHERE ID=?1");
  235.     stmt.bindInt64(1, rowID);
  236.     stmt.bindText(2, lastName);
  237.     if (!stmt.step()) return false;
  238.     }
  239.     return true;
  240. }
  241.  
  242.  
  243. long long CreateRecord(Database& db, const std::string firstName, const std::string lastName) {
  244.     // Divide this into two steps
  245.     // Create the row (atomic operation)
  246.     // Update the row
  247.     long long rowID = 0;
  248.     if (db.exec("INSERT INTO Customers (ID) VALUES (NULL)")) {
  249.         rowID = db.lastRowID();
  250.         UpdateRecord(db, rowID, firstName, lastName);
  251.     }
  252.     return rowID;
  253. }
  254.  
  255.  
  256. // Returns 0 if record does not exist
  257. long long FindRecord(Database& db, const std::string firstName, const std::string lastName) {
  258.     auto stmt = db.prepare("SELECT id FROM customers WHERE firstName LIKE ? AND lastName LIKE ?");
  259.     if (!stmt) return 0;
  260.     stmt.bindText(1, firstName);
  261.     stmt.bindText(2, lastName);
  262.     if (stmt.step()) {
  263.         return stmt.columnInt64(0);
  264.     }
  265.     return 0;
  266. }
  267.  
  268.  
  269. bool DeleteRecord(Database& db, long long rowID) {
  270.     auto stmt = db.prepare("DELETE FROM Customers WHERE ID=?");
  271.     stmt.bindInt64(1, rowID);
  272.     return stmt.step();
  273. }
  274.  
  275.  
  276. // Returns the number of records printed
  277. int PrintRecords(Database& db, int first, int count) {
  278.     // LIMIT is 0 based, so
  279.     // - first=0 and count=25 means print the first 25 records
  280.     // - first=25 and count=25 means print the next 25 records
  281.     auto stmt = db.prepare("SELECT * FROM customers LIMIT ?1,?2");
  282.     if (!stmt) return 0;
  283.     stmt.bindInt(1, first);
  284.     stmt.bindInt(2, count);
  285.     std::cout << "      ID       FIRST NAME        LAST NAME\n";
  286.     std::cout << "------------------------------------------\n";
  287.     int rowCount = 0;
  288.     while (stmt.step()) {
  289.         rowCount++;
  290.         std::cout << std::setw(8) << stmt.columnText(0) << " ";
  291.         std::cout << std::setw(16) << stmt.columnText(1) << " ";
  292.         std::cout << std::setw(16) << stmt.columnText(2) << "\n";
  293.     }
  294.     std::cout << "------------------------------------------\n";
  295.     return rowCount;
  296. }
  297.  
  298.  
  299. int main(int argc, char** argv) {
  300.     Database db;
  301.     if (!db.open(":memory:")) return -1;
  302.  
  303.     if (!db.exec("SELECT COUNT(*) FROM Customers")) {
  304.         db.exec("CREATE TABLE \"Customers\" ("
  305.                 "   \"ID\"  INTEGER PRIMARY KEY AUTOINCREMENT,"
  306.                 "   \"FirstName\"   TEXT,"
  307.                 "   \"LastName\"    TEXT"
  308.                 ")"
  309.         );
  310.         db.exec("INSERT INTO Customers (ID,FirstName,LastName) VALUES (NULL,'Jane','Austen')");
  311.         db.exec("INSERT INTO Customers (ID,FirstName,LastName) VALUES (NULL,'Charles','Dickens')");
  312.         db.exec("INSERT INTO Customers (ID,FirstName,LastName) VALUES (NULL,'Frank','Herbert')");
  313.     }
  314.  
  315.     long long rowID{ 0 };
  316.     if (rowID = FindRecord(db, "Jane", "Austen")) {
  317.         std::cout << "Jane Austen is ID " << rowID << "\n";
  318.         rowID = 0;
  319.     }
  320.  
  321.     if (rowID = FindRecord(db, "George", "Elliot")) {
  322.         std::cout << "George Elliot is ID " << rowID << "\n";
  323.     }
  324.     else {
  325.         rowID = CreateRecord(db, "George", "Elliot");
  326.         std::cout << "Created George Elliot record with ID " << rowID << "\n";
  327.     }
  328.  
  329.     rowID = CreateRecord(db, "Mark", "Twain");
  330.  
  331.     std::cout << "\n";
  332.     PrintRecords(db, 0, 25);
  333.  
  334.     // Change Mark Twain to Samuel Clemens
  335.     if (rowID) {
  336.         std::cout << "Updating Mark Twain\n";
  337.         UpdateRecord(db, rowID, "Samuel", "Clemens");
  338.     }
  339.  
  340.     std::cout << "\n";
  341.     PrintRecords(db, 0, 25);
  342.  
  343.     // Delete Mark Twain
  344.     if (rowID) DeleteRecord(db, rowID);
  345.  
  346.     return 0;
  347. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement