diff --git CMakeLists.txt CMakeLists.txt index 18be303..e0c24fd 100644 --- CMakeLists.txt +++ CMakeLists.txt @@ -4,7 +4,7 @@ if(POLICY CMP0005) cmake_policy(SET CMP0005 NEW) endif(POLICY CMP0005) - + project( Guayadeque ) IF( NOT EXISTS ${CMAKE_INSTALL_PREFIX} ) @@ -120,7 +120,7 @@ ENDIF( LIBAPPINDICATOR_LIBRARIES ) SET( ADD_WXSQLITE_SUPPORT 0 ) PKG_CHECK_MODULES( LIBWXSQLITE3 wxsqlite3 ) IF( NOT LIBWXSQLITE3_LIBRARIES ) - PKG_CHECK_MODULES( LIBWXSQLITE3 wxsqlite3-2.8 ) + PKG_CHECK_MODULES( LIBWXSQLITE3 wxsqlite3-3.2 ) IF( NOT LIBWXSQLITE3_LIBRARIES ) ADD_DEFINITIONS(-DADD_WXSQLITE_SUPPORT=1) SET( ADD_WXSQLITE_SUPPORT 1 ) @@ -142,15 +142,15 @@ IF( EXISTS ${PROJECT_SOURCE_DIR}/.svn ) ELSE(Subversion_FOUND) SET( _GUREVISION_ "" ) ENDIF(Subversion_FOUND) -ENDIF( EXISTS ${PROJECT_SOURCE_DIR}/.svn ) +ENDIF( EXISTS ${PROJECT_SOURCE_DIR}/.svn ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h ) -# +# #ADD_DEFINITIONS(${wxWidgets_DEFINITIONS}) ADD_DEFINITIONS( -Wall -O2 ) - -# We define the include paths here, our minimal source dir is one, + +# We define the include paths here, our minimal source dir is one, # and also the include dirs defined by wxWidgets INCLUDE_DIRECTORIES(${Guayadeque_SOURCE_DIR} ${Guayadeque_SOURCE_DIR}/src @@ -169,7 +169,7 @@ INCLUDE_DIRECTORIES(${Guayadeque_SOURCE_DIR} ${LIBAPPINDICATOR_INCLUDE_DIRS} ${LIBWXSQLITE3_INCLUDE_DIRS} ) - + ADD_SUBDIRECTORY( src ) ADD_SUBDIRECTORY( po ) diff --git src/wxsqlite3/wx/wxsqlite3.h src/wxsqlite3/wx/wxsqlite3.h index c05f1d5..0d259ec 100755 --- src/wxsqlite3/wx/wxsqlite3.h +++ src/wxsqlite3/wx/wxsqlite3.h @@ -17,11 +17,18 @@ #pragma interface "wxsqlite3.h" #endif -#include +#include #include +#include +#include +#include +#include #include "wx/wxsqlite3def.h" +/// wxSQLite3 version string +#define wxSQLITE3_VERSION_STRING wxT("wxSQLite3 3.2.1") + #define WXSQLITE_ERROR 1000 #define WXSQLITE_INTEGER 1 @@ -36,6 +43,7 @@ typedef long long int wxsqlite_int64; #endif +/// Enumeration of transaction types enum wxSQLite3TransactionType { WXSQLITE_TRANSACTION_DEFAULT, @@ -44,6 +52,7 @@ enum wxSQLite3TransactionType WXSQLITE_TRANSACTION_EXCLUSIVE }; +/// Enumeration of SQLite limitation types enum wxSQLite3LimitType { WXSQLITE_LIMIT_LENGTH = 0, @@ -55,9 +64,45 @@ enum wxSQLite3LimitType WXSQLITE_LIMIT_FUNCTION_ARG = 6, WXSQLITE_LIMIT_ATTACHED = 7, WXSQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8, - WXSQLITE_LIMIT_VARIABLE_NUMBER = 9 + WXSQLITE_LIMIT_VARIABLE_NUMBER = 9, + WXSQLITE_LIMIT_TRIGGER_DEPTH = 10 +}; + +/// Enumeration of journal modes +enum wxSQLite3JournalMode +{ + WXSQLITE_JOURNALMODE_DELETE = 0, // Commit by deleting journal file + WXSQLITE_JOURNALMODE_PERSIST = 1, // Commit by zeroing journal header + WXSQLITE_JOURNALMODE_OFF = 2, // Journal omitted. + WXSQLITE_JOURNALMODE_TRUNCATE = 3, // Commit by truncating journal + WXSQLITE_JOURNALMODE_MEMORY = 4, // In-memory journal file + WXSQLITE_JOURNALMODE_WAL = 5 // Use write-ahead logging }; +/// Enumeration of statement status counters +enum wxSQLite3StatementStatus +{ + WXSQLITE_STMTSTATUS_FULLSCAN_STEP = 1, + WXSQLITE_STMTSTATUS_SORT = 2, + WXSQLITE_STMTSTATUS_AUTOINDEX = 3, + WXSQLITE_STMTSTATUS_VM_STEP = 4 +}; + +#define WXSQLITE_OPEN_READONLY 0x00000001 +#define WXSQLITE_OPEN_READWRITE 0x00000002 +#define WXSQLITE_OPEN_CREATE 0x00000004 +#define WXSQLITE_OPEN_URI 0x00000040 +#define WXSQLITE_OPEN_MEMORY 0x00000080 +#define WXSQLITE_OPEN_NOMUTEX 0x00008000 +#define WXSQLITE_OPEN_FULLMUTEX 0x00010000 +#define WXSQLITE_OPEN_SHAREDCACHE 0x00020000 +#define WXSQLITE_OPEN_PRIVATECACHE 0x00040000 + +#define WXSQLITE_CHECKPOINT_PASSIVE 0 +#define WXSQLITE_CHECKPOINT_FULL 1 +#define WXSQLITE_CHECKPOINT_RESTART 2 +#define WXSQLITE_CHECKPOINT_TRUNCATE 3 + inline void operator++(wxSQLite3LimitType& value) { value = wxSQLite3LimitType(value+1); @@ -133,6 +178,17 @@ public: */ const char* Format(const char* format, ...); + /// Format a SQL statement using SQLite3's printf method + /** + * This method is like method Format but takes a va_list argument + * to pass the statement parameters. + * + * \param format SQL statement string with formatting options + * \param va va_list of statement parameters + * \return const char pointer to the resulting statement buffer + */ + const char* FormatV(const char* format, va_list va); + /// Dereference the internal buffer /** * \return const char pointer to the resulting statement buffer @@ -150,7 +206,7 @@ private: /** * A function context gives user defined scalar or aggregate functions * access to function arguments and function results. The "Execute" method -* resp. the "Aggregate" and "Finalize" methods receive the current +* resp. the "Aggregate" and "Finalize" methods receive the current * function context as an argument. */ class WXDLLIMPEXP_SQLITE3 wxSQLite3FunctionContext @@ -296,7 +352,7 @@ public: * binary zeros. * If this memory is used to store pointers to allocated objects, * it is important to free all allocated objects in the "Finalize" method. - * + * * \param len amount of memory needed in bytes * \return pointer to the allocated memory */ @@ -314,7 +370,11 @@ public: /// Execute the user defined authorizer function (internal use only) static int ExecAuthorizer(void*, int type, const char* arg1, const char* arg2, - const char* arg3, const char* arg4); + const char* arg3, const char* arg4 +#if WXSQLITE3_USER_AUTHENTICATION + , const char* arg5 +#endif + ); /// Execute the user defined commit hook (internal use only) static int ExecCommitHook(void* hook); @@ -323,10 +383,14 @@ public: static void ExecRollbackHook(void* hook); /// Execute the user defined update hook (internal use only) - static void ExecUpdateHook(void* hook, int type, - const char* database, const char* table, + static void ExecUpdateHook(void* hook, int type, + const char* database, const char* table, wxsqlite_int64 rowid); + /// Execute the user defined Write Ahead Log hook (internal use only) + static int ExecWriteAheadLogHook(void* hook, void* dbHandle, + const char* database, int numPages); + private: /// Constructor wxSQLite3FunctionContext(void* ctx, bool isAggregate, int argc = 0, void** argv = NULL); @@ -348,6 +412,9 @@ private: class WXDLLIMPEXP_SQLITE3 wxSQLite3ScalarFunction { public: + /// Constructor + wxSQLite3ScalarFunction() {} + /// Virtual destructor virtual ~wxSQLite3ScalarFunction() {} /// Execute the scalar function @@ -365,7 +432,7 @@ public: class WXDLLIMPEXP_SQLITE3 wxSQLite3AggregateFunction { public: - /// Virtual destructor + /// Constructor wxSQLite3AggregateFunction() { m_count = 0; } /// Virtual destructor @@ -434,7 +501,8 @@ public: SQLITE_DROP_VTABLE = 30, // Table Name Module Name SQLITE_FUNCTION = 31, // NULL Function Name SQLITE_SAVEPOINT = 32, // Operation Savepoint Name - SQLITE_MAX_CODE = SQLITE_SAVEPOINT + SQLITE_RECURSIVE = 33, // NULL NULL + SQLITE_MAX_CODE = SQLITE_RECURSIVE }; /// Return codes of the authorizer @@ -456,11 +524,13 @@ public: * \param arg2 second argument (value depends on "type") * \param arg3 third argument (name of database if applicable) * \param arg4 fourth argument (name of trigger or view if applicable) + * \param arg5 fifth argument (name of authorized user or empty if user authentication is not activated) * \return a wxAuthorizationResult, i.e. SQLITE_OK, SQLITE_DENY or SQLITE_IGNORE */ virtual wxAuthorizationResult Authorize(wxAuthorizationCode type, const wxString& arg1, const wxString& arg2, - const wxString& arg3, const wxString& arg4) = 0; + const wxString& arg3, const wxString& arg4, + const wxString& arg5) = 0; /// Convert authorization code to string /** * \param type wxAuthorizationCode. The value signifies what kind of operation is to be authorized. @@ -468,6 +538,12 @@ public: static wxString AuthorizationCodeToString(wxSQLite3Authorizer::wxAuthorizationCode type); }; +class wxSQLite3DatabaseReference; +class wxSQLite3StatementReference; +class wxSQLite3BlobReference; + +class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3Database; + /// Interface for a user defined hook function /** */ @@ -481,6 +557,9 @@ public: SQLITE_INSERT = 18, SQLITE_UPDATE = 23 }; + /// Default constructor + wxSQLite3Hook() : m_db(NULL) {} + /// Virtual destructor virtual ~wxSQLite3Hook() {} @@ -510,6 +589,59 @@ public: virtual void UpdateCallback(wxUpdateType WXUNUSED(type), const wxString& WXUNUSED(database), const wxString& WXUNUSED(table), wxLongLong WXUNUSED(rowid)) {} + + /// Execute the write-ahead log hook callback function + /** + * Please refer to the SQLite documentation for further information about the + * meaning of the parameters. + * + * \param database Name of the database + * \param numPages the number of pages + */ + virtual int WriteAheadLogCallback(const wxString& WXUNUSED(database), + int WXUNUSED(numPages)) { return 0; } + + /// Set the associated database + /** + * For the write-ahead log hook the associated database is set internally. + * \param db pointer to the associated database instance + */ + void SetDatabase(wxSQLite3Database* db) { m_db = db; } + + /// Get the associated database + /** + * For the write-ahead log hook the associated database can be accessed. + * + * \return pointer to the associated database instance + * \note Access to the associated database is only provided for write-ahead log hooks. + */ + wxSQLite3Database* GetDatabase() const { return m_db; } + +private: + wxSQLite3Database* m_db; +}; + +/// Interface for a user defined backup progress function +/** +*/ +class WXDLLIMPEXP_SQLITE3 wxSQLite3BackupProgress +{ +public: + /// Default constructor + wxSQLite3BackupProgress() {} + + /// Virtual destructor + virtual ~wxSQLite3BackupProgress() {} + + /// Execute the backup progress callback + /** + * This method allows an application to display information about the progress of a backup + * operation to the user. + * \param totalPages total number of pages to copy + * \param remainingPages number of pages remaining to be copied + * \return TRUE if backup should continue, FALSE otherwise + */ + virtual bool Progress(int WXUNUSED(totalPages), int WXUNUSED(remainingPages)) { return true; } }; /// Interface for a user defined collation sequence @@ -542,8 +674,9 @@ public: wxSQLite3ResultSet(const wxSQLite3ResultSet& resultSet); /// Constructor for internal use - wxSQLite3ResultSet(void* db, void* stmt, - bool eof, bool first = true, bool ownStmt = true); + wxSQLite3ResultSet(wxSQLite3DatabaseReference* db, + wxSQLite3StatementReference* stmt, + bool eof, bool first = true); /// Assignment constructor wxSQLite3ResultSet& operator=(const wxSQLite3ResultSet& resultSet); @@ -726,6 +859,7 @@ public: /// Get a column as a date value using the column index /** + * Date value is expected to be in format 'YYYY-MM-DD'. * \param columnIndex index of the column. Indices start with 0. * \return value of the column */ @@ -733,6 +867,7 @@ public: /// Get a column as a date value using the column name /** + * Date value is expected to be in format 'YYYY-MM-DD'. * \param columnName name of the column * \return value of the column */ @@ -740,6 +875,7 @@ public: /// Get a column as a time value using the column index /** + * Date value is expected to be in format 'HH:MM:SS'. * \param columnIndex index of the column. Indices start with 0. * \return value of the column */ @@ -747,6 +883,7 @@ public: /// Get a column as a time value using the column name /** + * Date value is expected to be in format 'HH:MM:SS'. * \param columnName name of the column * \return value of the column */ @@ -754,6 +891,7 @@ public: /// Get a column as a date and time value using the column index /** + * Date value is expected to be in format 'YYYY-MM-DD HH:MM:SS'. * \param columnIndex index of the column. Indices start with 0. * \return value of the column */ @@ -761,6 +899,7 @@ public: /// Get a column as a date and time value using the column name /** + * Date value is expected to be in format 'YYYY-MM-DD HH:MM:SS'. * \param columnName name of the column * \return value of the column */ @@ -768,6 +907,7 @@ public: /// Get a column as a timestamp value using the column index /** + * Date value is expected to be in format 'YYYY-MM-DD HH:MM:SS.mmm'. * \param columnIndex index of the column. Indices start with 0. * \return value of the column */ @@ -775,6 +915,7 @@ public: /// Get a column as a timestamp value using the column name /** + * Date value is expected to be in format 'YYYY-MM-DD HH:MM:SS.mmm'. * \param columnName name of the column * \return value of the column */ @@ -782,7 +923,8 @@ public: /// Get a column as a date and time value using the column index /** - * The date/time value is expected to be stored in the database as a numeric value (i.e. int64). + * The date/time value is expected to be stored in the database as a numeric value (i.e. int64), + * measured in milliseconds since 1970-01-01. * * \param columnIndex index of the column. Indices start with 0. * \return value of the column @@ -791,7 +933,8 @@ public: /// Get a column as a date and time value using the column name /** - * The date/time value is expected to be stored in the database as a numeric value (i.e. int64). + * The date/time value is expected to be stored in the database as a numeric value (i.e. int64), + * measured in milliseconds since 1970-01-01. * * \param columnName name of the column * \return value of the column @@ -800,6 +943,26 @@ public: /// Get a column as a date and time value using the column index /** + * The date/time value is expected to be stored in the database as an integer value (i.e. int64), + * measured in seconds since 1970-01-01. + * + * \param columnIndex index of the column. Indices start with 0. + * \return value of the column + */ + wxDateTime GetUnixDateTime(int columnIndex); + + /// Get a column as a date and time value using the column name + /** + * The date/time value is expected to be stored in the database as an integer value (i.e. int64), + * measured in seconds since 1970-01-01. + * + * \param columnName name of the column + * \return value of the column + */ + wxDateTime GetUnixDateTime(const wxString& columnName); + + /// Get a column as a date and time value using the column index + /** * The date/time value is expected to be stored in the database as a Julian Day Number (i.e. double). * * \param columnIndex index of the column. Indices start with 0. @@ -816,6 +979,26 @@ public: */ wxDateTime GetJulianDayNumber(const wxString& columnName); + /// Get a column as a date and time value using the column index + /** + * The date/time value is interpreted based on the type of column value. + * + * \param columnIndex index of the column. Indices start with 0. + * \param milliSeconds interpret integer value as milliseconds since 1970-01-01, default: false + * \return value of the column + */ + wxDateTime GetAutomaticDateTime(int columnIndex, bool milliSeconds = false); + + /// Get a column as a date and time value using the column name + /** + * The date/time value is interpreted based on the type of column value. + * + * \param columnName name of the column + * \param milliSeconds interpret integer value as milliseconds since 1970-01-01, default: false + * \return value of the column + */ + wxDateTime GetAutomaticDateTime(const wxString& columnName, bool milliSeconds = false); + /// Get a column as a boolean value using the column index /** * \param columnIndex index of the column. Indices start with 0. @@ -850,6 +1033,12 @@ public: */ bool Eof(); + /// Check whether the cursor has been moved + /** + * \return TRUE if the cursor has been moved using method NextRow, FALSE otherwise + */ + bool CursorMoved(); + /// Retrieve next row of the result set /** * Advances the cursor to the next row. @@ -880,12 +1069,14 @@ private: /// Check the validity of the associated statement void CheckStmt(); - void* m_db; ///< associated database - void* m_stmt; ///< associated statement + /// Finalize the result set (internal) + void Finalize(wxSQLite3DatabaseReference* db,wxSQLite3StatementReference* stmt); + + wxSQLite3DatabaseReference* m_db; ///< associated database + wxSQLite3StatementReference* m_stmt; ///< associated statement bool m_eof; ///< Flag for end of result set bool m_first; ///< Flag for first row of the result set int m_cols; ///< Numver of columns in row set - bool m_ownStmt; ///< Flag for ownership of the associated statement }; @@ -1138,7 +1329,7 @@ public: /// Constructor (internal use only) /** */ - wxSQLite3Statement(void* db, void* stmt); + wxSQLite3Statement(wxSQLite3DatabaseReference* db, wxSQLite3StatementReference* stmt); /// Destructor /** @@ -1157,6 +1348,14 @@ public: */ wxSQLite3ResultSet ExecuteQuery(); + /// Execute a scalar SQL query statement given as a wxString + /** + * Allows to easily retrieve the result of queries returning a single integer result + * like SELECT COUNT(*) FROM table WHERE condition. + * \return first column of first row as an int + */ + int ExecuteScalar(); + /// Get the number of statement parameters /** * \return the number of parameters in the prepared statement @@ -1165,7 +1364,7 @@ public: /// Get the index of a parameter with a given name /** - * \param paramName + * \param paramName * \return the index of the parameter with the given name. The name must match exactly. * If there is no parameter with the given name, return 0. */ @@ -1232,6 +1431,7 @@ public: /// Bind parameter to a date value /** + * Only the date part is stored in format 'YYYY-MM-DD'. * \param paramIndex index of the parameter. The first parameter has an index of 1. * \param date value of the parameter */ @@ -1239,6 +1439,7 @@ public: /// Bind parameter to a time value /** + * Only the time part is stored in format 'HH:MM:SS'. * \param paramIndex index of the parameter. The first parameter has an index of 1. * \param time value of the parameter */ @@ -1246,6 +1447,7 @@ public: /// Bind parameter to a date and time value /** + * Date and time are stored in format 'YYYY-MM-DD HH:MM:SS'. * \param paramIndex index of the parameter. The first parameter has an index of 1. * \param datetime value of the parameter */ @@ -1253,6 +1455,7 @@ public: /// Bind parameter to a timestamp value /** + * Timestamp is stored in format 'YYYY-MM-DD HH:MM:SS.mmm'. * \param paramIndex index of the parameter. The first parameter has an index of 1. * \param timestamp value of the parameter */ @@ -1261,6 +1464,7 @@ public: /// Bind parameter to a date and time value /** * The date/time value is transferred to the database as a numeric value (i.e. int64). + * The value is measured in milliseconds since 1970-01-01. * * \param paramIndex index of the parameter. The first parameter has an index of 1. * \param datetime value of the parameter @@ -1269,6 +1473,16 @@ public: /// Bind parameter to a date and time value /** + * The date/time value is transferred to the database as an integer value. + * The value is measured in seconds since 1970-01-01. + * + * \param paramIndex index of the parameter. The first parameter has an index of 1. + * \param datetime value of the parameter + */ + void BindUnixDateTime(int paramIndex, const wxDateTime& datetime); + + /// Bind parameter to a date and time value + /** * The date/time value is transferred to the database as a Julian Day Number value (i.e. double). * * \param paramIndex index of the parameter. The first parameter has an index of 1. @@ -1291,7 +1505,7 @@ public: /// Bind parameter to a Zero BLOB value /** - * Space for a BLOB is reserved and filled with binary zeros for later reference + * Space for a BLOB is reserved and filled with binary zeros for later reference * through a BLOB handle. * * \param paramIndex index of the parameter. The first parameter has an index of 1. @@ -1301,7 +1515,7 @@ public: /// Clear all parameter bindings /** - * Sets all the parameters in the prepared SQL statement back to NULL. + * Sets all the parameters in the prepared SQL statement back to NULL. */ void ClearBindings(); @@ -1318,7 +1532,15 @@ public: */ void Reset(); - /// Finalize the prepared staement + /// Determine whether the statement is read-only + /** + * \return TRUE if the statement is read-only, FALSE otherwise + * \since SQLite3 version 3.7.4 + * \note For SQLite3 version before version 3.7.4 this method returns always FALSE. + */ + bool IsReadOnly(); + + /// Finalize the prepared statement /** */ void Finalize(); @@ -1329,6 +1551,22 @@ public: */ bool IsOk(); + /// Determine if a prepared statement has been reset + /** + * \return TRUE if the prepared statement has been stepped at least once but has not run to completion and/or has not been reset, FALSE otherwise + */ + bool IsBusy(); + + /// Determine internal operation counters of the underlying prepared statement + /** + * Prepared statements maintain various counters to measure the performance of specific operations. + * This method allows to monitor the performance characteristics of the prepared statement. + * \param opCode operation code of the operation to be queried + * \param resetFlag flag whether the associated counter should be reset to zero (default: false) + * \return the counter value for the requested counter + */ + int Status(wxSQLite3StatementStatus opCode, bool resetFlag = false); + private: /// Check for valid database connection void CheckDatabase(); @@ -1336,8 +1574,11 @@ private: /// Check for valid statement void CheckStmt(); - void* m_db; ///< associated SQLite3 database - void* m_stmt; ///< associated SQLite3 statement + /// Finalize the result set (internal) + void Finalize(wxSQLite3DatabaseReference* db,wxSQLite3StatementReference* stmt); + + wxSQLite3DatabaseReference* m_db; ///< associated SQLite3 database + wxSQLite3StatementReference* m_stmt; ///< associated SQLite3 statement }; @@ -1363,7 +1604,7 @@ public: /// Constructor (internal use only) /** */ - wxSQLite3Blob(void* m_db, void* blobHandle, bool writable); + wxSQLite3Blob(wxSQLite3DatabaseReference* m_db, wxSQLite3BlobReference* blobHandle, bool writable); /// Destructor /** @@ -1404,6 +1645,15 @@ public: */ int GetSize(); + /// Rebind the associated BLOB to a new row + /** + * Please refer to the SQLite documentation for further information + * (see function sqlite3_blob_reopen) + * \param rowid id of the row to which the BLOB should be rebound + * \since SQLite3 version 3.7.4 + */ + void Rebind(wxLongLong rowid); + /// Finalize the BLOB /** */ @@ -1413,12 +1663,178 @@ private: /// Check for valid BLOB void CheckBlob(); - void* m_db; ///< associated SQLite3 database handle - void* m_blob; ///< associated SQLite3 BLOB handle - bool m_ok; ///< flag whether the BLOB handle is correctly initialized + void Finalize(wxSQLite3DatabaseReference* db, wxSQLite3BlobReference* blob); + + wxSQLite3DatabaseReference* m_db; ///< associated SQLite3 database handle + wxSQLite3BlobReference* m_blob; ///< associated SQLite3 BLOB handle bool m_writable; ///< flag whether the BLOB is writable or read only }; +/// Represents a named collection +/** +* A named collection is designed to facilitate using an array of +* integers or strings as the right-hand side of an IN operator. +* So instead of doing a prepared statement like this: +* +* SELECT * FROM table WHERE x IN (?,?,?,...,?); +* +* And then binding indivdual integers to each of ? slots, an application +* can create a named collection object (named "ex1" in the following +* example), prepare a statement like this: +* +* SELECT * FROM table WHERE x IN ex1; +* +* Then bind an array of integer or string values to the ex1 object +* to run the statement. +* +* USAGE: +* +* One or more named collection objects can be created as follows: +* +* wxSQLite3IntegerCollection p1, p2, p3; +* p1 = db.CreateIntegerCollection("ex1"); +* p2 = db.CreateIntegerCollection("ex2"); +* p3 = db.CreateIntegerCollection("ex3"); +* +* Each call to CreateIntegerCollection generates a new virtual table +* module and a singleton of that virtual table module in the TEMP +* database. Both the module and the virtual table instance use the +* name given by the second parameter. The virtual tables can then be +* used in prepared statements: +* +* SELECT * FROM t1, t2, t3 +* WHERE t1.x IN ex1 +* AND t2.y IN ex2 +* AND t3.z IN ex3; +* +* Each integer array is initially empty. New arrays can be bound to +* an integer array as follows: +* +* int a1[] = { 1, 2, 3, 4 }; +* int a2[] = { 5, 6, 7, 8, 9, 10, 11 }; +* wxArrayInt a3; +* // Fill a3 +* p1.Bind(4, a1); +* p2.Bind(7, a2); +* p3.Bind(a3); +* +* A single named collection object can be rebound multiple times. But do not +* attempt to change the bindings of a named collection while it is in the middle +* of a query. +* +* The array that holds the integer or string values is automatically allocated +* by the Bind method. +* +* The named collection object is automatically destroyed when its corresponding +* virtual table is dropped. Since the virtual tables are created in the +* TEMP database, they are automatically dropped when the database connection +* closes so the application does not normally need to take any special +* action to free the named collection objects. +*/ +class WXDLLIMPEXP_SQLITE3 wxSQLite3NamedCollection +{ +public: + /// Constructor + wxSQLite3NamedCollection(); + + /// Copy constructor + wxSQLite3NamedCollection(const wxSQLite3NamedCollection& collection); + + /// Assignement constructor + wxSQLite3NamedCollection& operator=(const wxSQLite3NamedCollection& collection); + + /// Constructor (internal use only) + wxSQLite3NamedCollection(const wxString& collectionName, void* collectionData); + + /// Destructor + virtual ~wxSQLite3NamedCollection(); + + /// Get the name of the collection + /** + * \return the name of the collection + */ + const wxString& GetName() { return m_name; } + +protected: + wxString m_name; ///< Name of the collection + void* m_data; ///< Reference to the actual array of values representing the collection + + friend class wxSQLite3Database; +}; + +/// Represents a named integer value collection +class WXDLLIMPEXP_SQLITE3 wxSQLite3IntegerCollection : public wxSQLite3NamedCollection +{ +public: + /// Constructor + wxSQLite3IntegerCollection(); + + /// Copy constructor + wxSQLite3IntegerCollection(const wxSQLite3IntegerCollection& collection); + + /// Assignement constructor + wxSQLite3IntegerCollection& operator=(const wxSQLite3IntegerCollection& collection); + + /// Constructor (internal use only) + wxSQLite3IntegerCollection(const wxString& collectionName, void* collectionData); + + /// Destructor + virtual ~wxSQLite3IntegerCollection(); + + /// Bind a new array of integer values + /** + * Bind a new array of integer values to this named collection object. + * \param integerCollection array of integer values to be bound + * \note Binding values to a named collection after closing the corresponding + * database results in undefined behaviour, i.e. the application is likely to crash. + */ + void Bind(const wxArrayInt& integerCollection); + + /// Bind a new array of integer values + /** + * Bind a new array of integer values to this named collection object. + * \param n number of elements in the array + * \param integerCollection array of integer values to be bound + * \note Binding values to a named collection after closing the corresponding + * database results in undefined behaviour, i.e. the application is likely to crash. + */ + void Bind(int n, int* integerCollection); + +private: + friend class wxSQLite3Database; +}; + +/// Represents a named string value collection +class WXDLLIMPEXP_SQLITE3 wxSQLite3StringCollection : public wxSQLite3NamedCollection +{ +public: + /// Constructor + wxSQLite3StringCollection(); + + /// Copy constructor + wxSQLite3StringCollection(const wxSQLite3StringCollection& collection); + + /// Assignement constructor + wxSQLite3StringCollection& operator=(const wxSQLite3StringCollection& collection); + + /// Constructor (internal use only) + wxSQLite3StringCollection(const wxString& collectionName, void* collectionData); + + /// Destructor + virtual ~wxSQLite3StringCollection(); + + /// Bind a new array of integer values + /** + * Bind a new array of integer values to this named collection object. + * \param stringCollection array of integer values to be bound + * \note Binding values to a named collection after closing the corresponding + * database results in undefined behaviour, i.e. the application is likely to crash. + */ + void Bind(const wxArrayString& stringCollection); + +private: + friend class wxSQLite3Database; +}; /// Represents a SQLite3 database object class WXDLLIMPEXP_SQLITE3 wxSQLite3Database @@ -1445,8 +1861,11 @@ public: * If the database file does not exist, then a new database will be created as needed. * \param[in] fileName Name of the database file. * \param[in] key Database encryption key. + * \param[in] flags Control over the database connection (see http://www.sqlite.org/c3ref/open.html for further information). + * Flag values are prefixed by WX to distinguish them from the original SQLite flag values. */ - void Open(const wxString& fileName, const wxString& key = wxEmptyString); + void Open(const wxString& fileName, const wxString& key = wxEmptyString, + int flags = WXSQLITE_OPEN_READWRITE | WXSQLITE_OPEN_CREATE); /// Open a SQLite3 database using a binary key /** @@ -1455,8 +1874,11 @@ public: * If the database file does not exist, then a new database will be created as needed. * \param[in] fileName Name of the database file. * \param[in] key Database encryption key. + * \param[in] flags Control over the database connection (see http://www.sqlite.org/c3ref/open.html for further information). + * Flag values are prefixed by WX to distinguish them from the original SQLite flag values. */ - void Open(const wxString& fileName, const wxMemoryBuffer& key); + void Open(const wxString& fileName, const wxMemoryBuffer& key, + int flags = WXSQLITE_OPEN_READWRITE | WXSQLITE_OPEN_CREATE); /// Check whether the database has been opened /** @@ -1464,19 +1886,147 @@ public: */ bool IsOpen() const; + /// Determine whether a database is read-only + /** + * \param[in] databaseName Name of the database (default "main"). + * \return TRUE if the database is read-only, FALSE otherwise + * \since SQLite3 version 3.7.11 + * \note For SQLite3 version before version 3.7.11 this method returns always FALSE. + */ + bool IsReadOnly(const wxString& databaseName = wxT("main")); + /// Close a SQLite3 database /** * Take care that all prepared statements have been finalized! - * Starting with version 3.6.0 SQLite has support to finialize all unfinalized - * prepared statements. The Close method has been changed to take advantage of - * this feature. Nevertheless it is recommended to explicitly finalize all - * wxSQLite3Statement instances before closing a database. * - * NOTE: Finalizing all wxSQLite3Blob instances before closing a database is still required! - * + * NOTE: Starting with version 3.6.0 SQLite has support to finialize all unfinalized + * prepared statements. Unfortunately this feature can't be used due to a possible + * crash if the RTree module is active. + * + * NOTE: Finalizing all wxSQLite3Blob instances before closing a database is required! + * */ void Close(); + /// Backup a SQLite3 database + /** + * This method is used to overwrite the contents of a database with the contents + * of this database. This is useful either for creating backups of the database or + * for copying an in-memory database to persistent files. + * + * NOTE: Exclusive access is required to the target database for the + * duration of the operation. However the source database is only + * read-locked while it is actually being read, it is not locked + * continuously for the entire operation. Thus, the backup may be + * performed on a live database without preventing other users from + * writing to the database for an extended period of time. + * + * NOTE: If the target database file already exists it must be a valid + * SQLite database, in case of an encrypted database the key used for + * backup must be the same as the key used for creation. + * If this does not hold true, the file should be deleted prior to + * performing the backup. + * + * \param[in] targetFileName Name of the target database file. + * \param[in] key Optional database encryption key for the target database. + * \param[in] sourceDatabaseName Optional name of the source database (default: 'main'). + */ + void Backup(const wxString& targetFileName, const wxString& key = wxEmptyString, + const wxString& sourceDatabaseName = wxT("main")); + void Backup(wxSQLite3BackupProgress* progressCallback, + const wxString& targetFileName, const wxString& key = wxEmptyString, + const wxString& sourceDatabaseName = wxT("main")); + + /// Backup a SQLite3 database + /** + * This method is used to overwrite the contents of a database with the contents + * of this database. This is useful either for creating backups of the database or + * for copying an in-memory database to persistent files. + * + * NOTE: Exclusive access is required to the target database for the + * duration of the operation. However the source database is only + * read-locked while it is actually being read, it is not locked + * continuously for the entire operation. Thus, the backup may be + * performed on a live database without preventing other users from + * writing to the database for an extended period of time. + * + * NOTE: If the target database file already exists it must be a valid + * SQLite database, in case of an encrypted database the key used for + * backup must be the same as the key used for creation. + * If this does not hold true, the file should be deleted prior to + * performing the backup. + * + * \param[in] targetFileName Name of the target database file. + * \param[in] key Binary database encryption key for the target database. + * \param[in] sourceDatabaseName Optional name of the source database (default: 'main'). + */ + void Backup(const wxString& targetFileName, const wxMemoryBuffer& key, + const wxString& sourceDatabaseName = wxT("main")); + void Backup(wxSQLite3BackupProgress* progressCallback, + const wxString& targetFileName, const wxMemoryBuffer& key, + const wxString& sourceDatabaseName = wxT("main")); + + /// Restore a SQLite3 database + /** + * This method is used to restore the contents of this database with the contents + * of another database. This is useful either for restoring a backup of the database or + * for copying a persistent file to an in-memory database. + * + * NOTE: Exclusive access is required to the target database for the + * duration of the operation. However the source database is only + * read-locked while it is actually being read, it is not locked + * continuously for the entire operation. Thus, the backup may be + * performed on a live database without preventing other users from + * writing to the database for an extended period of time. + * + * \param[in] sourceFileName Name of the source database file. + * \param[in] key Optional database encryption key for the source database. + * \param[in] targetDatabaseName Optional name of the target database (default: 'main'). + */ + void Restore(const wxString& sourceFileName, const wxString& key = wxEmptyString, + const wxString& targetDatabaseName = wxT("main")); + void Restore(wxSQLite3BackupProgress* progressCallback, + const wxString& sourceFileName, const wxString& key = wxEmptyString, + const wxString& targetDatabaseName = wxT("main")); + + /// Restore a SQLite3 database + /** + * This method is used to restore the contents of this database with the contents + * of another database. This is useful either for restoring a backup of the database or + * for copying a persistent file to an in-memory database. + * + * NOTE: Exclusive access is required to the target database for the + * duration of the operation. However the source database is only + * read-locked while it is actually being read, it is not locked + * continuously for the entire operation. Thus, the backup may be + * performed on a live database without preventing other users from + * writing to the database for an extended period of time. + * + * \param[in] sourceFileName Name of the source database file. + * \param[in] key Optional binary database encryption key for the source database. + * \param[in] targetDatabaseName Optional name of the target database (default: 'main'). + */ + void Restore(const wxString& sourceFileName, const wxMemoryBuffer& key, + const wxString& targetDatabaseName = wxT("main")); + void Restore(wxSQLite3BackupProgress* progressCallback, + const wxString& sourceFileName, const wxMemoryBuffer& key, + const wxString& targetDatabaseName = wxT("main")); + + /// Set the page count for backup or restore operations + /** + * Backup and restore operations perform in slices of a given number of pages. + * This method allows to set the size of a slice. The default size is 10 pages. + * + * \param[in] pageCount number of pages to be copied in one slice. + */ + void SetBackupRestorePageCount(int pageCount); + + /// Vacuum + /** + * Performs a VACUUM operation on the database. + */ + void Vacuum(); + /// Begin transaction /** * In SQLite transactions can be deferred, immediate, or exclusive. @@ -1492,7 +2042,7 @@ public: * to write to the database or do a BEGIN IMMEDIATE or BEGIN EXCLUSIVE. Other processes can continue * to read from the database, however. An exclusive transaction causes EXCLUSIVE locks to be acquired * on all databases. After a BEGIN EXCLUSIVE, it is guaranteed that no other thread or process will - * be able to read or write the database until the transaction is complete. + * be able to read or write the database until the transaction is complete. * * \param[in] transactionType type of transaction (default: DEFERRED). */ @@ -1510,16 +2060,27 @@ public: * \param savepointName optional name of a previously set savepoint */ void Rollback(const wxString& savepointName = wxEmptyString); - + /// Get the auto commit state /** * Test to see whether or not the database connection is in autocommit mode. * \return TRUE if it is and FALSE if not. * Autocommit mode is on by default. Autocommit is disabled by a BEGIN statement - * and reenabled by the next COMMIT or ROLLBACK. + * and reenabled by the next COMMIT or ROLLBACK. */ bool GetAutoCommit(); + /// Query the return code of the last rollback + /** + * When using the class wxSQLite3Transaction there is the possibility + * that the automatic rollback which is executed in case of an exception + * fails. This method allows to query the return code of that operation + * to check whether the automatic rollback succeeded or not. + * \return the return code of the last rollback. + * \note In case of a successful rollback the value 0 is returned. + */ + int QueryRollbackState(); + /// Set savepoint /* * Sets a savepoint with a given name @@ -1568,6 +2129,54 @@ public: */ void GetDatabaseList(wxArrayString& databaseNames, wxArrayString& databaseFiles); + /// Return the filename for a database connection + /** + * \param databaseName contains on return the list of the database names + */ + wxString GetDatabaseFilename(const wxString& databaseName); + + /// Enable or disable foreign key support + /** + * Starting with SQLite version 3.6.19 foreign key constraints can be enforced. + * Foreign key constraints are disabled by default (for backwards compatibility), + * so they must be enabled separately for each database connection. + * \note Future releases of SQLite might change so that foreign key constraints + * are enabled by default. No assumptions should be made about whether or not + * foreign keys are enabled by default + * \return TRUE if the requested action succeeded, FALSE otherwise + */ + bool EnableForeignKeySupport(bool enable); + + /// Check whether foreign key support is enabled for this database + /** + * \return TRUE if foreign key support is enabled, FALSE otherwise + */ + bool IsForeignKeySupportEnabled(); + + /// Set SQLite journal mode + /** + * \param mode the journal mode to be set + * \param database the attached database for which the journal mode should be set. If not given then + * the journal mode of all attached databases is set. + * \return the active journal mode + * \note The journal mode for an in-memory database is either MEMORY or OFF and can not be changed + * to a different value. An attempt to change the journal mode of an in-memory database to any setting + * other than MEMORY or OFF is ignored. Note also that the journal mode cannot be changed while a + * transaction is active. + * The WAL journaling mode uses a write-ahead log instead of a rollback journal to implement transactions. + * The WAL journaling mode is persistent; after being set it stays in effect across multiple database + * connections and after closing and reopening the database. A database in WAL journaling mode can only be + * accessed by SQLite version 3.7.0 or later. + */ + wxSQLite3JournalMode SetJournalMode(wxSQLite3JournalMode mode, const wxString& database = wxEmptyString); + + /// Get the active SQLite journal mode + /** + * \param database the attached database for which the journal mode should be queried (default: main) + * \return active journal mode + */ + wxSQLite3JournalMode GetJournalMode(const wxString& database = wxEmptyString); + /// Check the syntax of an SQL statement given as a wxString /** * \param sql query string @@ -1589,26 +2198,32 @@ public: */ bool CheckSyntax(const char* sql); - /// Execute a insert, update or delete SQL statement given as a wxString + /// Execute a data defining or manipulating SQL statement given as a wxString /** + * Execute a data defining or manipulating SQL statement given as a wxString, + * i.e. create, alter, drop, insert, update, delete and so on * \param sql query string * \return the number of database rows that were changed (or inserted or deleted) */ int ExecuteUpdate(const wxString& sql); - /// Execute a insert, update or delete SQL statement given as a statement buffer + /// Execute a data defining or manipulating SQL statement given as a statement buffer /** + * Execute a data defining or manipulating SQL statement given as a statement buffer, + * i.e. create, alter, drop, insert, update, delete and so on * \param sql query string * \return the number of database rows that were changed (or inserted or deleted) */ int ExecuteUpdate(const wxSQLite3StatementBuffer& sql); - /// Execute a insert, update or delete SQL statement given as a utf-8 character string + /// Execute a data defining or manipulating SQL statement given as a utf-8 character string /** + * Execute a data defining or manipulating SQL statement given as a utf-8 character string, + * i.e. create, alter, drop, insert, update, delete and so on * \param sql query string * \return the number of database rows that were changed (or inserted or deleted) */ - int ExecuteUpdate(const char* sql); + int ExecuteUpdate(const char* sql, bool saveRC = false); /// Execute a SQL query statement given as a wxString /** @@ -1715,29 +2330,72 @@ public: /// Get handle to a read only BLOB /** + * \param rowId + * \param columnName + * \param tableName + * \param dbName */ - wxSQLite3Blob GetReadOnlyBlob(wxLongLong rowId, - const wxString& columnName, - const wxString& tableName, + wxSQLite3Blob GetReadOnlyBlob(wxLongLong rowId, + const wxString& columnName, + const wxString& tableName, const wxString& dbName = wxEmptyString); /// Get handle to a writable BLOB /** + * \param rowId + * \param columnName + * \param tableName + * \param dbName */ - wxSQLite3Blob GetWritableBlob(wxLongLong rowId, - const wxString& columnName, - const wxString& tableName, + wxSQLite3Blob GetWritableBlob(wxLongLong rowId, + const wxString& columnName, + const wxString& tableName, const wxString& dbName = wxEmptyString); /// Get handle to a BLOB /** + * \param rowId + * \param columnName + * \param tableName + * \param dbName + * \param writable */ - wxSQLite3Blob GetBlob(wxLongLong rowId, - const wxString& columnName, - const wxString& tableName, + wxSQLite3Blob GetBlob(wxLongLong rowId, + const wxString& columnName, + const wxString& tableName, const wxString& dbName = wxEmptyString, bool writable = true); + /// Create a named integer value collection + /** + * Invoke this method to create a specific instance of an integer collection object. + * Initially the created collection is empty. Use it's Bind method to actually bind + * an array of values to the collection. + * \param collectionName name of the collection + * \return the new integer collection object. + * + * Each integer value collection object corresponds to a virtual table in the TEMP table + * with a name of collectionName. + * + * The virtual table will be dropped implicitly when the database connection is closed. + */ + wxSQLite3IntegerCollection CreateIntegerCollection(const wxString& collectionName); + + /// Create a named string value collection + /** + * Invoke this method to create a specific instance of a string collection object. + * Initially the created collection is empty. Use it's Bind method to actually bind + * an array of values to the collection. + * \param collectionName name of the collection + * \return the new string collection object. + * + * Each integer value collection object corresponds to a virtual table in the TEMP table + * with a name of collectionName. + * + * The virtual table will be dropped implicitly when the database connection is closed. + */ + wxSQLite3StringCollection CreateStringCollection(const wxString& collectionName); + /// Interrupt a long running query /** * Causes any pending database operation to abort and return at its earliest opportunity. @@ -1763,9 +2421,11 @@ public: * \param argCount number of arguments the scalar function takes. * If this argument is -1 then the scalar function may take any number of arguments. * \param function instance of an scalar function + * \param isDeterministic signals whether the function will always return the same result + * for the same input within a single SQL statement. (Default: false) * \return TRUE on successful registration, FALSE otherwise */ - bool CreateFunction(const wxString& name, int argCount, wxSQLite3ScalarFunction& function); + bool CreateFunction(const wxString& name, int argCount, wxSQLite3ScalarFunction& function, bool isDeterministic = false); /// Create a user-defined aggregate function /** @@ -1774,9 +2434,11 @@ public: * \param argCount number of arguments the aggregate function takes. * If this argument is -1 then the aggregate function may take any number of arguments. * \param function instance of an aggregate function + * \param isDeterministic signals whether the function will always return the same result + * for the same input within a single SQL statement. (Default: false) * \return TRUE on successful registration, FALSE otherwise */ - bool CreateFunction(const wxString& name, int argCount, wxSQLite3AggregateFunction& function); + bool CreateFunction(const wxString& name, int argCount, wxSQLite3AggregateFunction& function, bool isDeterministic = false); /// Create a user-defined authorizer function /** @@ -1798,10 +2460,10 @@ public: * \param commitHook address of an instance of a commit callback function */ void SetCommitHook(wxSQLite3Hook* commitHook); - + /// Create a user-defined rollback callback function /** - * Registers a callback function object to be invoked whenever a transaction is rolled back. + * Registers a callback function object to be invoked whenever a transaction is rolled back. * Registering a NULL function object disables the callback. Only a single rollback hook callback * can be registered at a time. * @@ -1823,6 +2485,41 @@ public: */ void SetUpdateHook(wxSQLite3Hook* updateHook); + /// Create a user-defined Write Ahead Log callback function + /** + * Registers a callback function object to be invoked whenever a commit has taken place in WAL journal mode. + * Registering a NULL function object disables the callback. Only a single Write Ahead Log hook callback + * can be registered at a time. + * \param walHook address of an instance of a Write Ahead Log callback function + */ + void SetWriteAheadLogHook(wxSQLite3Hook* walHook); + + /// Checkpoint database in write-ahead log mode + /** + * Causes an optionally named database to be checkpointed. + * If no database name is given, then a checkpoint is run on all databases associated with this + * database instance. If the database instance is not in write-ahead log mode then this method + * is a harmless no-op. + * \param database name of a database to be checkpointed + * \param mode checkpoint mode, allowed values: WXSQLITE_CHECKPOINT_PASSIVE (default), + * WXSQLITE_CHECKPOINT_FULL, WXSQLITE_CHECKPOINT_RESTART, WXSQLITE_CHECKPOINT_TRUNCATE + * (see http://www.sqlite.org/c3ref/wal_checkpoint_v2.html) + * \param logFrameCount size of write-ahead log in frames + * \param ckptFrameCount number of frames actually checkpointed + * \note The frame counts are set to zero if the SQLite version is below 3.7.6. + */ + void WriteAheadLogCheckpoint(const wxString& database, int mode = WXSQLITE_CHECKPOINT_PASSIVE, + int* logFrameCount = NULL, int* ckptFrameCount = NULL); + + /// Automatically checkpoint database in write-ahead log mode + /** + * Causes any database associated with this database instance to automatically checkpoint after + * committing a transaction if there are N or more frames in the write-ahead log file. + * Passing zero or a negative value as the nFrame parameter disables automatic checkpoints entirely. + * \param frameCount frame threshold + */ + void AutoWriteAheadLogCheckpoint(int frameCount); + /// Create a user-defined collation sequence /** * Registers a callback function object to be invoked whenever this collation is needed @@ -1855,7 +2552,7 @@ public: * \param fileName Name of the shared library containing extension. * \param entryPoint Name of the entry point. */ - void LoadExtension(const wxString& fileName, const wxString& entryPoint = _T("sqlite3_extension_init")); + void LoadExtension(const wxString& fileName, const wxString& entryPoint = wxT("sqlite3_extension_init")); /// Enable or disable loading of database extensions /** @@ -1889,6 +2586,55 @@ public: */ bool IsEncrypted() const { return m_isEncrypted; } + /// Authenticate the user on a database with user authentication + /** + * \param username name of the user to be authenticated + * \param password password + * \return TRUE if the authentication succeeded, FALSE otherwise + */ + bool UserLogin(const wxString& username, const wxString& password); + + /// Add a user to a database with user authentication + /** + * \param username name of the user to be added + * \param password password + * \param isAdmin TRUE to give the new user admin privileges (default: FALSE) + * \return TRUE if the authentication succeeded, FALSE otherwise + */ + bool UserAdd(const wxString& username, const wxString& password, bool isAdmin = false); + + /// Change password and/or privileges of a user on a database with user authentication + /** + * \param username name of the user for which the credentials should be changed + * \param password modified password + * \param isAdmin modified admin privilege + * \return TRUE if the credentials could be changed, FALSE otherwise + */ + bool UserChange(const wxString& username, const wxString& password, bool isAdmin); + + /// Delete a user from a database with user authentication + /** + * \param username name of the user to be removed + * \return TRUE if the user could be deleted, FALSE otherwise + */ + bool UserDelete(const wxString& username); + + /// Check whether a user of a database with user authentication is privileged + /** + * \param username name of the user to be checked + * \return TRUE if the user exists and is privileged, FALSE otherwise + */ + bool UserIsPrivileged(const wxString& username); + + /// Get a list of users for a database with user authentication + /** + * A privileged user can list the users registered in the database. For a non-privileged user this + * method will throw an exception. + * + * \param userList array of user names + */ + void GetUserList(wxArrayString& userList); + /// Query the value of a database limit /** * This method allows to query several database limits. Consult the SQLite @@ -1910,6 +2656,13 @@ public: */ int SetLimit(wxSQLite3LimitType id, int newValue); + /// Free memory used by a database connection + /** + * This method attempts to free as much heap memory as possible from database connection. + * Consult the SQLite documentation for further explanation. + */ + void ReleaseMemory(); + /// Convert database limit type to string /** * \param type The database limit type to be converted to string representation. @@ -1936,10 +2689,23 @@ public: */ static void ShutdownSQLite(); + /// Set temporary directory where SQLite stores temporary files (Windows only) + /** + * On Windows platforms it is recommended to set the temporary directory before + * using any SQLite databases. This method should not be called if you have + * currently open database connections. + * + * \return TRUE if the temporary directory could be set successfully, FALSE otherwise + * + * \note This method is supported only for Windows platforms and + * SQLite versions 3.7.14 or above. For all other platforms FALSE is returned. + */ + static bool SetTemporaryDirectory(const wxString& tempDirectory); + /// Get random bytes /** * SQLite contains a high-quality pseudo-random number generator. - * This method allows to access it for application specofoc purposes. + * This method allows to access it for application specific purposes. * * \param n The amount of random bytes to be created * \param random A memory buffer containing the random bytes on return @@ -1962,12 +2728,63 @@ public: */ static bool IsSharedCacheEnabled() { return ms_sharedCacheEnabled; } + /// Get the version of the wxSQLite3 wrapper + /** + * \return a string which contains the name and version number of the wxSQLite3 wrapper + */ + static wxString GetWrapperVersion(); + /// Get the version of the underlying SQLite3 library /** * \return a string which contains the version number of the library */ static wxString GetVersion(); + /// Get the source id of the underlying SQLite3 library + /** + * \return a string which contains the source id of the library + */ + static wxString GetSourceId(); + + /// Check SQLite compile option + /** + * Check whether the compile option with a given name has been used on building SQLite. + * The SQLITE_ prefix may be omitted from the option name passed to this method. + * + * \param optionName name of the compile option to be queried + * \return TRUE if the compile option was in use, FALSE otherwise + * + * \note If the option name is unknown or if the SQLite version is lower than 3.6.23 + * this method returns FALSE. + */ + static bool CompileOptionUsed(const wxString& optionName); + + /// Get SQLite compile option name + /** + * Get the name of a SQLite compile option at a given index. + * This method allows interating over the list of options that were defined + * at compile time. If the option index is out of range, an empty string is returned. + * The SQLITE_ prefix is omitted from any strings returned by this method. + * + * \param optionIndex Index of the compile option + * \return a string containing the name of the n-th + */ + static wxString GetCompileOptionName(int optionIndex); + + /// Convert journal mode to/from string + /** + * \param mode the wxSQLite3JournalMode enum value signifying the desired journal mode. + * \return the string representation of the journal mode + */ + static wxString ConvertJournalMode(wxSQLite3JournalMode mode); + + /// Convert journal mode to/from string + /** + * \param mode the string representation of the desired journal mode. + * \return the enum representation of the journal mode + */ + static wxSQLite3JournalMode ConvertJournalMode(const wxString& mode); + /// Check whether wxSQLite3 has been compiled with encryption support /** * \return TRUE if encryption support is enabled, FALSE otherwise @@ -1980,12 +2797,24 @@ public: */ static bool HasMetaDataSupport(); + /// Check whether wxSQLite3 has been compiled with user authentication support + /** + * \return TRUE if user authentication support is enabled, FALSE otherwise + */ + static bool HasUserAuthenticationSupport(); + /// Check whether wxSQLite3 has been compiled with loadable extension support /** * \return TRUE if loadable extension support is enabled, FALSE otherwise */ static bool HasLoadExtSupport(); + /// Check whether wxSQLite3 has been compiled with support for named collections + /** + * \return TRUE if named collection support is enabled, FALSE otherwise + */ + static bool HasNamedCollectionSupport(); + /// Check whether wxSQLite3 has support for incremental BLOBs /** * \return TRUE if incremental BLOB support is available, FALSE otherwise @@ -1998,9 +2827,21 @@ public: */ static bool HasSavepointSupport(); + /// Check whether wxSQLite3 has support for SQLite backup/restore + /** + * \return TRUE if SQLite backup/restore is supported, FALSE otherwise + */ + static bool HasBackupSupport(); + + /// Check whether wxSQLite3 has support for SQLite write-ahead log + /** + * \return TRUE if SQLite write-ahead log is supported, FALSE otherwise + */ + static bool HasWriteAheadLogSupport(); + protected: /// Access SQLite's internal database handle - void* GetDatabaseHandle() { return m_db; } + void* GetDatabaseHandle(); /// Activate the callback for needed collations for this database /** @@ -2013,7 +2854,7 @@ protected: /// Request the instantiation of a user defined collation sequence /** * This method is called for every undefined collation sequence. - * In a derived database class this method should call SetCollation registering an + * In a derived database class this method should call SetCollation registering an * appropriate collation class instance. * \param collationName name of the collation which is needed for string comparison */ @@ -2038,16 +2879,26 @@ private: /// Check for valid database connection void CheckDatabase(); - void* m_db; ///< associated SQLite3 database - int m_busyTimeoutMs; ///< Timeout in milli seconds - bool m_isEncrypted; ///< Flag whether the database is encrypted or not + /// Close associated database + void Close(wxSQLite3DatabaseReference* db); + + wxSQLite3DatabaseReference* m_db; ///< associated SQLite3 database + bool m_isOpen; ///< Flag whether the database is opened or not + int m_busyTimeoutMs; ///< Timeout in milli seconds + bool m_isEncrypted; ///< Flag whether the database is encrypted or not + int m_lastRollbackRC; ///< The return code of the last executed rollback operation + int m_backupPageCount; ///< Number of pages per slice for backup and restore operations static bool ms_sharedCacheEnabled; ///< Flag whether SQLite shared cache is enabled static bool ms_hasEncryptionSupport; ///< Flag whether wxSQLite3 has been compiled with encryption support static bool ms_hasMetaDataSupport; ///< Flag whether wxSQLite3 has been compiled with meta data support + static bool ms_hasUserAuthentication; ///< Flag whether wxSQLite3 has been compiled with user authentication support static bool ms_hasLoadExtSupport; ///< Flag whether wxSQLite3 has been compiled with loadable extension support - static bool ms_hasIncrementalBlobSupport; ///< Flag whether wxSQLite3 has support for incremental BLOBs - static bool ms_hasSavepointSupport; ///< Flag whether wxSQLite3 has support for SQLite savepoints + static bool ms_hasNamedCollectionSupport; ///< Flag whether wxSQLite3 has been compiled with support for named collections + static bool ms_hasIncrementalBlobSupport; ///< Flag whether wxSQLite3 has support for incremental BLOBs + static bool ms_hasSavepointSupport; ///< Flag whether wxSQLite3 has support for SQLite savepoints + static bool ms_hasBackupSupport; ///< Flag whether wxSQLite3 has support for SQLite backup/restore + static bool ms_hasWriteAheadLogSupport; ///< Flag whether wxSQLite3 has support for SQLite write-ahead log }; /// RAII class for managing transactions @@ -2058,9 +2909,9 @@ private: * \code * void doDB(wxSQLite3Database *db) * { -* wxSQLite3Transaction t(db); -* doDatabaseOperations(); -* t.Commit(); +* wxSQLite3Transaction t(db); +* doDatabaseOperations(); +* t.Commit(); * } * \endcode * In case doDatabseOperations() fails by throwing an exception, @@ -2073,7 +2924,7 @@ class WXDLLIMPEXP_SQLITE3 wxSQLite3Transaction public: /// Constructor. Start the Transaction. /** - * The constructor starts the transaction. + * The constructor starts the transaction. * \param db Pointer to the open Database. The pointer to the database * is NOT freed on destruction! * \param transactionType Type of the transaction to be opened. @@ -2114,19 +2965,48 @@ public: private: /// New operator (May only be created on the stack) - static void *operator new(size_t size); + static void *operator new(size_t size); /// Delete operator (May not be deleted (for symmetry)) - static void operator delete(void *ptr); + static void operator delete(void *ptr); /// Copy constructor (Must not be copied) - wxSQLite3Transaction(const wxSQLite3Transaction&); + wxSQLite3Transaction(const wxSQLite3Transaction&); /// Assignment operator (Must not be assigned) - wxSQLite3Transaction& operator=(const wxSQLite3Transaction&); + wxSQLite3Transaction& operator=(const wxSQLite3Transaction&); wxSQLite3Database* m_database; ///< Pointer to the associated database (no ownership) }; +#if wxUSE_REGEX + +/// User defined function for REGEXP operator +/** +*/ +class WXDLLIMPEXP_SQLITE3 wxSQLite3RegExpOperator : public wxSQLite3ScalarFunction +{ +public: + /// Constructor + wxSQLite3RegExpOperator(int flags = wxRE_DEFAULT); + + /// Virtual destructor + virtual ~wxSQLite3RegExpOperator(); + + /// Execute the scalar function + /** + * This method is invoked for each appearance of the scalar function in the SQL query. + * \param ctx function context which can be used to access arguments and result value + */ + virtual void Execute(wxSQLite3FunctionContext& ctx); + +private: + wxString m_exprStr; ///< Last regular expression string + wxRegEx m_regEx; ///< Regular expression cache (currently only 1 instance) + int m_flags; ///< Flags for regular expression +}; + +#endif // wxUSE_REGEX + #endif diff --git src/wxsqlite3/wx/wxsqlite3def.h src/wxsqlite3/wx/wxsqlite3def.h index 559652a..16e8d9e 100755 --- src/wxsqlite3/wx/wxsqlite3def.h +++ src/wxsqlite3/wx/wxsqlite3def.h @@ -4,44 +4,8 @@ // Author: Ulrich Telle // Modified by: // Created: 2005-07-14 -// Changes: 2005-10-03 - Upgrade to SQLite3 version 3.2.7 -// 2005-10-09 - Corrected error in wxSQLite3Table::FindColumnIndex -// 2005-10-30 - Added wxGTK build support -// 2005-11-01 - Corrected wxSQLite3ResultSet::GetInt64. -// Added wxSQLite3Table::GetInt64 -// 2005-11-09 - Optionally load SQLite library dynamically -// 2006-02-01 - Upgrade to SQLite3 version 3.3.3 -// 2006-02-12 - Upgrade to SQLite3 version 3.3.4 (wxMSW only) -// 2006-03-15 - Fixed a bug in wxSQLite3Database::Prepare -// Added wxSQLite3Database::IsOpen for convenience -// 2006-06-11 - Upgrade to SQLite3 version 3.3.6 -// Added support for optional SQLite meta data methods -// 2007-01-11 - Upgrade to SQLite3 version 3.3.10 -// Added support for BLOBs as wxMemoryBuffer objects -// Added support for loadable extensions -// Optional support for key based database encryption -// 2007-02-12 - Upgrade to SQLite3 version 3.3.12 -// 2007-05-01 - Upgrade to SQLite3 version 3.3.17 -// 2007-10-28 - Upgrade to SQLite3 version 3.5.2 -// 2007-11-17 - Fixed a bug in wxSQLite3Database::Close -// Eliminated several compile time warnings -// 2007-12-19 - Upgrade to SQLite3 version 3.5.4 -// Fixed a bug in wxSQLite3Database::Begin -// 2008-01-05 - Added support for shared cache mode -// Added support for access to original SQL statement -// for prepared statements (requires SQLite 3.5.3 or above) -// 2008-04-27 - Upgrade to SQLite3 version 3.5.8 -// Fixed several minor issues in the build files -// 2008-06-28 - Upgrade to SQLite3 version 3.5.9 -// 2008-07-19 - Upgrade to SQLite3 version 3.6.0 -// 2008-09-04 - Upgrade to SQLite3 version 3.6.2 -// 2008-11-22 - Upgrade to SQLite3 version 3.6.6 -// 2008-12-18 - Upgrade to SQLite3 version 3.6.7 -// Fixed a bug in method wxSQLite3Table::GetDouble -// 2009-01-14 - Upgrade to SQLite3 version 3.6.10 -// Added savepoint support -// Added IsOk methods to some classes -// +// Changes: (see version history below, and accompanying readme file) +// // Copyright: (c) Ulrich Telle // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// @@ -58,10 +22,10 @@ Several solutions already exist to access SQLite databases. To name just a few: - wxSQLite : - This is a wxWidgets wrapper for version 2.8.x of SQLite. + This is a wxWidgets wrapper for version 2.8.x of SQLite. SQLite version 3.x has a lot more features - which are not supported by this wrapper. - - CppSQLite : + - CppSQLite : Not wxWidgets specific, but with (partial) support for the newer version 3.x of SQLite. - DatabaseLayer : @@ -75,15 +39,201 @@ scalar or aggregate functions. Since SQLite stores strings in UTF-8 encoding, the wxSQLite3 methods provide automatic conversion - between wxStrings and UTF-8 strings. This works best for the \b Unicode builds of \b wxWidgets. - In \b ANSI builds the current locale conversion object \b wxConvCurrent is used for conversion - to/from UTF-8. Special care has to be taken if external administration tools are used to modify - the database contents, since not all of these tools operate in Unicode or UTF-8 mode. + between wxStrings and UTF-8 strings. The methods ToUTF8 and FromUTF8 of the wxString class (available + since wxWidgets 2.8.4) are used for the conversion. Special care has to be taken if external administration + tools are used to modify the database contents, since not all of these tools operate in Unicode or UTF-8 mode. \section version Version history
- + +
3.2.1 - March 2015
+
+Upgrade to SQLite version 3.8.8.3
+ +
+
3.2.0 - December 2014
+
+Upgrade to SQLite version 3.8.7.4
+Added support for the SQLite user authentication module
+ +
+
3.1.1 - June 2014
+
+Upgrade to SQLite version 3.8.5
+ +
+
3.1.0 - May 2014
+
+Upgrade to SQLite version 3.8.4.3
+Added flag isDeterministic to method wxSQLite3Database::CreateFunction
+Added new GUI sample
+Changed implementation of encryption extension (see Readme file in sqlite3 subfolder)
+ +
+
3.0.6 - December 2013
+
+Upgrade to SQLite version 3.8.2
+ +
+
3.0.5 - September 2013
+
+Upgrade to SQLite version 3.8.0.2
+Added support for setting the temporary directory for SQLite on Windows
+ +
+
3.0.4 - August 2013
+
+Upgrade to SQLite version 3.8.0
+Added support for querying performance characteristics of prepared statements
+ + +
+
3.0.3 - March 2013
+
+Upgrade to SQLite version 3.7.16
+ + +
+
3.0.2 - December 2012
+
+Upgrade to SQLite version 3.7.15.1
+Corrected an internal SQLite data structure to avoid compile time warnings
+Changed method wxSQLite3Exception::ErrorCodeAsString to return the error messages provided by SQLite
+ + +
+
3.0.1 - November 2012
+
+Upgrade to SQLite version 3.7.14.1
+Cleaned up and optimized Finalize methods
+Modified wxSQLite3Database::Close to avoid potential memory leaks
+Added method wxSQLite3Database::GetWrapperVersion
+Added method wxSQLite3Database::IsReadOnly
+Added method wxSQLite3Statement::BindUnixDateTime
+Added method wxSQLite3ResultSet::GetUnixDateTime
+Added method wxSQLite3ResultSet::GetAutomaticDateTime
+Fixed a potential memory leak in method wxSQLite3Database::ExecuteUpdate
+Added a wxsqlite3.pc file on request of the Fedora Project developers
+Replaced assert by wxASSERT in wxSQLite3Transaction constructor
+ + +
+
3.0.0 - January 2012
+
+Upgrade to SQLite version 3.7.10
+Added method wxSQLite3Database::Vacuum
+Added method wxSQLite3Database::GetDatabaseFilename
+Added method wxSQLite3Database::ReleaseMemory
+Added method wxSQLite3ResultSet::CursorMoved
+Added method wxSQLite3Statement::IsBusy
+Fixed a bug in method operator= of wxSQLite3StringCollection +causing an endless recursion on assignment
+Dropped the concept of SQLite3 pointer ownership in favor of reference +counted pointers allowing much more flexible use of wxSQLite3 classes
+Modified SQLite3 encryption extension (defining int64 datatype +for SHA2 algorithm)
+Dropped dbadmin sample from build files
+Added Premake support for SQLite3 library with encryption support +and for wxSQLite3 (experimental)
+ +
+
2.1.3 - August 2011
+
+Corrected default behaviour for attached databases in case of +an encrypted main database. (Now the attached database uses the same +encryption key as the main database if no explicit key is given. +Previously the attached database remained unencrypted.)
+Added an optional progress callback for metheods Backup and Restore
+Added method SetBackupRestorePageCount to set the number of pages +to be copied in one cycle of the backup/restore process
+ +
+
2.1.2 - July 2011
+
+Upgrade to SQLite version 3.7.7.1
+Modified wxSQLite3Transaction to make it exception safe
+ +
+
2.1.1 - April 2011
+
+Upgrade to SQLite version 3.7.6.1
+Added convenience method wxSQLite3Statement::ExecuteScalar
+Changed write-ahead log checkpoint method to new version (v2)
+ +
+
2.1.0 - March 2011
+
+Upgrade to SQLite version 3.7.5
+Added wxSQLite+, a database administration application written by Fred Cailleau-Lepetit, +as a GUI sample for wxSQLite3. Minor adjustments were applied to make wxSQLite+ +compatible with wxWidgets 2.9.x. Please note that wxSQLite+ is under GPL license.
+ +
+
2.0.2 - December 2010
+
+Upgrade to SQLite version 3.7.4
+Added support for rebinding a BLOB object to a new row
+Added support for determining if an SQL statement writes the database
+ +
+
2.0.1 - October 2010
+
+Upgrade to SQLite version 3.7.3
+Added parameter transferStatementOwnership to method wxSQLite3Statement::ExecuteQuery +to allow using the returned result set beyond the life time of the wxSQLite3Statement instance
+Eliminated the use of sqlite3_mprintf which caused linker problems when loading SQLite dynamically
+ +
+
2.0.0 - July 2010
+
+Upgrade to SQLite version 3.7.0
+Fixed a bug in class wxSQLite3ResultSet
+Added support for SQLite's write-ahead log journal mode
+Added support for named collections (see class wxSQLite3NamedCollection)
+Changed UTF-8 string handling to use methods To/FromUTF8 of the wxString class (requires wxWidgets 2.8.4 or higher)
+Compatible with wxWidgets 2.9.1
+ +
+
1.9.9 - March 2010
+
+Upgrade to SQLite version 3.6.23
+Fixed a bug when compiling for dynamic loading of SQLite
+Added static methods for accessing the run-time library compilation options diagnostics
+Added mathod FormatV to class wxSQLite3StatementBuffer
+ +
+
1.9.8 - February 2010
+
+Upgrade to SQLite version 3.6.22
+Fixed a bug when compiling without precompiled header support +(by including wx/arrstr.h)
+ +
+
1.9.7 - November 2009
+
+Upgrade to SQLite version 3.6.20
+Added methods to query, enable or disable foreign key support
+ +
+
1.9.6 - September 2009
+
+Upgrade to SQLite version 3.6.18
+Added method to get the SQLite library source id
+Added flags parameter to wxSQLite3Database::Open to allow additional control over the database +connection (see http://www.sqlite.org/c3ref/open.html for further information)
+Fixed a potential memory leak in wxSQLite3Statement class
+Converted encryption extension from C++ to pure C to make it +compatible with the SQLite amalgamation.
+ +
+
1.9.5 - February 2009
+
+Upgrade to SQLite version 3.6.11
+Added user defined function class for REGEXP operator.
+Added support for SQLite backup/restore API, introduced with SQLite 3.6.11
+ +
1.9.4 - January 2009
Upgrade to SQLite version 3.6.10
@@ -284,10 +434,14 @@ First public release
-\author Ulrich Telle (ulrich DOT telle AT gmx DOT de) +\author Ulrich Telle (ulrich DOT telle AT gmx DOT de) \section ackn Acknowledgements +Kudos to Fred Cailleau-Lepetit for developing wxSQLite+ as a sample demonstrating +the wxWidgets components wxAUI and wxSQLite3 and for allowing it to be included +in the wxSQLite3 distribution. + The following people have contributed to wxSQLite3:
    @@ -311,4 +465,16 @@ The following people have contributed to wxSQLite3: #define WXDLLIMPEXP_SQLITE3 #endif +/* + GCC warns about using __declspec on forward declarations + while MSVC complains about forward declarations without + __declspec for the classes later declared with it. To hide this + difference a separate macro for forward declarations is defined: + */ +#if defined(HAVE_VISIBILITY) || (defined(__WINDOWS__) && defined(__GNUC__)) + #define WXDLLIMPEXP_FWD_SQLITE3 +#else + #define WXDLLIMPEXP_FWD_SQLITE3 WXDLLIMPEXP_SQLITE3 +#endif + #endif // _WX_SQLITE3_DEF_H_ diff --git src/wxsqlite3/wx/wxsqlite3dyn.h src/wxsqlite3/wx/wxsqlite3dyn.h index a74672c..153a564 100755 --- src/wxsqlite3/wx/wxsqlite3dyn.h +++ src/wxsqlite3/wx/wxsqlite3dyn.h @@ -16,6 +16,13 @@ DYNFUNC(return, void *, sqlite3_aggregate_context, (sqlite3_c #if SQLITE_VERSION_NUMBER <= 3006000 DYNFUNC(return, int, sqlite3_aggregate_count, (sqlite3_context *p), (p)); #endif +#if SQLITE_VERSION_NUMBER >= 3006011 +DYNFUNC(return, sqlite3_backup*, sqlite3_backup_init, (sqlite3 *pDest, const char *zDestName, sqlite3 *pSource, const char *zSourceName), (pDest, zDestName, pSource, zSourceName)); +DYNFUNC(return, int, sqlite3_backup_step, (sqlite3_backup *p, int nPage), (p, nPage)); +DYNFUNC(return, int, sqlite3_backup_finish, (sqlite3_backup *p), (p)); +DYNFUNC(return, int, sqlite3_backup_remaining, (sqlite3_backup *p), (p)); +DYNFUNC(return, int, sqlite3_backup_pagecount, (sqlite3_backup *p), (p)); +#endif DYNFUNC(return, int, sqlite3_bind_blob, (sqlite3_stmt *pStmt, int i, const void *zData, int nData, void (*xDel)(void*)), (pStmt, i, zData, nData, xDel)); DYNFUNC(return, int, sqlite3_bind_double, (sqlite3_stmt *pStmt, int i, double rValue), (pStmt, i, rValue)); DYNFUNC(return, int, sqlite3_bind_int, (sqlite3_stmt *pStmt, int i, int iValue), (pStmt, i, iValue)); @@ -33,6 +40,9 @@ DYNFUNC(return, int, sqlite3_blob_open, (sqlite3 * DYNFUNC(return, int, sqlite3_blob_close, (sqlite3_blob *pBlob), (pBlob)); DYNFUNC(return, int, sqlite3_blob_bytes, (sqlite3_blob *pBlob), (pBlob)); DYNFUNC(return, int, sqlite3_blob_read, (sqlite3_blob *pBlob, void *z, int n, int iOffset), (pBlob, z, n, iOffset)); +#if SQLITE_VERSION_NUMBER >= 3007004 +DYNFUNC(return, int, sqlite3_blob_reopen, (sqlite3_blob *pBlob, sqlite3_int64 rowid), (pBlob, rowid)); +#endif DYNFUNC(return, int, sqlite3_blob_write, (sqlite3_blob *pBlob, const void *z, int n, int iOffset), (pBlob, z, n, iOffset)); #endif // DYNFUNC(return, int, sqlite3_busy_handler, (sqlite3 *db, int (*xBusy)(void*,int), void *pArg), (db, xBusy, pArg)); @@ -57,6 +67,10 @@ DYNFUNC(return, const unsigned char *, sqlite3_column_text, (sqlite3_s // DYNFUNC(return, const void *, sqlite3_column_text16, (sqlite3_stmt *pStmt, int iCol), (pStmt, iCol)); DYNFUNC(return, int, sqlite3_column_type, (sqlite3_stmt *pStmt, int iCol), (pStmt, iCol)); DYNFUNC(return, void *, sqlite3_commit_hook, (sqlite3 *db, int (*xCallback)(void*), void *pArg), (db, xCallback, pArg)); +#if SQLITE_VERSION_NUMBER >= 3006023 +DYNFUNC(return, int, sqlite3_compileoption_used, (const char *zOptName), (zOptName)); +DYNFUNC(return, const char *, sqlite3_compileoption_get, (int N), (N)); +#endif DYNFUNC(return, int, sqlite3_complete, (const char *sql), (sql)); // DYNFUNC(return, int, sqlite3_complete16, (const void *sql), (sql)); //DYNFUNC(return, sqlite3 *, sqlite3_context_db_handle, (sqlite3_context* ctx) (ctx)); @@ -64,18 +78,38 @@ DYNFUNC(return, int, sqlite3_create_collation, (sqlite3 * // DYNFUNC(return, int, sqlite3_create_collation16, (sqlite3 *db, const char *zName, int eTextRep, void*v, int(*xCompare)(void*,int,const void*,int,const void*)), (db, zName, eTextRep, v, xCompare)); DYNFUNC(return, int, sqlite3_create_function, (sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void*v, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*)), (db, zFunctionName, nArg, eTextRep, v, xFunc, xStep, xFinal)); // DYNFUNC(return, int, sqlite3_create_function16, (sqlite3 *db, const void *zFunctionName, int nArg, int eTextRep, void*v, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*)), (db, zFunctionName, nArg, eTextRep, v, xFunc, xStep, xFinal)); +#if SQLITE_VERSION_NUMBER >= 3007003 +DYNFUNC(return, int, sqlite3_create_function_v2, (sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*)), (db, zFunctionName, nArg, eTextRep, pApp, xFunc, xStep, xFinal, xDestroy)); +#endif +#if SQLITE_VERSION_NUMBER >= 3004001 +DYNFUNC(return, int, sqlite3_create_module, (sqlite3 *db, const char *zName, const sqlite3_module *p, void *pClientData), (db, zName, p, pClientData)); +DYNFUNC(return, int, sqlite3_create_module_v2, (sqlite3 *db, const char *zName, const sqlite3_module *p, void *pClientData, void(*xDestroy)(void*)), (db, zName, p, pClientData, xDestroy)); +#endif // DYNFUNC(return, int, sqlite3_data_count, (sqlite3_stmt *pStmt), (pStmt)); +#if SQLITE_VERSION_NUMBER >= 3007010 +DYNFUNC(return, const char *, sqlite3_db_filename, (sqlite3 *db, const char *zDbName), (db, zDbName)); +#endif // DYNFUNC(return, sqlite3 *, sqlite3_db_handle, (sqlite3_stmt *pStmt), (pStmt)); +#if SQLITE_VERSION_NUMBER >= 3007011 +DYNFUNC(return, int, sqlite3_db_readonly, (sqlite3 *db, const char *zDbName), (db, zDbName)); +#endif +#if SQLITE_VERSION_NUMBER >= 3007010 +DYNFUNC(return, int, sqlite3_db_release_memory, (sqlite3 *db), (db)); +#endif +DYNFUNC(return, int, sqlite3_declare_vtab, (sqlite3 *db, const char *zSQL), (db, zSQL)); DYNFUNC(return, int, sqlite3_enable_load_extension, (sqlite3 *db, int onoff), (db, onoff)); DYNFUNC(return, int, sqlite3_enable_shared_cache, (int enable), (enable)); // DYNFUNC(return, int, sqlite3_errcode, (sqlite3 *db), (db)); DYNFUNC(return, const char *, sqlite3_errmsg, (sqlite3 *db), (db)); // DYNFUNC(return, const void *, sqlite3_errmsg16, (sqlite3 *db), (db)); +#if SQLITE_VERSION_NUMBER >= 3007015 +DYNFUNC(return, const char *, sqlite3_errstr, (int rc), (rc)); +#endif DYNFUNC(return, int, sqlite3_exec, (sqlite3 *db, const char *sql, sqlite3_callback c, void *v, char **errmsg), (db, sql, c, v, errmsg)); // DYNFUNC(return, int, sqlite3_expired, (sqlite3_stmt *pStmt), (pStmt)); DYNFUNC(return, int, sqlite3_extended_result_codes, (sqlite3 *db, int onoff), (db, onoff)); DYNFUNC(return, int, sqlite3_finalize, (sqlite3_stmt *pStmt), (pStmt)); -DYNFUNC(;, void, sqlite3_free, (char *z), (z)); +DYNFUNC(;, void, sqlite3_free, (void *z), (z)); DYNFUNC(;, void, sqlite3_free_table, (char **result), (result)); DYNFUNC(return, int, sqlite3_get_autocommit, (sqlite3 *db), (db)); // DYNFUNC(return, void *, sqlite3_get_auxdata, (sqlite3_context *pCtx, int iArg), (pCtx, iArg)); @@ -90,6 +124,7 @@ DYNFUNC(return, const char *, sqlite3_libversion, (void), () // DYNFUNC(return, int, sqlite3_libversion_number, (void), ()); DYNFUNC(return, int, sqlite3_limit, (sqlite3 *db, int id, int newVal), (db, id, newVal)); DYNFUNC(return, int, sqlite3_load_extension, (sqlite3 *db, const char *zFile, const char *zProc, char **pzErrMsg), (db, zFile, zProc, pzErrMsg)); +DYNFUNC(return, void*, sqlite3_malloc, (int size), (size)); // DYNFUNC(return, char *, sqlite3_mprintf, (const char *zFormat,...), (zFormat,...)); #if SQLITE_VERSION_NUMBER >= 3006000 DYNFUNC(return, sqlite3_stmt *, sqlite3_next_stmt, (sqlite3 *pDb, sqlite3_stmt *pStmt), (pDb, pStmt)); @@ -100,7 +135,8 @@ DYNFUNC(return, int, sqlite3_open_v2, (const cha DYNFUNC(return, int, sqlite3_prepare_v2, (sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail), (db, zSql, nBytes, ppStmt, pzTail)); // DYNFUNC(return, int, sqlite3_prepare16_v2, (sqlite3 *db, const void *zSql, int nBytes, sqlite3_stmt **ppStmt, const void **pzTail), (db, zSql, nBytes, ppStmt, pzTail)); // DYNFUNC(;, void, sqlite3_progress_handler, (sqlite3 *db, int nOps, int (*xProgress)(void*), void *pArg), (db, nOps, xProgress, pArg)); -DYNFUNC(;, void, sqlite3_randomness, (int N, void *P) (N, P)); +DYNFUNC(;, void, sqlite3_randomness, (int N, void *P), (N, P)); +DYNFUNC(return, void*, sqlite3_realloc, (void* ptr, int newSize), (ptr, newSize)); DYNFUNC(return, int, sqlite3_reset, (sqlite3_stmt *pStmt), (pStmt)); DYNFUNC(;, void, sqlite3_result_blob, (sqlite3_context *pCtx, const void *z, int n, void (*xDel)(void *)), (pCtx, z, n, xDel)); DYNFUNC(;, void, sqlite3_result_double, (sqlite3_context *pCtx, double rVal), (pCtx, rVal)); @@ -118,20 +154,42 @@ DYNFUNC(;, void, sqlite3_result_value, (sqlite3_c DYNFUNC(;, void, sqlite3_result_zeroblob, (sqlite3_context *pCtx, int n), (pCtx, n)); #endif DYNFUNC(return, void *, sqlite3_rollback_hook, (sqlite3 *db, void (*xCallback)(void*), void *pArg), (db, xCallback, pArg)); +#if SQLITE_VERSION_NUMBER >= 3007003 +DYNFUNC(return, int, sqlite3_rtree_geometry_callback, (sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes), void *pContext), (db, zGeom, xGeom, pContext)); +#endif +#if SQLITE_VERSION_NUMBER >= 3008005 +DYNFUNC(return, int, sqlite3_rtree_query_callback, (sqlite3 *db, const char *zQueryFunc, int (*xQueryFunc)(sqlite3_rtree_query_info*), void *pContext, void (*xDestructor)(void*)), (db, zQueryFunc, xQueryFunc, pContext, xDestructor)); +#endif DYNFUNC(return, int, sqlite3_set_authorizer, (sqlite3 *db, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pArg), (db, xAuth, pArg)); // DYNFUNC(;, void, sqlite3_set_auxdata, (sqlite3_context *pCtx, int iArg, void *pAux, void (*xDelete)(void*)), (pCtx, iArg, pAux, xDelete)); #if SQLITE_VERSION_NUMBER >= 3006000 DYNFUNC(return, int, sqlite3_shutdown, (void), ()); #endif +DYNFUNC(return, int, sqlite3_sleep, (int ms), (ms)); // DYNFUNC(return, char *, sqlite3_snprintf, (int n, char *zBuf, const char *zFormat, ...), (n, zBuf, zFormat, ...)); +#if SQLITE_VERSION_NUMBER >= 3006018 +DYNFUNC(return, const char *, sqlite3_sourceid, (void), ()); +#endif #if SQLITE_VERSION_NUMBER >= 3005003 DYNFUNC(return, const char *, sqlite3_sql, (sqlite3_stmt *pStmt), (pStmt)); #endif DYNFUNC(return, int, sqlite3_step, (sqlite3_stmt *pStmt), (pStmt)); +#if SQLITE_VERSION_NUMBER >= 3007010 +DYNFUNC(return, int, sqlite3_stmt_busy, (sqlite3_stmt* pStmt), (pStmt)); +#endif +#if SQLITE_VERSION_NUMBER >= 3007004 +DYNFUNC(return, int, sqlite3_stmt_readonly, (sqlite3_stmt *pStmt), (pStmt)); +#endif +#if SQLITE_VERSION_NUMBER >= 3007000 +DYNFUNC(return, int, sqlite3_stmt_status, (sqlite3_stmt* pStmt, int op, int resetFlg), (pStmt, op, resetFlg)); +#endif DYNFUNC(return, int, sqlite3_threadsafe, (void), ()); // DYNFUNC(return, int, sqlite3_total_changes, (sqlite3 *db), (db)); // DYNFUNC(return, void *, sqlite3_trace, (sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg), (db, xTrace, pArg)); // DYNFUNC(return, int, sqlite3_transfer_bindings, (sqlite3_stmt *pStmt, sqlite3_stmt *pStmt), (pStmt, pStmt)); +#if SQLITE_VERSION_NUMBER >= 3007000 +// DYNFUNC(return, int, sqlite3_unlock_notify, (sqlite3 *pBlocked, void (*xNotify)(void **apArg, int nArg), void *pNotifyArg), (pBlocked, xNotify, pNotifyArg)); +#endif DYNFUNC(return, void *, sqlite3_update_hook, (sqlite3 *db, void (*xCallback)(void *, int, char const *, char const *, wxsqlite_int64), void *pArg), (db, xCallback, pArg)); DYNFUNC(return, void *, sqlite3_user_data, (sqlite3_context *pCtx), (pCtx)); DYNFUNC(return, const void *, sqlite3_value_blob, (sqlite3_value *pVal), (pVal)); @@ -146,7 +204,19 @@ DYNFUNC(return, const unsigned char *, sqlite3_value_text, (sqlite3_v // DYNFUNC(return, const void *, sqlite3_value_text16le, (sqlite3_value *pVal), (pVal)); DYNFUNC(return, int, sqlite3_value_type, (sqlite3_value *pVal), (pVal)); DYNFUNC(return, char *, sqlite3_vmprintf, (const char* p, va_list ap), (p, ap)); - +#if SQLITE_VERSION_NUMBER >= 3007000 +DYNFUNC(return, int, sqlite3_wal_autocheckpoint, (sqlite3 *db, int N), (db, N)); +DYNFUNC(return, int, sqlite3_wal_checkpoint, (sqlite3 *db, const char *zDb), (db, zDb)); +#if SQLITE_VERSION_NUMBER >= 3007006 +DYNFUNC(return, int, sqlite3_wal_checkpoint_v2, (sqlite3 *db, const char *zDb, int mode, int* logFrameCount, int* ckptFrameCount), (db, zDb, mode, logFrameCount, ckptFrameCount)); +#endif +DYNFUNC(return, void *, sqlite3_wal_hook, (sqlite3 *db, int (*xCallback)(void *, sqlite3 *, const char*, int), void *pArg), (db, xCallback, pArg)); +#endif +#if SQLITE_VERSION_NUMBER >= 3007014 +//#if defined(__WXMSW__) +DYNFUNC(return, int, sqlite3_win32_set_directory, (DWORD type, LPCWSTR zValue), (type, zValue)); +//#endif +#endif #if WXSQLITE3_HAVE_METADATA DYNFUNC(return, const char *, sqlite3_column_database_name, (sqlite3_stmt *pStmt, int iCol), (pStmt, iCol)); // DYNFUNC(return, const void *, sqlite3_column_database_name16, (sqlite3_stmt *pStmt, int iCol), (pStmt, iCol)); @@ -157,6 +227,20 @@ DYNFUNC(return, const char *, sqlite3_column_origin_name, (sqlite3_s DYNFUNC(return, int, sqlite3_table_column_metadata, (sqlite3 *db, const char *zDbName, const char *zTableName, const char *zColumnName, char const **pzDataType, char const **pzCollSeq, int *pNotNull, int *pPrimaryKey, int *pAutoinc), (db, zDbName, zTableName, zColumnName, pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc)); #endif +#if WXSQLITE3_USER_AUTHENTICATION +#if SQLITE_VERSION_NUMBER >= 3008007 +DYNFUNC(return, int, sqlite3_user_authenticate, (sqlite3 *db, const char *zUsername, const char *aPW, int nPW), (db, zUsername, aPW, nPW)); +DYNFUNC(return, int, sqlite3_user_add, (sqlite3 *db, const char *zUsername, const char *aPW, int nPW, int isAdmin), (db, zUsername, aPW, nPW, isAdmin)); +DYNFUNC(return, int, sqlite3_user_change, (sqlite3 *db, const char *zUsername, const char *aPW, int nPW, int isAdmin), (db, zUsername, aPW, nPW, isAdmin)); +DYNFUNC(return, int, sqlite3_user_delete, (sqlite3 *db, const char *zUsername), (db, zUsername)); +#endif +#endif + +#if WXSQLITE3_HAVE_CODEC +DYNFUNC(return, int, sqlite3_key, (sqlite3 *db, const void *pKey, int nKey), (db, pKey, nKey)); +DYNFUNC(return, int, sqlite3_rekey, (sqlite3 *db, const void *pKey, int nKey), (db, pKey, nKey)); +#endif + // SQLcrypt API // Additional error codes: SQLCRYPT3_TOOSHORT, SQLCRYPT3_TOOLONG, SQLCRYPT3_BADLIC // DYNFUNC(return, int, sqlcrypt3_passphrase, (sqlite3 *db, const char *key, int codec, char **errmsg), (db, key, codec, errmsg)); diff --git src/wxsqlite3/wx/wxsqlite3opt.h src/wxsqlite3/wx/wxsqlite3opt.h index afcc641..ba8dc79 100755 --- src/wxsqlite3/wx/wxsqlite3opt.h +++ src/wxsqlite3/wx/wxsqlite3opt.h @@ -4,7 +4,7 @@ // Author: Ulrich Telle // Modified by: // Created: 2007-02-01 -// +// // Copyright: (c) Ulrich Telle // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// @@ -29,6 +29,12 @@ #define WXSQLITE3_HAVE_METADATA 0 #endif +//! To enable SQLite's user authentication define WXSQLITE3_USER_AUTHENTICATION as 1 here. +//! Attention: SQLite needs to be compiled with SQLITE_USER_AUTHENTICATION for this to work +#ifndef WXSQLITE3_USER_AUTHENTICATION +#define WXSQLITE3_USER_AUTHENTICATION 0 +#endif + //! To enable SQLite's database encryption support define WXSQLITE3_HAVE_CODEC as 1 here. //! Attention: SQLite needs to be compiled with SQLITE_HAS_CODEC for this to work #ifndef WXSQLITE3_HAVE_CODEC @@ -41,4 +47,11 @@ #define WXSQLITE3_HAVE_LOAD_EXTENSION 0 #endif +//! To disable support for named collections define WXSQLITE3_USE_NAMED_COLLECTIONS as 0 here. +//! Attention: if WXSQLITE3_USE_NAMED_COLLECTIONS is defined as 1 (default) SQLite needs to be +//! compiled without SQLITE_OMIT_VIRTUALTABLE for this to work +#ifndef WXSQLITE3_USE_NAMED_COLLECTIONS +#define WXSQLITE3_USE_NAMED_COLLECTIONS 1 +#endif + #endif // _WX_SQLITE3_OPT_H_ diff --git src/wxsqlite3/wxsqlite3.cpp src/wxsqlite3/wxsqlite3.cpp index 36dfc34..8d4cdfa 100755 --- src/wxsqlite3/wxsqlite3.cpp +++ src/wxsqlite3/wxsqlite3.cpp @@ -25,6 +25,9 @@ #include "wx/wx.h" #endif +#include "wx/regex.h" +#include "wx/thread.h" + #include "wx/wxsqlite3.h" #include "wx/wxsqlite3opt.h" @@ -35,8 +38,23 @@ #pragma warning (disable:4610) #endif -#include "sqlite3.h" +#if WXSQLITE3_HAVE_CODEC +#define SQLITE_HAS_CODEC 1 +#else +#define SQLITE_HAS_CODEC 0 +#endif +#include "sqlite3.h" +#if WXSQLITE3_USER_AUTHENTICATION +#define SQLITE_USER_AUTHENTICATION +#ifdef __cplusplus +extern "C" { +#endif +#include "sqlite3userauth.h" +#ifdef __cplusplus +} +#endif +#endif // Dynamic loading of the SQLite library #if wxUSE_DYNAMIC_SQLITE3_LOAD @@ -88,10 +106,69 @@ static void InitSQLite3DLL() #include "wx/wxsqlite3dyn.h" #undef DYNFUNC +#else +// Define Windows specific SQLite API functions (not defined in sqlite3.h) +#if SQLITE_VERSION_NUMBER >= 3007014 +#if defined(__WXMSW__) +#ifdef __cplusplus +extern "C" { +#endif +SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue); +#ifdef __cplusplus +} +#endif +#endif +#endif #endif // wxUSE_DYNAMIC_SQLITE3_LOAD +typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,const char*); + // Error messages +#if wxCHECK_VERSION(2,9,0) +const char* wxERRMSG_NODB = wxTRANSLATE("No Database opened"); +const char* wxERRMSG_NOSTMT = wxTRANSLATE("Statement not accessible"); +const char* wxERRMSG_NOMEM = wxTRANSLATE("Out of memory"); +const char* wxERRMSG_DECODE = wxTRANSLATE("Cannot decode binary"); +const char* wxERRMSG_INVALID_INDEX = wxTRANSLATE("Invalid field index"); +const char* wxERRMSG_INVALID_NAME = wxTRANSLATE("Invalid field name"); +const char* wxERRMSG_INVALID_ROW = wxTRANSLATE("Invalid row index"); +const char* wxERRMSG_INVALID_QUERY = wxTRANSLATE("Invalid scalar query"); +const char* wxERRMSG_INVALID_BLOB = wxTRANSLATE("Invalid BLOB handle"); + +const char* wxERRMSG_NORESULT = wxTRANSLATE("Null Results pointer"); +const char* wxERRMSG_BIND_STR = wxTRANSLATE("Error binding string param"); +const char* wxERRMSG_BIND_INT = wxTRANSLATE("Error binding int param"); +const char* wxERRMSG_BIND_INT64 = wxTRANSLATE("Error binding int64 param"); +const char* wxERRMSG_BIND_DBL = wxTRANSLATE("Error binding double param"); +const char* wxERRMSG_BIND_BLOB = wxTRANSLATE("Error binding blob param"); +const char* wxERRMSG_BIND_DATETIME = wxTRANSLATE("Error binding date/time param"); +const char* wxERRMSG_BIND_NULL = wxTRANSLATE("Error binding NULL param"); +const char* wxERRMSG_BIND_ZEROBLOB = wxTRANSLATE("Error binding zero blob param"); +const char* wxERRMSG_BIND_CLEAR = wxTRANSLATE("Error clearing bindings"); + +const char* wxERRMSG_NOMETADATA = wxTRANSLATE("Meta data support not available"); +const char* wxERRMSG_NOCODEC = wxTRANSLATE("Encryption support not available"); +const char* wxERRMSG_NOLOADEXT = wxTRANSLATE("Loadable extension support not available"); +const char* wxERRMSG_NOINCBLOB = wxTRANSLATE("Incremental BLOB support not available"); +const char* wxERRMSG_NOBLOBREBIND = wxTRANSLATE("Rebind BLOB support not available"); +const char* wxERRMSG_NOSAVEPOINT = wxTRANSLATE("Savepoint support not available"); +const char* wxERRMSG_NOBACKUP = wxTRANSLATE("Backup/restore support not available"); +const char* wxERRMSG_NOWAL = wxTRANSLATE("Write Ahead Log support not available"); +const char* wxERRMSG_NOCOLLECTIONS = wxTRANSLATE("Named collection support not available"); + +const char* wxERRMSG_SHARED_CACHE = wxTRANSLATE("Setting SQLite shared cache mode failed"); + +const char* wxERRMSG_INITIALIZE = wxTRANSLATE("Initialization of SQLite failed"); +const char* wxERRMSG_SHUTDOWN = wxTRANSLATE("Shutdown of SQLite failed"); +const char* wxERRMSG_TEMPDIR = wxTRANSLATE("Setting temporary directory failed"); + +const char* wxERRMSG_SOURCEDB_BUSY = wxTRANSLATE("Source database is busy"); +const char* wxERRMSG_DBOPEN_FAILED = wxTRANSLATE("Database open failed"); +const char* wxERRMSG_DBCLOSE_FAILED = wxTRANSLATE("Database close failed"); +const char* wxERRMSG_DBASSIGN_FAILED = wxTRANSLATE("Database assignment failed"); +const char* wxERRMSG_FINALIZE_FAILED = wxTRANSLATE("Finalize failed"); +#else const wxChar* wxERRMSG_NODB = wxTRANSLATE("No Database opened"); const wxChar* wxERRMSG_NOSTMT = wxTRANSLATE("Statement not accessible"); const wxChar* wxERRMSG_NOMEM = wxTRANSLATE("Out of memory"); @@ -117,25 +194,215 @@ const wxChar* wxERRMSG_NOMETADATA = wxTRANSLATE("Meta data support not availa const wxChar* wxERRMSG_NOCODEC = wxTRANSLATE("Encryption support not available"); const wxChar* wxERRMSG_NOLOADEXT = wxTRANSLATE("Loadable extension support not available"); const wxChar* wxERRMSG_NOINCBLOB = wxTRANSLATE("Incremental BLOB support not available"); +const wxChar* wxERRMSG_NOBLOBREBIND = wxTRANSLATE("Rebind BLOB support not available"); const wxChar* wxERRMSG_NOSAVEPOINT = wxTRANSLATE("Savepoint support not available"); +const wxChar* wxERRMSG_NOBACKUP = wxTRANSLATE("Backup/restore support not available"); +const wxChar* wxERRMSG_NOWAL = wxTRANSLATE("Write Ahead Log support not available"); +const wxChar* wxERRMSG_NOCOLLECTIONS = wxTRANSLATE("Named collection support not available"); const wxChar* wxERRMSG_SHARED_CACHE = wxTRANSLATE("Setting SQLite shared cache mode failed"); const wxChar* wxERRMSG_INITIALIZE = wxTRANSLATE("Initialization of SQLite failed"); const wxChar* wxERRMSG_SHUTDOWN = wxTRANSLATE("Shutdown of SQLite failed"); +const wxChar* wxERRMSG_TEMPDIR = wxTRANSLATE("Setting temporary directory failed"); -// ---------------------------------------------------------------------------- -// inline conversion from UTF8 strings to wxStringe -// ---------------------------------------------------------------------------- +const wxChar* wxERRMSG_SOURCEDB_BUSY = wxTRANSLATE("Source database is busy"); +const wxChar* wxERRMSG_DBOPEN_FAILED = wxTRANSLATE("Database open failed"); +const wxChar* wxERRMSG_DBCLOSE_FAILED = wxTRANSLATE("Database close failed"); +const wxChar* wxERRMSG_DBASSIGN_FAILED = wxTRANSLATE("Database assignment failed"); +const wxChar* wxERRMSG_FINALIZE_FAILED = wxTRANSLATE("Finalize failed"); +#endif + +// Critical sections are used to make access to it thread safe if necessary. +#if wxUSE_THREADS +static wxCriticalSection gs_csDatabase; +static wxCriticalSection gs_csStatment; +static wxCriticalSection gs_csBlob; +#endif -inline wxString UTF8toWxString(const char* localValue) +class wxSQLite3DatabaseReference { -#if wxUSE_UNICODE - return wxString(localValue, wxConvUTF8); -#else - return wxString(wxConvUTF8.cMB2WC(localValue), *wxConvCurrent); +public: + /// Default constructor + wxSQLite3DatabaseReference(sqlite3* db = NULL) + : m_db(db) + { + m_db = db; + if (m_db != NULL) + { + m_isValid = true; + m_refCount = 1; + } + else + { + m_isValid = false; + m_refCount = 0; + } + } + + /// Default destructor + virtual ~wxSQLite3DatabaseReference() + { + } + +private: + /// Thread safe increment of the reference count + int IncrementRefCount() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csDatabase); #endif -} + return ++m_refCount; + } + + void Invalidate() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csDatabase); +#endif + m_isValid = false; + } + + /// Thread safe decrement of the reference count + int DecrementRefCount() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csDatabase); +#endif + if (m_refCount > 0) --m_refCount; + return m_refCount; + } + + sqlite3* m_db; ///< SQLite database reference + int m_refCount; ///< Reference count + bool m_isValid; ///< SQLite database reference is valid + + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3Database; + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3ResultSet; + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3Statement; + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3Blob; +}; + +class wxSQLite3StatementReference +{ +public: + /// Default constructor + wxSQLite3StatementReference(sqlite3_stmt* stmt = NULL) + : m_stmt(stmt) + { + m_stmt = stmt; + if (m_stmt != NULL) + { + m_isValid = true; + m_refCount = 0; + } + else + { + m_isValid = false; + m_refCount = 0; + } + } + + /// Default destructor + virtual ~wxSQLite3StatementReference() + { + } + +private: + /// Thread safe increment of the reference count + int IncrementRefCount() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csStatment); +#endif + return ++m_refCount; + } + + /// Thread safe decrement of the reference count + int DecrementRefCount() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csStatment); +#endif + if (m_refCount > 0) --m_refCount; + return m_refCount; + } + + void Invalidate() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csStatment); +#endif + m_isValid = false; + } + + sqlite3_stmt* m_stmt; ///< SQLite statement reference + int m_refCount; ///< Reference count + bool m_isValid; ///< SQLite statement reference is valid + + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3ResultSet; + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3Statement; +}; + +class wxSQLite3BlobReference +{ +public: + /// Default constructor + wxSQLite3BlobReference(sqlite3_blob* blob = NULL) + : m_blob(blob) + { + m_blob = blob; + if (m_blob != NULL) + { + m_isValid = true; + m_refCount = 0; + } + else + { + m_isValid = false; + m_refCount = 0; + } + } + + /// Default destructor + virtual ~wxSQLite3BlobReference() + { + } + +private: + /// Thread safe increment of the reference count + int IncrementRefCount() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csBlob); +#endif + return ++m_refCount; + } + + /// Thread safe decrement of the reference count + int DecrementRefCount() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csBlob); +#endif + if (m_refCount > 0) --m_refCount; + return m_refCount; + } + + void Invalidate() + { +#if wxUSE_THREADS + wxCriticalSectionLocker locker(gs_csBlob); +#endif + m_isValid = false; + } + + sqlite3_blob* m_blob; ///< SQLite blob reference + int m_refCount; ///< Reference count + bool m_isValid; ///< SQLite statement reference is valid + + friend class WXDLLIMPEXP_FWD_SQLITE3 wxSQLite3Blob; +}; // ---------------------------------------------------------------------------- // inline conversion from wxString to wxLongLong @@ -175,8 +442,8 @@ inline wxLongLong ConvertStringToLongLong(const wxString& str, wxLongLong defVal wxSQLite3Exception::wxSQLite3Exception(int errorCode, const wxString& errorMsg) : m_errorCode(errorCode) { - m_errorMessage = ErrorCodeAsString(errorCode) + _T("[") + - wxString::Format(_T("%d"), errorCode) + _T("]: ") + + m_errorMessage = ErrorCodeAsString(errorCode) + wxT("[") + + wxString::Format(wxT("%d"), errorCode) + wxT("]: ") + wxGetTranslation(errorMsg); } @@ -187,65 +454,94 @@ wxSQLite3Exception::wxSQLite3Exception(const wxSQLite3Exception& e) const wxString wxSQLite3Exception::ErrorCodeAsString(int errorCode) { +#if SQLITE_VERSION_NUMBER >= 3007015 + if (errorCode == WXSQLITE_ERROR) + { + return wxT("WXSQLITE_ERROR"); + } + else + { + const char* errmsg = sqlite3_errstr(errorCode); + return wxString::FromUTF8(errmsg); + } +#else switch (errorCode) { - case SQLITE_OK : return _T("SQLITE_OK"); - case SQLITE_ERROR : return _T("SQLITE_ERROR"); - case SQLITE_INTERNAL : return _T("SQLITE_INTERNAL"); - case SQLITE_PERM : return _T("SQLITE_PERM"); - case SQLITE_ABORT : return _T("SQLITE_ABORT"); - case SQLITE_BUSY : return _T("SQLITE_BUSY"); - case SQLITE_LOCKED : return _T("SQLITE_LOCKED"); - case SQLITE_NOMEM : return _T("SQLITE_NOMEM"); - case SQLITE_READONLY : return _T("SQLITE_READONLY"); - case SQLITE_INTERRUPT : return _T("SQLITE_INTERRUPT"); - case SQLITE_IOERR : return _T("SQLITE_IOERR"); - case SQLITE_CORRUPT : return _T("SQLITE_CORRUPT"); - case SQLITE_NOTFOUND : return _T("SQLITE_NOTFOUND"); - case SQLITE_FULL : return _T("SQLITE_FULL"); - case SQLITE_CANTOPEN : return _T("SQLITE_CANTOPEN"); - case SQLITE_PROTOCOL : return _T("SQLITE_PROTOCOL"); - case SQLITE_EMPTY : return _T("SQLITE_EMPTY"); - case SQLITE_SCHEMA : return _T("SQLITE_SCHEMA"); - case SQLITE_TOOBIG : return _T("SQLITE_TOOBIG"); - case SQLITE_CONSTRAINT : return _T("SQLITE_CONSTRAINT"); - case SQLITE_MISMATCH : return _T("SQLITE_MISMATCH"); - case SQLITE_MISUSE : return _T("SQLITE_MISUSE"); - case SQLITE_NOLFS : return _T("SQLITE_NOLFS"); - case SQLITE_AUTH : return _T("SQLITE_AUTH"); - case SQLITE_FORMAT : return _T("SQLITE_FORMAT"); - case SQLITE_RANGE : return _T("SQLITE_RANGE"); - case SQLITE_NOTADB : return _T("SQLITE_NOTADB"); - case SQLITE_ROW : return _T("SQLITE_ROW"); - case SQLITE_DONE : return _T("SQLITE_DONE"); + case SQLITE_OK : return wxT("SQLITE_OK"); + case SQLITE_ERROR : return wxT("SQLITE_ERROR"); + case SQLITE_INTERNAL : return wxT("SQLITE_INTERNAL"); + case SQLITE_PERM : return wxT("SQLITE_PERM"); + case SQLITE_ABORT : return wxT("SQLITE_ABORT"); + case SQLITE_BUSY : return wxT("SQLITE_BUSY"); + case SQLITE_LOCKED : return wxT("SQLITE_LOCKED"); + case SQLITE_NOMEM : return wxT("SQLITE_NOMEM"); + case SQLITE_READONLY : return wxT("SQLITE_READONLY"); + case SQLITE_INTERRUPT : return wxT("SQLITE_INTERRUPT"); + case SQLITE_IOERR : return wxT("SQLITE_IOERR"); + case SQLITE_CORRUPT : return wxT("SQLITE_CORRUPT"); + case SQLITE_NOTFOUND : return wxT("SQLITE_NOTFOUND"); + case SQLITE_FULL : return wxT("SQLITE_FULL"); + case SQLITE_CANTOPEN : return wxT("SQLITE_CANTOPEN"); + case SQLITE_PROTOCOL : return wxT("SQLITE_PROTOCOL"); + case SQLITE_EMPTY : return wxT("SQLITE_EMPTY"); + case SQLITE_SCHEMA : return wxT("SQLITE_SCHEMA"); + case SQLITE_TOOBIG : return wxT("SQLITE_TOOBIG"); + case SQLITE_CONSTRAINT : return wxT("SQLITE_CONSTRAINT"); + case SQLITE_MISMATCH : return wxT("SQLITE_MISMATCH"); + case SQLITE_MISUSE : return wxT("SQLITE_MISUSE"); + case SQLITE_NOLFS : return wxT("SQLITE_NOLFS"); + case SQLITE_AUTH : return wxT("SQLITE_AUTH"); + case SQLITE_FORMAT : return wxT("SQLITE_FORMAT"); + case SQLITE_RANGE : return wxT("SQLITE_RANGE"); + case SQLITE_NOTADB : return wxT("SQLITE_NOTADB"); + case SQLITE_ROW : return wxT("SQLITE_ROW"); + case SQLITE_DONE : return wxT("SQLITE_DONE"); // Extended error codes - case SQLITE_IOERR_READ : return _T("SQLITE_IOERR_READ"); - case SQLITE_IOERR_SHORT_READ : return _T("SQLITE_IOERR_SHORT_READ"); - case SQLITE_IOERR_WRITE : return _T("SQLITE_IOERR_WRITE"); - case SQLITE_IOERR_FSYNC : return _T("SQLITE_IOERR_FSYNC"); - case SQLITE_IOERR_DIR_FSYNC : return _T("SQLITE_IOERR_DIR_FSYNC"); - case SQLITE_IOERR_TRUNCATE : return _T("SQLITE_IOERR_TRUNCATE"); - case SQLITE_IOERR_FSTAT : return _T("SQLITE_IOERR_FSTAT"); - case SQLITE_IOERR_UNLOCK : return _T("SQLITE_IOERR_UNLOCK"); - case SQLITE_IOERR_RDLOCK : return _T("SQLITE_IOERR_RDLOCK"); - case SQLITE_IOERR_DELETE : return _T("SQLITE_IOERR_DELETE"); + case SQLITE_IOERR_READ : return wxT("SQLITE_IOERR_READ"); + case SQLITE_IOERR_SHORT_READ : return wxT("SQLITE_IOERR_SHORT_READ"); + case SQLITE_IOERR_WRITE : return wxT("SQLITE_IOERR_WRITE"); + case SQLITE_IOERR_FSYNC : return wxT("SQLITE_IOERR_FSYNC"); + case SQLITE_IOERR_DIR_FSYNC : return wxT("SQLITE_IOERR_DIR_FSYNC"); + case SQLITE_IOERR_TRUNCATE : return wxT("SQLITE_IOERR_TRUNCATE"); + case SQLITE_IOERR_FSTAT : return wxT("SQLITE_IOERR_FSTAT"); + case SQLITE_IOERR_UNLOCK : return wxT("SQLITE_IOERR_UNLOCK"); + case SQLITE_IOERR_RDLOCK : return wxT("SQLITE_IOERR_RDLOCK"); + case SQLITE_IOERR_DELETE : return wxT("SQLITE_IOERR_DELETE"); #if SQLITE_VERSION_NUMBER >= 3004000 - case SQLITE_IOERR_BLOCKED : return _T("SQLITE_IOERR_BLOCKED"); + case SQLITE_IOERR_BLOCKED : return wxT("SQLITE_IOERR_BLOCKED"); #endif #if SQLITE_VERSION_NUMBER >= 3005001 - case SQLITE_IOERR_NOMEM : return _T("SQLITE_IOERR_NOMEM"); + case SQLITE_IOERR_NOMEM : return wxT("SQLITE_IOERR_NOMEM"); #endif #if SQLITE_VERSION_NUMBER >= 3006000 - case SQLITE_IOERR_ACCESS : return _T("SQLITE_IOERR_ACCESS"); - case SQLITE_IOERR_CHECKRESERVEDLOCK : return _T("SQLITE_IOERR_CHECKRESERVEDLOCK"); + case SQLITE_IOERR_ACCESS : return wxT("SQLITE_IOERR_ACCESS"); + case SQLITE_IOERR_CHECKRESERVEDLOCK : return wxT("SQLITE_IOERR_CHECKRESERVEDLOCK"); #endif #if SQLITE_VERSION_NUMBER >= 3006002 - case SQLITE_IOERR_LOCK : return _T("SQLITE_IOERR_LOCK"); + case SQLITE_IOERR_LOCK : return wxT("SQLITE_IOERR_LOCK"); +#endif +#if SQLITE_VERSION_NUMBER >= 3006007 + case SQLITE_IOERR_CLOSE : return wxT("SQLITE_IOERR_CLOSE"); + case SQLITE_IOERR_DIR_CLOSE : return wxT("SQLITE_IOERR_DIR_CLOSE"); +#endif +#if SQLITE_VERSION_NUMBER >= 3007000 + case SQLITE_IOERR_SHMOPEN : return wxT("SQLITE_IOERR_SHMOPEN"); + case SQLITE_IOERR_SHMSIZE : return wxT("SQLITE_IOERR_SHMSIZE"); + case SQLITE_IOERR_SHMLOCK : return wxT("SQLITE_IOERR_SHMLOCK"); + case SQLITE_LOCKED_SHAREDCACHE : return wxT("SQLITE_LOCKED_SHAREDCACHE"); + case SQLITE_BUSY_RECOVERY : return wxT("SQLITE_BUSY_RECOVERY"); + case SQLITE_CANTOPEN_NOTEMPDIR : return wxT("SQLITE_CANTOPEN_NOTEMPDIR"); + #endif +#if SQLITE_VERSION_NUMBER >= 3007007 + case SQLITE_CORRUPT_VTAB : return wxT("SQLITE_CORRUPT_VTAB"); + case SQLITE_READONLY_RECOVERY : return wxT("SQLITE_READONLY_RECOVERY"); + case SQLITE_READONLY_CANTLOCK : return wxT("SQLITE_READONLY_CANTLOCK"); #endif - case WXSQLITE_ERROR : return _T("WXSQLITE_ERROR"); - default : return _T("UNKNOWN_ERROR"); + case WXSQLITE_ERROR : return wxT("WXSQLITE_ERROR"); + default : return wxT("UNKNOWN_ERROR"); } +#endif } wxSQLite3Exception::~wxSQLite3Exception() @@ -274,7 +570,6 @@ void wxSQLite3StatementBuffer::Clear() sqlite3_free(m_buffer); m_buffer = 0; } - } const char* wxSQLite3StatementBuffer::Format(const char* format, ...) @@ -287,71 +582,119 @@ const char* wxSQLite3StatementBuffer::Format(const char* format, ...) return m_buffer; } +const char* wxSQLite3StatementBuffer::FormatV(const char* format, va_list va) +{ + Clear(); + m_buffer = sqlite3_vmprintf(format, va); + return m_buffer; +} + // ---------------------------------------------------------------------------- // wxSQLite3ResultSet: class providing access to the result set of a query // ---------------------------------------------------------------------------- wxSQLite3ResultSet::wxSQLite3ResultSet() { - m_stmt = 0; + m_db = NULL; + m_stmt = NULL; m_eof = true; m_first = true; m_cols = 0; - m_ownStmt = false; } wxSQLite3ResultSet::wxSQLite3ResultSet(const wxSQLite3ResultSet& resultSet) { + m_db = resultSet.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } m_stmt = resultSet.m_stmt; - // Only one object can own the statement - const_cast(resultSet).m_stmt = 0; + if (m_stmt != NULL) + { + m_stmt->IncrementRefCount(); + } m_eof = resultSet.m_eof; m_first = resultSet.m_first; m_cols = resultSet.m_cols; - m_ownStmt = resultSet.m_ownStmt; } -wxSQLite3ResultSet::wxSQLite3ResultSet(void* db, - void* stmt, +wxSQLite3ResultSet::wxSQLite3ResultSet(wxSQLite3DatabaseReference* db, + wxSQLite3StatementReference* stmt, bool eof, - bool first, - bool ownStmt /*=true*/) + bool first) { m_db = db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } m_stmt = stmt; + if (m_stmt != NULL) + { + m_stmt->IncrementRefCount(); + } + CheckStmt(); m_eof = eof; m_first = first; - m_cols = sqlite3_column_count((sqlite3_stmt*) m_stmt); - m_ownStmt = ownStmt; + m_cols = sqlite3_column_count(m_stmt->m_stmt); } wxSQLite3ResultSet::~wxSQLite3ResultSet() { - try + if (m_stmt != NULL && m_stmt->DecrementRefCount() == 0) { - Finalize(); + if (m_stmt->m_isValid) + { + try + { + Finalize(m_db, m_stmt); + } + catch (...) + { + } + } + delete m_stmt; } - catch (...) + if (m_db != NULL && m_db->DecrementRefCount() == 0) { + if (m_db->m_isValid) + { + sqlite3_close(m_db->m_db); + } + delete m_db; } } wxSQLite3ResultSet& wxSQLite3ResultSet::operator=(const wxSQLite3ResultSet& resultSet) { - try - { - Finalize(); - } - catch (...) + if (this != &resultSet) { + wxSQLite3DatabaseReference* dbPrev = m_db; + wxSQLite3StatementReference* stmtPrev = m_stmt; + m_db = resultSet.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } + m_stmt = resultSet.m_stmt; + if (m_stmt != NULL) + { + m_stmt->IncrementRefCount(); + } + m_eof = resultSet.m_eof; + m_first = resultSet.m_first; + m_cols = resultSet.m_cols; + if (stmtPrev != NULL && stmtPrev->DecrementRefCount() == 0) + { + Finalize(dbPrev, stmtPrev); + delete stmtPrev; + } + if (dbPrev != NULL && dbPrev->DecrementRefCount() == 0) + { + delete dbPrev; + } } - m_stmt = resultSet.m_stmt; - // Only one object can own the statement - const_cast(resultSet).m_stmt = 0; - m_eof = resultSet.m_eof; - m_first = resultSet.m_first; - m_cols = resultSet.m_cols; - m_ownStmt = resultSet.m_ownStmt; return *this; } @@ -370,15 +713,15 @@ wxString wxSQLite3ResultSet::GetAsString(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - const char* localValue = (const char*) sqlite3_column_text((sqlite3_stmt*) m_stmt, columnIndex); - return UTF8toWxString(localValue); + const char* localValue = (const char*) sqlite3_column_text(m_stmt->m_stmt, columnIndex); + return wxString::FromUTF8(localValue); } wxString wxSQLite3ResultSet::GetAsString(const wxString& columnName) { int columnIndex = FindColumnIndex(columnName); - const char* localValue = (const char*) sqlite3_column_text((sqlite3_stmt*) m_stmt, columnIndex); - return UTF8toWxString(localValue); + const char* localValue = (const char*) sqlite3_column_text(m_stmt->m_stmt, columnIndex); + return wxString::FromUTF8(localValue); } int wxSQLite3ResultSet::GetInt(int columnIndex, int nullValue /* = 0 */) @@ -389,7 +732,7 @@ int wxSQLite3ResultSet::GetInt(int columnIndex, int nullValue /* = 0 */) } else { - return sqlite3_column_int((sqlite3_stmt*) m_stmt, columnIndex); + return sqlite3_column_int(m_stmt->m_stmt, columnIndex); } } @@ -408,7 +751,7 @@ wxLongLong wxSQLite3ResultSet::GetInt64(int columnIndex, wxLongLong nullValue /* } else { - return wxLongLong(sqlite3_column_int64((sqlite3_stmt*) m_stmt, columnIndex)); + return wxLongLong(sqlite3_column_int64(m_stmt->m_stmt, columnIndex)); } } @@ -426,7 +769,7 @@ double wxSQLite3ResultSet::GetDouble(int columnIndex, double nullValue /* = 0.0 } else { - return sqlite3_column_double((sqlite3_stmt*) m_stmt, columnIndex); + return sqlite3_column_double(m_stmt->m_stmt, columnIndex); } } @@ -444,8 +787,8 @@ wxString wxSQLite3ResultSet::GetString(int columnIndex, const wxString& nullValu } else { - const char* localValue = (const char*) sqlite3_column_text((sqlite3_stmt*) m_stmt, columnIndex); - return UTF8toWxString(localValue); + const char* localValue = (const char*) sqlite3_column_text(m_stmt->m_stmt, columnIndex); + return wxString::FromUTF8(localValue); } } @@ -464,8 +807,8 @@ const unsigned char* wxSQLite3ResultSet::GetBlob(int columnIndex, int& len) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - len = sqlite3_column_bytes((sqlite3_stmt*) m_stmt, columnIndex); - return (const unsigned char*) sqlite3_column_blob((sqlite3_stmt*) m_stmt, columnIndex); + len = sqlite3_column_bytes(m_stmt->m_stmt, columnIndex); + return (const unsigned char*) sqlite3_column_blob(m_stmt->m_stmt, columnIndex); } const unsigned char* wxSQLite3ResultSet::GetBlob(const wxString& columnName, int& len) @@ -483,8 +826,8 @@ wxMemoryBuffer& wxSQLite3ResultSet::GetBlob(int columnIndex, wxMemoryBuffer& buf throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - int len = sqlite3_column_bytes((sqlite3_stmt*) m_stmt, columnIndex); - const void* blob = sqlite3_column_blob((sqlite3_stmt*) m_stmt, columnIndex); + int len = sqlite3_column_bytes(m_stmt->m_stmt, columnIndex); + const void* blob = sqlite3_column_blob(m_stmt->m_stmt, columnIndex); buffer.AppendData((void*) blob, (size_t) len); return buffer; } @@ -504,7 +847,8 @@ wxDateTime wxSQLite3ResultSet::GetDate(int columnIndex) else { wxDateTime date; - if (date.ParseDate(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseDate(GetString(columnIndex)); + if (result != NULL) { return date; } @@ -531,7 +875,8 @@ wxDateTime wxSQLite3ResultSet::GetTime(int columnIndex) else { wxDateTime date; - if (date.ParseTime(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseTime(GetString(columnIndex)); + if (result != NULL) { return date; } @@ -557,7 +902,8 @@ wxDateTime wxSQLite3ResultSet::GetDateTime(int columnIndex) else { wxDateTime date; - if (date.ParseDateTime(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseDateTime(GetString(columnIndex)); + if (result != NULL) { date.SetMillisecond(0); return date; @@ -584,7 +930,8 @@ wxDateTime wxSQLite3ResultSet::GetTimestamp(int columnIndex) else { wxDateTime date; - if (date.ParseDateTime(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseDateTime(GetString(columnIndex)); + if (result != NULL) { return date; } @@ -620,6 +967,25 @@ wxDateTime wxSQLite3ResultSet::GetNumericDateTime(const wxString& columnName) return GetNumericDateTime(columnIndex); } +wxDateTime wxSQLite3ResultSet::GetUnixDateTime(int columnIndex) +{ + if (GetColumnType(columnIndex) == SQLITE_NULL) + { + return wxInvalidDateTime; + } + else + { + wxLongLong value = GetInt64(columnIndex); + return wxDateTime((time_t) value.GetValue()); + } +} + +wxDateTime wxSQLite3ResultSet::GetUnixDateTime(const wxString& columnName) +{ + int columnIndex = FindColumnIndex(columnName); + return GetUnixDateTime(columnIndex); +} + wxDateTime wxSQLite3ResultSet::GetJulianDayNumber(int columnIndex) { if (GetColumnType(columnIndex) == SQLITE_NULL) @@ -644,6 +1010,44 @@ bool wxSQLite3ResultSet::GetBool(int columnIndex) return GetInt(columnIndex) != 0; } +wxDateTime wxSQLite3ResultSet::GetAutomaticDateTime(int columnIndex, bool milliSeconds) +{ + wxDateTime result; + int columnType = GetColumnType(columnIndex); + switch (columnType) + { + case SQLITE3_TEXT: + result = GetDateTime(columnIndex); + break; + case SQLITE_INTEGER: + if (milliSeconds) + { + wxLongLong value = GetInt64(columnIndex); + result = wxDateTime(value); + } + else + { + time_t value = GetInt64(columnIndex).GetValue(); + result = wxDateTime(value); + } + break; + case SQLITE_FLOAT: + result = GetJulianDayNumber(columnIndex); + break; + case SQLITE_NULL: + default: + result = wxInvalidDateTime; + break; + } + return result; +} + +wxDateTime wxSQLite3ResultSet::GetAutomaticDateTime(const wxString& columnName, bool milliSeconds) +{ + int columnIndex = FindColumnIndex(columnName); + return GetAutomaticDateTime(columnIndex, milliSeconds); +} + bool wxSQLite3ResultSet::GetBool(const wxString& columnName) { int columnIndex = FindColumnIndex(columnName); @@ -665,14 +1069,14 @@ int wxSQLite3ResultSet::FindColumnIndex(const wxString& columnName) { CheckStmt(); - wxCharBuffer strColumnName = wxConvUTF8.cWC2MB(columnName.wc_str(*wxConvCurrent)); + wxCharBuffer strColumnName = columnName.ToUTF8(); const char* localColumnName = strColumnName; if (columnName.Len() > 0) { for (int columnIndex = 0; columnIndex < m_cols; columnIndex++) { - const char* temp = sqlite3_column_name((sqlite3_stmt*) m_stmt, columnIndex); + const char* temp = sqlite3_column_name(m_stmt->m_stmt, columnIndex); if (strcmp(localColumnName, temp) == 0) { @@ -693,8 +1097,8 @@ wxString wxSQLite3ResultSet::GetColumnName(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - const char* localValue = sqlite3_column_name((sqlite3_stmt*) m_stmt, columnIndex); - return UTF8toWxString(localValue); + const char* localValue = sqlite3_column_name(m_stmt->m_stmt, columnIndex); + return wxString::FromUTF8(localValue); } wxString wxSQLite3ResultSet::GetDeclaredColumnType(int columnIndex) @@ -706,8 +1110,8 @@ wxString wxSQLite3ResultSet::GetDeclaredColumnType(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - const char* localValue = sqlite3_column_decltype((sqlite3_stmt*) m_stmt, columnIndex); - return UTF8toWxString(localValue); + const char* localValue = sqlite3_column_decltype(m_stmt->m_stmt, columnIndex); + return wxString::FromUTF8(localValue); } int wxSQLite3ResultSet::GetColumnType(int columnIndex) @@ -719,7 +1123,7 @@ int wxSQLite3ResultSet::GetColumnType(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - return sqlite3_column_type((sqlite3_stmt*) m_stmt, columnIndex); + return sqlite3_column_type(m_stmt->m_stmt, columnIndex); } bool wxSQLite3ResultSet::Eof() @@ -728,6 +1132,12 @@ bool wxSQLite3ResultSet::Eof() return m_eof; } +bool wxSQLite3ResultSet::CursorMoved() +{ + CheckStmt(); + return !m_first; +} + bool wxSQLite3ResultSet::NextRow() { CheckStmt(); @@ -740,7 +1150,7 @@ bool wxSQLite3ResultSet::NextRow() } else { - rc = sqlite3_step((sqlite3_stmt*) m_stmt); + rc = sqlite3_step(m_stmt->m_stmt); } if (rc == SQLITE_DONE) // no more rows @@ -754,23 +1164,49 @@ bool wxSQLite3ResultSet::NextRow() } else { - rc = sqlite3_finalize((sqlite3_stmt*) m_stmt); - m_stmt = 0; - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + rc = sqlite3_finalize(m_stmt->m_stmt); + m_stmt->Invalidate(); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } void wxSQLite3ResultSet::Finalize() { - if (m_stmt && m_ownStmt) + Finalize(m_db, m_stmt); + if (m_stmt != NULL && m_stmt->DecrementRefCount() == 0) + { + delete m_stmt; + } + m_stmt = NULL; + if (m_db != NULL && m_db->DecrementRefCount() == 0) + { + if (m_db->m_isValid) + { + sqlite3_close(m_db->m_db); + } + delete m_db; + } + m_db = NULL; +} + +void wxSQLite3ResultSet::Finalize(wxSQLite3DatabaseReference* db,wxSQLite3StatementReference* stmt) +{ + if (stmt != NULL && stmt->m_isValid) { - int rc = sqlite3_finalize((sqlite3_stmt*) m_stmt); - m_stmt = 0; + int rc = sqlite3_finalize(stmt->m_stmt); + stmt->Invalidate(); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + if (db != NULL && db->m_isValid) + { + const char* localError = sqlite3_errmsg(db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + else + { + throw wxSQLite3Exception(rc, wxERRMSG_FINALIZE_FAILED); + } } } } @@ -780,20 +1216,20 @@ wxString wxSQLite3ResultSet::GetSQL() wxString sqlString = wxEmptyString; #if SQLITE_VERSION_NUMBER >= 3005003 CheckStmt(); - const char* sqlLocal = sqlite3_sql((sqlite3_stmt*) m_stmt); - if (sqlLocal != NULL) sqlString = UTF8toWxString(sqlLocal); + const char* sqlLocal = sqlite3_sql(m_stmt->m_stmt); + if (sqlLocal != NULL) sqlString = wxString::FromUTF8(sqlLocal); #endif return sqlString; } bool wxSQLite3ResultSet::IsOk() { - return (m_db != 0) && (m_stmt != 0); + return (m_db != NULL) && (m_db->m_isValid) && (m_stmt != NULL) && (m_stmt->m_isValid); } void wxSQLite3ResultSet::CheckStmt() { - if (m_stmt == 0) + if (m_stmt == NULL || m_stmt->m_stmt == NULL || !m_stmt->m_isValid) { throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOSTMT); } @@ -808,9 +1244,9 @@ wxString wxSQLite3ResultSet::GetDatabaseName(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - const char* localValue = sqlite3_column_database_name((sqlite3_stmt*) m_stmt, columnIndex); + const char* localValue = sqlite3_column_database_name(m_stmt->m_stmt, columnIndex); if (localValue != NULL) - return UTF8toWxString(localValue); + return wxString::FromUTF8(localValue); else return wxEmptyString; #else @@ -828,9 +1264,9 @@ wxString wxSQLite3ResultSet::GetTableName(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - const char* localValue = sqlite3_column_table_name((sqlite3_stmt*) m_stmt, columnIndex); + const char* localValue = sqlite3_column_table_name(m_stmt->m_stmt, columnIndex); if (localValue != NULL) - return UTF8toWxString(localValue); + return wxString::FromUTF8(localValue); else return wxEmptyString; #else @@ -848,9 +1284,9 @@ wxString wxSQLite3ResultSet::GetOriginName(int columnIndex) throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_INDEX); } - const char* localValue = sqlite3_column_origin_name((sqlite3_stmt*) m_stmt, columnIndex); + const char* localValue = sqlite3_column_origin_name(m_stmt->m_stmt, columnIndex); if (localValue != NULL) - return UTF8toWxString(localValue); + return wxString::FromUTF8(localValue); else return wxEmptyString; #else @@ -902,19 +1338,22 @@ wxSQLite3Table::~wxSQLite3Table() wxSQLite3Table& wxSQLite3Table::operator=(const wxSQLite3Table& table) { - try - { - Finalize(); - } - catch (...) + if (this != &table) { + try + { + Finalize(); + } + catch (...) + { + } + m_results = table.m_results; + // Only one object can own the results + const_cast(table).m_results = 0; + m_rows = table.m_rows; + m_cols = table.m_cols; + m_currentRow = table.m_currentRow; } - m_results = table.m_results; - // Only one object can own the results - const_cast(table).m_results = 0; - m_rows = table.m_rows; - m_cols = table.m_cols; - m_currentRow = table.m_currentRow; return *this; } @@ -943,7 +1382,7 @@ int wxSQLite3Table::FindColumnIndex(const wxString& columnName) { CheckResults(); - wxCharBuffer strColumnName = wxConvUTF8.cWC2MB(columnName.wc_str(*wxConvCurrent)); + wxCharBuffer strColumnName = columnName.ToUTF8(); const char* localColumnName = strColumnName; if (columnName.Len() > 0) @@ -969,7 +1408,7 @@ wxString wxSQLite3Table::GetAsString(int columnIndex) int nIndex = (m_currentRow*m_cols) + m_cols + columnIndex; const char* localValue = m_results[nIndex]; - return UTF8toWxString(localValue); + return wxString::FromUTF8(localValue); } wxString wxSQLite3Table::GetAsString(const wxString& columnName) @@ -1173,7 +1612,8 @@ wxString wxSQLite3Table::GetString(const wxString& columnName, const wxString& n wxDateTime wxSQLite3Table::GetDate(int columnIndex) { wxDateTime date; - if (date.ParseDate(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseDate(GetString(columnIndex)); + if (result != NULL) { return date; } @@ -1192,7 +1632,8 @@ wxDateTime wxSQLite3Table::GetDate(const wxString& columnName) wxDateTime wxSQLite3Table::GetTime(int columnIndex) { wxDateTime date; - if (date.ParseTime(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseTime(GetString(columnIndex)); + if (result != NULL) { return date; } @@ -1211,7 +1652,8 @@ wxDateTime wxSQLite3Table::GetTime(const wxString& columnName) wxDateTime wxSQLite3Table::GetDateTime(int columnIndex) { wxDateTime date; - if (date.ParseDateTime(GetString(columnIndex)) != NULL) + const wxChar* result = date.ParseDateTime(GetString(columnIndex)); + if (result != NULL) { return date; } @@ -1268,7 +1710,7 @@ wxString wxSQLite3Table::GetColumnName(int columnIndex) } const char* localValue = m_results[columnIndex]; - return UTF8toWxString(localValue); + return wxString::FromUTF8(localValue); } void wxSQLite3Table::SetRow(int row) @@ -1309,34 +1751,83 @@ wxSQLite3Statement::wxSQLite3Statement() wxSQLite3Statement::wxSQLite3Statement(const wxSQLite3Statement& statement) { m_db = statement.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } m_stmt = statement.m_stmt; - // Only one object can own prepared statement - const_cast(statement).m_stmt = 0; + if (m_stmt != NULL) + { + m_stmt->IncrementRefCount(); + } } -wxSQLite3Statement::wxSQLite3Statement(void* db, void* stmt) +wxSQLite3Statement::wxSQLite3Statement(wxSQLite3DatabaseReference* db, wxSQLite3StatementReference* stmt) { m_db = db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } m_stmt = stmt; + if (m_stmt != NULL) + { + m_stmt->IncrementRefCount(); + } } wxSQLite3Statement::~wxSQLite3Statement() { - try + if (m_stmt != NULL && m_stmt->DecrementRefCount() == 0) { - Finalize(); + if (m_stmt->m_isValid) + { + try + { + Finalize(m_db, m_stmt); + } + catch (...) + { + } + } + delete m_stmt; } - catch (...) + if (m_db != NULL && m_db->DecrementRefCount() == 0) { + if (m_db->m_isValid) + { + sqlite3_close(m_db->m_db); + } + delete m_db; } } wxSQLite3Statement& wxSQLite3Statement::operator=(const wxSQLite3Statement& statement) { - m_db = statement.m_db; - m_stmt = statement.m_stmt; - // Only one object can own prepared statement - const_cast(statement).m_stmt = 0; + if (this != &statement) + { + wxSQLite3DatabaseReference* dbPrev = m_db; + wxSQLite3StatementReference* stmtPrev = m_stmt; + m_db = statement.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } + m_stmt = statement.m_stmt; + if (m_stmt != NULL) + { + m_stmt->IncrementRefCount(); + } + if (stmtPrev != NULL && stmtPrev->DecrementRefCount() == 0) + { + Finalize(dbPrev, stmtPrev); + delete stmtPrev; + } + if (dbPrev != NULL && dbPrev->DecrementRefCount() == 0) + { + delete dbPrev; + } + } return *this; } @@ -1347,27 +1838,27 @@ int wxSQLite3Statement::ExecuteUpdate() const char* localError=0; - int rc = sqlite3_step((sqlite3_stmt*) m_stmt); + int rc = sqlite3_step(m_stmt->m_stmt); if (rc == SQLITE_DONE) { - int rowsChanged = sqlite3_changes((sqlite3*) m_db); + int rowsChanged = sqlite3_changes(m_db->m_db); - rc = sqlite3_reset((sqlite3_stmt*) m_stmt); + rc = sqlite3_reset(m_stmt->m_stmt); if (rc != SQLITE_OK) { - localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } return rowsChanged; } else { - rc = sqlite3_reset((sqlite3_stmt*) m_stmt); - localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + rc = sqlite3_reset(m_stmt->m_stmt); + localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } @@ -1376,55 +1867,70 @@ wxSQLite3ResultSet wxSQLite3Statement::ExecuteQuery() CheckDatabase(); CheckStmt(); - int rc = sqlite3_step((sqlite3_stmt*) m_stmt); + int rc = sqlite3_step(m_stmt->m_stmt); if (rc == SQLITE_DONE) // no more rows { - return wxSQLite3ResultSet(m_db, m_stmt, true/*eof*/, true/*first*/, false); + return wxSQLite3ResultSet(m_db, m_stmt, true/*eof*/, true/*first*/); } else if (rc == SQLITE_ROW) // one or more rows { - return wxSQLite3ResultSet(m_db, m_stmt, false/*eof*/, true/*first*/, false); + return wxSQLite3ResultSet(m_db, m_stmt, false/*eof*/, true/*first*/); } else { - rc = sqlite3_reset((sqlite3_stmt*) m_stmt); - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + rc = sqlite3_reset(m_stmt->m_stmt); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } -int wxSQLite3Statement::GetParamCount() +int wxSQLite3Statement::ExecuteScalar() +{ + wxSQLite3ResultSet resultSet = ExecuteQuery(); + + if (resultSet.Eof() || resultSet.GetColumnCount() < 1) + { + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_QUERY); + } + + long value = 0; + resultSet.GetAsString(0).ToLong(&value); + return (int) value; +} + +int wxSQLite3Statement::GetParamCount() { CheckStmt(); - return sqlite3_bind_parameter_count((sqlite3_stmt*) m_stmt); + return sqlite3_bind_parameter_count(m_stmt->m_stmt); } int wxSQLite3Statement::GetParamIndex(const wxString& paramName) { CheckStmt(); - wxCharBuffer strParamName = wxConvUTF8.cWC2MB(paramName.wc_str(*wxConvCurrent)); + wxCharBuffer strParamName = paramName.ToUTF8(); const char* localParamName = strParamName; - return sqlite3_bind_parameter_index((sqlite3_stmt*) m_stmt, localParamName); + return sqlite3_bind_parameter_index(m_stmt->m_stmt, localParamName); } wxString wxSQLite3Statement::GetParamName(int paramIndex) { CheckStmt(); - const char* localParamName = sqlite3_bind_parameter_name((sqlite3_stmt*) m_stmt, paramIndex); - return UTF8toWxString(localParamName); + const char* localParamName = sqlite3_bind_parameter_name(m_stmt->m_stmt, paramIndex); + wxString paramName = (localParamName != NULL) ? wxString::FromUTF8(localParamName) : wxString(wxT("")); + return paramName; } void wxSQLite3Statement::Bind(int paramIndex, const wxString& stringValue) { CheckStmt(); - wxCharBuffer strStringValue = wxConvUTF8.cWC2MB(stringValue.wc_str(*wxConvCurrent)); + wxCharBuffer strStringValue = stringValue.ToUTF8(); const char* localStringValue = strStringValue; - int rc = sqlite3_bind_text((sqlite3_stmt*) m_stmt, paramIndex, localStringValue, -1, SQLITE_TRANSIENT); + int rc = sqlite3_bind_text(m_stmt->m_stmt, paramIndex, localStringValue, -1, SQLITE_TRANSIENT); if (rc != SQLITE_OK) { @@ -1435,7 +1941,7 @@ void wxSQLite3Statement::Bind(int paramIndex, const wxString& stringValue) void wxSQLite3Statement::Bind(int paramIndex, int intValue) { CheckStmt(); - int rc = sqlite3_bind_int((sqlite3_stmt*) m_stmt, paramIndex, intValue); + int rc = sqlite3_bind_int(m_stmt->m_stmt, paramIndex, intValue); if (rc != SQLITE_OK) { @@ -1446,7 +1952,7 @@ void wxSQLite3Statement::Bind(int paramIndex, int intValue) void wxSQLite3Statement::Bind(int paramIndex, wxLongLong int64Value) { CheckStmt(); - int rc = sqlite3_bind_int64((sqlite3_stmt*) m_stmt, paramIndex, int64Value.GetValue()); + int rc = sqlite3_bind_int64(m_stmt->m_stmt, paramIndex, int64Value.GetValue()); if (rc != SQLITE_OK) { @@ -1457,7 +1963,7 @@ void wxSQLite3Statement::Bind(int paramIndex, wxLongLong int64Value) void wxSQLite3Statement::Bind(int paramIndex, double doubleValue) { CheckStmt(); - int rc = sqlite3_bind_double((sqlite3_stmt*) m_stmt, paramIndex, doubleValue); + int rc = sqlite3_bind_double(m_stmt->m_stmt, paramIndex, doubleValue); if (rc != SQLITE_OK) { @@ -1468,7 +1974,7 @@ void wxSQLite3Statement::Bind(int paramIndex, double doubleValue) void wxSQLite3Statement::Bind(int paramIndex, const char* charValue) { CheckStmt(); - int rc = sqlite3_bind_text((sqlite3_stmt*) m_stmt, paramIndex, charValue, -1, SQLITE_TRANSIENT); + int rc = sqlite3_bind_text(m_stmt->m_stmt, paramIndex, charValue, -1, SQLITE_TRANSIENT); if (rc != SQLITE_OK) { @@ -1479,8 +1985,8 @@ void wxSQLite3Statement::Bind(int paramIndex, const char* charValue) void wxSQLite3Statement::Bind(int paramIndex, const unsigned char* blobValue, int blobLen) { CheckStmt(); - int rc = sqlite3_bind_blob((sqlite3_stmt*) m_stmt, paramIndex, - (const void*)blobValue, blobLen, SQLITE_TRANSIENT); + int rc = sqlite3_bind_blob(m_stmt->m_stmt, paramIndex, + (const void*)blobValue, blobLen, SQLITE_TRANSIENT); if (rc != SQLITE_OK) { @@ -1492,8 +1998,8 @@ void wxSQLite3Statement::Bind(int paramIndex, const wxMemoryBuffer& blobValue) { CheckStmt(); int blobLen = (int) blobValue.GetDataLen(); - int rc = sqlite3_bind_blob((sqlite3_stmt*) m_stmt, paramIndex, - (const void*)blobValue.GetData(), blobLen, SQLITE_TRANSIENT); + int rc = sqlite3_bind_blob(m_stmt->m_stmt, paramIndex, + (const void*)blobValue.GetData(), blobLen, SQLITE_TRANSIENT); if (rc != SQLITE_OK) { @@ -1529,7 +2035,7 @@ void wxSQLite3Statement::BindDateTime(int paramIndex, const wxDateTime& datetime { if (datetime.IsValid()) { - Bind(paramIndex,datetime.Format(_T("%Y-%m-%d %H:%M:%S"))); + Bind(paramIndex,datetime.Format(wxT("%Y-%m-%d %H:%M:%S"))); } else { @@ -1541,7 +2047,7 @@ void wxSQLite3Statement::BindTimestamp(int paramIndex, const wxDateTime& timesta { if (timestamp.IsValid()) { - Bind(paramIndex,timestamp.Format(_T("%Y-%m-%d %H:%M:%S.%l"))); + Bind(paramIndex,timestamp.Format(wxT("%Y-%m-%d %H:%M:%S.%l"))); } else { @@ -1561,6 +2067,19 @@ void wxSQLite3Statement::BindNumericDateTime(int paramIndex, const wxDateTime& d } } +void wxSQLite3Statement::BindUnixDateTime(int paramIndex, const wxDateTime& datetime) +{ + if (datetime.IsValid()) + { + wxLongLong ticks = datetime.GetTicks(); + Bind(paramIndex, ticks); + } + else + { + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_BIND_DATETIME); + } +} + void wxSQLite3Statement::BindJulianDayNumber(int paramIndex, const wxDateTime& datetime) { if (datetime.IsValid()) @@ -1581,7 +2100,7 @@ void wxSQLite3Statement::BindBool(int paramIndex, bool value) void wxSQLite3Statement::BindNull(int paramIndex) { CheckStmt(); - int rc = sqlite3_bind_null((sqlite3_stmt*) m_stmt, paramIndex); + int rc = sqlite3_bind_null(m_stmt->m_stmt, paramIndex); if (rc != SQLITE_OK) { @@ -1593,7 +2112,7 @@ void wxSQLite3Statement::BindZeroBlob(int paramIndex, int blobSize) { #if SQLITE_VERSION_NUMBER >= 3004000 CheckStmt(); - int rc = sqlite3_bind_zeroblob((sqlite3_stmt*) m_stmt, paramIndex, blobSize); + int rc = sqlite3_bind_zeroblob(m_stmt->m_stmt, paramIndex, blobSize); if (rc != SQLITE_OK) { throw wxSQLite3Exception(rc, wxERRMSG_BIND_ZEROBLOB); @@ -1609,7 +2128,7 @@ void wxSQLite3Statement::ClearBindings() { CheckStmt(); #if 0 // missing in SQLite DLL - int rc = sqlite3_clear_bindings((sqlite3_stmt*) m_stmt); + int rc = sqlite3_clear_bindings(m_stmt->m_stmt); if (rc != SQLITE_OK) { @@ -1628,37 +2147,72 @@ wxString wxSQLite3Statement::GetSQL() wxString sqlString = wxEmptyString; #if SQLITE_VERSION_NUMBER >= 3005003 CheckStmt(); - const char* sqlLocal = sqlite3_sql((sqlite3_stmt*) m_stmt); - if (sqlLocal != NULL) sqlString = UTF8toWxString(sqlLocal); + const char* sqlLocal = sqlite3_sql(m_stmt->m_stmt); + if (sqlLocal != NULL) sqlString = wxString::FromUTF8(sqlLocal); #endif return sqlString; } void wxSQLite3Statement::Reset() { - if (m_stmt) + if (m_stmt != NULL && m_stmt->m_isValid) { - int rc = sqlite3_reset((sqlite3_stmt*) m_stmt); + int rc = sqlite3_reset(m_stmt->m_stmt); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } } +bool wxSQLite3Statement::IsReadOnly() +{ +#if SQLITE_VERSION_NUMBER >= 3007004 + CheckStmt(); + return sqlite3_stmt_readonly(m_stmt->m_stmt) != 0; +#else + return false; +#endif +} + void wxSQLite3Statement::Finalize() { - if (m_stmt) + Finalize(m_db, m_stmt); + if (m_stmt != NULL && m_stmt->DecrementRefCount() == 0) + { + delete m_stmt; + } + m_stmt = NULL; + if (m_db != NULL && m_db->DecrementRefCount() == 0) { - int rc = sqlite3_finalize((sqlite3_stmt*) m_stmt); - m_stmt = 0; + if (m_db->m_isValid) + { + sqlite3_close(m_db->m_db); + } + delete m_db; + } + m_db = NULL; +} +void wxSQLite3Statement::Finalize(wxSQLite3DatabaseReference* db,wxSQLite3StatementReference* stmt) +{ + if (stmt != NULL && stmt->m_isValid) + { + int rc = sqlite3_finalize(stmt->m_stmt); + stmt->Invalidate(); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + if (db != NULL && db->m_isValid) + { + const char* localError = sqlite3_errmsg(db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + else + { + throw wxSQLite3Exception(rc, wxERRMSG_FINALIZE_FAILED); + } } } } @@ -1668,9 +2222,30 @@ bool wxSQLite3Statement::IsOk() return (m_db != 0) && (m_stmt != 0); } +bool wxSQLite3Statement::IsBusy() +{ +#if SQLITE_VERSION_NUMBER >= 3007010 + CheckStmt(); + int rc = sqlite3_stmt_busy(m_stmt->m_stmt); + return (rc != 0); +#else + return false; +#endif +} + +int wxSQLite3Statement::Status(wxSQLite3StatementStatus opCode, bool resetFlag) +{ + int count = 0; +#if SQLITE_VERSION_NUMBER >= 3007000 + CheckStmt(); + count = sqlite3_stmt_status(m_stmt->m_stmt, (int) opCode, (resetFlag) ? 1 : 0 ); +#endif + return count; +} + void wxSQLite3Statement::CheckDatabase() { - if (m_db == 0) + if (m_db == NULL || m_db->m_db == NULL || !m_db->m_isValid) { throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NODB); } @@ -1678,7 +2253,7 @@ void wxSQLite3Statement::CheckDatabase() void wxSQLite3Statement::CheckStmt() { - if (m_stmt == 0) + if (m_stmt == NULL || m_stmt->m_stmt == NULL || !m_stmt->m_isValid) { throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOSTMT); } @@ -1690,59 +2265,83 @@ wxSQLite3Blob::wxSQLite3Blob() { m_db = NULL; m_blob = NULL; - m_ok = false; m_writable = false; } wxSQLite3Blob::wxSQLite3Blob(const wxSQLite3Blob& blob) { m_db = blob.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } m_blob = blob.m_blob; - m_ok = blob.m_ok; + if (m_blob != NULL) + { + m_blob->IncrementRefCount(); + } m_writable = blob.m_writable; } wxSQLite3Blob& wxSQLite3Blob::operator=(const wxSQLite3Blob& blob) { - try - { - Finalize(); - } - catch (...) + if (this != &blob) { + wxSQLite3DatabaseReference* dbPrev = m_db; + wxSQLite3BlobReference* blobPrev = m_blob; + m_db = blob.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } + m_blob = blob.m_blob; + if (m_blob != NULL) + { + m_blob->IncrementRefCount(); + } + m_writable = blob.m_writable; + if (blobPrev != NULL && blobPrev->DecrementRefCount() == 0) + { + Finalize(dbPrev, blobPrev); + delete blobPrev; + } + if (dbPrev != NULL && dbPrev->DecrementRefCount() == 0) + { + delete dbPrev; + } } - m_db = blob.m_db; - m_blob = blob.m_blob; - m_ok = blob.m_ok; - m_writable = blob.m_writable; - // only one blob can own the blob handle - const_cast(blob).m_ok = false; - return *this; } -wxSQLite3Blob::wxSQLite3Blob(void* db, void* blobHandle, bool writable) +wxSQLite3Blob::wxSQLite3Blob(wxSQLite3DatabaseReference* db, wxSQLite3BlobReference* blob, bool writable) { m_db = db; - m_blob = blobHandle; - m_ok = true; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } + m_blob = blob; + if (m_blob != NULL) + { + m_blob->IncrementRefCount(); + } m_writable = writable; -#if 0 -int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); - -void sqlite3_result_zeroblob(sqlite3_context*, int n); - -#endif } wxSQLite3Blob::~wxSQLite3Blob() { - try + if (m_blob != NULL && m_blob->DecrementRefCount() == 0) { - Finalize(); + Finalize(m_db, m_blob); + delete m_blob; } - catch (...) + if (m_db != NULL && m_db->DecrementRefCount() == 0) { + if (m_db->m_isValid) + { + sqlite3_close(m_db->m_db); + } + delete m_db; } } @@ -1750,17 +2349,16 @@ wxMemoryBuffer& wxSQLite3Blob::Read(wxMemoryBuffer& blobValue, int length, int o { #if SQLITE_VERSION_NUMBER >= 3004000 CheckBlob(); - char* localBuffer = new char[length]; - int rc = sqlite3_blob_read((sqlite3_blob*) m_blob, localBuffer, length, offset); + char* localBuffer = (char*) blobValue.GetAppendBuf((size_t) length); + int rc = sqlite3_blob_read(m_blob->m_blob, localBuffer, length, offset); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } - blobValue.AppendData((void*) localBuffer, (size_t) length); - delete [] localBuffer; + blobValue.UngetAppendBuf((size_t) length); #else wxUnusedVar(blobValue); wxUnusedVar(length); @@ -1777,13 +2375,13 @@ void wxSQLite3Blob::Write(const wxMemoryBuffer& blobValue, int offset) if (m_writable) { int blobLen = (int) blobValue.GetDataLen(); - int rc = sqlite3_blob_write((sqlite3_blob*) m_blob, + int rc = sqlite3_blob_write(m_blob->m_blob, (const void*) blobValue.GetData(), blobLen, offset); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } else @@ -1799,7 +2397,7 @@ void wxSQLite3Blob::Write(const wxMemoryBuffer& blobValue, int offset) bool wxSQLite3Blob::IsOk() { - return m_ok; + return (m_blob != NULL && m_blob->m_isValid); } bool wxSQLite3Blob::IsReadOnly() @@ -1811,25 +2409,52 @@ int wxSQLite3Blob::GetSize() { #if SQLITE_VERSION_NUMBER >= 3004000 CheckBlob(); - return sqlite3_blob_bytes((sqlite3_blob*) m_blob); + return sqlite3_blob_bytes(m_blob->m_blob); #else throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOINCBLOB); return 0; #endif } +void wxSQLite3Blob::Rebind(wxLongLong rowid) +{ +#if SQLITE_VERSION_NUMBER >= 3007004 + CheckBlob(); + int rc = sqlite3_blob_reopen(m_blob->m_blob, rowid.GetValue()); + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } +#else + wxUnusedVar(rowid); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOBLOBREBIND); +#endif +} + void wxSQLite3Blob::Finalize() { + Finalize(m_db, m_blob); +} + +void wxSQLite3Blob::Finalize(wxSQLite3DatabaseReference* db, wxSQLite3BlobReference* blob) +{ #if SQLITE_VERSION_NUMBER >= 3004000 - if (m_ok) + if (blob != NULL && blob->m_isValid) { - int rc = sqlite3_blob_close((sqlite3_blob*) m_blob); - m_blob = NULL; - m_ok = false; + int rc = sqlite3_blob_close(blob->m_blob); + blob->Invalidate(); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + if (db != NULL && db->m_isValid) + { + const char* localError = sqlite3_errmsg(db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + else + { + throw wxSQLite3Exception(rc, wxERRMSG_FINALIZE_FAILED); + } } } #else @@ -1839,7 +2464,7 @@ void wxSQLite3Blob::Finalize() void wxSQLite3Blob::CheckBlob() { - if (!m_ok) + if (m_db == NULL || !m_db->m_isValid || m_blob == NULL || !m_blob->m_isValid) { throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_INVALID_BLOB); } @@ -1875,12 +2500,24 @@ bool wxSQLite3Database::ms_hasMetaDataSupport = true; bool wxSQLite3Database::ms_hasMetaDataSupport = false; #endif +#if WXSQLITE3_USER_AUTHENTICATION +bool wxSQLite3Database::ms_hasUserAuthentication = true; +#else +bool wxSQLite3Database::ms_hasUserAuthentication = false; +#endif + #if WXSQLITE3_HAVE_LOAD_EXTENSION bool wxSQLite3Database::ms_hasLoadExtSupport = true; #else bool wxSQLite3Database::ms_hasLoadExtSupport = false; #endif +#if WXSQLITE3_USE_NAMED_COLLECTIONS +bool wxSQLite3Database::ms_hasNamedCollectionSupport = true; +#else +bool wxSQLite3Database::ms_hasNamedCollectionSupport = false; +#endif + #if SQLITE_VERSION_NUMBER >= 3004000 bool wxSQLite3Database::ms_hasIncrementalBlobSupport = true; #else @@ -1893,6 +2530,18 @@ bool wxSQLite3Database::ms_hasSavepointSupport = true; bool wxSQLite3Database::ms_hasSavepointSupport = false; #endif +#if SQLITE_VERSION_NUMBER >= 3006011 +bool wxSQLite3Database::ms_hasBackupSupport = true; +#else +bool wxSQLite3Database::ms_hasBackupSupport = false; +#endif + +#if SQLITE_VERSION_NUMBER >= 3007000 +bool wxSQLite3Database::ms_hasWriteAheadLogSupport = true; +#else +bool wxSQLite3Database::ms_hasWriteAheadLogSupport = false; +#endif + bool wxSQLite3Database::HasEncryptionSupport() { @@ -1906,12 +2555,24 @@ wxSQLite3Database::HasMetaDataSupport() } bool +wxSQLite3Database::HasUserAuthenticationSupport() +{ + return ms_hasUserAuthentication; +} + +bool wxSQLite3Database::HasLoadExtSupport() { return ms_hasLoadExtSupport; } bool +wxSQLite3Database::HasNamedCollectionSupport() +{ + return ms_hasNamedCollectionSupport; +} + +bool wxSQLite3Database::HasIncrementalBlobSupport() { return ms_hasIncrementalBlobSupport; @@ -1923,76 +2584,131 @@ wxSQLite3Database::HasSavepointSupport() return ms_hasSavepointSupport; } +bool +wxSQLite3Database::HasBackupSupport() +{ + return ms_hasBackupSupport; +} + +bool +wxSQLite3Database::HasWriteAheadLogSupport() +{ + return ms_hasWriteAheadLogSupport; +} + wxSQLite3Database::wxSQLite3Database() { m_db = 0; + m_isOpen = false; m_busyTimeoutMs = 60000; // 60 seconds m_isEncrypted = false; + m_lastRollbackRC = 0; + m_backupPageCount = 10; } wxSQLite3Database::wxSQLite3Database(const wxSQLite3Database& db) { m_db = db.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + } + m_isOpen = db.m_isOpen; m_busyTimeoutMs = 60000; // 60 seconds - m_isEncrypted = false; + m_isEncrypted = db.m_isEncrypted; + m_lastRollbackRC = db.m_lastRollbackRC; + m_backupPageCount = db.m_backupPageCount; } wxSQLite3Database::~wxSQLite3Database() { - Close(); + if (m_db != NULL && m_db->DecrementRefCount() == 0) + { + if (m_db->m_isValid) + { + Close(m_db); + } + delete m_db; + } } wxSQLite3Database& wxSQLite3Database::operator=(const wxSQLite3Database& db) { - m_db = db.m_db; - m_busyTimeoutMs = 60000; // 60 seconds - m_isEncrypted = db.m_isEncrypted; + if (this != &db) + { + wxSQLite3DatabaseReference* dbPrev = m_db; + m_db = db.m_db; + if (m_db != NULL) + { + m_db->IncrementRefCount(); + m_isOpen = db.m_isOpen; + m_busyTimeoutMs = 60000; // 60 seconds + m_isEncrypted = db.m_isEncrypted; + m_lastRollbackRC = db.m_lastRollbackRC; + m_backupPageCount = db.m_backupPageCount; + } + if (dbPrev != NULL && dbPrev->DecrementRefCount() == 0) + { + Close(dbPrev); + delete dbPrev; + } + + if (m_db == NULL) + { + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_DBASSIGN_FAILED); + } + } return *this; } -void wxSQLite3Database::Open(const wxString& fileName, const wxString& key) +void wxSQLite3Database::Open(const wxString& fileName, const wxString& key, int flags) { - wxCharBuffer strLocalKey = wxConvUTF8.cWC2MB(key.wc_str(*wxConvCurrent)); + wxCharBuffer strLocalKey = key.ToUTF8(); const char* localKey = strLocalKey; wxMemoryBuffer binaryKey; if (key.Length() > 0) { binaryKey.AppendData((void*) localKey, strlen(localKey)); } - Open(fileName, binaryKey); + Open(fileName, binaryKey, flags); } -void wxSQLite3Database::Open(const wxString& fileName, const wxMemoryBuffer& key) +void wxSQLite3Database::Open(const wxString& fileName, const wxMemoryBuffer& key, int flags) { - wxCharBuffer strFileName = wxConvUTF8.cWC2MB(fileName.wc_str(*wxConvCurrent)); + wxCharBuffer strFileName = fileName.ToUTF8(); const char* localFileName = strFileName; + sqlite3* db; - int rc = sqlite3_open((const char*) localFileName, (sqlite3**) &m_db); + int rc = sqlite3_open_v2((const char*) localFileName, &db, flags, NULL); if (rc != SQLITE_OK) { - Close(); - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = "Out of memory"; + if (db != NULL) + { + localError = sqlite3_errmsg(db); + sqlite3_close(db); + } + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } - rc = sqlite3_extended_result_codes((sqlite3*) m_db, 1); + rc = sqlite3_extended_result_codes(db, 1); if (rc != SQLITE_OK) { - Close(); - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(db); + sqlite3_close(db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } #if WXSQLITE3_HAVE_CODEC if (key.GetDataLen() > 0) { - rc = sqlite3_key((sqlite3*) m_db, key.GetData(), (int) key.GetDataLen()); + rc = sqlite3_key(db, key.GetData(), (int) key.GetDataLen()); if (rc != SQLITE_OK) { - Close(); - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(db); + sqlite3_close(db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } m_isEncrypted = true; } @@ -2000,17 +2716,42 @@ void wxSQLite3Database::Open(const wxString& fileName, const wxMemoryBuffer& key wxUnusedVar(key); #endif + wxSQLite3DatabaseReference* dbPrev = m_db; + m_db = new wxSQLite3DatabaseReference(db); + m_isOpen = true; SetBusyTimeout(m_busyTimeoutMs); + if (dbPrev != NULL && dbPrev->DecrementRefCount() == 0) + { + delete dbPrev; + } } bool wxSQLite3Database::IsOpen() const { - return (m_db != NULL); + return (m_db != NULL && m_db->m_isValid && m_isOpen); +} + +bool wxSQLite3Database::IsReadOnly(const wxString& databaseName) +{ +#if SQLITE_VERSION_NUMBER >= 3007011 + CheckDatabase(); + wxCharBuffer strDatabaseName = databaseName.ToUTF8(); + const char* localDatabaseName = strDatabaseName; + return sqlite3_db_readonly(m_db->m_db, localDatabaseName) > 0; +#else + return false; +#endif } void wxSQLite3Database::Close() { - if (m_db) + CheckDatabase(); + Close(m_db); +} + +void wxSQLite3Database::Close(wxSQLite3DatabaseReference* db) +{ + if (db != NULL && db->m_isValid) { #if SQLITE_VERSION_NUMBER >= 3006000 // Unfortunately the following code leads to a crash if the RTree module is used @@ -2018,103 +2759,358 @@ void wxSQLite3Database::Close() #if 0 // Finalize all unfinalized prepared statements sqlite3_stmt *pStmt; - while( (pStmt = sqlite3_next_stmt((sqlite3*) m_db, 0))!=0 ) + while( (pStmt = sqlite3_next_stmt(db->m_db, 0))!=0 ) { sqlite3_finalize(pStmt); } #endif #endif - sqlite3_close((sqlite3*) m_db); - m_db = 0; - m_isEncrypted = false; + if (db->m_refCount <= 1) + { + sqlite3_close(db->m_db); + db->Invalidate(); + m_isEncrypted = false; + } + m_isOpen = false; } } -void wxSQLite3Database::Begin(wxSQLite3TransactionType transactionType) +static bool +BackupRestoreCallback(int total, int remaining, wxSQLite3BackupProgress* progressCallback) { - wxString sql; - switch (transactionType) - { - case WXSQLITE_TRANSACTION_DEFERRED: - sql << _T("begin deferred transaction"); - break; - case WXSQLITE_TRANSACTION_IMMEDIATE: - sql << _T("begin immediate transaction"); - break; - case WXSQLITE_TRANSACTION_EXCLUSIVE: - sql << _T("begin exclusive transaction"); - break; - default: - sql << _T("begin transaction"); - break; - } - ExecuteUpdate(sql); + return progressCallback->Progress(total, remaining); } -void wxSQLite3Database::Commit() +void wxSQLite3Database::Backup(const wxString& targetFileName, const wxString& key, + const wxString& sourceDatabaseName) { - ExecuteUpdate("commit transaction"); + Backup(NULL, targetFileName, key, sourceDatabaseName); } -void wxSQLite3Database::Rollback(const wxString& savepointName) +void wxSQLite3Database::Backup(wxSQLite3BackupProgress* progressCallback, + const wxString& targetFileName, const wxString& key, + const wxString& sourceDatabaseName) { -#if SQLITE_VERSION_NUMBER >= 3006008 - if (savepointName.IsEmpty()) - { -#endif - ExecuteUpdate("rollback transaction"); -#if SQLITE_VERSION_NUMBER >= 3006008 - } - else + wxCharBuffer strLocalKey = key.ToUTF8(); + const char* localKey = strLocalKey; + wxMemoryBuffer binaryKey; + if (key.Length() > 0) { - ExecuteUpdate(wxString(_T("rollback transaction to savepoint "))+savepointName); + binaryKey.AppendData((void*) localKey, strlen(localKey)); } -#endif + Backup(progressCallback, targetFileName, binaryKey, sourceDatabaseName); } -bool wxSQLite3Database::GetAutoCommit() +void wxSQLite3Database::Backup(const wxString& targetFileName, const wxMemoryBuffer& key, + const wxString& sourceDatabaseName) { - CheckDatabase(); - return sqlite3_get_autocommit((sqlite3*) m_db) != 0; + Backup(NULL, targetFileName, key, sourceDatabaseName); } -void wxSQLite3Database::Savepoint(const wxString& savepointName) +void wxSQLite3Database::Backup(wxSQLite3BackupProgress* progressCallback, + const wxString& targetFileName, const wxMemoryBuffer& key, + const wxString& sourceDatabaseName) { -#if SQLITE_VERSION_NUMBER >= 3006008 - ExecuteUpdate(wxString(_T("savepoint "))+savepointName); +#if SQLITE_VERSION_NUMBER >= 3006011 + CheckDatabase(); + + wxCharBuffer strFileName = targetFileName.ToUTF8(); + const char* localTargetFileName = strFileName; + wxCharBuffer strDatabaseName = sourceDatabaseName.ToUTF8(); + const char* localSourceDatabaseName = strDatabaseName; + + sqlite3* pDest; + sqlite3_backup* pBackup; + int rc; + rc = sqlite3_open(localTargetFileName, &pDest); + if (rc != SQLITE_OK) + { + sqlite3_close(pDest); + throw wxSQLite3Exception(rc, wxERRMSG_DBOPEN_FAILED); + } +#if WXSQLITE3_HAVE_CODEC + if (key.GetDataLen() > 0) + { + rc = sqlite3_key(pDest, key.GetData(), (int) key.GetDataLen()); + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(pDest); + sqlite3_close(pDest); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + } #else - wxUnusedVar(savepointName); - throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOSAVEPOINT); + wxUnusedVar(key); #endif -} -void wxSQLite3Database::ReleaseSavepoint(const wxString& savepointName) -{ -#if SQLITE_VERSION_NUMBER >= 3006008 - ExecuteUpdate(wxString(_T("release savepoint "))+savepointName); + pBackup = sqlite3_backup_init(pDest, "main", m_db->m_db, localSourceDatabaseName); + if (pBackup == 0) + { + const char* localError = sqlite3_errmsg(pDest); + sqlite3_close(pDest); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + + do + { + rc = sqlite3_backup_step(pBackup, m_backupPageCount); + if (progressCallback != NULL) + { + if (!BackupRestoreCallback(sqlite3_backup_pagecount(pBackup), + sqlite3_backup_remaining(pBackup), + progressCallback)) + { + rc = SQLITE_DONE; + } + } +#if 0 + xProgress(sqlite3_backup_remaining(pBackup), + sqlite3_backup_pagecount(pBackup)); +#endif + if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) + { + sqlite3_sleep(250); + } + } + while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); + + sqlite3_backup_finish(pBackup); + if (rc == SQLITE_DONE) + { + sqlite3_close(pDest); + } + else + { + const char* localError = sqlite3_errmsg(pDest); + sqlite3_close(pDest); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } #else - wxUnusedVar(savepointName); - throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOSAVEPOINT); + wxUnusedVar(targetFileName); + wxUnusedVar(sourceDatabaseName); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOBACKUP); #endif } -wxSQLite3Statement wxSQLite3Database::PrepareStatement(const wxString& sql) +void wxSQLite3Database::Restore(const wxString& sourceFileName, const wxString& key, + const wxString& targetDatabaseName) { - wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent)); - const char* localSql = strSql; - return PrepareStatement(localSql); + Restore(NULL, sourceFileName, key, targetDatabaseName); } -wxSQLite3Statement wxSQLite3Database::PrepareStatement(const wxSQLite3StatementBuffer& sql) +void wxSQLite3Database::Restore(wxSQLite3BackupProgress* progressCallback, + const wxString& sourceFileName, const wxString& key, + const wxString& targetDatabaseName) { - return PrepareStatement((const char*) sql); + wxCharBuffer strLocalKey = key.ToUTF8(); + const char* localKey = strLocalKey; + wxMemoryBuffer binaryKey; + if (key.Length() > 0) + { + binaryKey.AppendData((void*) localKey, strlen(localKey)); + } + Restore(progressCallback, sourceFileName, binaryKey, targetDatabaseName); } -wxSQLite3Statement wxSQLite3Database::PrepareStatement(const char* sql) +void wxSQLite3Database::Restore(const wxString& sourceFileName, const wxMemoryBuffer& key, + const wxString& targetDatabaseName) +{ + Restore(NULL, sourceFileName, key, targetDatabaseName); +} + +void wxSQLite3Database::Restore(wxSQLite3BackupProgress* progressCallback, + const wxString& sourceFileName, const wxMemoryBuffer& key, + const wxString& targetDatabaseName) +{ +#if SQLITE_VERSION_NUMBER >= 3006011 + CheckDatabase(); + + wxCharBuffer strFileName = sourceFileName.ToUTF8(); + const char* localSourceFileName = strFileName; + wxCharBuffer strDatabaseName = targetDatabaseName.ToUTF8(); + const char* localTargetDatabaseName = strDatabaseName; + + sqlite3* pSrc; + sqlite3_backup* pBackup; + int rc; + int nTimeout = 0; + + rc = sqlite3_open(localSourceFileName, &pSrc); + if (rc != SQLITE_OK) + { + sqlite3_close(pSrc); + throw wxSQLite3Exception(rc, wxERRMSG_DBOPEN_FAILED); + } +#if WXSQLITE3_HAVE_CODEC + if (key.GetDataLen() > 0) + { + rc = sqlite3_key(pSrc, key.GetData(), (int) key.GetDataLen()); + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(pSrc); + sqlite3_close(pSrc); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + } +#else + wxUnusedVar(key); +#endif + + pBackup = sqlite3_backup_init(m_db->m_db, localTargetDatabaseName, pSrc, "main"); + if (pBackup == 0) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + sqlite3_close(pSrc); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + + do + { + rc = sqlite3_backup_step(pBackup, m_backupPageCount); + if (progressCallback != NULL) + { + if (!BackupRestoreCallback(sqlite3_backup_pagecount(pBackup), + sqlite3_backup_remaining(pBackup), progressCallback)) + { + rc = SQLITE_DONE; + } + } + if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) + { + if (nTimeout++ >= 20) break; + sqlite3_sleep(250); + } + else + { + nTimeout = 0; + } + } + while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); + + sqlite3_backup_finish(pBackup); + if (rc == SQLITE_DONE) + { + sqlite3_close(pSrc); + } + else if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) + { + sqlite3_close(pSrc); + throw wxSQLite3Exception(rc, wxERRMSG_SOURCEDB_BUSY); + } + else + { + const char* localError = sqlite3_errmsg(pSrc); + sqlite3_close(pSrc); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } +#else + wxUnusedVar(sourceFileName); + wxUnusedVar(targetDatabaseName); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOBACKUP); +#endif +} + +void wxSQLite3Database::SetBackupRestorePageCount(int pageCount) +{ + m_backupPageCount = pageCount; +} + +void wxSQLite3Database::Vacuum() +{ + ExecuteUpdate("vacuum"); +} + +void wxSQLite3Database::Begin(wxSQLite3TransactionType transactionType) +{ + wxString sql; + switch (transactionType) + { + case WXSQLITE_TRANSACTION_DEFERRED: + sql << wxT("begin deferred transaction"); + break; + case WXSQLITE_TRANSACTION_IMMEDIATE: + sql << wxT("begin immediate transaction"); + break; + case WXSQLITE_TRANSACTION_EXCLUSIVE: + sql << wxT("begin exclusive transaction"); + break; + default: + sql << wxT("begin transaction"); + break; + } + ExecuteUpdate(sql); +} + +void wxSQLite3Database::Commit() +{ + ExecuteUpdate("commit transaction"); +} + +void wxSQLite3Database::Rollback(const wxString& savepointName) +{ +#if SQLITE_VERSION_NUMBER >= 3006008 + if (savepointName.IsEmpty()) + { +#endif + ExecuteUpdate("rollback transaction"); +#if SQLITE_VERSION_NUMBER >= 3006008 + } + else + { + ExecuteUpdate(wxString(wxT("rollback transaction to savepoint "))+savepointName); + } +#endif +} + +bool wxSQLite3Database::GetAutoCommit() +{ + CheckDatabase(); + return sqlite3_get_autocommit(m_db->m_db) != 0; +} + +int wxSQLite3Database::QueryRollbackState() +{ + return m_lastRollbackRC; +} + +void wxSQLite3Database::Savepoint(const wxString& savepointName) +{ +#if SQLITE_VERSION_NUMBER >= 3006008 + ExecuteUpdate(wxString(wxT("savepoint "))+savepointName); +#else + wxUnusedVar(savepointName); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOSAVEPOINT); +#endif +} + +void wxSQLite3Database::ReleaseSavepoint(const wxString& savepointName) +{ +#if SQLITE_VERSION_NUMBER >= 3006008 + ExecuteUpdate(wxString(wxT("release savepoint "))+savepointName); +#else + wxUnusedVar(savepointName); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOSAVEPOINT); +#endif +} + +wxSQLite3Statement wxSQLite3Database::PrepareStatement(const wxString& sql) +{ + wxCharBuffer strSql = sql.ToUTF8(); + const char* localSql = strSql; + return PrepareStatement(localSql); +} + +wxSQLite3Statement wxSQLite3Database::PrepareStatement(const wxSQLite3StatementBuffer& sql) +{ + return PrepareStatement((const char*) sql); +} + +wxSQLite3Statement wxSQLite3Database::PrepareStatement(const char* sql) { CheckDatabase(); sqlite3_stmt* stmt = (sqlite3_stmt*) Prepare(sql); - return wxSQLite3Statement(m_db, stmt); + wxSQLite3StatementReference* stmtRef = new wxSQLite3StatementReference(stmt); + return wxSQLite3Statement(m_db, stmtRef); } bool wxSQLite3Database::TableExists(const wxString& tableName, const wxString& databaseName) @@ -2122,11 +3118,11 @@ bool wxSQLite3Database::TableExists(const wxString& tableName, const wxString& d wxString sql; if (databaseName.IsEmpty()) { - sql = _T("select count(*) from sqlite_master where type='table' and name like ?"); + sql = wxT("select count(*) from sqlite_master where type='table' and name like ?"); } else { - sql = wxString(_T("select count(*) from ")) + databaseName + wxString(_T(".sqlite_master where type='table' and name like ?")); + sql = wxString(wxT("select count(*) from ")) + databaseName + wxString(wxT(".sqlite_master where type='table' and name like ?")); } wxSQLite3Statement stmt = PrepareStatement(sql); stmt.Bind(1, tableName); @@ -2180,9 +3176,116 @@ void wxSQLite3Database::GetDatabaseList(wxArrayString& databaseNames, wxArrayStr } } +wxString wxSQLite3Database::GetDatabaseFilename(const wxString& databaseName) +{ +#if SQLITE_VERSION_NUMBER >= 3007010 + CheckDatabase(); + wxCharBuffer strDatabaseName = databaseName.ToUTF8(); + const char* localDatabaseName = strDatabaseName; + const char* localFilename = sqlite3_db_filename(m_db->m_db, localDatabaseName); + return wxString::FromUTF8(localFilename); +#else + wxUnusedVar(databaseName); + return wxEmptyString; +#endif +} + +bool wxSQLite3Database::EnableForeignKeySupport(bool enable) +{ + if (enable) + { + ExecuteUpdate("PRAGMA foreign_keys=ON;"); + } + else + { + ExecuteUpdate("PRAGMA foreign_keys=OFF;"); + } + bool enabled = IsForeignKeySupportEnabled(); + return (enable && enabled) || (!enable && !enabled); +} + +bool wxSQLite3Database::IsForeignKeySupportEnabled() +{ + bool enabled = false; + wxSQLite3ResultSet resultSet = ExecuteQuery("PRAGMA foreign_keys;"); + if (resultSet.NextRow()) + { + enabled = (resultSet.GetInt(0) == 1); + } + return enabled; +} + +wxSQLite3JournalMode +wxSQLite3Database::SetJournalMode(wxSQLite3JournalMode journalMode, const wxString& database) +{ + wxString mode = ConvertJournalMode(journalMode); + wxString query = wxT("PRAGMA "); + if (!database.IsEmpty()) + { + query += database; + query += wxT("."); + } + query += wxT("journal_mode="); + query += mode; + query += wxT(";"); + wxSQLite3ResultSet resultSet = ExecuteQuery(query); + if (resultSet.NextRow()) + { + mode = resultSet.GetString(0); + } + return ConvertJournalMode(mode); +} + +wxSQLite3JournalMode +wxSQLite3Database::GetJournalMode(const wxString& database) +{ + wxString mode = wxT("DELETE"); + wxString query = wxT("PRAGMA "); + if (!database.IsEmpty()) + { + query += database; + query += wxT("."); + } + query += wxT("journal_mode;"); + wxSQLite3ResultSet resultSet = ExecuteQuery(query); + if (resultSet.NextRow()) + { + mode = resultSet.GetString(0); + } + return ConvertJournalMode(mode); +} + +/* static */ +wxString wxSQLite3Database::ConvertJournalMode(wxSQLite3JournalMode mode) +{ + wxString journalMode; + if (mode == WXSQLITE_JOURNALMODE_DELETE) journalMode = wxT("DELETE"); + else if (mode == WXSQLITE_JOURNALMODE_PERSIST) journalMode = wxT("PERSIST"); + else if (mode == WXSQLITE_JOURNALMODE_OFF) journalMode = wxT("OFF"); + else if (mode == WXSQLITE_JOURNALMODE_TRUNCATE) journalMode = wxT("TRUNCATE"); + else if (mode == WXSQLITE_JOURNALMODE_MEMORY) journalMode = wxT("MEMORY"); + else if (mode == WXSQLITE_JOURNALMODE_WAL) journalMode = wxT("WAL"); + else journalMode = wxT("DELETE"); + return journalMode; +} + +/* static */ +wxSQLite3JournalMode wxSQLite3Database::ConvertJournalMode(const wxString& mode) +{ + wxSQLite3JournalMode journalMode; + if (mode.IsSameAs(wxT("DELETE"))) journalMode = WXSQLITE_JOURNALMODE_DELETE; + else if (mode.IsSameAs(wxT("PERSIST"))) journalMode = WXSQLITE_JOURNALMODE_PERSIST; + else if (mode.IsSameAs(wxT("OFF"))) journalMode = WXSQLITE_JOURNALMODE_OFF; + else if (mode.IsSameAs(wxT("TRUNCATE"))) journalMode = WXSQLITE_JOURNALMODE_TRUNCATE; + else if (mode.IsSameAs(wxT("MEMORY"))) journalMode = WXSQLITE_JOURNALMODE_MEMORY; + else if (mode.IsSameAs(wxT("WAL"))) journalMode = WXSQLITE_JOURNALMODE_WAL; + else journalMode = WXSQLITE_JOURNALMODE_DELETE; + return journalMode; +} + bool wxSQLite3Database::CheckSyntax(const wxString& sql) { - wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent)); + wxCharBuffer strSql = sql.ToUTF8(); const char* localSql = strSql; return CheckSyntax(localSql); } @@ -2199,7 +3302,7 @@ bool wxSQLite3Database::CheckSyntax(const char* sql) int wxSQLite3Database::ExecuteUpdate(const wxString& sql) { - wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent)); + wxCharBuffer strSql = sql.ToUTF8(); const char* localSql = strSql; return ExecuteUpdate(localSql); } @@ -2209,27 +3312,36 @@ int wxSQLite3Database::ExecuteUpdate(const wxSQLite3StatementBuffer& sql) return ExecuteUpdate((const char*) sql); } -int wxSQLite3Database::ExecuteUpdate(const char* sql) +int wxSQLite3Database::ExecuteUpdate(const char* sql, bool saveRC) { CheckDatabase(); - char* localError=0; + char* localError = 0; - int rc = sqlite3_exec((sqlite3*) m_db, sql, 0, 0, &localError); + int rc = sqlite3_exec(m_db->m_db, sql, 0, 0, &localError); + if (saveRC) + { + if (strncmp(sql, "rollback transaction", 20) == 0) + { + m_lastRollbackRC = rc; + } + } if (rc == SQLITE_OK) { - return sqlite3_changes((sqlite3*) m_db); + return sqlite3_changes(m_db->m_db); } else { - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + wxString errmsg = wxString::FromUTF8(localError); + sqlite3_free(localError); + throw wxSQLite3Exception(rc, errmsg); } } wxSQLite3ResultSet wxSQLite3Database::ExecuteQuery(const wxString& sql) { - wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent)); + wxCharBuffer strSql = sql.ToUTF8(); const char* localSql = strSql; return ExecuteQuery(localSql); } @@ -2249,23 +3361,25 @@ wxSQLite3ResultSet wxSQLite3Database::ExecuteQuery(const char* sql) if (rc == SQLITE_DONE) // no rows { - return wxSQLite3ResultSet(m_db, stmt, true /* eof */); + wxSQLite3StatementReference* stmtRef = new wxSQLite3StatementReference(stmt); + return wxSQLite3ResultSet(m_db, stmtRef, true /* eof */); } else if (rc == SQLITE_ROW) // one or more rows { - return wxSQLite3ResultSet(m_db, stmt, false /* eof */); + wxSQLite3StatementReference* stmtRef = new wxSQLite3StatementReference(stmt); + return wxSQLite3ResultSet(m_db, stmtRef, false /* eof */); } else { rc = sqlite3_finalize(stmt); - const char* localError= sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError= sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } int wxSQLite3Database::ExecuteScalar(const wxString& sql) { - wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent)); + wxCharBuffer strSql = sql.ToUTF8(); const char* localSql = strSql; return ExecuteScalar(localSql); } @@ -2291,7 +3405,7 @@ int wxSQLite3Database::ExecuteScalar(const char* sql) wxSQLite3Table wxSQLite3Database::GetTable(const wxString& sql) { - wxCharBuffer strSql = wxConvUTF8.cWC2MB(sql.wc_str(*wxConvCurrent)); + wxCharBuffer strSql = sql.ToUTF8(); const char* localSql = strSql; return GetTable(localSql); } @@ -2311,7 +3425,7 @@ wxSQLite3Table wxSQLite3Database::GetTable(const char* sql) int rows(0); int cols(0); - rc = sqlite3_get_table((sqlite3*) m_db, sql, &results, &rows, &cols, &localError); + rc = sqlite3_get_table(m_db->m_db, sql, &results, &rows, &cols, &localError); if (rc == SQLITE_OK) { @@ -2319,14 +3433,14 @@ wxSQLite3Table wxSQLite3Database::GetTable(const char* sql) } else { - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } wxLongLong wxSQLite3Database::GetLastRowId() { CheckDatabase(); - return wxLongLong(sqlite3_last_insert_rowid((sqlite3*) m_db)); + return wxLongLong(sqlite3_last_insert_rowid(m_db->m_db)); } wxSQLite3Blob wxSQLite3Database::GetReadOnlyBlob(wxLongLong rowId, @@ -2352,22 +3466,23 @@ wxSQLite3Blob wxSQLite3Database::GetBlob(wxLongLong rowId, bool writable) { #if SQLITE_VERSION_NUMBER >= 3004000 - wxCharBuffer strColumnName = wxConvUTF8.cWC2MB(columnName.wc_str(*wxConvCurrent)); + wxCharBuffer strColumnName = columnName.ToUTF8(); const char* localColumnName = strColumnName; - wxCharBuffer strTableName = wxConvUTF8.cWC2MB(tableName.wc_str(*wxConvCurrent)); + wxCharBuffer strTableName = tableName.ToUTF8(); const char* localTableName = strTableName; - wxCharBuffer strDbName = wxConvUTF8.cWC2MB(dbName.wc_str(*wxConvCurrent)); + wxCharBuffer strDbName = dbName.ToUTF8(); const char* localDbName = (!dbName.IsEmpty()) ? (const char*) strDbName : (const char*) NULL; int flags = (writable) ? 1 : 0; sqlite3_blob* blobHandle; CheckDatabase(); - int rc = sqlite3_blob_open((sqlite3*) m_db, localDbName, localTableName, localColumnName, rowId.GetValue(), flags, &blobHandle); + int rc = sqlite3_blob_open(m_db->m_db, localDbName, localTableName, localColumnName, rowId.GetValue(), flags, &blobHandle); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } - return wxSQLite3Blob(m_db, (void*) blobHandle, writable); + wxSQLite3BlobReference* blobRef = new wxSQLite3BlobReference(blobHandle); + return wxSQLite3Blob(m_db, blobRef, writable); #else wxUnusedVar(rowId); wxUnusedVar(columnName); @@ -2382,39 +3497,93 @@ wxSQLite3Blob wxSQLite3Database::GetBlob(wxLongLong rowId, void wxSQLite3Database::Interrupt() { CheckDatabase(); - sqlite3_interrupt((sqlite3*) m_db); + sqlite3_interrupt(m_db->m_db); } void wxSQLite3Database::SetBusyTimeout(int nMillisecs) { CheckDatabase(); m_busyTimeoutMs = nMillisecs; - sqlite3_busy_timeout((sqlite3*) m_db, m_busyTimeoutMs); + sqlite3_busy_timeout(m_db->m_db, m_busyTimeoutMs); +} + +wxString wxSQLite3Database::GetWrapperVersion() +{ + return wxString(wxSQLITE3_VERSION_STRING); } wxString wxSQLite3Database::GetVersion() { - return UTF8toWxString(sqlite3_libversion()); + return wxString::FromUTF8(sqlite3_libversion()); +} + +wxString wxSQLite3Database::GetSourceId() +{ +#if SQLITE_VERSION_NUMBER >= 3006018 + return wxString::FromUTF8(sqlite3_sourceid()); +#else + return wxEmptyString; +#endif +} + +bool wxSQLite3Database::CompileOptionUsed(const wxString& optionName) +{ +#if SQLITE_VERSION_NUMBER >= 3006023 + wxCharBuffer strOption = optionName.ToUTF8(); + const char* localOption = strOption; + return sqlite3_compileoption_used(localOption) == 1; +#else + return false; +#endif +} + +wxString wxSQLite3Database::GetCompileOptionName(int optionIndex) +{ +#if SQLITE_VERSION_NUMBER >= 3006023 + const char* unknownOption = ""; + const char* optionName = sqlite3_compileoption_get(optionIndex); + if (optionName == NULL) + { + optionName = unknownOption; + } + return wxString::FromUTF8(optionName); +#else + return wxEmptyString; +#endif } -bool wxSQLite3Database::CreateFunction(const wxString& funcName, int argCount, wxSQLite3ScalarFunction& function) +bool wxSQLite3Database::CreateFunction(const wxString& funcName, int argCount, wxSQLite3ScalarFunction& function, bool isDeterministic) { CheckDatabase(); - wxCharBuffer strFuncName = wxConvUTF8.cWC2MB(funcName.wc_str(*wxConvCurrent)); + wxCharBuffer strFuncName = funcName.ToUTF8(); const char* localFuncName = strFuncName; - int rc = sqlite3_create_function((sqlite3*) m_db, localFuncName, argCount, - SQLITE_UTF8, &function, + int flags = SQLITE_UTF8; +#if SQLITE_VERSION_NUMBER >= 3008003 + if (isDeterministic) + { + flags |= SQLITE_DETERMINISTIC; + } +#endif + int rc = sqlite3_create_function(m_db->m_db, localFuncName, argCount, + flags, &function, (void (*)(sqlite3_context*,int,sqlite3_value**)) wxSQLite3FunctionContext::ExecScalarFunction, NULL, NULL); return rc == SQLITE_OK; } -bool wxSQLite3Database::CreateFunction(const wxString& funcName, int argCount, wxSQLite3AggregateFunction& function) +bool wxSQLite3Database::CreateFunction(const wxString& funcName, int argCount, wxSQLite3AggregateFunction& function, bool isDeterministic) { CheckDatabase(); - wxCharBuffer strFuncName = wxConvUTF8.cWC2MB(funcName.wc_str(*wxConvCurrent)); + wxCharBuffer strFuncName = funcName.ToUTF8(); const char* localFuncName = strFuncName; - int rc = sqlite3_create_function((sqlite3*) m_db, localFuncName, argCount, - SQLITE_UTF8, &function, + int flags = SQLITE_UTF8; +#if SQLITE_VERSION_NUMBER >= 3008003 + if (isDeterministic) + { + flags |= SQLITE_DETERMINISTIC; + } +#endif + int rc = sqlite3_create_function(m_db->m_db, localFuncName, argCount, + flags, &function, NULL, (void (*)(sqlite3_context*,int,sqlite3_value**)) wxSQLite3FunctionContext::ExecAggregateStep, (void (*)(sqlite3_context*)) wxSQLite3FunctionContext::ExecAggregateFinalize); @@ -2424,7 +3593,7 @@ bool wxSQLite3Database::CreateFunction(const wxString& funcName, int argCount, w bool wxSQLite3Database::SetAuthorizer(wxSQLite3Authorizer& authorizer) { CheckDatabase(); - int rc = sqlite3_set_authorizer((sqlite3*) m_db, wxSQLite3FunctionContext::ExecAuthorizer, &authorizer); + int rc = sqlite3_set_authorizer(m_db->m_db, (sqlite3_xauth) wxSQLite3FunctionContext::ExecAuthorizer, &authorizer); return rc == SQLITE_OK; } @@ -2433,11 +3602,11 @@ void wxSQLite3Database::SetCommitHook(wxSQLite3Hook* commitHook) CheckDatabase(); if (commitHook) { - sqlite3_commit_hook((sqlite3*) m_db, (int(*)(void*)) wxSQLite3FunctionContext::ExecCommitHook, commitHook); + sqlite3_commit_hook(m_db->m_db, (int(*)(void*)) wxSQLite3FunctionContext::ExecCommitHook, commitHook); } else { - sqlite3_commit_hook((sqlite3*) m_db, (int(*)(void*)) NULL, NULL); + sqlite3_commit_hook(m_db->m_db, (int(*)(void*)) NULL, NULL); } } @@ -2446,11 +3615,11 @@ void wxSQLite3Database::SetRollbackHook(wxSQLite3Hook* rollbackHook) CheckDatabase(); if (rollbackHook) { - sqlite3_rollback_hook((sqlite3*) m_db, (void(*)(void*)) wxSQLite3FunctionContext::ExecRollbackHook, rollbackHook); + sqlite3_rollback_hook(m_db->m_db, (void(*)(void*)) wxSQLite3FunctionContext::ExecRollbackHook, rollbackHook); } else { - sqlite3_rollback_hook((sqlite3*) m_db, (void(*)(void*)) NULL, NULL); + sqlite3_rollback_hook(m_db->m_db, (void(*)(void*)) NULL, NULL); } } @@ -2459,44 +3628,111 @@ void wxSQLite3Database::SetUpdateHook(wxSQLite3Hook* updateHook) CheckDatabase(); if (updateHook) { - sqlite3_update_hook((sqlite3*) m_db, (void(*)(void*,int,const char*,const char*, wxsqlite_int64)) wxSQLite3FunctionContext::ExecUpdateHook, updateHook); + sqlite3_update_hook(m_db->m_db, (void(*)(void*,int,const char*,const char*, wxsqlite_int64)) wxSQLite3FunctionContext::ExecUpdateHook, updateHook); + } + else + { + sqlite3_update_hook(m_db->m_db, (void(*)(void*,int,const char*,const char*, wxsqlite_int64)) NULL, NULL); + } +} + +void wxSQLite3Database::SetWriteAheadLogHook(wxSQLite3Hook* walHook) +{ +#if SQLITE_VERSION_NUMBER >= 3007000 + CheckDatabase(); + if (walHook) + { + walHook->SetDatabase(this); + sqlite3_wal_hook(m_db->m_db, (int(*)(void *,sqlite3*,const char*,int)) wxSQLite3FunctionContext::ExecWriteAheadLogHook, walHook); } else { - sqlite3_update_hook((sqlite3*) m_db, (void(*)(void*,int,const char*,const char*, wxsqlite_int64)) NULL, NULL); + sqlite3_wal_hook(m_db->m_db, (int(*)(void *,sqlite3*,const char*,int)) NULL, NULL); + } +#else + wxUnusedVar(walHook); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOWAL); +#endif +} + +void wxSQLite3Database::WriteAheadLogCheckpoint(const wxString& database, int mode, + int* logFrameCount, int* ckptFrameCount) +{ +#if SQLITE_VERSION_NUMBER >= 3007000 + CheckDatabase(); + wxCharBuffer strDatabase = database.ToUTF8(); + const char* localDatabase = strDatabase; +#if SQLITE_VERSION_NUMBER >= 3007006 + int rc = sqlite3_wal_checkpoint_v2(m_db->m_db, localDatabase, mode, logFrameCount, ckptFrameCount); +#else + int rc = sqlite3_wal_checkpoint(m_db->m_db, localDatabase); + if (logFrameCount != NULL) *logFrameCount = 0; + if (ckptFrameCount != NULL) *ckptFrameCount = 0; +#endif + + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } +#else + wxUnusedVar(database); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOWAL); +#endif +} + +void wxSQLite3Database::AutoWriteAheadLogCheckpoint(int frameCount) +{ +#if SQLITE_VERSION_NUMBER >= 3007000 + CheckDatabase(); + int rc = sqlite3_wal_autocheckpoint(m_db->m_db, frameCount); + + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } +#else + wxUnusedVar(frameCount); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOWAL); +#endif } void wxSQLite3Database::SetCollation(const wxString& collationName, wxSQLite3Collation* collation) { CheckDatabase(); - wxCharBuffer strCollationName = wxConvUTF8.cWC2MB(collationName.wc_str(*wxConvCurrent)); + wxCharBuffer strCollationName = collationName.ToUTF8(); const char* localCollationName = strCollationName; int rc; if (collation) { - rc = sqlite3_create_collation((sqlite3*) m_db, localCollationName, SQLITE_UTF8, collation, (int(*)(void*,int,const void*,int,const void*)) wxSQLite3Database::ExecComparisonWithCollation); + rc = sqlite3_create_collation(m_db->m_db, localCollationName, SQLITE_UTF8, collation, (int(*)(void*,int,const void*,int,const void*)) wxSQLite3Database::ExecComparisonWithCollation); } else { - rc = sqlite3_create_collation((sqlite3*) m_db, localCollationName, SQLITE_UTF8, NULL, (int(*)(void*,int,const void*,int,const void*)) NULL); + rc = sqlite3_create_collation(m_db->m_db, localCollationName, SQLITE_UTF8, NULL, (int(*)(void*,int,const void*,int,const void*)) NULL); } } +void* wxSQLite3Database::GetDatabaseHandle() +{ + return m_db->m_db; +} + void wxSQLite3Database::SetCollationNeededCallback() { CheckDatabase(); - int rc = sqlite3_collation_needed((sqlite3*) m_db, this, (void(*)(void*,sqlite3*,int,const char*)) wxSQLite3Database::ExecCollationNeeded); + int rc = sqlite3_collation_needed(m_db->m_db, this, (void(*)(void*,sqlite3*,int,const char*)) wxSQLite3Database::ExecCollationNeeded); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } } void wxSQLite3Database::CheckDatabase() { - if (!m_db) + if (m_db == NULL || m_db->m_db == NULL || !m_db->m_isValid || !m_isOpen) { throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NODB); } @@ -2509,12 +3745,12 @@ void* wxSQLite3Database::Prepare(const char* sql) const char* tail=0; sqlite3_stmt* stmt; - int rc = sqlite3_prepare_v2((sqlite3*) m_db, sql, -1, &stmt, &tail); + int rc = sqlite3_prepare_v2(m_db->m_db, sql, -1, &stmt, &tail); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } return stmt; @@ -2525,30 +3761,14 @@ int wxSQLite3Database::ExecComparisonWithCollation(void* collation, int len1, const void* text1, int len2, const void* text2) { -#if wxUSE_UNICODE - wxString locText1((const char*) text1, wxConvUTF8, (size_t) len1); - wxString locText2((const char*) text2, wxConvUTF8, (size_t) len2); -#else - size_t len = (len1 > len2) ? len1 : len2; - char* buffer = new char[len+1]; - memcpy(buffer, text1, len1); - buffer[len1] = '\0'; - wxString locText1(wxConvUTF8.cMB2WC((const char*) buffer), *wxConvCurrent); - memcpy(buffer, text2, len2); - buffer[len2] = '\0'; - wxString locText2(wxConvUTF8.cMB2WC((const char*) buffer), *wxConvCurrent); - delete [] buffer; -#endif + wxString locText1 = wxString::FromUTF8((const char*) text1, (size_t) len1); + wxString locText2 = wxString::FromUTF8((const char*) text2, (size_t) len2); return ((wxSQLite3Collation*) collation)->Compare(locText1, locText2); } void wxSQLite3Database::ExecCollationNeeded(void* db, void*, int, const char* collationName) { -#if wxUSE_UNICODE - wxString locCollation((const char*) collationName, wxConvUTF8); -#else - wxString locCollation(wxConvUTF8.cMB2WC((const char*) collationName), *wxConvCurrent); -#endif + wxString locCollation = wxString::FromUTF8((const char*) collationName); ((wxSQLite3Database*) db)->SetNeededCollation(locCollation); } @@ -2556,29 +3776,30 @@ void wxSQLite3Database::GetMetaData(const wxString& databaseName, const wxString wxString* dataType, wxString* collation, bool* notNull, bool* primaryKey, bool* autoIncrement) { #if WXSQLITE3_HAVE_METADATA - wxCharBuffer strDatabaseName = wxConvUTF8.cWC2MB(databaseName.wc_str(*wxConvCurrent)); + CheckDatabase(); + wxCharBuffer strDatabaseName = databaseName.ToUTF8(); const char* localDatabaseName = strDatabaseName; if (databaseName == wxEmptyString) localDatabaseName = NULL; - wxCharBuffer strTableName = wxConvUTF8.cWC2MB(tableName.wc_str(*wxConvCurrent)); + wxCharBuffer strTableName = tableName.ToUTF8(); const char* localTableName = strTableName; - wxCharBuffer strColumnName = wxConvUTF8.cWC2MB(columnName.wc_str(*wxConvCurrent)); + wxCharBuffer strColumnName = columnName.ToUTF8(); const char* localColumnName = strColumnName; const char* localDataType; const char* localCollation; int localNotNull; int localPrimaryKey; int localAutoIncrement; - int rc = sqlite3_table_column_metadata((sqlite3*) m_db, localDatabaseName, localTableName, localColumnName, + int rc = sqlite3_table_column_metadata(m_db->m_db, localDatabaseName, localTableName, localColumnName, &localDataType, &localCollation, &localNotNull, &localPrimaryKey, &localAutoIncrement); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } - if (dataType != NULL) *dataType = UTF8toWxString(localDataType); - if (collation != NULL) *collation = UTF8toWxString(localCollation); + if (dataType != NULL) *dataType = wxString::FromUTF8(localDataType); + if (collation != NULL) *collation = wxString::FromUTF8(localCollation); if (notNull != NULL) *notNull = (localNotNull != 0); if (primaryKey != NULL) *primaryKey = (localPrimaryKey != 0); @@ -2599,16 +3820,17 @@ void wxSQLite3Database::GetMetaData(const wxString& databaseName, const wxString void wxSQLite3Database::LoadExtension(const wxString& fileName, const wxString& entryPoint) { #if WXSQLITE3_HAVE_LOAD_EXTENSION - wxCharBuffer strFileName = wxConvUTF8.cWC2MB(fileName.wc_str(*wxConvCurrent)); + CheckDatabase(); + wxCharBuffer strFileName = fileName.ToUTF8(); const char* localFileName = strFileName; - wxCharBuffer strEntryPoint = wxConvUTF8.cWC2MB(entryPoint.wc_str(*wxConvCurrent)); + wxCharBuffer strEntryPoint = entryPoint.ToUTF8(); const char* localEntryPoint = strEntryPoint; - int rc = sqlite3_load_extension((sqlite3 *) m_db, localFileName, localEntryPoint, NULL); + int rc = sqlite3_load_extension(m_db->m_db, localFileName, localEntryPoint, NULL); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } #else wxUnusedVar(fileName); @@ -2620,12 +3842,13 @@ void wxSQLite3Database::LoadExtension(const wxString& fileName, const wxString& void wxSQLite3Database::EnableLoadExtension(bool enable) { #if WXSQLITE3_HAVE_LOAD_EXTENSION + CheckDatabase(); int onoff = (enable) ? 1 : 0; - int rc = sqlite3_enable_load_extension((sqlite3 *) m_db, onoff); + int rc = sqlite3_enable_load_extension(m_db->m_db, onoff); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } #else wxUnusedVar(enable); @@ -2636,7 +3859,7 @@ void wxSQLite3Database::EnableLoadExtension(bool enable) void wxSQLite3Database::ReKey(const wxString& newKey) { #if WXSQLITE3_HAVE_CODEC - wxCharBuffer strLocalNewKey = wxConvUTF8.cWC2MB(newKey.wc_str(*wxConvCurrent)); + wxCharBuffer strLocalNewKey = newKey.ToUTF8(); const char* localNewKey = strLocalNewKey; wxMemoryBuffer binaryNewKey; if (newKey.Length() > 0) @@ -2653,11 +3876,12 @@ void wxSQLite3Database::ReKey(const wxString& newKey) void wxSQLite3Database::ReKey(const wxMemoryBuffer& newKey) { #if WXSQLITE3_HAVE_CODEC - int rc = sqlite3_rekey((sqlite3*) m_db, newKey.GetData(), (int) newKey.GetDataLen()); + CheckDatabase(); + int rc = sqlite3_rekey(m_db->m_db, newKey.GetData(), (int) newKey.GetDataLen()); if (rc != SQLITE_OK) { - const char* localError = sqlite3_errmsg((sqlite3*) m_db); - throw wxSQLite3Exception(rc, UTF8toWxString(localError)); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); } #else wxUnusedVar(newKey); @@ -2665,14 +3889,152 @@ void wxSQLite3Database::ReKey(const wxMemoryBuffer& newKey) #endif } -int wxSQLite3Database::GetLimit(wxSQLite3LimitType id) +bool wxSQLite3Database::UserLogin(const wxString& username, const wxString& password) { - int value = -1; -#if SQLITE_VERSION_NUMBER >= 3005008 +#if WXSQLITE3_USER_AUTHENTICATION && SQLITE_VERSION_NUMBER >= 3008007 CheckDatabase(); - if (id >= WXSQLITE_LIMIT_LENGTH && id <= WXSQLITE_LIMIT_VARIABLE_NUMBER) + wxCharBuffer strUsername = username.ToUTF8(); + const char* localUsername = strUsername; + wxCharBuffer strPassword = password.ToUTF8(); + const char* localPassword = strPassword; + + int rc = sqlite3_user_authenticate(m_db->m_db, localUsername, localPassword, strlen(localPassword)); + bool authenticated = (rc == SQLITE_OK); + if (rc != SQLITE_OK && rc != SQLITE_AUTH) { - value = sqlite3_limit((sqlite3 *) m_db, id, -1); + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + return authenticated; +#else + wxUnusedVar(username); + wxUnusedVar(password); + return true; +#endif +} + +bool wxSQLite3Database::UserAdd(const wxString& username, const wxString& password, bool isAdmin) +{ +#if WXSQLITE3_USER_AUTHENTICATION && SQLITE_VERSION_NUMBER >= 3008007 + CheckDatabase(); + wxCharBuffer strUsername = username.ToUTF8(); + const char* localUsername = strUsername; + wxCharBuffer strPassword = password.ToUTF8(); + const char* localPassword = strPassword; + int nIsAdmin = (isAdmin) ? 1 : 0; + int rc = sqlite3_user_add(m_db->m_db, localUsername, localPassword, strlen(localPassword), nIsAdmin); + bool authenticated = (rc == SQLITE_OK); + if (rc != SQLITE_OK && rc != SQLITE_AUTH) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + return authenticated; +#else + wxUnusedVar(username); + wxUnusedVar(password); + wxUnusedVar(isAdmin); + return true; +#endif +} + +bool wxSQLite3Database::UserChange(const wxString& username, const wxString& password, bool isAdmin) +{ +#if WXSQLITE3_USER_AUTHENTICATION && SQLITE_VERSION_NUMBER >= 3008007 + CheckDatabase(); + wxCharBuffer strUsername = username.ToUTF8(); + const char* localUsername = strUsername; + wxCharBuffer strPassword = password.ToUTF8(); + const char* localPassword = strPassword; + int nIsAdmin = (isAdmin) ? 1 : 0; + int rc = sqlite3_user_change(m_db->m_db, localUsername, localPassword, strlen(localPassword), nIsAdmin); + bool authenticated = (rc == SQLITE_OK); + if (rc != SQLITE_OK && rc != SQLITE_AUTH) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + return authenticated; +#else + wxUnusedVar(username); + wxUnusedVar(password); + wxUnusedVar(isAdmin); + return false; +#endif +} + +bool wxSQLite3Database::UserDelete(const wxString& username) +{ +#if WXSQLITE3_USER_AUTHENTICATION && SQLITE_VERSION_NUMBER >= 3008007 + CheckDatabase(); + wxCharBuffer strUsername = username.ToUTF8(); + const char* localUsername = strUsername; + + int rc = sqlite3_user_delete(m_db->m_db, localUsername); + bool authenticated = (rc == SQLITE_OK); + if (rc != SQLITE_OK && rc != SQLITE_AUTH) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + return authenticated; +#else + wxUnusedVar(username); + return false; +#endif +} + +bool wxSQLite3Database::UserIsPrivileged(const wxString& username) +{ +#if WXSQLITE3_USER_AUTHENTICATION && SQLITE_VERSION_NUMBER >= 3008007 + CheckDatabase(); + bool isPrivileged = false; + wxString sql = wxT("select isAdmin from main.sqlite_user where uname=?;"); + wxSQLite3Statement stmt = PrepareStatement(sql); + stmt.Bind(1, username); + wxSQLite3ResultSet resultSet = stmt.ExecuteQuery(); + if (resultSet.NextRow()) + { + isPrivileged = resultSet.GetBool(0); + } + return isPrivileged; +#else + wxUnusedVar(username); + return false; +#endif +} + +void wxSQLite3Database::GetUserList(wxArrayString& userList) +{ + userList.Empty(); +#if WXSQLITE3_USER_AUTHENTICATION && SQLITE_VERSION_NUMBER >= 3008007 + CheckDatabase(); + wxSQLite3ResultSet resultSet = ExecuteQuery("select uname from main.sqlite_user order by uname;"); + while (resultSet.NextRow()) + { + userList.Add(resultSet.GetString(0)); + } +#else + wxUnusedVar(userList); +#endif +} + +#if 0 +CREATE TABLE sqlite_user( + uname TEXT PRIMARY KEY, + isAdmin BOOLEAN, + pw BLOB + ) WITHOUT ROWID; +#endif + +int wxSQLite3Database::GetLimit(wxSQLite3LimitType id) +{ + int value = -1; +#if SQLITE_VERSION_NUMBER >= 3005008 + CheckDatabase(); + if (id >= WXSQLITE_LIMIT_LENGTH && id <= WXSQLITE_LIMIT_VARIABLE_NUMBER) + { + value = sqlite3_limit(m_db->m_db, id, -1); } #else wxUnusedVar(id); @@ -2687,7 +4049,7 @@ int wxSQLite3Database::SetLimit(wxSQLite3LimitType id, int newValue) CheckDatabase(); if (id >= WXSQLITE_LIMIT_LENGTH && id <= WXSQLITE_LIMIT_VARIABLE_NUMBER) { - value = sqlite3_limit((sqlite3 *) m_db, id, newValue); + value = sqlite3_limit(m_db->m_db, id, newValue); } #else wxUnusedVar(id); @@ -2696,19 +4058,33 @@ int wxSQLite3Database::SetLimit(wxSQLite3LimitType id, int newValue) return value; } +void wxSQLite3Database::ReleaseMemory() +{ +#if SQLITE_VERSION_NUMBER >= 3007010 + CheckDatabase(); + int rc = sqlite3_db_release_memory(m_db->m_db); + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } +#endif +} + static const wxChar* limitCodeString[] = -{ _T("SQLITE_LIMIT_LENGTH"), _T("SQLITE_LIMIT_SQL_LENGTH"), - _T("SQLITE_LIMIT_COLUMN"), _T("SQLITE_LIMIT_EXPR_DEPTH"), - _T("SQLITE_LIMIT_COMPOUND_SELECT"), _T("SQLITE_LIMIT_VDBE_OP"), - _T("SQLITE_LIMIT_FUNCTION_ARG"), _T("SQLITE_LIMIT_ATTACHED"), - _T("SQLITE_LIMIT_LIKE_PATTERN_LENGTH"), _T("SQLITE_LIMIT_VARIABLE_NUMBER") +{ wxT("SQLITE_LIMIT_LENGTH"), wxT("SQLITE_LIMIT_SQL_LENGTH"), + wxT("SQLITE_LIMIT_COLUMN"), wxT("SQLITE_LIMIT_EXPR_DEPTH"), + wxT("SQLITE_LIMIT_COMPOUND_SELECT"), wxT("SQLITE_LIMIT_VDBE_OP"), + wxT("SQLITE_LIMIT_FUNCTION_ARG"), wxT("SQLITE_LIMIT_ATTACHED"), + wxT("SQLITE_LIMIT_LIKE_PATTERN_LENGTH"), wxT("SQLITE_LIMIT_VARIABLE_NUMBER"), + wxT("SQLITE_LIMIT_TRIGGER_DEPTH") }; /* static */ wxString wxSQLite3Database::LimitTypeToString(wxSQLite3LimitType type) { - const wxChar* limitString = _T("Unknown"); + const wxChar* limitString = wxT("Unknown"); if (type >= WXSQLITE_LIMIT_LENGTH && type <= WXSQLITE_LIMIT_VARIABLE_NUMBER) { limitString = limitCodeString[type]; @@ -2741,6 +4117,31 @@ void wxSQLite3Database::ShutdownSQLite() } /* static */ +bool wxSQLite3Database::SetTemporaryDirectory(const wxString& tempDirectory) +{ + bool ok = false; +#if SQLITE_VERSION_NUMBER >= 3007014 +#if defined(__WXMSW__) + DWORD SQLITE_WIN32_TEMP_DIRECTORY_TYPE = 2; +#if wxUSE_UNICODE + const wxChar* zValue = tempDirectory.wc_str(); +#else + const wxWCharBuffer zValue = tempDirectory.wc_str(wxConvLocal); +#endif + int rc = sqlite3_win32_set_directory(SQLITE_WIN32_TEMP_DIRECTORY_TYPE, zValue); + ok = (rc == SQLITE_OK); +#if 0 + if (rc != SQLITE_OK) + { + throw wxSQLite3Exception(rc, wxERRMSG_TEMPDIR); + } +#endif +#endif +#endif + return ok; +} + +/* static */ bool wxSQLite3Database::Randomness(int n, wxMemoryBuffer& random) { bool ok = false; @@ -2857,7 +4258,7 @@ wxString wxSQLite3FunctionContext::GetString(int argIndex, const wxString& nullV if (!IsNull(argIndex)) { const char* localValue = (const char*) sqlite3_value_text((sqlite3_value*) m_argv[argIndex]); - return UTF8toWxString(localValue); + return wxString::FromUTF8(localValue); } else { @@ -2901,7 +4302,7 @@ void wxSQLite3FunctionContext::SetResult(double value) void wxSQLite3FunctionContext::SetResult(const wxString& value) { - wxCharBuffer strValue = wxConvUTF8.cWC2MB(value.wc_str(*wxConvCurrent)); + wxCharBuffer strValue = value.ToUTF8(); const char* localValue = strValue; sqlite3_result_text((sqlite3_context*) m_ctx, localValue, -1, SQLITE_TRANSIENT); } @@ -2939,7 +4340,7 @@ void wxSQLite3FunctionContext::SetResultArg(int argIndex) void wxSQLite3FunctionContext::SetResultError(const wxString& errmsg) { - wxCharBuffer strErrmsg = wxConvUTF8.cWC2MB(errmsg.wc_str(*wxConvCurrent)); + wxCharBuffer strErrmsg = errmsg.ToUTF8(); const char* localErrmsg = strErrmsg; sqlite3_result_error((sqlite3_context*) m_ctx, localErrmsg, -1); } @@ -2998,21 +4399,23 @@ void wxSQLite3FunctionContext::ExecAggregateFinalize(void* ctx) /* static */ int wxSQLite3FunctionContext::ExecAuthorizer(void* func, int type, const char* arg1, const char* arg2, - const char* arg3, const char* arg4) -{ -#if wxUSE_UNICODE - wxString locArg1(arg1, wxConvUTF8); - wxString locArg2(arg2, wxConvUTF8); - wxString locArg3(arg3, wxConvUTF8); - wxString locArg4(arg4, wxConvUTF8); + const char* arg3, const char* arg4 +#if WXSQLITE3_USER_AUTHENTICATION + , const char* arg5 +#endif + ) +{ + wxString locArg1 = wxString::FromUTF8(arg1); + wxString locArg2 = wxString::FromUTF8(arg2); + wxString locArg3 = wxString::FromUTF8(arg3); + wxString locArg4 = wxString::FromUTF8(arg4); +#if WXSQLITE3_USER_AUTHENTICATION + wxString locArg5 = wxString::FromUTF8(arg5); #else - wxString locArg1(wxConvUTF8.cMB2WC(arg1), *wxConvCurrent); - wxString locArg2(wxConvUTF8.cMB2WC(arg2), *wxConvCurrent); - wxString locArg3(wxConvUTF8.cMB2WC(arg3), *wxConvCurrent); - wxString locArg4(wxConvUTF8.cMB2WC(arg4), *wxConvCurrent); + wxString locArg5 = wxEmptyString; #endif wxSQLite3Authorizer::wxAuthorizationCode localType = (wxSQLite3Authorizer::wxAuthorizationCode) type; - return (int) ((wxSQLite3Authorizer*) func)->Authorize(localType, locArg1, locArg2, locArg3, locArg3); + return (int) ((wxSQLite3Authorizer*) func)->Authorize(localType, locArg1, locArg2, locArg3, locArg4, locArg5); } /* static */ @@ -3032,13 +4435,8 @@ void wxSQLite3FunctionContext::ExecUpdateHook(void* hook, int type, const char* database, const char* table, wxsqlite_int64 rowid) { -#if wxUSE_UNICODE - wxString locDatabase(database, wxConvUTF8); - wxString locTable(table, wxConvUTF8); -#else - wxString locDatabase(wxConvUTF8.cMB2WC(database), *wxConvCurrent); - wxString locTable(wxConvUTF8.cMB2WC(table), *wxConvCurrent); -#endif + wxString locDatabase = wxString::FromUTF8(database); + wxString locTable = wxString::FromUTF8(table); wxSQLite3Hook::wxUpdateType locType = (wxSQLite3Hook::wxUpdateType) type; wxLongLong locRowid = rowid; ((wxSQLite3Hook*) hook)->UpdateCallback(locType, locDatabase, locTable, locRowid); @@ -3049,25 +4447,35 @@ wxSQLite3FunctionContext::wxSQLite3FunctionContext(void* ctx, bool isAggregate, { } +/* static */ +int wxSQLite3FunctionContext::ExecWriteAheadLogHook(void* hook, void* dbHandle, + const char* database, int numPages) +{ + wxString locDatabase = wxString::FromUTF8(database); + wxUnusedVar(dbHandle); + return (int) ((wxSQLite3Hook*) hook)->WriteAheadLogCallback(locDatabase, numPages); +} + static const wxChar* authCodeString[] = -{ _T("SQLITE_COPY"), _T("SQLITE_CREATE_INDEX"), _T("SQLITE_CREATE_TABLE"), - _T("SQLITE_CREATE_TEMP_INDEX"), _T("SQLITE_CREATE_TEMP_TABLE"), _T("SQLITE_CREATE_TEMP_TRIGGER"), - _T("SQLITE_CREATE_TEMP_VIEW"), _T("SQLITE_CREATE_TRIGGER"), _T("SQLITE_CREATE_VIEW"), - _T("SQLITE_DELETE"), _T("SQLITE_DROP_INDEX"), _T("SQLITE_DROP_TABLE"), - _T("SQLITE_DROP_TEMP_INDEX"), _T("SQLITE_DROP_TEMP_TABLE"), _T("SQLITE_DROP_TEMP_TRIGGER"), - _T("SQLITE_DROP_TEMP_VIEW"), _T("SQLITE_DROP_TRIGGER"), _T("SQLITE_DROP_VIEW"), - _T("SQLITE_INSERT"), _T("SQLITE_PRAGMA"), _T("SQLITE_READ"), - _T("SQLITE_SELECT"), _T("SQLITE_TRANSACTION"), _T("SQLITE_UPDATE"), - _T("SQLITE_ATTACH"), _T("SQLITE_DETACH"), _T("SQLITE_ALTER_TABLE"), - _T("SQLITE_REINDEX"), _T("SQLITE_ANALYZE"), _T("SQLITE_CREATE_VTABLE"), - _T("SQLITE_DROP_VTABLE"), _T("SQLITE_FUNCTION"), _T("SQLITE_SAVEPOINT") +{ wxT("SQLITE_COPY"), wxT("SQLITE_CREATE_INDEX"), wxT("SQLITE_CREATE_TABLE"), + wxT("SQLITE_CREATE_TEMP_INDEX"), wxT("SQLITE_CREATE_TEMP_TABLE"), wxT("SQLITE_CREATE_TEMP_TRIGGER"), + wxT("SQLITE_CREATE_TEMP_VIEW"), wxT("SQLITE_CREATE_TRIGGER"), wxT("SQLITE_CREATE_VIEW"), + wxT("SQLITE_DELETE"), wxT("SQLITE_DROP_INDEX"), wxT("SQLITE_DROP_TABLE"), + wxT("SQLITE_DROP_TEMP_INDEX"), wxT("SQLITE_DROP_TEMP_TABLE"), wxT("SQLITE_DROP_TEMP_TRIGGER"), + wxT("SQLITE_DROP_TEMP_VIEW"), wxT("SQLITE_DROP_TRIGGER"), wxT("SQLITE_DROP_VIEW"), + wxT("SQLITE_INSERT"), wxT("SQLITE_PRAGMA"), wxT("SQLITE_READ"), + wxT("SQLITE_SELECT"), wxT("SQLITE_TRANSACTION"), wxT("SQLITE_UPDATE"), + wxT("SQLITE_ATTACH"), wxT("SQLITE_DETACH"), wxT("SQLITE_ALTER_TABLE"), + wxT("SQLITE_REINDEX"), wxT("SQLITE_ANALYZE"), wxT("SQLITE_CREATE_VTABLE"), + wxT("SQLITE_DROP_VTABLE"), wxT("SQLITE_FUNCTION"), wxT("SQLITE_SAVEPOINT"), + wxT("SQLITE_RECURSIVE") }; /* static */ wxString wxSQLite3Authorizer::AuthorizationCodeToString(wxSQLite3Authorizer::wxAuthorizationCode type) { - const wxChar* authString = _T("Unknown"); + const wxChar* authString = wxT("Unknown"); if (type >= SQLITE_COPY && type <= SQLITE_MAX_CODE) { authString = authCodeString[type]; @@ -3097,7 +4505,14 @@ wxSQLite3Transaction::~wxSQLite3Transaction() { if (m_database != NULL) { - m_database->Rollback(); + try + { + m_database->Rollback(); + } + catch (...) + { + // Intentionally do nothing + } } } @@ -3126,3 +4541,654 @@ void wxSQLite3Transaction::Rollback() } m_database = NULL; } + +// --- User defined function classes + +#if wxUSE_REGEX + +wxSQLite3RegExpOperator::wxSQLite3RegExpOperator(int flags) : m_flags(flags) +{ +} + +wxSQLite3RegExpOperator::~wxSQLite3RegExpOperator() +{ +} + +void wxSQLite3RegExpOperator::Execute(wxSQLite3FunctionContext& ctx) +{ + int argCount = ctx.GetArgCount(); + if (argCount == 2) + { + wxString exprStr = ctx.GetString(0); + wxString textStr = ctx.GetString(1); + if (!m_exprStr.IsSameAs(exprStr)) + { + m_exprStr = exprStr; + m_regEx.Compile(m_exprStr, m_flags); + } + if (m_regEx.IsValid()) + { + int rc = (m_regEx.Matches(textStr)) ? 1 : 0; + ctx.SetResult(rc); + } + else + { + ctx.SetResultError(wxString(_("Regular expression invalid: '"))+exprStr+_T("'.")); + } + } + else + { + ctx.SetResultError(wxString::Format(_("REGEXP called with wrong number of arguments: %d instead of 2."), argCount)); + } +} + +#endif + +// --- Support for named collections + +#if WXSQLITE3_USE_NAMED_COLLECTIONS + +// The following code is based on the SQLite test_intarray source code. + +#include +#include + +/// Definition of the sqlite3_intarray object (internal) +struct sqlite3_intarray +{ + int n; // Number of elements in the array + sqlite3_int64* a; // Contents of the array + void (*xFree)(void*); // Function used to free a[] +}; + +// Objects used internally by the virtual table implementation +typedef struct intarray_vtab intarray_vtab; +typedef struct intarray_cursor intarray_cursor; + +/// Definition of intarray table object (internal) +struct intarray_vtab +{ + sqlite3_vtab base; // Base class + sqlite3_intarray* pContent; // Content of the integer array +}; + +/// Definition of intarray cursor object (internal) +struct intarray_cursor +{ + sqlite3_vtab_cursor base; // Base class + int i; // Current cursor position +}; + +// Free an sqlite3_intarray object. +static void intarrayFree(sqlite3_intarray* p) +{ + if (p->a != NULL && p->xFree) + { + p->xFree(p->a); + } + sqlite3_free(p); +} + +// Table destructor for the intarray module. +static int intarrayDestroy(sqlite3_vtab* p) +{ + intarray_vtab* pVtab = (intarray_vtab*)p; + sqlite3_free(pVtab); + return 0; +} + +// Table constructor for the intarray module. +static int intarrayCreate(sqlite3* db, // Database where module is created + void* pAux, // clientdata for the module + int /*argc*/, // Number of arguments + const char* const* /*argv*/, // Value for all arguments + sqlite3_vtab** ppVtab, // Write the new virtual table object here + char** /*pzErr*/) // Put error message text here +{ + int rc = SQLITE_NOMEM; + intarray_vtab* pVtab = (intarray_vtab*) sqlite3_malloc(sizeof(intarray_vtab)); + + if (pVtab) + { + memset(pVtab, 0, sizeof(intarray_vtab)); + pVtab->pContent = (sqlite3_intarray*)pAux; + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"); + } + *ppVtab = (sqlite3_vtab*)pVtab; + return rc; +} + +// Open a new cursor on the intarray table. +static int intarrayOpen(sqlite3_vtab* /*pVTab*/, sqlite3_vtab_cursor** ppCursor) +{ + int rc = SQLITE_NOMEM; + intarray_cursor* pCur = (intarray_cursor*) sqlite3_malloc(sizeof(intarray_cursor)); + if (pCur) + { + memset(pCur, 0, sizeof(intarray_cursor)); + *ppCursor = (sqlite3_vtab_cursor *)pCur; + rc = SQLITE_OK; + } + return rc; +} + +// Close a intarray table cursor. +static int intarrayClose(sqlite3_vtab_cursor* cur) +{ + intarray_cursor* pCur = (intarray_cursor*)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + +// Retrieve a column of data. +static int intarrayColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int /*i*/) +{ + intarray_cursor* pCur = (intarray_cursor*)cur; + intarray_vtab* pVtab = (intarray_vtab*)cur->pVtab; + if (pCur->i >= 0 && pCur->i < pVtab->pContent->n) + { + sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]); + } + return SQLITE_OK; +} + +// Retrieve the current rowid. +static int intarrayRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) +{ + intarray_cursor* pCur = (intarray_cursor*)cur; + *pRowid = pCur->i; + return SQLITE_OK; +} + +static int intarrayEof(sqlite3_vtab_cursor* cur) +{ + intarray_cursor* pCur = (intarray_cursor*)cur; + intarray_vtab* pVtab = (intarray_vtab*)cur->pVtab; + return pCur->i >= pVtab->pContent->n; +} + +// Advance the cursor to the next row. +static int intarrayNext(sqlite3_vtab_cursor* cur) +{ + intarray_cursor* pCur = (intarray_cursor*)cur; + pCur->i++; + return SQLITE_OK; +} + +// Reset a intarray table cursor. +static int intarrayFilter(sqlite3_vtab_cursor* pVtabCursor, + int /*idxNum*/, const char* /*idxStr*/, + int /*argc*/, sqlite3_value** /*argv*/) +{ + intarray_cursor* pCur = (intarray_cursor*) pVtabCursor; + pCur->i = 0; + return SQLITE_OK; +} + +// Analyse the WHERE condition. +static int intarrayBestIndex(sqlite3_vtab* /*tab*/, sqlite3_index_info* /*pIdxInfo*/) +{ + return SQLITE_OK; +} + +// Definition of a virtual table module for integer collections +static sqlite3_module intarrayModule = +{ + 0, // iVersion + intarrayCreate, // xCreate - create a new virtual table + intarrayCreate, // xConnect - connect to an existing vtab + intarrayBestIndex, // xBestIndex - find the best query index + intarrayDestroy, // xDisconnect - disconnect a vtab + intarrayDestroy, // xDestroy - destroy a vtab + intarrayOpen, // xOpen - open a cursor + intarrayClose, // xClose - close a cursor + intarrayFilter, // xFilter - configure scan constraints + intarrayNext, // xNext - advance a cursor + intarrayEof, // xEof + intarrayColumn, // xColumn - read data + intarrayRowid, // xRowid - read data + 0, // xUpdate + 0, // xBegin + 0, // xSync + 0, // xCommit + 0, // xRollback + 0, // xFindMethod + 0, // xRename +#if SQLITE_VERSION_NUMBER >= 3007007 + 0, // xSavepoint + 0, // xRelease + 0 // xRollbackTo +#endif +}; + +/// Definition of the sqlite3_chararray object (internal) +struct sqlite3_chararray +{ + int n; // Number of elements in the array + char** a; // Contents of the array + void (*xFree)(void*); // Function used to free a[] +}; + +// Objects used internally by the virtual table implementation +typedef struct chararray_vtab chararray_vtab; +typedef struct chararray_cursor chararray_cursor; + +/// Definition of chararray table object (internal) +struct chararray_vtab +{ + sqlite3_vtab base; // Base class + sqlite3_chararray* pContent; // Content of the char array +}; + +/// Definition of chararray cursor object (internal) +struct chararray_cursor +{ + sqlite3_vtab_cursor base; // Base class + int i; // Current cursor position +}; + +// Free an sqlite3_chararray object. +static void chararrayFree(sqlite3_chararray* p) +{ + if (p->a != NULL && p->xFree) + { + int j; + for (j = 0; j < p->n; ++j) + { + p->xFree(p->a[j]); + } + p->xFree(p->a); + } + sqlite3_free(p); +} + +// Table destructor for the chararray module. +static int chararrayDestroy(sqlite3_vtab* p) +{ + chararray_vtab* pVtab = (chararray_vtab*)p; + sqlite3_free(pVtab); + return 0; +} + +// Table constructor for the chararray module. +static int chararrayCreate(sqlite3* db, // Database where module is created + void* pAux, // clientdata for the module + int /*argc*/, // Number of arguments + const char* const* /*argv*/, // Value for all arguments + sqlite3_vtab** ppVtab, // Write the new virtual table object here + char** /*pzErr*/) // Put error message text here +{ + int rc = SQLITE_NOMEM; + chararray_vtab* pVtab = (chararray_vtab*) sqlite3_malloc(sizeof(chararray_vtab)); + + if (pVtab) + { + memset(pVtab, 0, sizeof(chararray_vtab)); + pVtab->pContent = (sqlite3_chararray*) pAux; + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value CHAR PRIMARY KEY)"); + } + *ppVtab = (sqlite3_vtab*) pVtab; + return rc; +} + +// Open a new cursor on the chararray table. +static int chararrayOpen(sqlite3_vtab* /*pVTab*/, sqlite3_vtab_cursor** ppCursor) +{ + int rc = SQLITE_NOMEM; + chararray_cursor* pCur = (chararray_cursor*) sqlite3_malloc(sizeof(chararray_cursor)); + if (pCur) + { + memset(pCur, 0, sizeof(chararray_cursor)); + *ppCursor = (sqlite3_vtab_cursor *)pCur; + rc = SQLITE_OK; + } + return rc; +} + +// Close a chararray table cursor. +static int chararrayClose(sqlite3_vtab_cursor* cur) +{ + chararray_cursor* pCur = (chararray_cursor*)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + +// Retrieve a column of data. +static int chararrayColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int /*i*/) +{ + chararray_cursor* pCur = (chararray_cursor*)cur; + chararray_vtab* pVtab = (chararray_vtab*)cur->pVtab; + if (pCur->i >= 0 && pCur->i < pVtab->pContent->n) + { + sqlite3_result_text(ctx, pVtab->pContent->a[pCur->i], -1, SQLITE_STATIC); + } + return SQLITE_OK; +} + +// Retrieve the current rowid. +static int chararrayRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) +{ + chararray_cursor* pCur = (chararray_cursor*)cur; + *pRowid = pCur->i; + return SQLITE_OK; +} + +static int chararrayEof(sqlite3_vtab_cursor* cur) +{ + chararray_cursor* pCur = (chararray_cursor*)cur; + chararray_vtab* pVtab = (chararray_vtab*)cur->pVtab; + return pCur->i >= pVtab->pContent->n; +} + +// Advance the cursor to the next row. +static int chararrayNext(sqlite3_vtab_cursor* cur) +{ + chararray_cursor* pCur = (chararray_cursor*)cur; + pCur->i++; + return SQLITE_OK; +} + +// Reset a chararray table cursor. +static int chararrayFilter(sqlite3_vtab_cursor* pVtabCursor, + int /*idxNum*/, const char* /*idxStr*/, + int /*argc*/, sqlite3_value** /*argv*/) +{ + chararray_cursor *pCur = (chararray_cursor *)pVtabCursor; + pCur->i = 0; + return SQLITE_OK; +} + +// Analyse the WHERE condition. +static int chararrayBestIndex(sqlite3_vtab* /*tab*/, sqlite3_index_info* /*pIdxInfo*/) +{ + return SQLITE_OK; +} + +// Definition of a virtual table module for string collections +static sqlite3_module chararrayModule = +{ + 0, // iVersion + chararrayCreate, // xCreate - create a new virtual table + chararrayCreate, // xConnect - connect to an existing vtab + chararrayBestIndex, // xBestIndex - find the best query index + chararrayDestroy, // xDisconnect - disconnect a vtab + chararrayDestroy, // xDestroy - destroy a vtab + chararrayOpen, // xOpen - open a cursor + chararrayClose, // xClose - close a cursor + chararrayFilter, // xFilter - configure scan constraints + chararrayNext, // xNext - advance a cursor + chararrayEof, // xEof + chararrayColumn, // xColumn - read data + chararrayRowid, // xRowid - read data + 0, // xUpdate + 0, // xBegin + 0, // xSync + 0, // xCommit + 0, // xRollback + 0, // xFindMethod + 0, // xRename +#if SQLITE_VERSION_NUMBER >= 3007007 + 0, // xSavepoint + 0, // xRelease + 0 // xRollbackTo +#endif +}; + +#endif // WXSQLITE3_USE_NAMED_COLLECTIONS + +wxSQLite3NamedCollection::wxSQLite3NamedCollection() +{ + m_name = wxEmptyString; + m_data = NULL; +} + +wxSQLite3NamedCollection::wxSQLite3NamedCollection(const wxString& collectionName, void* collectionData) +{ + m_name = collectionName; + m_data = collectionData; +} + +wxSQLite3NamedCollection::wxSQLite3NamedCollection(const wxSQLite3NamedCollection& collection) + : m_name(collection.m_name), m_data(collection.m_data) +{ +} + +wxSQLite3NamedCollection& +wxSQLite3NamedCollection::operator=(const wxSQLite3NamedCollection& collection) +{ + if (this != &collection) + { + m_name = collection.m_name; + m_data = collection.m_data; + } + return *this; +} + +wxSQLite3NamedCollection::~wxSQLite3NamedCollection() +{ +} + +wxSQLite3IntegerCollection::wxSQLite3IntegerCollection() + : wxSQLite3NamedCollection(wxEmptyString, NULL) +{ +} + +wxSQLite3IntegerCollection::wxSQLite3IntegerCollection(const wxSQLite3IntegerCollection& collection) + : wxSQLite3NamedCollection(collection) +{ +} + +wxSQLite3IntegerCollection& +wxSQLite3IntegerCollection::operator=(const wxSQLite3IntegerCollection& collection) +{ + if (this != &collection) + { + wxSQLite3NamedCollection::operator=(collection); + } + return *this; +} + +wxSQLite3IntegerCollection::wxSQLite3IntegerCollection(const wxString& collectionName, void* collectionData) + : wxSQLite3NamedCollection(collectionName, collectionData) +{ +} + +wxSQLite3IntegerCollection::~wxSQLite3IntegerCollection() +{ +} + +void +wxSQLite3IntegerCollection::Bind(const wxArrayInt& integerCollection) +{ + size_t n = integerCollection.Count(); + sqlite3_intarray* pIntArray = (sqlite3_intarray*) m_data; + if (m_data != NULL) + { + if (pIntArray->a != NULL && pIntArray->xFree) + { + pIntArray->xFree(pIntArray->a); + } + } + pIntArray->n = n; + if (n > 0) + { + pIntArray->a = (sqlite3_int64*) sqlite3_malloc(sizeof(sqlite3_int64)*n); + pIntArray->xFree = sqlite3_free; + } + else + { + pIntArray->a = NULL; + pIntArray->xFree = NULL; + } + + size_t j; + for (j = 0; j < n; ++j) + { + pIntArray->a[j] = integerCollection[j]; + } +} + +void +wxSQLite3IntegerCollection::Bind(int n, int* integerCollection) +{ + sqlite3_intarray* pIntArray = (sqlite3_intarray*) m_data; + if (m_data != NULL) + { + if (pIntArray->a != NULL && pIntArray->xFree) + { + pIntArray->xFree(pIntArray->a); + } + } + pIntArray->n = n; + if (n > 0) + { + pIntArray->a = (sqlite3_int64*) sqlite3_malloc(sizeof(sqlite3_int64)*n); + pIntArray->xFree = sqlite3_free; + } + else + { + pIntArray->a = NULL; + pIntArray->xFree = NULL; + } + + int j; + for (j = 0; j < n; ++j) + { + pIntArray->a[j] = integerCollection[j]; + } +} + +wxSQLite3IntegerCollection +wxSQLite3Database::CreateIntegerCollection(const wxString& collectionName) +{ +#if WXSQLITE3_USE_NAMED_COLLECTIONS + CheckDatabase(); + int rc = SQLITE_OK; + wxCharBuffer strCollectionName = collectionName.ToUTF8(); + const char* zName = strCollectionName; + sqlite3_intarray* p = (sqlite3_intarray*) sqlite3_malloc( sizeof(*p) ); + if (p == 0) + { + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOMEM); + } + p->n = 0; + p->a= NULL; + p->xFree = NULL; + rc = sqlite3_create_module_v2(m_db->m_db, zName, &intarrayModule, p, (void(*)(void*))intarrayFree); + if (rc == SQLITE_OK) + { + wxSQLite3StatementBuffer zBuffer; + const char* zSql = zBuffer.Format("CREATE VIRTUAL TABLE temp.%Q USING %Q", zName, zName); + rc = sqlite3_exec(m_db->m_db, zSql, 0, 0, 0); + } + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + return wxSQLite3IntegerCollection(collectionName, p); +#else + wxUnusedVar(collectionName); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOCOLLECTIONS); +#endif // WXSQLITE3_USE_NAMED_COLLECTIONS +} + +wxSQLite3StringCollection::wxSQLite3StringCollection() + : wxSQLite3NamedCollection(wxEmptyString, NULL) +{ +} + +wxSQLite3StringCollection::wxSQLite3StringCollection(const wxSQLite3StringCollection& collection) + : wxSQLite3NamedCollection(collection) +{ +} + +wxSQLite3StringCollection& +wxSQLite3StringCollection::operator=(const wxSQLite3StringCollection& collection) +{ + if (this != &collection) + { + wxSQLite3NamedCollection::operator=(collection); + } + return *this; +} + +wxSQLite3StringCollection::wxSQLite3StringCollection(const wxString& collectionName, void* collectionData) + : wxSQLite3NamedCollection(collectionName, collectionData) +{ +} + +wxSQLite3StringCollection::~wxSQLite3StringCollection() +{ +} + +void +wxSQLite3StringCollection::Bind(const wxArrayString& stringCollection) +{ + size_t n = stringCollection.Count(); + sqlite3_chararray* pCharArray = (sqlite3_chararray*) m_data; + if (m_data != NULL) + { + if (pCharArray->a != NULL && pCharArray->xFree) + { + pCharArray->xFree(pCharArray->a); + } + } + pCharArray->n = n; + if (n > 0) + { + pCharArray->a = (char**) sqlite3_malloc(sizeof(char*)*n); + pCharArray->xFree = sqlite3_free; + } + else + { + pCharArray->a = NULL; + pCharArray->xFree = NULL; + } + + size_t j; + for (j = 0; j < n; ++j) + { + wxCharBuffer strValue = stringCollection[j].ToUTF8(); + const char* zValue = strValue; + size_t k = strlen(zValue) + 1; + pCharArray->a[j] = (char*) sqlite3_malloc(sizeof(char)*k); + strcpy(pCharArray->a[j], zValue); + } +} + +wxSQLite3StringCollection +wxSQLite3Database::CreateStringCollection(const wxString& collectionName) +{ +#if WXSQLITE3_USE_NAMED_COLLECTIONS + CheckDatabase(); + int rc = SQLITE_OK; + wxCharBuffer strCollectionName = collectionName.ToUTF8(); + const char* zName = strCollectionName; + sqlite3_chararray* p = (sqlite3_chararray*) sqlite3_malloc( sizeof(*p) ); + if (p == 0) + { + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOMEM); + } + p->n = 0; + p->a= NULL; + p->xFree = NULL; + rc = sqlite3_create_module_v2(m_db->m_db, zName, &chararrayModule, p, (void(*)(void*))chararrayFree); + if (rc == SQLITE_OK) + { + wxSQLite3StatementBuffer zBuffer; + const char* zSql = zBuffer.Format("CREATE VIRTUAL TABLE temp.%Q USING %Q", zName, zName); + rc = sqlite3_exec(m_db->m_db, zSql, 0, 0, 0); + } + if (rc != SQLITE_OK) + { + const char* localError = sqlite3_errmsg(m_db->m_db); + throw wxSQLite3Exception(rc, wxString::FromUTF8(localError)); + } + return wxSQLite3StringCollection(collectionName, p); +#else + wxUnusedVar(collectionName); + throw wxSQLite3Exception(WXSQLITE_ERROR, wxERRMSG_NOCOLLECTIONS); +#endif // WXSQLITE3_USE_NAMED_COLLECTIONS +} +