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::postconnect()
393 throw DbErrors("Cannot execute postconnect actions: no active connection...");
395 const std::string cmd
{
396 "PRAGMA cache_size=4096; PRAGMA synchronous='NORMAL'; PRAGMA count_changes='OFF';"};
398 if (setErr(sqlite3_exec(getHandle(), cmd
.c_str(), NULL
, NULL
, NULL
), cmd
.c_str()) != SQLITE_OK
)
400 throw DbErrors("%s", getErrorMsg());
403 return DB_COMMAND_OK
;
406 int SqliteDatabase::create()
408 return connect(true);
411 int SqliteDatabase::copy(const char* backup_name
)
414 throw DbErrors("Can't copy database: no active connection...");
416 CLog::LogF(LOGDEBUG
, "Copying from {} to {} at {}", db
, backup_name
, host
);
419 std::string backup_db
= backup_name
;
421 sqlite3
* pFile
; /* Database connection opened on zFilename */
422 sqlite3_backup
* pBackup
; /* Backup object used to copy data */
425 if (backup_name
[0] == '/' || backup_name
[0] == '\\')
426 backup_db
= backup_db
.substr(1);
428 // ensure the ".db" extension is appended to the end
429 if (backup_db
.find(".db") != (backup_db
.length() - 3))
432 std::string backup_path
= host
+ backup_db
;
434 /* Open the database file identified by zFilename. Exit early if this fails
435 ** for any reason. */
436 rc
= sqlite3_open(backup_path
.c_str(), &pFile
);
439 pBackup
= sqlite3_backup_init(pFile
, "main", getHandle(), "main");
443 (void)sqlite3_backup_step(pBackup
, -1);
444 (void)sqlite3_backup_finish(pBackup
);
447 rc
= sqlite3_errcode(pFile
);
450 (void)sqlite3_close(pFile
);
453 throw DbErrors("Can't copy database. (%d)", rc
);
458 int SqliteDatabase::drop_analytics(void)
460 // SqliteDatabase::copy used a full database copy, so we have a new version
461 // with all the analytics stuff. We should clean database from everything but data
463 throw DbErrors("Can't drop extras database: no active connection...");
468 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "Cleaning indexes from database {} at {}", db
, host
);
469 snprintf(sqlcmd
, sizeof(sqlcmd
),
470 "SELECT name FROM sqlite_master WHERE type == 'index' AND sql IS NOT NULL");
471 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
472 return DB_UNEXPECTED_RESULT
;
474 for (size_t i
= 0; i
< res
.records
.size(); i
++)
476 snprintf(sqlcmd
, sizeof(sqlcmd
), "DROP INDEX '%s'",
477 res
.records
[i
]->at(0).get_asString().c_str());
478 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
479 return DB_UNEXPECTED_RESULT
;
483 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "Cleaning views from database {} at {}", db
, host
);
484 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT name FROM sqlite_master WHERE type == 'view'");
485 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
486 return DB_UNEXPECTED_RESULT
;
488 for (size_t i
= 0; i
< res
.records
.size(); i
++)
490 snprintf(sqlcmd
, sizeof(sqlcmd
), "DROP VIEW '%s'",
491 res
.records
[i
]->at(0).get_asString().c_str());
492 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
493 return DB_UNEXPECTED_RESULT
;
497 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "Cleaning triggers from database {} at {}", db
, host
);
498 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT name FROM sqlite_master WHERE type == 'trigger'");
499 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
500 return DB_UNEXPECTED_RESULT
;
502 for (size_t i
= 0; i
< res
.records
.size(); i
++)
504 snprintf(sqlcmd
, sizeof(sqlcmd
), "DROP TRIGGER '%s'",
505 res
.records
[i
]->at(0).get_asString().c_str());
506 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
507 return DB_UNEXPECTED_RESULT
;
509 // res would be cleared on destruct
511 return DB_COMMAND_OK
;
514 int SqliteDatabase::drop()
517 throw DbErrors("Can't drop database: no active connection...");
519 if (!unlink(db
.c_str()))
521 throw DbErrors("Can't drop database: can't unlink the file %s,\nError: %s", db
.c_str(),
524 return DB_COMMAND_OK
;
527 long SqliteDatabase::nextid(const char* sname
)
529 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "nextid for {}", sname
);
532 return DB_UNEXPECTED_RESULT
;
533 int id
; /*,nrow,ncol;*/
536 snprintf(sqlcmd
, sizeof(sqlcmd
), "SELECT nextid FROM %s WHERE seq_name = '%s'",
537 sequence_table
.c_str(), sname
);
538 if ((last_err
= sqlite3_exec(getHandle(), sqlcmd
, &callback
, &res
, NULL
)) != SQLITE_OK
)
540 return DB_UNEXPECTED_RESULT
;
542 if (res
.records
.empty())
545 snprintf(sqlcmd
, sizeof(sqlcmd
), "INSERT INTO %s (nextid,seq_name) VALUES (%d,'%s')",
546 sequence_table
.c_str(), id
, sname
);
547 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
548 return DB_UNEXPECTED_RESULT
;
553 id
= res
.records
[0]->at(0).get_asInt() + 1;
554 snprintf(sqlcmd
, sizeof(sqlcmd
), "UPDATE %s SET nextid=%d WHERE seq_name = '%s'",
555 sequence_table
.c_str(), id
, sname
);
556 if ((last_err
= sqlite3_exec(conn
, sqlcmd
, NULL
, NULL
, NULL
)) != SQLITE_OK
)
557 return DB_UNEXPECTED_RESULT
;
560 return DB_UNEXPECTED_RESULT
;
563 // methods for transactions
564 // ---------------------------------------------
565 void SqliteDatabase::start_transaction()
569 assert(!_in_transaction
);
570 sqlite3_exec(conn
, "begin IMMEDIATE", NULL
, NULL
, NULL
);
571 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "Sqlite start transaction");
574 CLog::LogF(LOGERROR
, "error: nested transactions are not supported.");
576 _in_transaction
= true;
580 void SqliteDatabase::commit_transaction()
584 assert(_in_transaction
);
585 sqlite3_exec(conn
, "commit", NULL
, NULL
, NULL
);
586 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "Sqlite commit transaction");
587 _in_transaction
= false;
591 void SqliteDatabase::rollback_transaction()
595 assert(_in_transaction
);
596 sqlite3_exec(conn
, "rollback", NULL
, NULL
, NULL
);
597 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "Sqlite rollback transaction");
598 _in_transaction
= false;
602 // methods for formatting
603 // ---------------------------------------------
604 std::string
SqliteDatabase::vprepare(const char* format
, va_list args
)
606 std::string strFormat
= format
;
607 std::string strResult
= "";
611 // %q is the sqlite format string for %s.
612 // Any bad character, like "'", will be replaced with a proper one
614 while ((pos
= strFormat
.find("%s", pos
)) != std::string::npos
)
615 strFormat
.replace(pos
++, 2, "%q");
617 // the %I64 enhancement is not supported by sqlite3_vmprintf
618 // must be %ll instead
620 while ((pos
= strFormat
.find("%I64", pos
)) != std::string::npos
)
621 strFormat
.replace(pos
++, 4, "%ll");
623 p
= sqlite3_vmprintf(strFormat
.c_str(), args
);
630 // Strip SEPARATOR from all GROUP_CONCAT statements:
631 // before: GROUP_CONCAT(field SEPARATOR '; ')
632 // after: GROUP_CONCAT(field, '; ')
633 // Can not specify separator when have DISTINCT, comma used by default
634 pos
= strResult
.find("GROUP_CONCAT(");
635 while (pos
!= std::string::npos
)
637 size_t pos2
= strResult
.find(" SEPARATOR ", pos
+ 1);
638 if (pos2
!= std::string::npos
)
639 strResult
.replace(pos2
, 10, ",");
640 pos
= strResult
.find("GROUP_CONCAT(", pos
+ 1);
642 // Replace CONCAT with || to concatenate text fields:
643 // before: CONCAT(field1, field2, field3)
644 // after: field1 || field2 || field3
645 // Avoid commas in substatements and within single quotes
646 // before: CONCAT(field1, ',', REPLACE(field2, ',', '-'), field3)
647 // after: field1 || ',' || REPLACE(field2, ',', '-') || field3
648 pos
= strResult
.find("CONCAT(");
649 while (pos
!= std::string::npos
)
651 if (pos
== 0 || strResult
[pos
- 1] == ' ') // Not GROUP_CONCAT
653 // Check each char for other bracket or single quote pairs
654 unsigned int brackets
= 1;
656 size_t index
= pos
+ 7; // start after "CONCAT("
657 while (index
< strResult
.size() && brackets
!= 0)
659 if (strResult
[index
] == '(')
661 else if (strResult
[index
] == ')')
665 strResult
.erase(index
, 1); //Remove closing bracket of CONCAT
667 else if (strResult
[index
] == '\'')
669 else if (strResult
[index
] == ',' && brackets
== 1 && !quoted
)
670 strResult
.replace(index
, 1, "||");
673 strResult
.erase(pos
, 7); //Remove "CONCAT("
675 pos
= strResult
.find("CONCAT(", pos
+ 1);
681 //************* SqliteDataset implementation ***************
683 SqliteDataset::SqliteDataset() : Dataset()
690 SqliteDataset::SqliteDataset(SqliteDatabase
* newDb
) : Dataset(newDb
)
697 SqliteDataset::~SqliteDataset()
701 void SqliteDataset::set_autorefresh(bool val
)
706 //--------- protected functions implementation -----------------//
708 sqlite3
* SqliteDataset::handle()
712 return static_cast<SqliteDatabase
*>(db
)->getHandle();
718 void SqliteDataset::make_query(StringList
& _sql
)
722 throw DbErrors("No Database Connection");
728 db
->start_transaction();
730 for (const std::string
& i
: _sql
)
734 Dataset::parse_sql(query
);
735 if (db
->setErr(sqlite3_exec(this->handle(), query
.c_str(), NULL
, NULL
, &err
),
736 query
.c_str()) != SQLITE_OK
)
738 std::string message
= db
->getErrorMsg();
741 message
.append(" (");
746 throw DbErrors("%s", message
.c_str());
750 if (db
->in_transaction() && autocommit
)
751 db
->commit_transaction();
761 if (db
->in_transaction())
762 db
->rollback_transaction();
767 void SqliteDataset::make_insert()
769 make_query(insert_sql
);
773 void SqliteDataset::make_edit()
775 make_query(update_sql
);
778 void SqliteDataset::make_deletion()
780 make_query(delete_sql
);
783 void SqliteDataset::fill_fields()
785 //cout <<"rr "<<result.records.size()<<"|" << frecno <<"\n";
786 if ((db
== NULL
) || (result
.record_header
.empty()) ||
787 (result
.records
.size() < (unsigned int)frecno
))
790 if (fields_object
->size() == 0) // Filling columns name
792 const unsigned int ncols
= result
.record_header
.size();
793 fields_object
->resize(ncols
);
794 for (unsigned int i
= 0; i
< ncols
; i
++)
796 (*fields_object
)[i
].props
= result
.record_header
[i
];
797 std::string name
= result
.record_header
[i
].name
;
798 name2indexMap
.insert({str_toLower(name
.data()), i
});
803 if (result
.records
.size() != 0)
805 const sql_record
* row
= result
.records
[frecno
];
808 const unsigned int ncols
= row
->size();
809 fields_object
->resize(ncols
);
810 for (unsigned int i
= 0; i
< ncols
; i
++)
811 (*fields_object
)[i
].val
= row
->at(i
);
815 const unsigned int ncols
= result
.record_header
.size();
816 fields_object
->resize(ncols
);
817 for (unsigned int i
= 0; i
< ncols
; i
++)
818 (*fields_object
)[i
].val
= "";
821 //------------- public functions implementation -----------------//
822 bool SqliteDataset::dropIndex(const char* table
, const char* index
)
826 sql
= static_cast<SqliteDatabase
*>(db
)->prepare("DROP INDEX IF EXISTS %s", index
);
828 return (exec(sql
) == SQLITE_OK
);
831 int SqliteDataset::exec(const std::string
& sql
)
834 throw DbErrors("No Database Connection");
835 std::string qry
= sql
;
839 // Strip size constraints from indexes (not supported in sqlite)
842 // before: CREATE UNIQUE INDEX ixPath ON path ( strPath(255) )
843 // after: CREATE UNIQUE INDEX ixPath ON path ( strPath )
845 // NOTE: unexpected results occur if brackets are not matched
846 if (qry
.find("CREATE UNIQUE INDEX") != std::string::npos
||
847 (qry
.find("CREATE INDEX") != std::string::npos
))
852 if ((pos
= qry
.find('(')) != std::string::npos
)
855 while ((pos
= qry
.find('(', pos
)) != std::string::npos
)
857 if ((pos2
= qry
.find(')', pos
)) != std::string::npos
)
859 qry
.replace(pos
, pos2
- pos
+ 1, "");
865 // Strip ON table from DROP INDEX statements:
866 // before: DROP INDEX foo ON table
867 // after: DROP INDEX foo
868 size_t pos
= qry
.find("DROP INDEX ");
869 if (pos
!= std::string::npos
)
871 pos
= qry
.find(" ON ", pos
+ 1);
873 if (pos
!= std::string::npos
)
877 const auto start
= std::chrono::steady_clock::now();
881 db
->setErr(sqlite3_exec(handle(), qry
.c_str(), &callback
, &exec_res
, &errmsg
), qry
.c_str());
883 const auto end
= std::chrono::steady_clock::now();
884 const auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
886 CLog::LogFC(LOGDEBUG
, LOGDATABASE
, "{} ms for query: {}", duration
.count(), qry
);
888 if (res
== SQLITE_OK
)
896 DbErrors
err("%s (%s)", db
->getErrorMsg(), errmsg
);
897 sqlite3_free(errmsg
);
902 throw DbErrors("%s", db
->getErrorMsg());
907 int SqliteDataset::exec()
912 const void* SqliteDataset::getExecRes()
917 bool SqliteDataset::query(const std::string
& query
)
920 throw DbErrors("No Database Connection");
921 const std::string
& qry
= query
;
922 int fs
= qry
.find("select");
923 int fS
= qry
.find("SELECT");
924 if (!(fs
>= 0 || fS
>= 0))
925 throw DbErrors("MUST be select SQL!");
929 sqlite3_stmt
* stmt
= NULL
;
930 if (db
->setErr(sqlite3_prepare_v2(handle(), query
.c_str(), -1, &stmt
, NULL
), query
.c_str()) !=
932 throw DbErrors("%s", db
->getErrorMsg());
935 const unsigned int numColumns
= sqlite3_column_count(stmt
);
936 result
.record_header
.resize(numColumns
);
937 for (unsigned int i
= 0; i
< numColumns
; i
++)
938 result
.record_header
[i
].name
= sqlite3_column_name(stmt
, i
);
941 while (sqlite3_step(stmt
) == SQLITE_ROW
)
942 { // have a row of data
943 sql_record
* res
= new sql_record
;
944 res
->resize(numColumns
);
945 for (unsigned int i
= 0; i
< numColumns
; i
++)
947 field_value
& v
= res
->at(i
);
948 switch (sqlite3_column_type(stmt
, i
))
951 v
.set_asInt64(sqlite3_column_int64(stmt
, i
));
954 v
.set_asDouble(sqlite3_column_double(stmt
, i
));
957 v
.set_asString(reinterpret_cast<const char*>(sqlite3_column_text(stmt
, i
)),
958 sqlite3_column_bytes(stmt
, i
));
961 v
.set_asString(reinterpret_cast<const char*>(sqlite3_column_text(stmt
, i
)),
962 sqlite3_column_bytes(stmt
, i
));
971 result
.records
.push_back(res
);
973 if (db
->setErr(sqlite3_finalize(stmt
), query
.c_str()) == SQLITE_OK
)
982 throw DbErrors("%s", db
->getErrorMsg());
986 void SqliteDataset::open(const std::string
& sql
)
992 void SqliteDataset::open()
994 if (select_sql
.size())
1000 ds_state
= dsInactive
;
1004 void SqliteDataset::close()
1008 edit_object
->clear();
1009 fields_object
->clear();
1010 ds_state
= dsInactive
;
1014 void SqliteDataset::cancel()
1016 if ((ds_state
== dsInsert
) || (ds_state
== dsEdit
))
1018 if (result
.record_header
.size())
1019 ds_state
= dsSelect
;
1021 ds_state
= dsInactive
;
1025 int SqliteDataset::num_rows()
1027 return result
.records
.size();
1030 bool SqliteDataset::eof()
1035 bool SqliteDataset::bof()
1040 void SqliteDataset::first()
1043 this->fill_fields();
1046 void SqliteDataset::last()
1052 void SqliteDataset::prev(void)
1058 void SqliteDataset::next(void)
1065 void SqliteDataset::free_row(void)
1067 if (frecno
< 0 || (unsigned int)frecno
>= result
.records
.size())
1070 sql_record
* row
= result
.records
[frecno
];
1074 result
.records
[frecno
] = NULL
;
1078 bool SqliteDataset::seek(int pos
)
1080 if (ds_state
== dsSelect
)
1089 int64_t SqliteDataset::lastinsertid()
1092 throw DbErrors("No Database Connection");
1093 return sqlite3_last_insert_rowid(handle());
1096 long SqliteDataset::nextid(const char* seq_name
)
1099 return db
->nextid(seq_name
);
1101 return DB_UNEXPECTED_RESULT
;
1104 void SqliteDataset::interrupt()
1106 sqlite3_interrupt(handle());
1108 } // namespace dbiplus