1 /**********************************************************************
2 * Copyright (C) 2004, Leo Seib, Hannover
4 * Project:SQLiteDataset C++ Dynamic Library
5 * Module: SQLiteDataset class realisation file
6 * Author: Leo Seib E-Mail: leoseib@web.de
9 * SPDX-License-Identifier: MIT
10 * See LICENSES/README.md for more information.
13 #include "sqlitedataset.h"
15 #include "utils/StringUtils.h"
16 #include "utils/URIUtils.h"
17 #include "utils/XTimeUtils.h"
18 #include "utils/log.h"
25 using namespace std::chrono_literals
;
29 #define X(VAL) std::make_pair(VAL, #VAL)
30 //!@todo Remove ifdefs when sqlite version requirement has been bumped to at least 3.26.0
31 const std::map
<int, const char*> g_SqliteErrorStrings
= {
63 #if defined(SQLITE_ERROR_MISSING_COLLSEQ)
64 X(SQLITE_ERROR_MISSING_COLLSEQ
),
66 #if defined(SQLITE_ERROR_RETRY)
67 X(SQLITE_ERROR_RETRY
),
69 #if defined(SQLITE_ERROR_SNAPSHOT)
70 X(SQLITE_ERROR_SNAPSHOT
),
73 X(SQLITE_IOERR_SHORT_READ
),
74 X(SQLITE_IOERR_WRITE
),
75 X(SQLITE_IOERR_FSYNC
),
76 X(SQLITE_IOERR_DIR_FSYNC
),
77 X(SQLITE_IOERR_TRUNCATE
),
78 X(SQLITE_IOERR_FSTAT
),
79 X(SQLITE_IOERR_UNLOCK
),
80 X(SQLITE_IOERR_RDLOCK
),
81 X(SQLITE_IOERR_DELETE
),
82 X(SQLITE_IOERR_BLOCKED
),
83 X(SQLITE_IOERR_NOMEM
),
84 X(SQLITE_IOERR_ACCESS
),
85 X(SQLITE_IOERR_CHECKRESERVEDLOCK
),
87 X(SQLITE_IOERR_CLOSE
),
88 X(SQLITE_IOERR_DIR_CLOSE
),
89 X(SQLITE_IOERR_SHMOPEN
),
90 X(SQLITE_IOERR_SHMSIZE
),
91 X(SQLITE_IOERR_SHMLOCK
),
92 X(SQLITE_IOERR_SHMMAP
),
94 X(SQLITE_IOERR_DELETE_NOENT
),
96 X(SQLITE_IOERR_GETTEMPPATH
),
97 X(SQLITE_IOERR_CONVPATH
),
98 #if defined(SQLITE_IOERR_VNODE)
99 X(SQLITE_IOERR_VNODE
),
101 #if defined(SQLITE_IOERR_AUTH)
102 X(SQLITE_IOERR_AUTH
),
104 #if defined(SQLITE_IOERR_BEGIN_ATOMIC)
105 X(SQLITE_IOERR_BEGIN_ATOMIC
),
107 #if defined(SQLITE_IOERR_COMMIT_ATOMIC)
108 X(SQLITE_IOERR_COMMIT_ATOMIC
),
110 #if defined(SQLITE_IOERR_ROLLBACK_ATOMIC)
111 X(SQLITE_IOERR_ROLLBACK_ATOMIC
),
113 X(SQLITE_LOCKED_SHAREDCACHE
),
114 #if defined(SQLITE_LOCKED_VTAB)
115 X(SQLITE_LOCKED_VTAB
),
117 X(SQLITE_BUSY_RECOVERY
),
118 X(SQLITE_BUSY_SNAPSHOT
),
119 X(SQLITE_CANTOPEN_NOTEMPDIR
),
120 X(SQLITE_CANTOPEN_ISDIR
),
121 X(SQLITE_CANTOPEN_FULLPATH
),
122 X(SQLITE_CANTOPEN_CONVPATH
),
123 #if defined(SQLITE_CANTOPEN_DIRTYWAL)
124 X(SQLITE_CANTOPEN_DIRTYWAL
),
126 X(SQLITE_CORRUPT_VTAB
),
127 #if defined(SQLITE_CORRUPT_SEQUENCE)
128 X(SQLITE_CORRUPT_SEQUENCE
),
130 X(SQLITE_READONLY_RECOVERY
),
131 X(SQLITE_READONLY_CANTLOCK
),
132 X(SQLITE_READONLY_ROLLBACK
),
133 X(SQLITE_READONLY_DBMOVED
),
134 #if defined(SQLITE_READONLY_CANTINIT)
135 X(SQLITE_READONLY_CANTINIT
),
137 #if defined(SQLITE_READONLY_DIRECTORY)
138 X(SQLITE_READONLY_DIRECTORY
),
140 X(SQLITE_ABORT_ROLLBACK
),
141 X(SQLITE_CONSTRAINT_CHECK
),
142 X(SQLITE_CONSTRAINT_COMMITHOOK
),
143 X(SQLITE_CONSTRAINT_FOREIGNKEY
),
144 X(SQLITE_CONSTRAINT_FUNCTION
),
145 X(SQLITE_CONSTRAINT_NOTNULL
),
146 X(SQLITE_CONSTRAINT_PRIMARYKEY
),
147 X(SQLITE_CONSTRAINT_TRIGGER
),
148 X(SQLITE_CONSTRAINT_UNIQUE
),
149 X(SQLITE_CONSTRAINT_VTAB
),
150 X(SQLITE_CONSTRAINT_ROWID
),
151 X(SQLITE_NOTICE_RECOVER_WAL
),
152 X(SQLITE_NOTICE_RECOVER_ROLLBACK
),
153 X(SQLITE_WARNING_AUTOINDEX
),
155 #if defined(SQLITE_OK_LOAD_PERMANENTLY)
156 X(SQLITE_OK_LOAD_PERMANENTLY
),
164 //************* Callback function ***************************
166 int callback(void* res_ptr
, int ncol
, char** result
, char** cols
)
168 result_set
* r
= static_cast<result_set
*>(res_ptr
);
170 if (!r
->record_header
.size())
172 r
->record_header
.reserve(ncol
);
173 for (int i
= 0; i
< ncol
; i
++)
176 header
.name
= cols
[i
];
177 r
->record_header
.push_back(header
);
183 sql_record
* rec
= new sql_record
;
185 for (int i
= 0; i
< ncol
; i
++)
187 field_value
& v
= rec
->at(i
);
188 if (result
[i
] == NULL
)
195 v
.set_asString(result
[i
]);
198 r
->records
.push_back(rec
);
203 static int busy_callback(void*, int busyCount
)
205 KODI::TIME::Sleep(100ms
);
209 //************* SqliteDatabase implementation ***************
211 SqliteDatabase::SqliteDatabase()
215 _in_transaction
= false; // for transaction
217 error
= "Unknown database error"; //S_NO_CONNECTION;
225 SqliteDatabase::~SqliteDatabase()
230 Dataset
* SqliteDatabase::CreateDataset() const
232 return new SqliteDataset(const_cast<SqliteDatabase
*>(this));
235 void SqliteDatabase::setHostName(const char* newHost
)
239 // hostname is the relative folder to the database, ensure it's slash terminated
240 if (host
[host
.length() - 1] != '/' && host
[host
.length() - 1] != '\\')
243 // ensure the fully qualified path has slashes in the correct direction
244 if ((host
[1] == ':') && isalpha(host
[0]))
247 while ((pos
= host
.find('/', pos
)) != std::string::npos
)
248 host
.replace(pos
++, 1, "\\");
253 while ((pos
= host
.find('\\', pos
)) != std::string::npos
)
254 host
.replace(pos
++, 1, "/");
258 void SqliteDatabase::setDatabase(const char* newDb
)
262 // db is the filename for the database, ensure it's not slash prefixed
263 if (newDb
[0] == '/' || newDb
[0] == '\\')
266 // ensure the ".db" extension is appended to the end
267 if (db
.find(".db") != (db
.length() - 3))
271 int SqliteDatabase::status(void)
274 return DB_CONNECTION_NONE
;
275 return DB_CONNECTION_OK
;
278 int SqliteDatabase::setErr(int err_code
, const char* qry
)
280 std::stringstream ss
;
281 ss
<< "[" << db
<< "] ";
282 auto errorIt
= g_SqliteErrorStrings
.find(err_code
);
283 if (errorIt
!= g_SqliteErrorStrings
.end())
285 ss
<< "SQLite error " << errorIt
->second
;
289 ss
<< "Undefined SQLite error " << err_code
;
292 ss
<< " (" << sqlite3_errmsg(conn
) << ")";
293 ss
<< "\nQuery: " << qry
;
298 const char* SqliteDatabase::getErrorMsg()
300 return error
.c_str();
303 static int AlphaNumericCollation(
304 void* not_used
, int nKey1
, const void* pKey1
, int nKey2
, const void* pKey2
)
306 return StringUtils::AlphaNumericCollation(nKey1
, pKey1
, nKey2
, pKey2
);
309 int SqliteDatabase::connect(bool create
)
311 if (host
.empty() || db
.empty())
312 return DB_CONNECTION_NONE
;
314 //CLog::Log(LOGDEBUG, "Connecting to sqlite:{}:{}", host, db);
316 std::string db_fullpath
= URIUtils::AddFileToFolder(host
, db
);
321 int flags
= SQLITE_OPEN_READWRITE
;
323 flags
|= SQLITE_OPEN_CREATE
;
324 int errorCode
= sqlite3_open_v2(db_fullpath
.c_str(), &conn
, flags
, NULL
);
325 if (create
&& errorCode
== SQLITE_CANTOPEN
)
327 CLog::Log(LOGFATAL
, "SqliteDatabase: can't open {}", db_fullpath
);
328 throw std::runtime_error("SqliteDatabase: can't open " + db_fullpath
);
330 else if (errorCode
== SQLITE_OK
)
332 sqlite3_extended_result_codes(conn
, 1);
333 sqlite3_busy_handler(conn
, busy_callback
, NULL
);
334 if (setErr(sqlite3_exec(getHandle(), "PRAGMA empty_result_callbacks=ON", NULL
, NULL
, NULL
),
335 "PRAGMA empty_result_callbacks=ON") != SQLITE_OK
)
337 throw DbErrors("%s", getErrorMsg());
339 else if (sqlite3_db_readonly(conn
, nullptr) == 1)
341 CLog::Log(LOGFATAL
, "SqliteDatabase: {} is read only", db_fullpath
);
342 throw std::runtime_error("SqliteDatabase: " + db_fullpath
+ " is read only");
344 errorCode
= sqlite3_create_collation(conn
, "ALPHANUM", SQLITE_UTF8
, 0, AlphaNumericCollation
);
345 if (errorCode
!= SQLITE_OK
)
347 CLog::Log(LOGFATAL
, "SqliteDatabase: can not register collation");
348 throw std::runtime_error("SqliteDatabase: can not register collation " + db_fullpath
);
351 return DB_CONNECTION_OK
;
354 catch (const DbErrors
&)
360 return DB_CONNECTION_NONE
;
363 bool SqliteDatabase::exists(void)
371 // performing a select all on the sqlite_master will return rows if there are tables
372 // defined indicating it's not empty and therefore must "exist".
373 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT * FROM sqlite_master");
374 if ((last_err
= sqlite3_exec(getHandle(), sqlcmd
, &callback
, &res
, NULL
)) == SQLITE_OK
)
376 bRet
= (res
.records
.size() > 0);
382 void SqliteDatabase::disconnect(void)
390 int SqliteDatabase::create()
392 return connect(true);
395 int SqliteDatabase::copy(const char* backup_name
)
398 throw DbErrors("Can't copy database: no active connection...");
400 CLog::Log(LOGDEBUG
, "Copying from {} to {} at {}", db
, backup_name
, host
);
403 std::string backup_db
= backup_name
;
405 sqlite3
* pFile
; /* Database connection opened on zFilename */
406 sqlite3_backup
* pBackup
; /* Backup object used to copy data */
409 if (backup_name
[0] == '/' || backup_name
[0] == '\\')
410 backup_db
= backup_db
.substr(1);
412 // ensure the ".db" extension is appended to the end
413 if (backup_db
.find(".db") != (backup_db
.length() - 3))
416 std::string backup_path
= host
+ backup_db
;
418 /* Open the database file identified by zFilename. Exit early if this fails
419 ** for any reason. */
420 rc
= sqlite3_open(backup_path
.c_str(), &pFile
);
423 pBackup
= sqlite3_backup_init(pFile
, "main", getHandle(), "main");
427 (void)sqlite3_backup_step(pBackup
, -1);
428 (void)sqlite3_backup_finish(pBackup
);
431 rc
= sqlite3_errcode(pFile
);
434 (void)sqlite3_close(pFile
);
437 throw DbErrors("Can't copy database. (%d)", rc
);
442 int SqliteDatabase::drop_analytics(void)
444 // SqliteDatabase::copy used a full database copy, so we have a new version
445 // with all the analytics stuff. We should clean database from everything but data
447 throw DbErrors("Can't drop extras database: no active connection...");
452 CLog::Log(LOGDEBUG
, "Cleaning indexes from database {} at {}", db
, host
);
453 snprintf(sqlcmd
, sizeof(sqlcmd
),
454 "SELECT name FROM sqlite_master WHERE type == 'index' AND sql IS NOT NULL");
455 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
456 return DB_UNEXPECTED_RESULT
;
458 for (size_t i
= 0; i
< res
.records
.size(); i
++)
460 snprintf(sqlcmd
, sizeof(sqlcmd
), "DROP INDEX '%s'",
461 res
.records
[i
]->at(0).get_asString().c_str());
462 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
463 return DB_UNEXPECTED_RESULT
;
467 CLog::Log(LOGDEBUG
, "Cleaning views from database {} at {}", db
, host
);
468 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT name FROM sqlite_master WHERE type == 'view'");
469 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
470 return DB_UNEXPECTED_RESULT
;
472 for (size_t i
= 0; i
< res
.records
.size(); i
++)
474 snprintf(sqlcmd
, sizeof(sqlcmd
), "DROP VIEW '%s'",
475 res
.records
[i
]->at(0).get_asString().c_str());
476 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
477 return DB_UNEXPECTED_RESULT
;
481 CLog::Log(LOGDEBUG
, "Cleaning triggers from database {} at {}", db
, host
);
482 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT name FROM sqlite_master WHERE type == 'trigger'");
483 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
484 return DB_UNEXPECTED_RESULT
;
486 for (size_t i
= 0; i
< res
.records
.size(); i
++)
488 snprintf(sqlcmd
, sizeof(sqlcmd
), "DROP TRIGGER '%s'",
489 res
.records
[i
]->at(0).get_asString().c_str());
490 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
491 return DB_UNEXPECTED_RESULT
;
493 // res would be cleared on destruct
495 return DB_COMMAND_OK
;
498 int SqliteDatabase::drop()
501 throw DbErrors("Can't drop database: no active connection...");
503 if (!unlink(db
.c_str()))
505 throw DbErrors("Can't drop database: can't unlink the file %s,\nError: %s", db
.c_str(),
508 return DB_COMMAND_OK
;
511 long SqliteDatabase::nextid(const char* sname
)
514 return DB_UNEXPECTED_RESULT
;
515 int id
; /*,nrow,ncol;*/
518 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT nextid FROM %s WHERE seq_name = '%s'",
519 sequence_table
.c_str(), sname
);
520 if ((last_err
= sqlite3_exec(getHandle(), sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
522 return DB_UNEXPECTED_RESULT
;
524 if (res
.records
.empty())
527 snprintf(sqlcmd
, sizeof(sqlcmd
), "INSERT INTO %s (nextid,seq_name) VALUES (%d,'%s')",
528 sequence_table
.c_str(), id
, sname
);
529 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
530 return DB_UNEXPECTED_RESULT
;
535 id
= res
.records
[0]->at(0).get_asInt() + 1;
536 snprintf(sqlcmd
, sizeof(sqlcmd
), "UPDATE %s SET nextid=%d WHERE seq_name = '%s'",
537 sequence_table
.c_str(), id
, sname
);
538 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
539 return DB_UNEXPECTED_RESULT
;
542 return DB_UNEXPECTED_RESULT
;
545 // methods for transactions
546 // ---------------------------------------------
547 void SqliteDatabase::start_transaction()
551 sqlite3_exec(conn
, "begin IMMEDIATE", NULL
, NULL
, NULL
);
552 _in_transaction
= true;
556 void SqliteDatabase::commit_transaction()
560 sqlite3_exec(conn
, "commit", NULL
, NULL
, NULL
);
561 _in_transaction
= false;
565 void SqliteDatabase::rollback_transaction()
569 sqlite3_exec(conn
, "rollback", NULL
, NULL
, NULL
);
570 _in_transaction
= false;
574 // methods for formatting
575 // ---------------------------------------------
576 std::string
SqliteDatabase::vprepare(const char* format
, va_list args
)
578 std::string strFormat
= format
;
579 std::string strResult
= "";
583 // %q is the sqlite format string for %s.
584 // Any bad character, like "'", will be replaced with a proper one
586 while ((pos
= strFormat
.find("%s", pos
)) != std::string::npos
)
587 strFormat
.replace(pos
++, 2, "%q");
589 // the %I64 enhancement is not supported by sqlite3_vmprintf
590 // must be %ll instead
592 while ((pos
= strFormat
.find("%I64", pos
)) != std::string::npos
)
593 strFormat
.replace(pos
++, 4, "%ll");
595 p
= sqlite3_vmprintf(strFormat
.c_str(), args
);
602 // Strip SEPARATOR from all GROUP_CONCAT statements:
603 // before: GROUP_CONCAT(field SEPARATOR '; ')
604 // after: GROUP_CONCAT(field, '; ')
605 // Can not specify separator when have DISTINCT, comma used by default
606 pos
= strResult
.find("GROUP_CONCAT(");
607 while (pos
!= std::string::npos
)
609 size_t pos2
= strResult
.find(" SEPARATOR ", pos
+ 1);
610 if (pos2
!= std::string::npos
)
611 strResult
.replace(pos2
, 10, ",");
612 pos
= strResult
.find("GROUP_CONCAT(", pos
+ 1);
614 // Replace CONCAT with || to concatenate text fields:
615 // before: CONCAT(field1, field2, field3)
616 // after: field1 || field2 || field3
617 // Avoid commas in substatements and within single quotes
618 // before: CONCAT(field1, ',', REPLACE(field2, ',', '-'), field3)
619 // after: field1 || ',' || REPLACE(field2, ',', '-') || field3
620 pos
= strResult
.find("CONCAT(");
621 while (pos
!= std::string::npos
)
623 if (pos
== 0 || strResult
[pos
- 1] == ' ') // Not GROUP_CONCAT
625 // Check each char for other bracket or single quote pairs
626 unsigned int brackets
= 1;
628 size_t index
= pos
+ 7; // start after "CONCAT("
629 while (index
< strResult
.size() && brackets
!= 0)
631 if (strResult
[index
] == '(')
633 else if (strResult
[index
] == ')')
637 strResult
.erase(index
, 1); //Remove closing bracket of CONCAT
639 else if (strResult
[index
] == '\'')
641 else if (strResult
[index
] == ',' && brackets
== 1 && !quoted
)
642 strResult
.replace(index
, 1, "||");
645 strResult
.erase(pos
, 7); //Remove "CONCAT("
647 pos
= strResult
.find("CONCAT(", pos
+ 1);
653 //************* SqliteDataset implementation ***************
655 SqliteDataset::SqliteDataset() : Dataset()
662 SqliteDataset::SqliteDataset(SqliteDatabase
* newDb
) : Dataset(newDb
)
669 SqliteDataset::~SqliteDataset()
673 void SqliteDataset::set_autorefresh(bool val
)
678 //--------- protected functions implementation -----------------//
680 sqlite3
* SqliteDataset::handle()
684 return static_cast<SqliteDatabase
*>(db
)->getHandle();
690 void SqliteDataset::make_query(StringList
& _sql
)
694 throw DbErrors("No Database Connection");
700 db
->start_transaction();
702 for (const std::string
& i
: _sql
)
706 Dataset::parse_sql(query
);
707 if (db
->setErr(sqlite3_exec(this->handle(), query
.c_str(), NULL
, NULL
, &err
),
708 query
.c_str()) != SQLITE_OK
)
710 std::string message
= db
->getErrorMsg();
713 message
.append(" (");
718 throw DbErrors("%s", message
.c_str());
722 if (db
->in_transaction() && autocommit
)
723 db
->commit_transaction();
733 if (db
->in_transaction())
734 db
->rollback_transaction();
739 void SqliteDataset::make_insert()
741 make_query(insert_sql
);
745 void SqliteDataset::make_edit()
747 make_query(update_sql
);
750 void SqliteDataset::make_deletion()
752 make_query(delete_sql
);
755 void SqliteDataset::fill_fields()
757 //cout <<"rr "<<result.records.size()<<"|" << frecno <<"\n";
758 if ((db
== NULL
) || (result
.record_header
.empty()) ||
759 (result
.records
.size() < (unsigned int)frecno
))
762 if (fields_object
->size() == 0) // Filling columns name
764 const unsigned int ncols
= result
.record_header
.size();
765 fields_object
->resize(ncols
);
766 for (unsigned int i
= 0; i
< ncols
; i
++)
768 (*fields_object
)[i
].props
= result
.record_header
[i
];
769 std::string name
= result
.record_header
[i
].name
;
770 name2indexMap
.insert({str_toLower(name
.data()), i
});
775 if (result
.records
.size() != 0)
777 const sql_record
* row
= result
.records
[frecno
];
780 const unsigned int ncols
= row
->size();
781 fields_object
->resize(ncols
);
782 for (unsigned int i
= 0; i
< ncols
; i
++)
783 (*fields_object
)[i
].val
= row
->at(i
);
787 const unsigned int ncols
= result
.record_header
.size();
788 fields_object
->resize(ncols
);
789 for (unsigned int i
= 0; i
< ncols
; i
++)
790 (*fields_object
)[i
].val
= "";
793 //------------- public functions implementation -----------------//
794 bool SqliteDataset::dropIndex(const char* table
, const char* index
)
798 sql
= static_cast<SqliteDatabase
*>(db
)->prepare("DROP INDEX IF EXISTS %s", index
);
800 return (exec(sql
) == SQLITE_OK
);
803 int SqliteDataset::exec(const std::string
& sql
)
806 throw DbErrors("No Database Connection");
807 std::string qry
= sql
;
811 // Strip size constraints from indexes (not supported in sqlite)
814 // before: CREATE UNIQUE INDEX ixPath ON path ( strPath(255) )
815 // after: CREATE UNIQUE INDEX ixPath ON path ( strPath )
817 // NOTE: unexpected results occur if brackets are not matched
818 if (qry
.find("CREATE UNIQUE INDEX") != std::string::npos
||
819 (qry
.find("CREATE INDEX") != std::string::npos
))
824 if ((pos
= qry
.find('(')) != std::string::npos
)
827 while ((pos
= qry
.find('(', pos
)) != std::string::npos
)
829 if ((pos2
= qry
.find(')', pos
)) != std::string::npos
)
831 qry
.replace(pos
, pos2
- pos
+ 1, "");
837 // Strip ON table from DROP INDEX statements:
838 // before: DROP INDEX foo ON table
839 // after: DROP INDEX foo
840 size_t pos
= qry
.find("DROP INDEX ");
841 if (pos
!= std::string::npos
)
843 pos
= qry
.find(" ON ", pos
+ 1);
845 if (pos
!= std::string::npos
)
850 if ((res
= db
->setErr(sqlite3_exec(handle(), qry
.c_str(), &callback
, &exec_res
, &errmsg
),
851 qry
.c_str())) == SQLITE_OK
)
857 DbErrors
err("%s (%s)", db
->getErrorMsg(), errmsg
);
858 sqlite3_free(errmsg
);
863 throw DbErrors("%s", db
->getErrorMsg());
868 int SqliteDataset::exec()
873 const void* SqliteDataset::getExecRes()
878 bool SqliteDataset::query(const std::string
& query
)
881 throw DbErrors("No Database Connection");
882 const std::string
& qry
= query
;
883 int fs
= qry
.find("select");
884 int fS
= qry
.find("SELECT");
885 if (!(fs
>= 0 || fS
>= 0))
886 throw DbErrors("MUST be select SQL!");
890 sqlite3_stmt
* stmt
= NULL
;
891 if (db
->setErr(sqlite3_prepare_v2(handle(), query
.c_str(), -1, &stmt
, NULL
), query
.c_str()) !=
893 throw DbErrors("%s", db
->getErrorMsg());
896 const unsigned int numColumns
= sqlite3_column_count(stmt
);
897 result
.record_header
.resize(numColumns
);
898 for (unsigned int i
= 0; i
< numColumns
; i
++)
899 result
.record_header
[i
].name
= sqlite3_column_name(stmt
, i
);
902 while (sqlite3_step(stmt
) == SQLITE_ROW
)
903 { // have a row of data
904 sql_record
* res
= new sql_record
;
905 res
->resize(numColumns
);
906 for (unsigned int i
= 0; i
< numColumns
; i
++)
908 field_value
& v
= res
->at(i
);
909 switch (sqlite3_column_type(stmt
, i
))
912 v
.set_asInt64(sqlite3_column_int64(stmt
, i
));
915 v
.set_asDouble(sqlite3_column_double(stmt
, i
));
918 v
.set_asString(reinterpret_cast<const char*>(sqlite3_column_text(stmt
, i
)),
919 sqlite3_column_bytes(stmt
, i
));
922 v
.set_asString(reinterpret_cast<const char*>(sqlite3_column_text(stmt
, i
)),
923 sqlite3_column_bytes(stmt
, i
));
932 result
.records
.push_back(res
);
934 if (db
->setErr(sqlite3_finalize(stmt
), query
.c_str()) == SQLITE_OK
)
943 throw DbErrors("%s", db
->getErrorMsg());
947 void SqliteDataset::open(const std::string
& sql
)
953 void SqliteDataset::open()
955 if (select_sql
.size())
961 ds_state
= dsInactive
;
965 void SqliteDataset::close()
969 edit_object
->clear();
970 fields_object
->clear();
971 ds_state
= dsInactive
;
975 void SqliteDataset::cancel()
977 if ((ds_state
== dsInsert
) || (ds_state
== dsEdit
))
979 if (result
.record_header
.size())
982 ds_state
= dsInactive
;
986 int SqliteDataset::num_rows()
988 return result
.records
.size();
991 bool SqliteDataset::eof()
996 bool SqliteDataset::bof()
1001 void SqliteDataset::first()
1004 this->fill_fields();
1007 void SqliteDataset::last()
1013 void SqliteDataset::prev(void)
1019 void SqliteDataset::next(void)
1026 void SqliteDataset::free_row(void)
1028 if (frecno
< 0 || (unsigned int)frecno
>= result
.records
.size())
1031 sql_record
* row
= result
.records
[frecno
];
1035 result
.records
[frecno
] = NULL
;
1039 bool SqliteDataset::seek(int pos
)
1041 if (ds_state
== dsSelect
)
1050 int64_t SqliteDataset::lastinsertid()
1053 throw DbErrors("No Database Connection");
1054 return sqlite3_last_insert_rowid(handle());
1057 long SqliteDataset::nextid(const char* seq_name
)
1060 return db
->nextid(seq_name
);
1062 return DB_UNEXPECTED_RESULT
;
1065 void SqliteDataset::interrupt()
1067 sqlite3_interrupt(handle());
1069 } // namespace dbiplus