Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
database::backends::sqlite_backend Class Reference

SQLite implementation of database_backend interface. More...

#include <sqlite_backend.h>

Inheritance diagram for database::backends::sqlite_backend:
Inheritance graph
Collaboration diagram for database::backends::sqlite_backend:
Collaboration graph

Public Member Functions

 sqlite_backend ()
 Default constructor.
 
 ~sqlite_backend () override=default
 Destructor - ensures proper cleanup.
 
kcenon::common::Result< core::database_resultselect_query (const std::string &query_string) override
 Execute a SELECT query.
 
kcenon::common::VoidResult execute_query (const std::string &query_string) override
 Execute a general SQL query (DDL, DML)
 
kcenon::common::Result< core::database_resultselect_prepared (const std::string &query, const std::vector< core::database_value > &params) override
 Execute a parameterized SELECT query (prepared statement)
 
kcenon::common::VoidResult execute_prepared (const std::string &query, const std::vector< core::database_value > &params) override
 Execute a parameterized DML/DDL query (prepared statement)
 
kcenon::common::VoidResult begin_transaction () override
 Begin a transaction.
 
kcenon::common::VoidResult commit_transaction () override
 Commit the current transaction.
 
kcenon::common::VoidResult rollback_transaction () override
 Rollback the current transaction.
 
bool in_transaction () const override
 Check if backend is currently in a transaction.
 
std::string last_error () const override
 Get last error message from backend.
 
std::map< std::string, std::string > connection_info () const override
 Get backend-specific connection information.
 
- Public Member Functions inherited from database::core::backend_base< sqlite_backend, database_types::sqlite >
 backend_base ()=default
 Default constructor.
 
 backend_base (const backend_base &)=delete
 
 backend_base (backend_base &&) noexcept=delete
 
 ~backend_base () override
 Virtual destructor.
 
backend_baseoperator= (const backend_base &)=delete
 
backend_baseoperator= (backend_base &&) noexcept=delete
 
database_types type () const override
 Get the database type of this backend.
 
kcenon::common::VoidResult initialize (const connection_config &config) override
 Initialize the database backend.
 
kcenon::common::VoidResult shutdown () override
 Shutdown the database backend gracefully.
 
bool is_initialized () const override
 Check if backend is initialized and ready.
 
- Public Member Functions inherited from database::core::database_backend
virtual ~database_backend ()=default
 

Static Public Member Functions

static constexpr const char * backend_name ()
 Backend name for error messages.
 
- Static Public Member Functions inherited from database::core::backend_base< sqlite_backend, database_types::sqlite >
static std::unique_ptr< database_backendcreate ()
 Factory method for backend_registry.
 

Protected Member Functions

kcenon::common::VoidResult do_initialize (const core::connection_config &config)
 Database-specific initialization logic.
 
kcenon::common::VoidResult do_shutdown ()
 Database-specific shutdown logic.
 

Private Member Functions

unsigned int execute_modification_query (const std::string &query_string)
 Execute a modification query (INSERT, UPDATE, DELETE)
 
core::database_value convert_sqlite_value (void *stmt, int column_index)
 Convert SQLite column value to database_value.
 

Private Attributes

void * connection_ {nullptr}
 SQLite connection (sqlite3*)
 
std::atomic< bool > in_transaction_ {false}
 Transaction state.
 
std::string last_error_
 Last error message.
 
core::connection_config connection_config_
 Cached connection config.
 
std::recursive_mutex sqlite_mutex_
 Mutex for thread safety.
 

Friends

class core::backend_base< sqlite_backend, database_types::sqlite >
 

Additional Inherited Members

- Static Protected Member Functions inherited from database::core::database_backend
static std::string expand_params (const std::string &query, const std::vector< database_value > &params)
 Expand positional parameters into a SQL string (fallback)
 
- Protected Attributes inherited from database::core::backend_base< sqlite_backend, database_types::sqlite >
std::atomic< bool > initialized_
 Initialization state.
 

Detailed Description

SQLite implementation of database_backend interface.

This class implements the database_backend interface for SQLite via backend_base CRTP template, using the SQLite3 C API.

Design Pattern: Strategy pattern with CRTP

  • Extends backend_base for common lifecycle management
  • Uses SQLite3 C API for database access
  • Provides Result-based error handling
  • Supports transactions natively
  • Thread-safe with internal mutex

Thread Safety:

  • All operations are thread-safe via recursive mutex
  • Suitable for multi-threaded access

Usage:

// Runtime selection via backend_registry
auto backend = backend_registry::instance().create("sqlite");
config.database = "./mydb.db"; // File path or ":memory:" for in-memory DB
if (auto result = backend->initialize(config); !result) {
// Handle error
}
auto rows = backend->select_query("SELECT * FROM users");
Configuration for database connection.

Definition at line 68 of file sqlite_backend.h.

Constructor & Destructor Documentation

◆ sqlite_backend()

database::backends::sqlite_backend::sqlite_backend ( )

Default constructor.

Definition at line 30 of file sqlite_backend.cpp.

31 : connection_(nullptr)
32{
33}
void * connection_
SQLite connection (sqlite3*)

◆ ~sqlite_backend()

database::backends::sqlite_backend::~sqlite_backend ( )
overridedefault

Destructor - ensures proper cleanup.

Member Function Documentation

◆ backend_name()

static constexpr const char * database::backends::sqlite_backend::backend_name ( )
inlinestaticconstexpr

Backend name for error messages.

Definition at line 75 of file sqlite_backend.h.

75{ return "sqlite_backend"; }

◆ begin_transaction()

kcenon::common::VoidResult database::backends::sqlite_backend::begin_transaction ( )
overridevirtual

Begin a transaction.

Returns
VoidResult::ok() on success, error on failure

Implements database::core::database_backend.

Definition at line 525 of file sqlite_backend.cpp.

526{
527 if (!is_initialized()) {
528 last_error_ = "Backend not initialized";
529 return kcenon::common::error_info{
530 static_cast<int>(database::error_code::invalid_state),
532 "sqlite_backend"
533 };
534 }
535
536 if (in_transaction_) {
537 last_error_ = "Transaction already active";
538 return kcenon::common::error_info{
539 static_cast<int>(database::error_code::invalid_state),
541 "sqlite_backend"
542 };
543 }
544
545 auto result = execute_query("BEGIN TRANSACTION");
546 if (result.is_err()) {
547 return result;
548 }
549
550 in_transaction_ = true;
551 last_error_.clear();
552 return kcenon::common::ok();
553}
kcenon::common::VoidResult execute_query(const std::string &query_string) override
Execute a general SQL query (DDL, DML)
std::string last_error_
Last error message.
std::atomic< bool > in_transaction_
Transaction state.

References execute_query(), in_transaction_, database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), and last_error_.

Here is the call graph for this function:

◆ commit_transaction()

kcenon::common::VoidResult database::backends::sqlite_backend::commit_transaction ( )
overridevirtual

Commit the current transaction.

Returns
VoidResult::ok() on success, error on failure

Implements database::core::database_backend.

Definition at line 555 of file sqlite_backend.cpp.

556{
557 if (!is_initialized()) {
558 last_error_ = "Backend not initialized";
559 return kcenon::common::error_info{
560 static_cast<int>(database::error_code::invalid_state),
562 "sqlite_backend"
563 };
564 }
565
566 if (!in_transaction_) {
567 last_error_ = "No active transaction";
568 return kcenon::common::error_info{
569 static_cast<int>(database::error_code::invalid_state),
571 "sqlite_backend"
572 };
573 }
574
575 auto result = execute_query("COMMIT");
576 if (result.is_err()) {
577 return result;
578 }
579
580 in_transaction_ = false;
581 last_error_.clear();
582 return kcenon::common::ok();
583}

References execute_query(), in_transaction_, database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), and last_error_.

Here is the call graph for this function:

◆ connection_info()

std::map< std::string, std::string > database::backends::sqlite_backend::connection_info ( ) const
overridevirtual

Get backend-specific connection information.

Returns
Map of connection properties (for debugging/monitoring)

Example keys: "server_version", "connection_id", "protocol_version"

Implements database::core::database_backend.

Definition at line 622 of file sqlite_backend.cpp.

623{
624 std::map<std::string, std::string> info;
625 info["backend"] = "sqlite";
626 info["database"] = connection_config_.database;
627 info["initialized"] = initialized_ ? "true" : "false";
628 info["in_transaction"] = in_transaction_ ? "true" : "false";
629 return info;
630}
core::connection_config connection_config_
Cached connection config.
@ info
Informational messages (default)

References connection_config_, database::core::connection_config::database, in_transaction_, and database::core::backend_base< sqlite_backend, database_types::sqlite >::initialized_.

◆ convert_sqlite_value()

core::database_value database::backends::sqlite_backend::convert_sqlite_value ( void * stmt,
int column_index )
private

Convert SQLite column value to database_value.

Parameters
stmtSQLite prepared statement
column_indexColumn index in the result set
Returns
database_value containing the converted value

Definition at line 123 of file sqlite_backend.cpp.

124{
125#ifdef USE_SQLITE
126 sqlite3_stmt* sqlite_stmt = static_cast<sqlite3_stmt*>(stmt);
127
128 int sqlite_type = sqlite3_column_type(sqlite_stmt, column_index);
129
130 switch (sqlite_type) {
131 case SQLITE_INTEGER:
132 return static_cast<int64_t>(sqlite3_column_int64(sqlite_stmt, column_index));
133
134 case SQLITE_FLOAT:
135 return sqlite3_column_double(sqlite_stmt, column_index);
136
137 case SQLITE_TEXT:
138 {
139 const char* text = reinterpret_cast<const char*>(sqlite3_column_text(sqlite_stmt, column_index));
140 return std::string(text ? text : "");
141 }
142
143 case SQLITE_BLOB:
144 {
145 // For BLOB data, convert to string representation
146 const void* blob = sqlite3_column_blob(sqlite_stmt, column_index);
147 int blob_size = sqlite3_column_bytes(sqlite_stmt, column_index);
148 if (blob && blob_size > 0) {
149 const char* blob_chars = static_cast<const char*>(blob);
150 return std::string(blob_chars, blob_size);
151 }
152 return std::string();
153 }
154
155 case SQLITE_NULL:
156 default:
157 return nullptr;
158 }
159#endif
160 return nullptr;
161}

Referenced by select_prepared(), and select_query().

Here is the caller graph for this function:

◆ do_initialize()

kcenon::common::VoidResult database::backends::sqlite_backend::do_initialize ( const core::connection_config & config)
protected

Database-specific initialization logic.

Parameters
configConnection configuration
Returns
VoidResult::ok() on success, error on failure

Definition at line 35 of file sqlite_backend.cpp.

36{
37 connection_config_ = config;
38
39 // Use database field as file path, default to ":memory:" for in-memory database
40 std::string db_path = config.database.empty() ? ":memory:" : config.database;
41
42#ifdef USE_SQLITE
43 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
44 try {
45 sqlite3* db = nullptr;
46
47 // Open or create the database
48 int result = sqlite3_open(db_path.c_str(), &db);
49
50 if (result != SQLITE_OK) {
51 last_error_ = std::string("Connection failed: ") + sqlite3_errmsg(db);
52 logger_.error("do_initialize", last_error_);
53 if (db) {
54 sqlite3_close(db);
55 }
56 return kcenon::common::error_info{
59 "sqlite_backend"
60 };
61 }
62
63 connection_ = db;
64
65 // Enable foreign key constraints
66 char* error_msg = nullptr;
67 if (sqlite3_exec(db, "PRAGMA foreign_keys = ON", nullptr, nullptr, &error_msg) != SQLITE_OK) {
68 logger_.warning(std::string("Failed to enable foreign key constraints: ") + (error_msg ? error_msg : ""));
69 if (error_msg) sqlite3_free(error_msg);
70 }
71
72 last_error_.clear();
73 return kcenon::common::ok();
74 } catch (const std::exception& e) {
75 last_error_ = std::string("Connection error: ") + e.what();
76 logger_.error("do_initialize", last_error_);
77 }
78#else
79 logger_.warning("SQLite support not compiled. Mock mode enabled.");
80 // Mock mode for testing without SQLite
81 last_error_.clear();
82 return kcenon::common::ok();
83#endif
84
85 if (last_error_.empty()) {
86 last_error_ = "Failed to connect to SQLite database";
87 }
88 return kcenon::common::error_info{
91 "sqlite_backend"
92 };
93}
std::recursive_mutex sqlite_mutex_
Mutex for thread safety.

References connection_, connection_config_, database::connection_failed, database::core::connection_config::database, last_error_, and sqlite_mutex_.

◆ do_shutdown()

kcenon::common::VoidResult database::backends::sqlite_backend::do_shutdown ( )
protected

Database-specific shutdown logic.

Returns
VoidResult::ok() on success, error on failure

Definition at line 95 of file sqlite_backend.cpp.

96{
97 // Rollback any active transaction before disconnecting
98 if (in_transaction_) {
100 }
101
102#ifdef USE_SQLITE
103 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
104 if (connection_) {
105 sqlite3* db = static_cast<sqlite3*>(connection_);
106 int result = sqlite3_close(db);
107 connection_ = nullptr;
108 if (result != SQLITE_OK) {
109 last_error_ = "Failed to close SQLite database";
110 return kcenon::common::error_info{
113 "sqlite_backend"
114 };
115 }
116 }
117#endif
118
119 last_error_.clear();
120 return kcenon::common::ok();
121}
kcenon::common::VoidResult rollback_transaction() override
Rollback the current transaction.

References connection_, database::connection_failed, in_transaction_, last_error_, rollback_transaction(), and sqlite_mutex_.

Here is the call graph for this function:

◆ execute_modification_query()

unsigned int database::backends::sqlite_backend::execute_modification_query ( const std::string & query_string)
private

Execute a modification query (INSERT, UPDATE, DELETE)

Parameters
query_stringSQL query to execute
Returns
Number of affected rows

Definition at line 163 of file sqlite_backend.cpp.

164{
165#ifdef USE_SQLITE
166 if (!connection_) return 0;
167 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
168 try {
169 sqlite3* db = static_cast<sqlite3*>(connection_);
170 char* error_msg = nullptr;
171
172 int result = sqlite3_exec(db, query_string.c_str(), nullptr, nullptr, &error_msg);
173
174 if (result != SQLITE_OK) {
175 last_error_ = std::string("Modification query failed: ") + (error_msg ? error_msg : "Unknown error");
176 logger_.error("execute_modification_query", last_error_);
177 if (error_msg) sqlite3_free(error_msg);
178 return 0;
179 }
180
181 last_error_.clear();
182 return static_cast<unsigned int>(sqlite3_changes(db));
183 } catch (const std::exception& e) {
184 last_error_ = std::string("Modification query error: ") + e.what();
185 logger_.error("execute_modification_query", last_error_);
186 }
187#else
188 logger_.warning("SQLite support not compiled. Modification query: " + query_string.substr(0, 20) + "...");
189 return 1; // Mock: return 1 affected row
190#endif
191 return 0;
192}

References connection_, last_error_, and sqlite_mutex_.

◆ execute_prepared()

kcenon::common::VoidResult database::backends::sqlite_backend::execute_prepared ( const std::string & query,
const std::vector< core::database_value > & params )
nodiscardoverridevirtual

Execute a parameterized DML/DDL query (prepared statement)

Parameters
querySQL with positional placeholders ($1, $2, ... or ?, ?, ...)
paramsParameter values to bind
Returns
VoidResult::ok() on success, error on failure
See also
select_prepared for details on parameterized queries

Reimplemented from database::core::database_backend.

Definition at line 433 of file sqlite_backend.cpp.

436{
437 if (!is_initialized()) {
438 last_error_ = "Backend not initialized";
439 return kcenon::common::error_info{
440 static_cast<int>(database::error_code::invalid_state),
442 "sqlite_backend"
443 };
444 }
445
446#ifdef USE_SQLITE
447 if (!connection_) {
448 last_error_ = "No active SQLite connection";
449 logger_.error("execute_prepared", last_error_);
450 return kcenon::common::error_info{
453 "sqlite_backend"
454 };
455 }
456
457 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
458 sqlite3* db = static_cast<sqlite3*>(connection_);
459 sqlite3_stmt* stmt = nullptr;
460
461 int rc = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
462 if (rc != SQLITE_OK) {
463 last_error_ = std::string("Prepare error: ") + sqlite3_errmsg(db);
464 logger_.error("execute_prepared", last_error_);
465 return kcenon::common::error_info{
466 static_cast<int>(database::error_code::query_failed),
468 "sqlite_backend"
469 };
470 }
471
472 for (size_t i = 0; i < params.size(); ++i) {
473 int bind_idx = static_cast<int>(i + 1);
474 int bind_rc = SQLITE_OK;
475
476 std::visit([&bind_rc, stmt, bind_idx](const auto& v) {
477 using T = std::decay_t<decltype(v)>;
478 if constexpr (std::is_same_v<T, std::nullptr_t>) {
479 bind_rc = sqlite3_bind_null(stmt, bind_idx);
480 } else if constexpr (std::is_same_v<T, bool>) {
481 bind_rc = sqlite3_bind_int(stmt, bind_idx, v ? 1 : 0);
482 } else if constexpr (std::is_same_v<T, int64_t>) {
483 bind_rc = sqlite3_bind_int64(stmt, bind_idx, v);
484 } else if constexpr (std::is_same_v<T, double>) {
485 bind_rc = sqlite3_bind_double(stmt, bind_idx, v);
486 } else if constexpr (std::is_same_v<T, std::string>) {
487 bind_rc = sqlite3_bind_text(stmt, bind_idx, v.c_str(),
488 static_cast<int>(v.size()), SQLITE_TRANSIENT);
489 }
490 }, params[i]);
491
492 if (bind_rc != SQLITE_OK) {
493 last_error_ = std::string("Bind error at param ") + std::to_string(i + 1)
494 + ": " + sqlite3_errmsg(db);
495 logger_.error("execute_prepared", last_error_);
496 sqlite3_finalize(stmt);
497 return kcenon::common::error_info{
498 static_cast<int>(database::error_code::query_failed),
500 "sqlite_backend"
501 };
502 }
503 }
504
505 rc = sqlite3_step(stmt);
506 sqlite3_finalize(stmt);
507
508 if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
509 last_error_ = std::string("Execute prepared error: ") + sqlite3_errmsg(db);
510 logger_.error("execute_prepared", last_error_);
511 return kcenon::common::error_info{
512 static_cast<int>(database::error_code::query_failed),
514 "sqlite_backend"
515 };
516 }
517
518 last_error_.clear();
519 return kcenon::common::ok();
520#else
521 return database_backend::execute_prepared(query, params);
522#endif
523}

References connection_, database::connection_failed, database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), last_error_, database::query_failed, and sqlite_mutex_.

Here is the call graph for this function:

◆ execute_query()

kcenon::common::VoidResult database::backends::sqlite_backend::execute_query ( const std::string & query_string)
overridevirtual

Execute a general SQL query (DDL, DML)

Parameters
query_stringSQL statement
Returns
VoidResult::ok() on success, error on failure

Implements database::core::database_backend.

Definition at line 280 of file sqlite_backend.cpp.

281{
282 if (!is_initialized()) {
283 last_error_ = "Backend not initialized";
284 return kcenon::common::error_info{
285 static_cast<int>(database::error_code::invalid_state),
287 "sqlite_backend"
288 };
289 }
290
291#ifdef USE_SQLITE
292 if (!connection_) {
293 last_error_ = "No active SQLite connection";
294 logger_.error("execute_query", last_error_);
295 return kcenon::common::error_info{
298 "sqlite_backend"
299 };
300 }
301
302 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
303 sqlite3* db = static_cast<sqlite3*>(connection_);
304
305 char* error_msg = nullptr;
306 int result = sqlite3_exec(db, query_string.c_str(), nullptr, nullptr, &error_msg);
307
308 if (result != SQLITE_OK) {
309 last_error_ = std::string("Execute error: ") + (error_msg ? error_msg : "Unknown error");
310 logger_.error("execute_query", last_error_);
311 if (error_msg) sqlite3_free(error_msg);
312 return kcenon::common::error_info{
313 static_cast<int>(database::error_code::query_failed),
315 "sqlite_backend"
316 };
317 }
318
319 last_error_.clear();
320 return kcenon::common::ok();
321#else
322 // Mock execution
323 logger_.info("SQLite support not compiled. Mock execute: " + query_string);
324 last_error_.clear();
325 return kcenon::common::ok();
326#endif
327}

References connection_, database::connection_failed, database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), last_error_, database::query_failed, and sqlite_mutex_.

Referenced by begin_transaction(), commit_transaction(), and rollback_transaction().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ in_transaction()

bool database::backends::sqlite_backend::in_transaction ( ) const
overridevirtual

Check if backend is currently in a transaction.

Returns
true if transaction is active

Implements database::core::database_backend.

Definition at line 612 of file sqlite_backend.cpp.

613{
614 return in_transaction_;
615}

References in_transaction_.

◆ last_error()

std::string database::backends::sqlite_backend::last_error ( ) const
overridevirtual

Get last error message from backend.

Returns
Error message, or empty string if no error

Implements database::core::database_backend.

Definition at line 617 of file sqlite_backend.cpp.

618{
619 return last_error_;
620}

References last_error_.

◆ rollback_transaction()

kcenon::common::VoidResult database::backends::sqlite_backend::rollback_transaction ( )
overridevirtual

Rollback the current transaction.

Returns
VoidResult::ok() on success, error on failure

Implements database::core::database_backend.

Definition at line 585 of file sqlite_backend.cpp.

586{
587 if (!is_initialized()) {
588 last_error_ = "Backend not initialized";
589 return kcenon::common::error_info{
590 static_cast<int>(database::error_code::invalid_state),
592 "sqlite_backend"
593 };
594 }
595
596 if (!in_transaction_) {
597 // Not an error - already rolled back or never started
598 return kcenon::common::ok();
599 }
600
601 auto result = execute_query("ROLLBACK");
602 in_transaction_ = false; // Force state reset even on error
603
604 if (result.is_err()) {
605 return result;
606 }
607
608 last_error_.clear();
609 return kcenon::common::ok();
610}

References execute_query(), in_transaction_, database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), and last_error_.

Referenced by do_shutdown().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ select_prepared()

kcenon::common::Result< core::database_result > database::backends::sqlite_backend::select_prepared ( const std::string & query,
const std::vector< core::database_value > & params )
nodiscardoverridevirtual

Execute a parameterized SELECT query (prepared statement)

Parameters are bound at the wire-protocol level, providing stronger SQL injection protection than string escaping. Backends that support native prepared statements (PostgreSQL, SQLite) should override this.

Parameters
querySQL with positional placeholders ($1, $2, ... or ?, ?, ...)
paramsParameter values to bind
Returns
Query results as rows, or error
Note
Default implementation falls back to string interpolation via execute_query/select_query for backends that have not yet implemented native prepared statement support.

Reimplemented from database::core::database_backend.

Definition at line 329 of file sqlite_backend.cpp.

332{
333 if (!is_initialized()) {
334 last_error_ = "Backend not initialized";
335 return kcenon::common::error_info{
336 static_cast<int>(database::error_code::invalid_state),
338 "sqlite_backend"
339 };
340 }
341
343
344#ifdef USE_SQLITE
345 if (!connection_) {
346 last_error_ = "No active connection";
347 return kcenon::common::error_info{
350 "sqlite_backend"
351 };
352 }
353
354 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
355 sqlite3* db = static_cast<sqlite3*>(connection_);
356 sqlite3_stmt* stmt = nullptr;
357
358 int rc = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
359 if (rc != SQLITE_OK) {
360 last_error_ = std::string("Prepare error: ") + sqlite3_errmsg(db);
361 logger_.error("select_prepared", last_error_);
362 return kcenon::common::error_info{
363 static_cast<int>(database::error_code::query_failed),
365 "sqlite_backend"
366 };
367 }
368
369 // Bind parameters
370 for (size_t i = 0; i < params.size(); ++i) {
371 int bind_idx = static_cast<int>(i + 1); // SQLite uses 1-based indexing
372 int bind_rc = SQLITE_OK;
373
374 std::visit([&bind_rc, stmt, bind_idx](const auto& v) {
375 using T = std::decay_t<decltype(v)>;
376 if constexpr (std::is_same_v<T, std::nullptr_t>) {
377 bind_rc = sqlite3_bind_null(stmt, bind_idx);
378 } else if constexpr (std::is_same_v<T, bool>) {
379 bind_rc = sqlite3_bind_int(stmt, bind_idx, v ? 1 : 0);
380 } else if constexpr (std::is_same_v<T, int64_t>) {
381 bind_rc = sqlite3_bind_int64(stmt, bind_idx, v);
382 } else if constexpr (std::is_same_v<T, double>) {
383 bind_rc = sqlite3_bind_double(stmt, bind_idx, v);
384 } else if constexpr (std::is_same_v<T, std::string>) {
385 bind_rc = sqlite3_bind_text(stmt, bind_idx, v.c_str(),
386 static_cast<int>(v.size()), SQLITE_TRANSIENT);
387 }
388 }, params[i]);
389
390 if (bind_rc != SQLITE_OK) {
391 last_error_ = std::string("Bind error at param ") + std::to_string(i + 1)
392 + ": " + sqlite3_errmsg(db);
393 logger_.error("select_prepared", last_error_);
394 sqlite3_finalize(stmt);
395 return kcenon::common::error_info{
396 static_cast<int>(database::error_code::query_failed),
398 "sqlite_backend"
399 };
400 }
401 }
402
403 // Execute and collect results
404 int col_count = sqlite3_column_count(stmt);
405 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
406 core::database_row db_row;
407 for (int col = 0; col < col_count; ++col) {
408 std::string column_name = sqlite3_column_name(stmt, col);
409 db_row[column_name] = convert_sqlite_value(stmt, col);
410 }
411 result.push_back(std::move(db_row));
412 }
413
414 sqlite3_finalize(stmt);
415
416 if (rc != SQLITE_DONE) {
417 last_error_ = std::string("Step error: ") + sqlite3_errmsg(db);
418 logger_.error("select_prepared", last_error_);
419 return kcenon::common::error_info{
420 static_cast<int>(database::error_code::query_failed),
422 "sqlite_backend"
423 };
424 }
425#else
426 return database_backend::select_prepared(query, params);
427#endif
428
429 last_error_.clear();
430 return result;
431}
core::database_value convert_sqlite_value(void *stmt, int column_index)
Convert SQLite column value to database_value.
std::vector< database_row > database_result
std::map< std::string, database_value > database_row

References connection_, database::connection_failed, convert_sqlite_value(), database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), last_error_, database::query_failed, and sqlite_mutex_.

Here is the call graph for this function:

◆ select_query()

kcenon::common::Result< core::database_result > database::backends::sqlite_backend::select_query ( const std::string & query_string)
overridevirtual

Execute a SELECT query.

Parameters
query_stringSQL SELECT statement
Returns
Query results as rows, or error

Implements database::core::database_backend.

Definition at line 194 of file sqlite_backend.cpp.

195{
196 if (!is_initialized()) {
197 last_error_ = "Backend not initialized";
198 return kcenon::common::error_info{
199 static_cast<int>(database::error_code::invalid_state),
201 "sqlite_backend"
202 };
203 }
204
206
207#ifdef USE_SQLITE
208 if (!connection_) {
209 last_error_ = "No active connection";
210 return kcenon::common::error_info{
213 "sqlite_backend"
214 };
215 }
216 std::lock_guard<std::recursive_mutex> lock(sqlite_mutex_);
217 try {
218 sqlite3* db = static_cast<sqlite3*>(connection_);
219 sqlite3_stmt* stmt = nullptr;
220
221 // Prepare the statement
222 int prepare_result = sqlite3_prepare_v2(db, query_string.c_str(), -1, &stmt, nullptr);
223 if (prepare_result != SQLITE_OK) {
224 last_error_ = std::string("Prepare failed: ") + sqlite3_errmsg(db);
225 logger_.error("select_query", last_error_);
226 return kcenon::common::error_info{
227 static_cast<int>(database::error_code::query_failed),
229 "sqlite_backend"
230 };
231 }
232
233 // Get column count and names
234 int column_count = sqlite3_column_count(stmt);
235 std::vector<std::string> column_names;
236 for (int i = 0; i < column_count; i++) {
237 column_names.push_back(sqlite3_column_name(stmt, i));
238 }
239
240 // Execute and fetch results
241 while (sqlite3_step(stmt) == SQLITE_ROW) {
243
244 for (int i = 0; i < column_count; i++) {
245 const std::string& column_name = column_names[i];
246 row[column_name] = convert_sqlite_value(stmt, i);
247 }
248
249 result.push_back(std::move(row));
250 }
251
252 // Clean up
253 sqlite3_finalize(stmt);
254
255 } catch (const std::exception& e) {
256 last_error_ = std::string("Select query error: ") + e.what();
257 logger_.error("select_query", last_error_);
258 return kcenon::common::error_info{
259 static_cast<int>(database::error_code::query_failed),
261 "sqlite_backend"
262 };
263 }
264#else
265 logger_.warning("SQLite support not compiled. Select query: " + query_string.substr(0, 20) + "...");
266 // Return mock data for testing
267 if (query_string.find("SELECT") != std::string::npos) {
268 core::database_row mock_row;
269 mock_row["id"] = int64_t(1);
270 mock_row["name"] = std::string("sqlite_mock_data");
271 mock_row["active"] = true;
272 result.push_back(mock_row);
273 }
274#endif
275
276 last_error_.clear();
277 return result;
278}

References connection_, database::connection_failed, convert_sqlite_value(), database::invalid_state, database::core::backend_base< sqlite_backend, database_types::sqlite >::is_initialized(), last_error_, database::query_failed, and sqlite_mutex_.

Here is the call graph for this function:

Friends And Related Symbol Documentation

◆ core::backend_base< sqlite_backend, database_types::sqlite >

Definition at line 111 of file sqlite_backend.h.

Member Data Documentation

◆ connection_

void* database::backends::sqlite_backend::connection_ {nullptr}
private

SQLite connection (sqlite3*)

Definition at line 145 of file sqlite_backend.h.

145{nullptr};

Referenced by do_initialize(), do_shutdown(), execute_modification_query(), execute_prepared(), execute_query(), select_prepared(), and select_query().

◆ connection_config_

core::connection_config database::backends::sqlite_backend::connection_config_
private

Cached connection config.

Definition at line 148 of file sqlite_backend.h.

Referenced by connection_info(), and do_initialize().

◆ in_transaction_

std::atomic<bool> database::backends::sqlite_backend::in_transaction_ {false}
private

Transaction state.

Definition at line 146 of file sqlite_backend.h.

146{false};

Referenced by begin_transaction(), commit_transaction(), connection_info(), do_shutdown(), in_transaction(), and rollback_transaction().

◆ last_error_

std::string database::backends::sqlite_backend::last_error_
mutableprivate

◆ sqlite_mutex_

std::recursive_mutex database::backends::sqlite_backend::sqlite_mutex_
mutableprivate

The documentation for this class was generated from the following files: