Documented musik::core::db.

Upgraded to SQLite 3.5.9 to fix previous problem with LEFT OUTER JOIN.
Changed PRAGMA page_size to be equal to NTFS page size. Should give more speed when writing to database.
This commit is contained in:
Daniel Önnerby 2008-05-15 06:37:49 +00:00
parent 05488a5bd7
commit b82e3af11c
11 changed files with 2407 additions and 1438 deletions

View File

@ -30,7 +30,7 @@
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.305 2008/04/16 00:28:14 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.312 2008/05/12 12:39:56 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@ -93,8 +93,8 @@ extern "C" {
** with the value (X*1000000 + Y*1000 + Z) where X, Y, and
** Z are the major version, minor version, and release number.
*/
#define SQLITE_VERSION "3.5.8"
#define SQLITE_VERSION_NUMBER 3005008
#define SQLITE_VERSION "3.5.9"
#define SQLITE_VERSION_NUMBER 3005009
/*
** CAPI3REF: Run-Time Library Version Numbers {F10020}
@ -155,7 +155,7 @@ int sqlite3_threadsafe(void);
/*
** CAPI3REF: Database Connection Handle {F12000}
** KEYWORDS: {database connection}
** KEYWORDS: {database connection} {database connections}
**
** Each open SQLite database is represented by pointer to an instance of the
** opaque structure named "sqlite3". It is useful to think of an sqlite3
@ -1984,7 +1984,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** The third options is behavior that is always used for [sqlite3_open()]
** and [sqlite3_open16()].
**
** If the 4th parameter to [sqlite3_open_v2()] is not one of the
** If the 3rd parameter to [sqlite3_open_v2()] is not one of the
** combinations shown above then the behavior is undefined.
**
** If the filename is ":memory:", then an private
@ -2205,11 +2205,6 @@ typedef struct sqlite3_stmt sqlite3_stmt;
** to the lessor of V and the hard upper bound on the size
** of C that is set at compile-time.
**
** {F12764} A successful call to [sqlite3_limit(D,C,V)] where V is zero
** changes the limit on the size of construct C in
** [database connection] D to be the hard upper bound on the size
** of C that is set at compile-time.
**
** {F12766} A successful call to [sqlite3_limit(D,C,V)] where V is negative
** leaves the state of [database connection] D unchanged.
**
@ -2684,11 +2679,12 @@ int sqlite3_bind_parameter_count(sqlite3_stmt*);
**
** This routine returns a pointer to the name of the n-th
** SQL parameter in a [prepared statement].
** SQL parameters of the form ":AAA" or "@AAA" or "$AAA" have a name
** which is the string ":AAA" or "@AAA" or "$VVV".
** In other words, the initial ":" or "$" or "@"
** SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA"
** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA"
** respectively.
** In other words, the initial ":" or "$" or "@" or "?"
** is included as part of the name.
** Parameters of the form "?" or "?NNN" have no name.
** Parameters of the form "?" without a following integer have no name.
**
** The first host parameter has an index of 1, not 0.
**
@ -2708,8 +2704,7 @@ int sqlite3_bind_parameter_count(sqlite3_stmt*);
** a UTF-8 rendering of the name of the SQL parameter in
** [prepared statement] S having index N, or
** NULL if there is no SQL parameter with index N or if the
** parameter with index N is an anonymous parameter "?" or
** a numbered parameter "?NNN".
** parameter with index N is an anonymous parameter "?".
*/
const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
@ -3006,7 +3001,7 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** new "v2" interface is recommended for new applications but the legacy
** interface will continue to be supported.
**
** In the lagacy interface, the return value will be either [SQLITE_BUSY],
** In the legacy interface, the return value will be either [SQLITE_BUSY],
** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
** With the "v2" interface, any of the other [SQLITE_OK | result code]
** or [SQLITE_IOERR_READ | extended result code] might be returned as
@ -4051,7 +4046,7 @@ typedef void (*sqlite3_destructor_type)(void*);
**
** {F16436} The [sqlite3_result_text(C,V,N,D)] interface changes the
** return value of function C to be the UTF8 string
** V up the first zero if N is negative
** V up to the first zero if N is negative
** or the first N bytes of V if N is non-negative.
**
** {F16439} The [sqlite3_result_text16(C,V,N,D)] interface changes the
@ -5276,10 +5271,11 @@ int sqlite3_blob_bytes(sqlite3_blob *);
** the [sqlite3_blob_read(P,Z,N,X)] interface returns an
** appropriate [error code] or [extended error code].
**
** {F17868} If an error occurs during evaluation of [sqlite3_blob_read(D,...)]
** {F17868} If an error occurs during evaluation of [sqlite3_blob_read(P,...)]
** then subsequent calls to [sqlite3_errcode(D)],
** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] will return
** information approprate for that error.
** information approprate for that error, where D is the
** database handle that was used to open blob handle P.
*/
int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);

File diff suppressed because it is too large Load Diff

View File

@ -43,11 +43,30 @@
using namespace musik::core::db;
//////////////////////////////////////////
///\brief
///Constructor
///
///\param sql
///SQL
///
///\param connection
///Connection to execute the statement on
//////////////////////////////////////////
CachedStatement::CachedStatement(const char* sql,Connection &connection) : Statement(connection){
this->sqlStatement.assign(sql);
this->stmt = this->connection->GetCachedStatement(sql);
}
//////////////////////////////////////////
///\brief
///Destructor
///
///Will return the cached statement to the Connection
///
///\see
///musik::core::db::Connection::ReturnCachedStatement
//////////////////////////////////////////
CachedStatement::~CachedStatement(){
sqlite3_reset(this->stmt);
sqlite3_clear_bindings(this->stmt);

View File

@ -40,10 +40,27 @@
#include "core/db/Statement.h"
namespace musik{ namespace core{ namespace db{
// Forward declare
class Statement;
class CachedStatement : public Statement{
//////////////////////////////////////////
///\brief
///Same as Statement, but keeps the statement in a cache when destructed
///
///Be careful using this class only on "static" SQL statement
///This means that you should not use this on statements looking like
///this: CacheStatement("SELECT * FROM mytable WHERE id="+id,db);
///since the cache will grow for each id in this example
///
///Instead, do like this:
///CacheStatement("SELECT * FROM mytable WHERE id=?",db);
///And use the BindInt to set the id
///
///\see
///musik::core::db::Statement
//////////////////////////////////////////
class CachedStatement : public Statement{
public:
CachedStatement(const char* sql,Connection &connection);
~CachedStatement();

View File

@ -40,15 +40,41 @@
using namespace musik::core::db;
//////////////////////////////////////////
///\brief
///Constructor
//////////////////////////////////////////
Connection::Connection() : connection(NULL),transactionCounter(0) {
}
//////////////////////////////////////////
///\brief
///Destructor
///
///Will automatically close the connection if it's not closed before
//////////////////////////////////////////
Connection::~Connection(){
this->Close();
}
//////////////////////////////////////////
///\brief
///Open a connection to the database
///
///\param database
///Connection string. In SQLite this is the filename
///
///\param options
///Bit options. Unused at the moment
///
///\param cache
///Cachesize in KB
///
///\returns
///Error code returned by SQLite
//////////////////////////////////////////
int Connection::Open(const utfchar *database,unsigned int options,unsigned int cache){
int error;
#ifdef UTF_WIDECHAR
@ -63,6 +89,22 @@ int Connection::Open(const utfchar *database,unsigned int options,unsigned int c
return error;
}
//////////////////////////////////////////
///\brief
///Open a connection to the database
///
///\param database
///Connection string. In SQLite this is the filename
///
///\param options
///Bit options. Unused at the moment
///
///\param cache
///Cachesize in KB
///
///\returns
///Error code returned by SQLite
//////////////////////////////////////////
int Connection::Open(const utfstring &database,unsigned int options,unsigned int cache){
int error;
#ifdef UTF_WIDECHAR
@ -79,6 +121,13 @@ int Connection::Open(const utfstring &database,unsigned int options,unsigned int
//////////////////////////////////////////
///\brief
///Close connection to the database
///
///\returns
///Errorcode ( musik::core::db::ReturnCode )
//////////////////////////////////////////
int Connection::Close(){
// Clear the cache
@ -96,6 +145,19 @@ int Connection::Close(){
}
//////////////////////////////////////////
///\brief
///Execute a SQL string
///
///\param sql
///SQL to execute
///
///\returns
///Errorcode musik::core::db::ReturnCode
///
///\see
///musik::core::db::ReturnCode
//////////////////////////////////////////
int Connection::Execute(const char* sql){
sqlite3_stmt *stmt = NULL;
if(sqlite3_prepare_v2(this->connection,sql,-1,&stmt,NULL)!=SQLITE_OK){
@ -116,6 +178,19 @@ int Connection::Execute(const char* sql){
}
//////////////////////////////////////////
///\brief
///Execute a SQL string
///
///\param sql
///SQL to execute
///
///\returns
///Errorcode musik::core::db::ReturnCode
///
///\see
///musik::core::db::ReturnCode
//////////////////////////////////////////
int Connection::Execute(const wchar_t* sql){
sqlite3_stmt *stmt = NULL;
if(sqlite3_prepare16_v2(this->connection,sql,-1,&stmt,NULL)!=SQLITE_OK){
@ -136,32 +211,69 @@ int Connection::Execute(const wchar_t* sql){
}
//////////////////////////////////////////
///\brief
///Get the last inserted row ID
///
///\returns
///Last inserted row ID
///
///\see
///http://www.sqlite.org/c3ref/last_insert_rowid.html
//////////////////////////////////////////
int Connection::LastInsertedId(){
return sqlite3_last_insert_rowid(this->connection);
}
//////////////////////////////////////////
///\brief
///Initializes the database.
///
///\param cache
///Size of the cache to use in kilobytes
///
///This will set all the initial PRAGMAS
//////////////////////////////////////////
void Connection::Initialize(unsigned int cache){
sqlite3_busy_timeout(this->connection,10000);
sqlite3_exec(this->connection,"PRAGMA synchronous=OFF",NULL,NULL,NULL); // Maybe FULL if multiuser document
sqlite3_exec(this->connection,"PRAGMA page_size=1024",NULL,NULL,NULL); // According to windows standard page size
sqlite3_exec(this->connection,"PRAGMA auto_vacuum=0",NULL,NULL,NULL); // No autovaccum.
sqlite3_exec(this->connection,"PRAGMA synchronous=OFF",NULL,NULL,NULL); // Not a critical DB. Sync set to OFF
sqlite3_exec(this->connection,"PRAGMA page_size=4096",NULL,NULL,NULL); // According to windows standard page size
sqlite3_exec(this->connection,"PRAGMA auto_vacuum=0",NULL,NULL,NULL); // No autovaccum.
if(cache==0){
cache=4096;
cache=1024; // Default cache set to 4Mb
}else{
// Divide by 4 to since the page_size is 4096
// Total cache is the same as page_size*cache_size
cache = cache/4;
}
std::string cacheSize("PRAGMA cache_size=" + boost::lexical_cast<std::string>(cache));
sqlite3_exec(this->connection,cacheSize.c_str(),NULL,NULL,NULL); // size * 1.5kb = 6Mb cache
sqlite3_exec(this->connection,"PRAGMA case_sensitive_like=0",NULL,NULL,NULL); // More speed if case insensitive
sqlite3_exec(this->connection,"PRAGMA count_changes=0",NULL,NULL,NULL); // If set it counts changes on SQL UPDATE. More speed when not.
sqlite3_exec(this->connection,"PRAGMA legacy_file_format=OFF",NULL,NULL,NULL); // Set compatible with ALL sqlite 3.x versions
sqlite3_exec(this->connection,"PRAGMA temp_store=MEMORY",NULL,NULL,NULL); // MEMORY, not file. More speed.
sqlite3_exec(this->connection,"PRAGMA case_sensitive_like=0",NULL,NULL,NULL); // More speed if case insensitive
sqlite3_exec(this->connection,"PRAGMA count_changes=0",NULL,NULL,NULL); // If set it counts changes on SQL UPDATE. More speed when not.
sqlite3_exec(this->connection,"PRAGMA legacy_file_format=OFF",NULL,NULL,NULL); // No reason to be backwards compatible :)
sqlite3_exec(this->connection,"PRAGMA temp_store=MEMORY",NULL,NULL,NULL); // MEMORY, not file. More speed.
}
//////////////////////////////////////////
///\brief
///Internal method used by the CachedStatement to locate if a statement already exists
///
///\param sql
///SQL to check for
///
///\returns
///The cached or newly created statement
///
///\see
///musik::core::db::CachedStatment
//////////////////////////////////////////
sqlite3_stmt *Connection::GetCachedStatement(const char* sql){
sqlite3_stmt *newStmt(NULL);
@ -184,6 +296,19 @@ sqlite3_stmt *Connection::GetCachedStatement(const char* sql){
}
//////////////////////////////////////////
///\brief
///Used by CachedStatement when destructed to return it's statement.
///
///\param sql
///SQL string
///
///\param stmt
///Statement to return
///
///\see
///musik::core::db::CachedStatment
//////////////////////////////////////////
void Connection::ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt){
StatementCache::iterator cacheStmt = this->cachedStatements.find(sql);
if(cacheStmt==this->cachedStatements.end()){
@ -195,6 +320,10 @@ void Connection::ReturnCachedStatement(const char* sql,sqlite3_stmt *stmt){
}
}
//////////////////////////////////////////
///\brief
///Interrupts the current running statement(s)
//////////////////////////////////////////
void Connection::Interrupt(){
sqlite3_interrupt(this->connection);
}

View File

@ -48,7 +48,13 @@
namespace musik{ namespace core{ namespace db{
class Connection : boost::noncopyable{
//////////////////////////////////////////
///\brief
///Database Wrapper
///
///A Connection to the database
//////////////////////////////////////////
class Connection : boost::noncopyable{
public:
Connection();
~Connection();

View File

@ -41,27 +41,50 @@
using namespace musik::core::db;
//////////////////////////////////////////
///\brief
///Constructor
///
///\param connection
///Connection to run transaction on
//////////////////////////////////////////
ScopedTransaction::ScopedTransaction(Connection &connection) : canceled(false){
this->connection = &connection;
this->Begin();
}
//////////////////////////////////////////
///\brief
///Destructor will end the transaction if it's the last nested transaction
//////////////////////////////////////////
ScopedTransaction::~ScopedTransaction(){
this->End();
}
//////////////////////////////////////////
///\brief
///If canceled, this all statements in the transaction scope will be canceled
//////////////////////////////////////////
void ScopedTransaction::Cancel(){
this->canceled = true;
}
//////////////////////////////////////////
///\brief
///Sometimes it's a good option to be able to commit a transaction and restart it all over.
//////////////////////////////////////////
void ScopedTransaction::CommitAndRestart(){
this->End();
this->Begin();
}
//////////////////////////////////////////
///\brief
///Runs the acctual BEGIN TRANSACTION on the database
//////////////////////////////////////////
void ScopedTransaction::Begin(){
if(this->connection->transactionCounter==0){
this->connection->Execute("BEGIN TRANSACTION");
@ -69,6 +92,10 @@ void ScopedTransaction::Begin(){
++this->connection->transactionCounter;
}
//////////////////////////////////////////
///\brief
///Runs the COMMIT or ROLLBACK on the database
//////////////////////////////////////////
void ScopedTransaction::End(){
--this->connection->transactionCounter;
if(this->connection->transactionCounter==0){

View File

@ -43,9 +43,25 @@
namespace musik{ namespace core{ namespace db{
// Forward declare
class Connection;
class ScopedTransaction : boost::noncopyable{
//////////////////////////////////////////
///\brief
///ScopedTransaction is used to make transactions easier
///
///Usage like this:
///\code
///{
/// musik::code::db::ScopedTransaction transactionScope(db)
/// //everything in this scope is included in the transation
///}
///\endcode
///
///\remarks
///Nested transations are partially supported. First scope is the one deciding when transaction is commited.
//////////////////////////////////////////
class ScopedTransaction : boost::noncopyable{
public:
ScopedTransaction(Connection &connection);
~ScopedTransaction();

View File

@ -42,6 +42,16 @@
using namespace musik::core::db;
//////////////////////////////////////////
///\brief
///Constructor
///
///\param sql
///SQL to be precomiled
///
///\param connection
///database Connection
//////////////////////////////////////////
Statement::Statement(const char* sql,Connection &connection) : connection(&connection),stmt(NULL){
int err = sqlite3_prepare_v2(this->connection->connection,sql,-1,&this->stmt,NULL);
/* #ifdef _DEBUG
@ -52,23 +62,27 @@ Statement::Statement(const char* sql,Connection &connection) : connection(&conne
#endif*/
}
//////////////////////////////////////////
///\brief
///Constructor used by the CachedStatement
//////////////////////////////////////////
Statement::Statement(Connection &connection) : connection(&connection),stmt(NULL) {
}
//////////////////////////////////////////
///\brief
///Destructor that will finalize the statement
//////////////////////////////////////////
Statement::~Statement(){
int err=sqlite3_finalize(this->stmt);
}
void Statement::Finalize(){
/* #ifdef _DEBUG
if(err!=0){
const char *errorMsg = sqlite3_errmsg(this->connection->connection);
_ASSERT(false);
}
#endif*/
}
//////////////////////////////////////////
///\brief
///Reset a statment to be able to re-execute it later
//////////////////////////////////////////
void Statement::Reset(){
int err = sqlite3_reset(this->stmt);
/* #ifdef _DEBUG
@ -79,43 +93,124 @@ void Statement::Reset(){
#endif*/
}
//////////////////////////////////////////
///\brief
///Unbinds all previously binded parameters
//////////////////////////////////////////
void Statement::UnBindAll(){
DB_ASSERT(sqlite3_clear_bindings(this->stmt));
}
//////////////////////////////////////////
///\brief
///Execute/Step through the statment
///
///\returns
///musik::core::db::ReturnCode
//////////////////////////////////////////
int Statement::Step(){
return sqlite3_step(this->stmt);
}
//////////////////////////////////////////
///\brief
///Bind a integer to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindInt
///Integer to bind
//////////////////////////////////////////
void Statement::BindInt(int position,int bindInt){
DB_ASSERT(sqlite3_bind_int(this->stmt,position+1,bindInt));
}
//////////////////////////////////////////
///\brief
///Bind a 64bit integer to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindInt
///Integer to bind
//////////////////////////////////////////
void Statement::BindInt64(int position,UINT64 bindInt){
DB_ASSERT(sqlite3_bind_int64(this->stmt,position+1,bindInt));
}
//////////////////////////////////////////
///\brief
///Bind a text to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindText
///Text to bind
//////////////////////////////////////////
void Statement::BindText(int position,const char* bindText){
DB_ASSERT(sqlite3_bind_text(this->stmt,position+1,bindText,-1,SQLITE_STATIC));
}
//////////////////////////////////////////
///\brief
///Bind a text to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindText
///Text to bind
//////////////////////////////////////////
void Statement::BindText(int position,const std::string &bindText){
DB_ASSERT(sqlite3_bind_text(this->stmt,position+1,bindText.c_str(),-1,SQLITE_STATIC));
}
//////////////////////////////////////////
///\brief
///Bind a text to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindText
///Text to bind
//////////////////////////////////////////
void Statement::BindTextW(int position,const wchar_t* bindText){
DB_ASSERT(sqlite3_bind_text16(this->stmt,position+1,bindText,-1,SQLITE_STATIC));
}
//////////////////////////////////////////
///\brief
///Bind a text to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindText
///Text to bind
//////////////////////////////////////////
void Statement::BindTextW(int position,const std::wstring &bindText){
DB_ASSERT(sqlite3_bind_text16(this->stmt,position+1,bindText.c_str(),-1,SQLITE_STATIC));
}
//////////////////////////////////////////
///\brief
///Bind a text to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindText
///Text to bind
//////////////////////////////////////////
void Statement::BindTextUTF(int position,const utfchar* bindText){
#ifdef UTF_WIDECHAR
DB_ASSERT(sqlite3_bind_text16(this->stmt,position+1,bindText,-1,SQLITE_STATIC));
@ -124,6 +219,16 @@ void Statement::BindTextUTF(int position,const utfchar* bindText){
#endif
}
//////////////////////////////////////////
///\brief
///Bind a text to a statment parameter
///
///\param position
///Position of the parameter (0 is the first)
///
///\param bindText
///Text to bind
//////////////////////////////////////////
void Statement::BindTextUTF(int position,const utfstring &bindText){
#ifdef UTF_WIDECHAR
DB_ASSERT(sqlite3_bind_text16(this->stmt,position+1,bindText.c_str(),-1,SQLITE_STATIC));
@ -136,26 +241,76 @@ void Statement::BindTextUTF(int position,const utfstring &bindText){
}
//////////////////////////////////////////
///\brief
///Get the results of a column if Step() return a musik::core::db::Row
///
///\param column
///Column to get (0 is the first)
///
///\returns
///Column data
//////////////////////////////////////////
int Statement::ColumnInt(int column){
return sqlite3_column_int(this->stmt,column);
}
//////////////////////////////////////////
///\brief
///Get the results of a column if Step() return a musik::core::db::Row
///
///\param column
///Column to get (0 is the first)
///
///\returns
///Column data
//////////////////////////////////////////
UINT64 Statement::ColumnInt64(int column){
return sqlite3_column_int64(this->stmt,column);
}
//////////////////////////////////////////
///\brief
///Get the results of a column if Step() return a musik::core::db::Row
///
///\param column
///Column to get (0 is the first)
///
///\returns
///Column data
//////////////////////////////////////////
const char* Statement::ColumnText(int column){
return (char*)sqlite3_column_text(this->stmt,column);
}
//////////////////////////////////////////
///\brief
///Get the results of a column if Step() return a musik::core::db::Row
///
///\param column
///Column to get (0 is the first)
///
///\returns
///Column data
//////////////////////////////////////////
const wchar_t* Statement::ColumnTextW(int column){
return (wchar_t*)sqlite3_column_text16(this->stmt,column);
}
//////////////////////////////////////////
///\brief
///Get the results of a column if Step() return a musik::core::db::Row
///
///\param column
///Column to get (0 is the first)
///
///\returns
///Column data
//////////////////////////////////////////
const utfchar* Statement::ColumnTextUTF(int column){
#ifdef UTF_WIDECHAR
return (utfchar*)sqlite3_column_text16(this->stmt,column);

View File

@ -48,7 +48,11 @@ namespace musik{ namespace core{ namespace db{
class CachedStatement;
class Statement : boost::noncopyable{
//////////////////////////////////////////
///\brief
///Class for precompiling SQL statements
//////////////////////////////////////////
class Statement : boost::noncopyable{
public:
Statement(const char* sql,Connection &connection);
virtual ~Statement();
@ -77,9 +81,6 @@ namespace musik{ namespace core{ namespace db{
sqlite3_stmt *stmt;
Connection *connection;
protected:
virtual void Finalize();
private:
friend class CachedStatement;
Statement(Connection &connection);

View File

@ -47,7 +47,12 @@
namespace musik{ namespace core{ namespace db{
enum ReturnCode : int{
//////////////////////////////////////////
///\brief
///The ReturnCode is used by several method to return the status of a execution
//////////////////////////////////////////
enum ReturnCode : int{
OK = SQLITE_OK,
Row = SQLITE_ROW,
Done = SQLITE_DONE,