X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FCppSQLite3.cpp;h=4ad953f9287c513c1cb1394caf61e17c433505ed;hb=bae8afe7ee037a3368be7060c3a0708582e7d413;hp=165f9110bebc1ed0baab8e592fc5e6d09ebebb9a;hpb=121c751257f813b87a554f19807bf757a8eae8da;p=creaImageIO.git diff --git a/src/CppSQLite3.cpp b/src/CppSQLite3.cpp index 165f911..4ad953f 100644 --- a/src/CppSQLite3.cpp +++ b/src/CppSQLite3.cpp @@ -1,1500 +1,3000 @@ -//////////////////////////////////////////////////////////////////////////////// -// CppSQLite3 - A C++ wrapper around the SQLite3 embedded database library. -// -// Copyright (c) 2004 Rob Groves. All Rights Reserved. rob.groves@btinternet.com -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement, is hereby granted, provided that the above copyright notice, -// this paragraph and the following two paragraphs appear in all copies, -// modifications, and distributions. -// -// IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, -// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST -// PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF -// ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -// -// V3.0 03/08/2004 -Initial Version for sqlite3 -// -// V3.1 16/09/2004 -Implemented getXXXXField using sqlite3 functions -// -Added CppSQLiteDB3::tableExists() -//////////////////////////////////////////////////////////////////////////////// -#include "CppSQLite3.h" -#include - - -// Named constant for passing to CppSQLite3Exception when passing it a string -// that cannot be deleted. -static const bool DONT_DELETE_MSG=false; - -//////////////////////////////////////////////////////////////////////////////// -// Prototypes for SQLite functions not included in SQLite DLL, but copied below -// from SQLite encode.c -//////////////////////////////////////////////////////////////////////////////// -int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out); -int sqlite3_decode_binary(const unsigned char *in, unsigned char *out); - -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, - const char* szErrMess, - bool bDeleteMsg/*=true*/) : - mnErrCode(nErrCode) -{ - mpszErrMess = sqlite3_mprintf("%s[%d]: %s", - errorCodeAsString(nErrCode), - nErrCode, - szErrMess ? szErrMess : ""); - /* - if (bDeleteMsg && szErrMess) - { - sqlite3_free(szErrMess); - } - */ -} - -CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, - char* szErrMess, - bool bDeleteMsg/*=true*/) : - mnErrCode(nErrCode) -{ - mpszErrMess = sqlite3_mprintf("%s[%d]: %s", - errorCodeAsString(nErrCode), - nErrCode, - szErrMess ? szErrMess : ""); - - if (bDeleteMsg && szErrMess) - { - sqlite3_free(szErrMess); - } -} - -CppSQLite3Exception::CppSQLite3Exception(const CppSQLite3Exception& e) : - mnErrCode(e.mnErrCode) -{ - mpszErrMess = 0; - if (e.mpszErrMess) - { - mpszErrMess = sqlite3_mprintf("%s", e.mpszErrMess); - } -} - - -const char* CppSQLite3Exception::errorCodeAsString(int nErrCode) -{ - switch (nErrCode) - { - case SQLITE_OK : return "SQLITE_OK"; - case SQLITE_ERROR : return "SQLITE_ERROR"; - case SQLITE_INTERNAL : return "SQLITE_INTERNAL"; - case SQLITE_PERM : return "SQLITE_PERM"; - case SQLITE_ABORT : return "SQLITE_ABORT"; - case SQLITE_BUSY : return "SQLITE_BUSY"; - case SQLITE_LOCKED : return "SQLITE_LOCKED"; - case SQLITE_NOMEM : return "SQLITE_NOMEM"; - case SQLITE_READONLY : return "SQLITE_READONLY"; - case SQLITE_INTERRUPT : return "SQLITE_INTERRUPT"; - case SQLITE_IOERR : return "SQLITE_IOERR"; - case SQLITE_CORRUPT : return "SQLITE_CORRUPT"; - case SQLITE_NOTFOUND : return "SQLITE_NOTFOUND"; - case SQLITE_FULL : return "SQLITE_FULL"; - case SQLITE_CANTOPEN : return "SQLITE_CANTOPEN"; - case SQLITE_PROTOCOL : return "SQLITE_PROTOCOL"; - case SQLITE_EMPTY : return "SQLITE_EMPTY"; - case SQLITE_SCHEMA : return "SQLITE_SCHEMA"; - case SQLITE_TOOBIG : return "SQLITE_TOOBIG"; - case SQLITE_CONSTRAINT : return "SQLITE_CONSTRAINT"; - case SQLITE_MISMATCH : return "SQLITE_MISMATCH"; - case SQLITE_MISUSE : return "SQLITE_MISUSE"; - case SQLITE_NOLFS : return "SQLITE_NOLFS"; - case SQLITE_AUTH : return "SQLITE_AUTH"; - case SQLITE_FORMAT : return "SQLITE_FORMAT"; - case SQLITE_RANGE : return "SQLITE_RANGE"; - case SQLITE_ROW : return "SQLITE_ROW"; - case SQLITE_DONE : return "SQLITE_DONE"; - case CPPSQLITE_ERROR : return "CPPSQLITE_ERROR"; - default: return "UNKNOWN_ERROR"; - } -} - - -CppSQLite3Exception::~CppSQLite3Exception() -{ - if (mpszErrMess) - { - sqlite3_free(mpszErrMess); - mpszErrMess = 0; - } -} - - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3Buffer::CppSQLite3Buffer() -{ - mpBuf = 0; -} - - -CppSQLite3Buffer::~CppSQLite3Buffer() -{ - clear(); -} - - -void CppSQLite3Buffer::clear() -{ - if (mpBuf) - { - sqlite3_free(mpBuf); - mpBuf = 0; - } - -} - - -const char* CppSQLite3Buffer::format(const char* szFormat, ...) -{ - clear(); - va_list va; - va_start(va, szFormat); - mpBuf = sqlite3_vmprintf(szFormat, va); - va_end(va); - return mpBuf; -} - - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3Binary::CppSQLite3Binary() : - mpBuf(0), - mnBinaryLen(0), - mnBufferLen(0), - mnEncodedLen(0), - mbEncoded(false) -{ -} - - -CppSQLite3Binary::~CppSQLite3Binary() -{ - clear(); -} - - -void CppSQLite3Binary::setBinary(const unsigned char* pBuf, int nLen) -{ - mpBuf = allocBuffer(nLen); - memcpy(mpBuf, pBuf, nLen); -} - - -void CppSQLite3Binary::setEncoded(const unsigned char* pBuf) -{ - clear(); - - mnEncodedLen = strlen((const char*)pBuf); - mnBufferLen = mnEncodedLen + 1; // Allow for NULL terminator - - mpBuf = (unsigned char*)malloc(mnBufferLen); - - if (!mpBuf) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Cannot allocate memory", - DONT_DELETE_MSG); - } - - memcpy(mpBuf, pBuf, mnBufferLen); - mbEncoded = true; -} - - -const unsigned char* CppSQLite3Binary::getEncoded() -{ - if (!mbEncoded) - { - unsigned char* ptmp = (unsigned char*)malloc(mnBinaryLen); - memcpy(ptmp, mpBuf, mnBinaryLen); - mnEncodedLen = sqlite3_encode_binary(ptmp, mnBinaryLen, mpBuf); - free(ptmp); - mbEncoded = true; - } - - return mpBuf; -} - - -const unsigned char* CppSQLite3Binary::getBinary() -{ - if (mbEncoded) - { - // in/out buffers can be the same - mnBinaryLen = sqlite3_decode_binary(mpBuf, mpBuf); - - if (mnBinaryLen == -1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Cannot decode binary", - DONT_DELETE_MSG); - } - - mbEncoded = false; - } - - return mpBuf; -} - - -int CppSQLite3Binary::getBinaryLength() -{ - getBinary(); - return mnBinaryLen; -} - - -unsigned char* CppSQLite3Binary::allocBuffer(int nLen) -{ - clear(); - - // Allow extra space for encoded binary as per comments in - // SQLite encode.c See bottom of this file for implementation - // of SQLite functions use 3 instead of 2 just to be sure ;-) - mnBinaryLen = nLen; - mnBufferLen = 3 + (257*nLen)/254; - - mpBuf = (unsigned char*)malloc(mnBufferLen); - - if (!mpBuf) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Cannot allocate memory", - DONT_DELETE_MSG); - } - - mbEncoded = false; - - return mpBuf; -} - - -void CppSQLite3Binary::clear() -{ - if (mpBuf) - { - mnBinaryLen = 0; - mnBufferLen = 0; - free(mpBuf); - mpBuf = 0; - } -} - - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3Query::CppSQLite3Query() -{ - mpVM = 0; - mbEof = true; - mnCols = 0; - mbOwnVM = false; -} - - -CppSQLite3Query::CppSQLite3Query(const CppSQLite3Query& rQuery) -{ - mpVM = rQuery.mpVM; - // Only one object can own the VM - const_cast(rQuery).mpVM = 0; - mbEof = rQuery.mbEof; - mnCols = rQuery.mnCols; - mbOwnVM = rQuery.mbOwnVM; -} - - -CppSQLite3Query::CppSQLite3Query(sqlite3* pDB, - sqlite3_stmt* pVM, - bool bEof, - bool bOwnVM/*=true*/) -{ - mpDB = pDB; - mpVM = pVM; - mbEof = bEof; - mnCols = sqlite3_column_count(mpVM); - mbOwnVM = bOwnVM; -} - - -CppSQLite3Query::~CppSQLite3Query() -{ - try - { - finalize(); - } - catch (...) - { - } -} - - -CppSQLite3Query& CppSQLite3Query::operator=(const CppSQLite3Query& rQuery) -{ - try - { - finalize(); - } - catch (...) - { - } - mpVM = rQuery.mpVM; - // Only one object can own the VM - const_cast(rQuery).mpVM = 0; - mbEof = rQuery.mbEof; - mnCols = rQuery.mnCols; - mbOwnVM = rQuery.mbOwnVM; - return *this; -} - - -int CppSQLite3Query::numFields() -{ - checkVM(); - return mnCols; -} - - -const char* CppSQLite3Query::fieldValue(int nField) -{ - checkVM(); - - if (nField < 0 || nField > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - return (const char*)sqlite3_column_text(mpVM, nField); -} - - -const char* CppSQLite3Query::fieldValue(const char* szField) -{ - int nField = fieldIndex(szField); - return (const char*)sqlite3_column_text(mpVM, nField); -} - - -int CppSQLite3Query::getIntField(int nField, int nNullValue/*=0*/) -{ - if (fieldDataType(nField) == SQLITE_NULL) - { - return nNullValue; - } - else - { - return sqlite3_column_int(mpVM, nField); - } -} - - -int CppSQLite3Query::getIntField(const char* szField, int nNullValue/*=0*/) -{ - int nField = fieldIndex(szField); - return getIntField(nField, nNullValue); -} - - -double CppSQLite3Query::getFloatField(int nField, double fNullValue/*=0.0*/) -{ - if (fieldDataType(nField) == SQLITE_NULL) - { - return fNullValue; - } - else - { - return sqlite3_column_double(mpVM, nField); - } -} - - -double CppSQLite3Query::getFloatField(const char* szField, double fNullValue/*=0.0*/) -{ - int nField = fieldIndex(szField); - return getFloatField(nField, fNullValue); -} - - -const char* CppSQLite3Query::getStringField(int nField, const char* szNullValue/*=""*/) -{ - if (fieldDataType(nField) == SQLITE_NULL) - { - return szNullValue; - } - else - { - return (const char*)sqlite3_column_text(mpVM, nField); - } -} - - -const char* CppSQLite3Query::getStringField(const char* szField, const char* szNullValue/*=""*/) -{ - int nField = fieldIndex(szField); - return getStringField(nField, szNullValue); -} - - -const unsigned char* CppSQLite3Query::getBlobField(int nField, int& nLen) -{ - checkVM(); - - if (nField < 0 || nField > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - nLen = sqlite3_column_bytes(mpVM, nField); - return (const unsigned char*)sqlite3_column_blob(mpVM, nField); -} - - -const unsigned char* CppSQLite3Query::getBlobField(const char* szField, int& nLen) -{ - int nField = fieldIndex(szField); - return getBlobField(nField, nLen); -} - - -bool CppSQLite3Query::fieldIsNull(int nField) -{ - return (fieldDataType(nField) == SQLITE_NULL); -} - - -bool CppSQLite3Query::fieldIsNull(const char* szField) -{ - int nField = fieldIndex(szField); - return (fieldDataType(nField) == SQLITE_NULL); -} - - -int CppSQLite3Query::fieldIndex(const char* szField) -{ - checkVM(); - - if (szField) - { - for (int nField = 0; nField < mnCols; nField++) - { - const char* szTemp = sqlite3_column_name(mpVM, nField); - - if (strcmp(szField, szTemp) == 0) - { - return nField; - } - } - } - - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field name requested", - DONT_DELETE_MSG); -} - - -const char* CppSQLite3Query::fieldName(int nCol) -{ - checkVM(); - - if (nCol < 0 || nCol > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - return sqlite3_column_name(mpVM, nCol); -} - - -const char* CppSQLite3Query::fieldDeclType(int nCol) -{ - checkVM(); - - if (nCol < 0 || nCol > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - return sqlite3_column_decltype(mpVM, nCol); -} - - -int CppSQLite3Query::fieldDataType(int nCol) -{ - checkVM(); - - if (nCol < 0 || nCol > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - return sqlite3_column_type(mpVM, nCol); -} - - -bool CppSQLite3Query::eof() -{ - checkVM(); - return mbEof; -} - - -void CppSQLite3Query::nextRow() -{ - checkVM(); - - int nRet = sqlite3_step(mpVM); - - if (nRet == SQLITE_DONE) - { - // no rows - mbEof = true; - } - else if (nRet == SQLITE_ROW) - { - // more rows, nothing to do - } - else - { - nRet = sqlite3_finalize(mpVM); - mpVM = 0; - const char* szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, - (char*)szError, - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Query::finalize() -{ - if (mpVM && mbOwnVM) - { - int nRet = sqlite3_finalize(mpVM); - mpVM = 0; - if (nRet != SQLITE_OK) - { - const char* szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } - } -} - - -void CppSQLite3Query::checkVM() -{ - if (mpVM == 0) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Null Virtual Machine pointer", - DONT_DELETE_MSG); - } -} - - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3Table::CppSQLite3Table() -{ - mpaszResults = 0; - mnRows = 0; - mnCols = 0; - mnCurrentRow = 0; -} - - -CppSQLite3Table::CppSQLite3Table(const CppSQLite3Table& rTable) -{ - mpaszResults = rTable.mpaszResults; - // Only one object can own the results - const_cast(rTable).mpaszResults = 0; - mnRows = rTable.mnRows; - mnCols = rTable.mnCols; - mnCurrentRow = rTable.mnCurrentRow; -} - - -CppSQLite3Table::CppSQLite3Table(char** paszResults, int nRows, int nCols) -{ - mpaszResults = paszResults; - mnRows = nRows; - mnCols = nCols; - mnCurrentRow = 0; -} - - -CppSQLite3Table::~CppSQLite3Table() -{ - try - { - finalize(); - } - catch (...) - { - } -} - - -CppSQLite3Table& CppSQLite3Table::operator=(const CppSQLite3Table& rTable) -{ - try - { - finalize(); - } - catch (...) - { - } - mpaszResults = rTable.mpaszResults; - // Only one object can own the results - const_cast(rTable).mpaszResults = 0; - mnRows = rTable.mnRows; - mnCols = rTable.mnCols; - mnCurrentRow = rTable.mnCurrentRow; - return *this; -} - - -void CppSQLite3Table::finalize() -{ - if (mpaszResults) - { - sqlite3_free_table(mpaszResults); - mpaszResults = 0; - } -} - - -int CppSQLite3Table::numFields() -{ - checkResults(); - return mnCols; -} - - -int CppSQLite3Table::numRows() -{ - checkResults(); - return mnRows; -} - - -const char* CppSQLite3Table::fieldValue(int nField) -{ - checkResults(); - - if (nField < 0 || nField > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; - return mpaszResults[nIndex]; -} - - -const char* CppSQLite3Table::fieldValue(const char* szField) -{ - checkResults(); - - if (szField) - { - for (int nField = 0; nField < mnCols; nField++) - { - if (strcmp(szField, mpaszResults[nField]) == 0) - { - int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; - return mpaszResults[nIndex]; - } - } - } - - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field name requested", - DONT_DELETE_MSG); -} - - -int CppSQLite3Table::getIntField(int nField, int nNullValue/*=0*/) -{ - if (fieldIsNull(nField)) - { - return nNullValue; - } - else - { - return atoi(fieldValue(nField)); - } -} - - -int CppSQLite3Table::getIntField(const char* szField, int nNullValue/*=0*/) -{ - if (fieldIsNull(szField)) - { - return nNullValue; - } - else - { - return atoi(fieldValue(szField)); - } -} - - -double CppSQLite3Table::getFloatField(int nField, double fNullValue/*=0.0*/) -{ - if (fieldIsNull(nField)) - { - return fNullValue; - } - else - { - return atof(fieldValue(nField)); - } -} - - -double CppSQLite3Table::getFloatField(const char* szField, double fNullValue/*=0.0*/) -{ - if (fieldIsNull(szField)) - { - return fNullValue; - } - else - { - return atof(fieldValue(szField)); - } -} - - -const char* CppSQLite3Table::getStringField(int nField, const char* szNullValue/*=""*/) -{ - if (fieldIsNull(nField)) - { - return szNullValue; - } - else - { - return fieldValue(nField); - } -} - - -const char* CppSQLite3Table::getStringField(const char* szField, const char* szNullValue/*=""*/) -{ - if (fieldIsNull(szField)) - { - return szNullValue; - } - else - { - return fieldValue(szField); - } -} - - -bool CppSQLite3Table::fieldIsNull(int nField) -{ - checkResults(); - return (fieldValue(nField) == 0); -} - - -bool CppSQLite3Table::fieldIsNull(const char* szField) -{ - checkResults(); - return (fieldValue(szField) == 0); -} - - -const char* CppSQLite3Table::fieldName(int nCol) -{ - checkResults(); - - if (nCol < 0 || nCol > mnCols-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid field index requested", - DONT_DELETE_MSG); - } - - return mpaszResults[nCol]; -} - - -void CppSQLite3Table::setRow(int nRow) -{ - checkResults(); - - if (nRow < 0 || nRow > mnRows-1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid row index requested", - DONT_DELETE_MSG); - } - - mnCurrentRow = nRow; -} - - -void CppSQLite3Table::checkResults() -{ - if (mpaszResults == 0) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Null Results pointer", - DONT_DELETE_MSG); - } -} - - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3Statement::CppSQLite3Statement() -{ - mpDB = 0; - mpVM = 0; -} - - -CppSQLite3Statement::CppSQLite3Statement(const CppSQLite3Statement& rStatement) -{ - mpDB = rStatement.mpDB; - mpVM = rStatement.mpVM; - // Only one object can own VM - const_cast(rStatement).mpVM = 0; -} - - -CppSQLite3Statement::CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM) -{ - mpDB = pDB; - mpVM = pVM; -} - - -CppSQLite3Statement::~CppSQLite3Statement() -{ - try - { - finalize(); - } - catch (...) - { - } -} - - -CppSQLite3Statement& CppSQLite3Statement::operator=(const CppSQLite3Statement& rStatement) -{ - mpDB = rStatement.mpDB; - mpVM = rStatement.mpVM; - // Only one object can own VM - const_cast(rStatement).mpVM = 0; - return *this; -} - - -int CppSQLite3Statement::execDML() -{ - checkDB(); - checkVM(); - - const char* szError=0; - - int nRet = sqlite3_step(mpVM); - - if (nRet == SQLITE_DONE) - { - int nRowsChanged = sqlite3_changes(mpDB); - - nRet = sqlite3_reset(mpVM); - - if (nRet != SQLITE_OK) - { - szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } - - return nRowsChanged; - } - else - { - nRet = sqlite3_reset(mpVM); - szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } -} - - -CppSQLite3Query CppSQLite3Statement::execQuery() -{ - checkDB(); - checkVM(); - - int nRet = sqlite3_step(mpVM); - - if (nRet == SQLITE_DONE) - { - // no rows - return CppSQLite3Query(mpDB, mpVM, true/*eof*/, false); - } - else if (nRet == SQLITE_ROW) - { - // at least 1 row - return CppSQLite3Query(mpDB, mpVM, false/*eof*/, false); - } - else - { - nRet = sqlite3_reset(mpVM); - const char* szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::bind(int nParam, const char* szValue) -{ - checkVM(); - int nRes = sqlite3_bind_text(mpVM, nParam, szValue, -1, SQLITE_TRANSIENT); - - if (nRes != SQLITE_OK) - { - throw CppSQLite3Exception(nRes, - "Error binding string param", - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::bind(int nParam, const int nValue) -{ - checkVM(); - int nRes = sqlite3_bind_int(mpVM, nParam, nValue); - - if (nRes != SQLITE_OK) - { - throw CppSQLite3Exception(nRes, - "Error binding int param", - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::bind(int nParam, const double dValue) -{ - checkVM(); - int nRes = sqlite3_bind_double(mpVM, nParam, dValue); - - if (nRes != SQLITE_OK) - { - throw CppSQLite3Exception(nRes, - "Error binding double param", - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::bind(int nParam, const unsigned char* blobValue, int nLen) -{ - checkVM(); - int nRes = sqlite3_bind_blob(mpVM, nParam, - (const void*)blobValue, nLen, SQLITE_TRANSIENT); - - if (nRes != SQLITE_OK) - { - throw CppSQLite3Exception(nRes, - "Error binding blob param", - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::bindNull(int nParam) -{ - checkVM(); - int nRes = sqlite3_bind_null(mpVM, nParam); - - if (nRes != SQLITE_OK) - { - throw CppSQLite3Exception(nRes, - "Error binding NULL param", - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::reset() -{ - if (mpVM) - { - int nRet = sqlite3_reset(mpVM); - - if (nRet != SQLITE_OK) - { - const char* szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } - } -} - - -void CppSQLite3Statement::finalize() -{ - if (mpVM) - { - int nRet = sqlite3_finalize(mpVM); - mpVM = 0; - - if (nRet != SQLITE_OK) - { - const char* szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } - } -} - - -void CppSQLite3Statement::checkDB() -{ - if (mpDB == 0) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Database not open", - DONT_DELETE_MSG); - } -} - - -void CppSQLite3Statement::checkVM() -{ - if (mpVM == 0) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Null Virtual Machine pointer", - DONT_DELETE_MSG); - } -} - - -//////////////////////////////////////////////////////////////////////////////// - -CppSQLite3DB::CppSQLite3DB() -{ - mpDB = 0; - mnBusyTimeoutMs = 60000; // 60 seconds -} - - -CppSQLite3DB::CppSQLite3DB(const CppSQLite3DB& db) -{ - mpDB = db.mpDB; - mnBusyTimeoutMs = 60000; // 60 seconds -} - - -CppSQLite3DB::~CppSQLite3DB() -{ - close(); -} - - -CppSQLite3DB& CppSQLite3DB::operator=(const CppSQLite3DB& db) -{ - mpDB = db.mpDB; - mnBusyTimeoutMs = 60000; // 60 seconds - return *this; -} - - -void CppSQLite3DB::open(const char* szFile) -{ - int nRet = sqlite3_open(szFile, &mpDB); - - if (nRet != SQLITE_OK) - { - const char* szError = sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } - - setBusyTimeout(mnBusyTimeoutMs); -} - - -void CppSQLite3DB::close() -{ - if (mpDB) - { - sqlite3_close(mpDB); - mpDB = 0; - } -} - - -CppSQLite3Statement CppSQLite3DB::compileStatement(const char* szSQL) -{ - checkDB(); - - sqlite3_stmt* pVM = compile(szSQL); - return CppSQLite3Statement(mpDB, pVM); -} - - -bool CppSQLite3DB::tableExists(const char* szTable) -{ - char szSQL[128]; - sprintf(szSQL, - "select count(*) from sqlite_master where type='table' and name='%s'", - szTable); - int nRet = execScalar(szSQL); - return (nRet > 0); -} - - -int CppSQLite3DB::execDML(const char* szSQL) -{ - checkDB(); - - char* szError=0; - - int nRet = sqlite3_exec(mpDB, szSQL, 0, 0, &szError); - - if (nRet == SQLITE_OK) - { - return sqlite3_changes(mpDB); - } - else - { - throw CppSQLite3Exception(nRet, szError); - } -} - - -CppSQLite3Query CppSQLite3DB::execQuery(const char* szSQL) -{ - checkDB(); - - sqlite3_stmt* pVM = compile(szSQL); - - int nRet = sqlite3_step(pVM); - - if (nRet == SQLITE_DONE) - { - // no rows - return CppSQLite3Query(mpDB, pVM, true/*eof*/); - } - else if (nRet == SQLITE_ROW) - { - // at least 1 row - return CppSQLite3Query(mpDB, pVM, false/*eof*/); - } - else - { - nRet = sqlite3_finalize(pVM); - const char* szError= sqlite3_errmsg(mpDB); - throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); - } -} - - -int CppSQLite3DB::execScalar(const char* szSQL) -{ - CppSQLite3Query q = execQuery(szSQL); - - if (q.eof() || q.numFields() < 1) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Invalid scalar query", - DONT_DELETE_MSG); - } - - return atoi(q.fieldValue(0)); -} - - -CppSQLite3Table CppSQLite3DB::getTable(const char* szSQL) -{ - checkDB(); - - char* szError=0; - char** paszResults=0; - int nRet; - int nRows(0); - int nCols(0); - - nRet = sqlite3_get_table(mpDB, szSQL, &paszResults, &nRows, &nCols, &szError); - - if (nRet == SQLITE_OK) - { - return CppSQLite3Table(paszResults, nRows, nCols); - } - else - { - throw CppSQLite3Exception(nRet, szError); - } -} - - -sqlite_int64 CppSQLite3DB::lastRowId() -{ - return sqlite3_last_insert_rowid(mpDB); -} - - -void CppSQLite3DB::setBusyTimeout(int nMillisecs) -{ - mnBusyTimeoutMs = nMillisecs; - sqlite3_busy_timeout(mpDB, mnBusyTimeoutMs); -} - - -void CppSQLite3DB::checkDB() -{ - if (!mpDB) - { - throw CppSQLite3Exception(CPPSQLITE_ERROR, - "Database not open", - DONT_DELETE_MSG); - } -} - - -sqlite3_stmt* CppSQLite3DB::compile(const char* szSQL) -{ - checkDB(); - - char* szError=0; - const char* szTail=0; - sqlite3_stmt* pVM; - - int nRet = sqlite3_prepare(mpDB, szSQL, -1, &pVM, &szTail); - - if (nRet != SQLITE_OK) - { - throw CppSQLite3Exception(nRet, szError); - } - - return pVM; -} - - -//////////////////////////////////////////////////////////////////////////////// -// SQLite encode.c reproduced here, containing implementation notes and source -// for sqlite3_encode_binary() and sqlite3_decode_binary() -//////////////////////////////////////////////////////////////////////////////// - -/* -** 2002 April 25 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains helper routines used to translate binary data into -** a null-terminated string (suitable for use in SQLite) and back again. -** These are convenience routines for use by people who want to store binary -** data in an SQLite database. The code in this file is not used by any other -** part of the SQLite library. -** -** $Id: CppSQLite3.cpp,v 1.2 2008/12/15 12:54:44 guigues Exp $ -*/ - -/* -** How This Encoder Works -** -** The output is allowed to contain any character except 0x27 (') and -** 0x00. This is accomplished by using an escape character to encode -** 0x27 and 0x00 as a two-byte sequence. The escape character is always -** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The -** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally, -** the escape character itself is encoded as the two-character sequence -** 0x01 0x02. -** -** To summarize, the encoder works by using an escape sequences as follows: -** -** 0x00 -> 0x01 0x01 -** 0x01 -> 0x01 0x02 -** 0x27 -> 0x01 0x03 -** -** If that were all the encoder did, it would work, but in certain cases -** it could double the size of the encoded string. For example, to -** encode a string of 100 0x27 characters would require 100 instances of -** the 0x01 0x03 escape sequence resulting in a 200-character output. -** We would prefer to keep the size of the encoded string smaller than -** this. -** -** To minimize the encoding size, we first add a fixed offset value to each -** byte in the sequence. The addition is modulo 256. (That is to say, if -** the sum of the original character value and the offset exceeds 256, then -** the higher order bits are truncated.) The offset is chosen to minimize -** the number of characters in the string that need to be escaped. For -** example, in the case above where the string was composed of 100 0x27 -** characters, the offset might be 0x01. Each of the 0x27 characters would -** then be converted into an 0x28 character which would not need to be -** escaped at all and so the 100 character input string would be converted -** into just 100 characters of output. Actually 101 characters of output - -** we have to record the offset used as the first byte in the sequence so -** that the string can be decoded. Since the offset value is stored as -** part of the output string and the output string is not allowed to contain -** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27. -** -** Here, then, are the encoding steps: -** -** (1) Choose an offset value and make it the first character of -** output. -** -** (2) Copy each input character into the output buffer, one by -** one, adding the offset value as you copy. -** -** (3) If the value of an input character plus offset is 0x00, replace -** that one character by the two-character sequence 0x01 0x01. -** If the sum is 0x01, replace it with 0x01 0x02. If the sum -** is 0x27, replace it with 0x01 0x03. -** -** (4) Put a 0x00 terminator at the end of the output. -** -** Decoding is obvious: -** -** (5) Copy encoded characters except the first into the decode -** buffer. Set the first encoded character aside for use as -** the offset in step 7 below. -** -** (6) Convert each 0x01 0x01 sequence into a single character 0x00. -** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27. -** -** (7) Subtract the offset value that was the first character of -** the encoded buffer from all characters in the output buffer. -** -** The only tricky part is step (1) - how to compute an offset value to -** minimize the size of the output buffer. This is accomplished by testing -** all offset values and picking the one that results in the fewest number -** of escapes. To do that, we first scan the entire input and count the -** number of occurances of each character value in the input. Suppose -** the number of 0x00 characters is N(0), the number of occurances of 0x01 -** is N(1), and so forth up to the number of occurances of 0xff is N(255). -** An offset of 0 is not allowed so we don't have to test it. The number -** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number -** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth. -** In this way we find the offset that gives the minimum number of escapes, -** and thus minimizes the length of the output string. -*/ - -/* -** Encode a binary buffer "in" of size n bytes so that it contains -** no instances of characters '\'' or '\000'. The output is -** null-terminated and can be used as a string value in an INSERT -** or UPDATE statement. Use sqlite3_decode_binary() to convert the -** string back into its original binary. -** -** The result is written into a preallocated output buffer "out". -** "out" must be able to hold at least 2 +(257*n)/254 bytes. -** In other words, the output will be expanded by as much as 3 -** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. -** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) -** -** The return value is the number of characters in the encoded -** string, excluding the "\000" terminator. -*/ -int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out){ - int i, j, e, m; - int cnt[256]; - if( n<=0 ){ - out[0] = 'x'; - out[1] = 0; - return 1; - } - memset(cnt, 0, sizeof(cnt)); - for(i=n-1; i>=0; i--){ cnt[in[i]]++; } - m = n; - for(i=1; i<256; i++){ - int sum; - if( i=='\'' ) continue; - sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff]; - if( sum + + + + + +// Named constant for passing to CppSQLite3Exception when passing it a string + +// that cannot be deleted. + +static const bool DONT_DELETE_MSG=false; + + + +//////////////////////////////////////////////////////////////////////////////// + +// Prototypes for SQLite functions not included in SQLite DLL, but copied below + +// from SQLite encode.c + +//////////////////////////////////////////////////////////////////////////////// + +int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out); + +int sqlite3_decode_binary(const unsigned char *in, unsigned char *out); + + + +//////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, + + const char* szErrMess, + + bool bDeleteMsg/*=true*/) : + + mnErrCode(nErrCode) + +{ + + mpszErrMess = sqlite3_mprintf("%s[%d]: %s", + + errorCodeAsString(nErrCode), + + nErrCode, + + szErrMess ? szErrMess : ""); + + /* + + if (bDeleteMsg && szErrMess) + + { + + sqlite3_free(szErrMess); + + } + + */ + +} + + + +CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, + + char* szErrMess, + + bool bDeleteMsg/*=true*/) : + + mnErrCode(nErrCode) + +{ + + mpszErrMess = sqlite3_mprintf("%s[%d]: %s", + + errorCodeAsString(nErrCode), + + nErrCode, + + szErrMess ? szErrMess : ""); + + + + if (bDeleteMsg && szErrMess) + + { + + sqlite3_free(szErrMess); + + } + +} + + + +CppSQLite3Exception::CppSQLite3Exception(const CppSQLite3Exception& e) : + + mnErrCode(e.mnErrCode) + +{ + + mpszErrMess = 0; + + if (e.mpszErrMess) + + { + + mpszErrMess = sqlite3_mprintf("%s", e.mpszErrMess); + + } + +} + + + + + +const char* CppSQLite3Exception::errorCodeAsString(int nErrCode) + +{ + + switch (nErrCode) + + { + + case SQLITE_OK : return "SQLITE_OK"; + + case SQLITE_ERROR : return "SQLITE_ERROR"; + + case SQLITE_INTERNAL : return "SQLITE_INTERNAL"; + + case SQLITE_PERM : return "SQLITE_PERM"; + + case SQLITE_ABORT : return "SQLITE_ABORT"; + + case SQLITE_BUSY : return "SQLITE_BUSY"; + + case SQLITE_LOCKED : return "SQLITE_LOCKED"; + + case SQLITE_NOMEM : return "SQLITE_NOMEM"; + + case SQLITE_READONLY : return "SQLITE_READONLY"; + + case SQLITE_INTERRUPT : return "SQLITE_INTERRUPT"; + + case SQLITE_IOERR : return "SQLITE_IOERR"; + + case SQLITE_CORRUPT : return "SQLITE_CORRUPT"; + + case SQLITE_NOTFOUND : return "SQLITE_NOTFOUND"; + + case SQLITE_FULL : return "SQLITE_FULL"; + + case SQLITE_CANTOPEN : return "SQLITE_CANTOPEN"; + + case SQLITE_PROTOCOL : return "SQLITE_PROTOCOL"; + + case SQLITE_EMPTY : return "SQLITE_EMPTY"; + + case SQLITE_SCHEMA : return "SQLITE_SCHEMA"; + + case SQLITE_TOOBIG : return "SQLITE_TOOBIG"; + + case SQLITE_CONSTRAINT : return "SQLITE_CONSTRAINT"; + + case SQLITE_MISMATCH : return "SQLITE_MISMATCH"; + + case SQLITE_MISUSE : return "SQLITE_MISUSE"; + + case SQLITE_NOLFS : return "SQLITE_NOLFS"; + + case SQLITE_AUTH : return "SQLITE_AUTH"; + + case SQLITE_FORMAT : return "SQLITE_FORMAT"; + + case SQLITE_RANGE : return "SQLITE_RANGE"; + + case SQLITE_ROW : return "SQLITE_ROW"; + + case SQLITE_DONE : return "SQLITE_DONE"; + + case CPPSQLITE_ERROR : return "CPPSQLITE_ERROR"; + + default: return "UNKNOWN_ERROR"; + + } + +} + + + + + +CppSQLite3Exception::~CppSQLite3Exception() + +{ + + if (mpszErrMess) + + { + + sqlite3_free(mpszErrMess); + + mpszErrMess = 0; + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Buffer::CppSQLite3Buffer() + +{ + + mpBuf = 0; + +} + + + + + +CppSQLite3Buffer::~CppSQLite3Buffer() + +{ + + clear(); + +} + + + + + +void CppSQLite3Buffer::clear() + +{ + + if (mpBuf) + + { + + sqlite3_free(mpBuf); + + mpBuf = 0; + + } + + + +} + + + + + +const char* CppSQLite3Buffer::format(const char* szFormat, ...) + +{ + + clear(); + + va_list va; + + va_start(va, szFormat); + + mpBuf = sqlite3_vmprintf(szFormat, va); + + va_end(va); + + return mpBuf; + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Binary::CppSQLite3Binary() : + + mpBuf(0), + + mnBinaryLen(0), + + mnBufferLen(0), + + mnEncodedLen(0), + + mbEncoded(false) + +{ + +} + + + + + +CppSQLite3Binary::~CppSQLite3Binary() + +{ + + clear(); + +} + + + + + +void CppSQLite3Binary::setBinary(const unsigned char* pBuf, int nLen) + +{ + + mpBuf = allocBuffer(nLen); + + memcpy(mpBuf, pBuf, nLen); + +} + + + + + +void CppSQLite3Binary::setEncoded(const unsigned char* pBuf) + +{ + + clear(); + + + + mnEncodedLen = strlen((const char*)pBuf); + + mnBufferLen = mnEncodedLen + 1; // Allow for NULL terminator + + + + mpBuf = (unsigned char*)malloc(mnBufferLen); + + + + if (!mpBuf) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Cannot allocate memory", + + DONT_DELETE_MSG); + + } + + + + memcpy(mpBuf, pBuf, mnBufferLen); + + mbEncoded = true; + +} + + + + + +const unsigned char* CppSQLite3Binary::getEncoded() + +{ + + if (!mbEncoded) + + { + + unsigned char* ptmp = (unsigned char*)malloc(mnBinaryLen); + + memcpy(ptmp, mpBuf, mnBinaryLen); + + mnEncodedLen = sqlite3_encode_binary(ptmp, mnBinaryLen, mpBuf); + + free(ptmp); + + mbEncoded = true; + + } + + + + return mpBuf; + +} + + + + + +const unsigned char* CppSQLite3Binary::getBinary() + +{ + + if (mbEncoded) + + { + + // in/out buffers can be the same + + mnBinaryLen = sqlite3_decode_binary(mpBuf, mpBuf); + + + + if (mnBinaryLen == -1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Cannot decode binary", + + DONT_DELETE_MSG); + + } + + + + mbEncoded = false; + + } + + + + return mpBuf; + +} + + + + + +int CppSQLite3Binary::getBinaryLength() + +{ + + getBinary(); + + return mnBinaryLen; + +} + + + + + +unsigned char* CppSQLite3Binary::allocBuffer(int nLen) + +{ + + clear(); + + + + // Allow extra space for encoded binary as per comments in + + // SQLite encode.c See bottom of this file for implementation + + // of SQLite functions use 3 instead of 2 just to be sure ;-) + + mnBinaryLen = nLen; + + mnBufferLen = 3 + (257*nLen)/254; + + + + mpBuf = (unsigned char*)malloc(mnBufferLen); + + + + if (!mpBuf) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Cannot allocate memory", + + DONT_DELETE_MSG); + + } + + + + mbEncoded = false; + + + + return mpBuf; + +} + + + + + +void CppSQLite3Binary::clear() + +{ + + if (mpBuf) + + { + + mnBinaryLen = 0; + + mnBufferLen = 0; + + free(mpBuf); + + mpBuf = 0; + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Query::CppSQLite3Query() + +{ + + mpVM = 0; + + mbEof = true; + + mnCols = 0; + + mbOwnVM = false; + +} + + + + + +CppSQLite3Query::CppSQLite3Query(const CppSQLite3Query& rQuery) + +{ + + mpVM = rQuery.mpVM; + + // Only one object can own the VM + + const_cast(rQuery).mpVM = 0; + + mbEof = rQuery.mbEof; + + mnCols = rQuery.mnCols; + + mbOwnVM = rQuery.mbOwnVM; + +} + + + + + +CppSQLite3Query::CppSQLite3Query(sqlite3* pDB, + + sqlite3_stmt* pVM, + + bool bEof, + + bool bOwnVM/*=true*/) + +{ + + mpDB = pDB; + + mpVM = pVM; + + mbEof = bEof; + + mnCols = sqlite3_column_count(mpVM); + + mbOwnVM = bOwnVM; + +} + + + + + +CppSQLite3Query::~CppSQLite3Query() + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + +} + + + + + +CppSQLite3Query& CppSQLite3Query::operator=(const CppSQLite3Query& rQuery) + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + + mpVM = rQuery.mpVM; + + // Only one object can own the VM + + const_cast(rQuery).mpVM = 0; + + mbEof = rQuery.mbEof; + + mnCols = rQuery.mnCols; + + mbOwnVM = rQuery.mbOwnVM; + + return *this; + +} + + + + + +int CppSQLite3Query::numFields() + +{ + + checkVM(); + + return mnCols; + +} + + + + + +const char* CppSQLite3Query::fieldValue(int nField) + +{ + + checkVM(); + + + + if (nField < 0 || nField > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return (const char*)sqlite3_column_text(mpVM, nField); + +} + + + + + +const char* CppSQLite3Query::fieldValue(const char* szField) + +{ + + int nField = fieldIndex(szField); + + return (const char*)sqlite3_column_text(mpVM, nField); + +} + + + + + +int CppSQLite3Query::getIntField(int nField, int nNullValue/*=0*/) + +{ + + if (fieldDataType(nField) == SQLITE_NULL) + + { + + return nNullValue; + + } + + else + + { + + return sqlite3_column_int(mpVM, nField); + + } + +} + + + + + +int CppSQLite3Query::getIntField(const char* szField, int nNullValue/*=0*/) + +{ + + int nField = fieldIndex(szField); + + return getIntField(nField, nNullValue); + +} + + + + + +double CppSQLite3Query::getFloatField(int nField, double fNullValue/*=0.0*/) + +{ + + if (fieldDataType(nField) == SQLITE_NULL) + + { + + return fNullValue; + + } + + else + + { + + return sqlite3_column_double(mpVM, nField); + + } + +} + + + + + +double CppSQLite3Query::getFloatField(const char* szField, double fNullValue/*=0.0*/) + +{ + + int nField = fieldIndex(szField); + + return getFloatField(nField, fNullValue); + +} + + + + + +const char* CppSQLite3Query::getStringField(int nField, const char* szNullValue/*=""*/) + +{ + + if (fieldDataType(nField) == SQLITE_NULL) + + { + + return szNullValue; + + } + + else + + { + + return (const char*)sqlite3_column_text(mpVM, nField); + + } + +} + + + + + +const char* CppSQLite3Query::getStringField(const char* szField, const char* szNullValue/*=""*/) + +{ + + int nField = fieldIndex(szField); + + return getStringField(nField, szNullValue); + +} + + + + + +const unsigned char* CppSQLite3Query::getBlobField(int nField, int& nLen) + +{ + + checkVM(); + + + + if (nField < 0 || nField > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + nLen = sqlite3_column_bytes(mpVM, nField); + + return (const unsigned char*)sqlite3_column_blob(mpVM, nField); + +} + + + + + +const unsigned char* CppSQLite3Query::getBlobField(const char* szField, int& nLen) + +{ + + int nField = fieldIndex(szField); + + return getBlobField(nField, nLen); + +} + + + + + +bool CppSQLite3Query::fieldIsNull(int nField) + +{ + + return (fieldDataType(nField) == SQLITE_NULL); + +} + + + + + +bool CppSQLite3Query::fieldIsNull(const char* szField) + +{ + + int nField = fieldIndex(szField); + + return (fieldDataType(nField) == SQLITE_NULL); + +} + + + + + +int CppSQLite3Query::fieldIndex(const char* szField) + +{ + + checkVM(); + + + + if (szField) + + { + + for (int nField = 0; nField < mnCols; nField++) + + { + + const char* szTemp = sqlite3_column_name(mpVM, nField); + + + + if (strcmp(szField, szTemp) == 0) + + { + + return nField; + + } + + } + + } + + + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field name requested", + + DONT_DELETE_MSG); + +} + + + + + +const char* CppSQLite3Query::fieldName(int nCol) + +{ + + checkVM(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return sqlite3_column_name(mpVM, nCol); + +} + + + + + +const char* CppSQLite3Query::fieldDeclType(int nCol) + +{ + + checkVM(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return sqlite3_column_decltype(mpVM, nCol); + +} + + + + + +int CppSQLite3Query::fieldDataType(int nCol) + +{ + + checkVM(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return sqlite3_column_type(mpVM, nCol); + +} + + + + + +bool CppSQLite3Query::eof() + +{ + + checkVM(); + + return mbEof; + +} + + + + + +void CppSQLite3Query::nextRow() + +{ + + checkVM(); + + + + int nRet = sqlite3_step(mpVM); + + + + if (nRet == SQLITE_DONE) + + { + + // no rows + + mbEof = true; + + } + + else if (nRet == SQLITE_ROW) + + { + + // more rows, nothing to do + + } + + else + + { + + nRet = sqlite3_finalize(mpVM); + + mpVM = 0; + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, + + (char*)szError, + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Query::finalize() + +{ + + if (mpVM && mbOwnVM) + + { + + int nRet = sqlite3_finalize(mpVM); + + mpVM = 0; + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + } + +} + + + + + +void CppSQLite3Query::checkVM() + +{ + + if (mpVM == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Null Virtual Machine pointer", + + DONT_DELETE_MSG); + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Table::CppSQLite3Table() + +{ + + mpaszResults = 0; + + mnRows = 0; + + mnCols = 0; + + mnCurrentRow = 0; + +} + + + + + +CppSQLite3Table::CppSQLite3Table(const CppSQLite3Table& rTable) + +{ + + mpaszResults = rTable.mpaszResults; + + // Only one object can own the results + + const_cast(rTable).mpaszResults = 0; + + mnRows = rTable.mnRows; + + mnCols = rTable.mnCols; + + mnCurrentRow = rTable.mnCurrentRow; + +} + + + + + +CppSQLite3Table::CppSQLite3Table(char** paszResults, int nRows, int nCols) + +{ + + mpaszResults = paszResults; + + mnRows = nRows; + + mnCols = nCols; + + mnCurrentRow = 0; + +} + + + + + +CppSQLite3Table::~CppSQLite3Table() + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + +} + + + + + +CppSQLite3Table& CppSQLite3Table::operator=(const CppSQLite3Table& rTable) + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + + mpaszResults = rTable.mpaszResults; + + // Only one object can own the results + + const_cast(rTable).mpaszResults = 0; + + mnRows = rTable.mnRows; + + mnCols = rTable.mnCols; + + mnCurrentRow = rTable.mnCurrentRow; + + return *this; + +} + + + + + +void CppSQLite3Table::finalize() + +{ + + if (mpaszResults) + + { + + sqlite3_free_table(mpaszResults); + + mpaszResults = 0; + + } + +} + + + + + +int CppSQLite3Table::numFields() + +{ + + checkResults(); + + return mnCols; + +} + + + + + +int CppSQLite3Table::numRows() + +{ + + checkResults(); + + return mnRows; + +} + + + + + +const char* CppSQLite3Table::fieldValue(int nField) + +{ + + checkResults(); + + + + if (nField < 0 || nField > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; + + return mpaszResults[nIndex]; + +} + + + + + +const char* CppSQLite3Table::fieldValue(const char* szField) + +{ + + checkResults(); + + + + if (szField) + + { + + for (int nField = 0; nField < mnCols; nField++) + + { + + if (strcmp(szField, mpaszResults[nField]) == 0) + + { + + int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; + + return mpaszResults[nIndex]; + + } + + } + + } + + + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field name requested", + + DONT_DELETE_MSG); + +} + + + + + +int CppSQLite3Table::getIntField(int nField, int nNullValue/*=0*/) + +{ + + if (fieldIsNull(nField)) + + { + + return nNullValue; + + } + + else + + { + + return atoi(fieldValue(nField)); + + } + +} + + + + + +int CppSQLite3Table::getIntField(const char* szField, int nNullValue/*=0*/) + +{ + + if (fieldIsNull(szField)) + + { + + return nNullValue; + + } + + else + + { + + return atoi(fieldValue(szField)); + + } + +} + + + + + +double CppSQLite3Table::getFloatField(int nField, double fNullValue/*=0.0*/) + +{ + + if (fieldIsNull(nField)) + + { + + return fNullValue; + + } + + else + + { + + return atof(fieldValue(nField)); + + } + +} + + + + + +double CppSQLite3Table::getFloatField(const char* szField, double fNullValue/*=0.0*/) + +{ + + if (fieldIsNull(szField)) + + { + + return fNullValue; + + } + + else + + { + + return atof(fieldValue(szField)); + + } + +} + + + + + +const char* CppSQLite3Table::getStringField(int nField, const char* szNullValue/*=""*/) + +{ + + if (fieldIsNull(nField)) + + { + + return szNullValue; + + } + + else + + { + + return fieldValue(nField); + + } + +} + + + + + +const char* CppSQLite3Table::getStringField(const char* szField, const char* szNullValue/*=""*/) + +{ + + if (fieldIsNull(szField)) + + { + + return szNullValue; + + } + + else + + { + + return fieldValue(szField); + + } + +} + + + + + +bool CppSQLite3Table::fieldIsNull(int nField) + +{ + + checkResults(); + + return (fieldValue(nField) == 0); + +} + + + + + +bool CppSQLite3Table::fieldIsNull(const char* szField) + +{ + + checkResults(); + + return (fieldValue(szField) == 0); + +} + + + + + +const char* CppSQLite3Table::fieldName(int nCol) + +{ + + checkResults(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return mpaszResults[nCol]; + +} + + + + + +void CppSQLite3Table::setRow(int nRow) + +{ + + checkResults(); + + + + if (nRow < 0 || nRow > mnRows-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid row index requested", + + DONT_DELETE_MSG); + + } + + + + mnCurrentRow = nRow; + +} + + + + + +void CppSQLite3Table::checkResults() + +{ + + if (mpaszResults == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Null Results pointer", + + DONT_DELETE_MSG); + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Statement::CppSQLite3Statement() + +{ + + mpDB = 0; + + mpVM = 0; + +} + + + + + +CppSQLite3Statement::CppSQLite3Statement(const CppSQLite3Statement& rStatement) + +{ + + mpDB = rStatement.mpDB; + + mpVM = rStatement.mpVM; + + // Only one object can own VM + + const_cast(rStatement).mpVM = 0; + +} + + + + + +CppSQLite3Statement::CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM) + +{ + + mpDB = pDB; + + mpVM = pVM; + +} + + + + + +CppSQLite3Statement::~CppSQLite3Statement() + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + +} + + + + + +CppSQLite3Statement& CppSQLite3Statement::operator=(const CppSQLite3Statement& rStatement) + +{ + + mpDB = rStatement.mpDB; + + mpVM = rStatement.mpVM; + + // Only one object can own VM + + const_cast(rStatement).mpVM = 0; + + return *this; + +} + + + + + +int CppSQLite3Statement::execDML() + +{ + + checkDB(); + + checkVM(); + + + + const char* szError=0; + + + + int nRet = sqlite3_step(mpVM); + + + + if (nRet == SQLITE_DONE) + + { + + int nRowsChanged = sqlite3_changes(mpDB); + + + + nRet = sqlite3_reset(mpVM); + + + + if (nRet != SQLITE_OK) + + { + + szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + + + return nRowsChanged; + + } + + else + + { + + nRet = sqlite3_reset(mpVM); + + szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + +} + + + + + +CppSQLite3Query CppSQLite3Statement::execQuery() + +{ + + checkDB(); + + checkVM(); + + + + int nRet = sqlite3_step(mpVM); + + + + if (nRet == SQLITE_DONE) + + { + + // no rows + + return CppSQLite3Query(mpDB, mpVM, true/*eof*/, false); + + } + + else if (nRet == SQLITE_ROW) + + { + + // at least 1 row + + return CppSQLite3Query(mpDB, mpVM, false/*eof*/, false); + + } + + else + + { + + nRet = sqlite3_reset(mpVM); + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const char* szValue) + +{ + + checkVM(); + + int nRes = sqlite3_bind_text(mpVM, nParam, szValue, -1, SQLITE_TRANSIENT); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding string param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const int nValue) + +{ + + checkVM(); + + int nRes = sqlite3_bind_int(mpVM, nParam, nValue); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding int param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const double dValue) + +{ + + checkVM(); + + int nRes = sqlite3_bind_double(mpVM, nParam, dValue); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding double param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const unsigned char* blobValue, int nLen) + +{ + + checkVM(); + + int nRes = sqlite3_bind_blob(mpVM, nParam, + + (const void*)blobValue, nLen, SQLITE_TRANSIENT); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding blob param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bindNull(int nParam) + +{ + + checkVM(); + + int nRes = sqlite3_bind_null(mpVM, nParam); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding NULL param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::reset() + +{ + + if (mpVM) + + { + + int nRet = sqlite3_reset(mpVM); + + + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + } + +} + + + + + +void CppSQLite3Statement::finalize() + +{ + + if (mpVM) + + { + + int nRet = sqlite3_finalize(mpVM); + + mpVM = 0; + + + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + } + +} + + + + + +void CppSQLite3Statement::checkDB() + +{ + + if (mpDB == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Database not open", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::checkVM() + +{ + + if (mpVM == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Null Virtual Machine pointer", + + DONT_DELETE_MSG); + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3DB::CppSQLite3DB() + +{ + + mpDB = 0; + + mnBusyTimeoutMs = 60000; // 60 seconds + +} + + + + + +CppSQLite3DB::CppSQLite3DB(const CppSQLite3DB& db) + +{ + + mpDB = db.mpDB; + + mnBusyTimeoutMs = 60000; // 60 seconds + +} + + + + + +CppSQLite3DB::~CppSQLite3DB() + +{ + + close(); + +} + + + + + +CppSQLite3DB& CppSQLite3DB::operator=(const CppSQLite3DB& db) + +{ + + mpDB = db.mpDB; + + mnBusyTimeoutMs = 60000; // 60 seconds + + return *this; + +} + + + + + +void CppSQLite3DB::open(const char* szFile) + +{ + + int nRet = sqlite3_open(szFile, &mpDB); + + + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + + + setBusyTimeout(mnBusyTimeoutMs); + +} + + + + + +void CppSQLite3DB::close() + +{ + + if (mpDB) + + { + + sqlite3_close(mpDB); + + mpDB = 0; + + } + +} + + + + + +CppSQLite3Statement CppSQLite3DB::compileStatement(const char* szSQL) + +{ + + checkDB(); + + + + sqlite3_stmt* pVM = compile(szSQL); + + return CppSQLite3Statement(mpDB, pVM); + +} + + + + + +bool CppSQLite3DB::tableExists(const char* szTable) + +{ + + char szSQL[128]; + + sprintf(szSQL, + + "select count(*) from sqlite_master where type='table' and name='%s'", + + szTable); + + int nRet = execScalar(szSQL); + + return (nRet > 0); + +} + + + + + +int CppSQLite3DB::execDML(const char* szSQL) + +{ + + checkDB(); + + + + char* szError=0; + + + + int nRet = sqlite3_exec(mpDB, szSQL, 0, 0, &szError); + + + + if (nRet == SQLITE_OK) + + { + + return sqlite3_changes(mpDB); + + } + + else + + { + + throw CppSQLite3Exception(nRet, szError); + + } + +} + + + + + +CppSQLite3Query CppSQLite3DB::execQuery(const char* szSQL) + +{ + + checkDB(); + + + + sqlite3_stmt* pVM = compile(szSQL); + + + + int nRet = sqlite3_step(pVM); + + + + if (nRet == SQLITE_DONE) + + { + + // no rows + + return CppSQLite3Query(mpDB, pVM, true/*eof*/); + + } + + else if (nRet == SQLITE_ROW) + + { + + // at least 1 row + + return CppSQLite3Query(mpDB, pVM, false/*eof*/); + + } + + else + + { + + nRet = sqlite3_finalize(pVM); + + const char* szError= sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + +} + + + + + +int CppSQLite3DB::execScalar(const char* szSQL) + +{ + + CppSQLite3Query q = execQuery(szSQL); + + + + if (q.eof() || q.numFields() < 1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid scalar query", + + DONT_DELETE_MSG); + + } + + + + return atoi(q.fieldValue(0)); + +} + + + + + +CppSQLite3Table CppSQLite3DB::getTable(const char* szSQL) + +{ + + checkDB(); + + + + char* szError=0; + + char** paszResults=0; + + int nRet; + + int nRows(0); + + int nCols(0); + + + + nRet = sqlite3_get_table(mpDB, szSQL, &paszResults, &nRows, &nCols, &szError); + + + + if (nRet == SQLITE_OK) + + { + + return CppSQLite3Table(paszResults, nRows, nCols); + + } + + else + + { + + throw CppSQLite3Exception(nRet, szError); + + } + +} + + + + + +sqlite_int64 CppSQLite3DB::lastRowId() + +{ + + return sqlite3_last_insert_rowid(mpDB); + +} + + + + + +void CppSQLite3DB::setBusyTimeout(int nMillisecs) + +{ + + mnBusyTimeoutMs = nMillisecs; + + sqlite3_busy_timeout(mpDB, mnBusyTimeoutMs); + +} + + + + + +void CppSQLite3DB::checkDB() + +{ + + if (!mpDB) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Database not open", + + DONT_DELETE_MSG); + + } + +} + + + + + +sqlite3_stmt* CppSQLite3DB::compile(const char* szSQL) + +{ + + checkDB(); + + + + char* szError=0; + + const char* szTail=0; + + sqlite3_stmt* pVM; + + + + int nRet = sqlite3_prepare(mpDB, szSQL, -1, &pVM, &szTail); + + + + if (nRet != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRet, szError); + + } + + + + return pVM; + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + +// SQLite encode.c reproduced here, containing implementation notes and source + +// for sqlite3_encode_binary() and sqlite3_decode_binary() + +//////////////////////////////////////////////////////////////////////////////// + + + +/* + +** 2002 April 25 + +** + +** The author disclaims copyright to this source code. In place of + +** a legal notice, here is a blessing: + +** + +** May you do good and not evil. + +** May you find forgiveness for yourself and forgive others. + +** May you share freely, never taking more than you give. + +** + +************************************************************************* + +** This file contains helper routines used to translate binary data into + +** a null-terminated string (suitable for use in SQLite) and back again. + +** These are convenience routines for use by people who want to store binary + +** data in an SQLite database. The code in this file is not used by any other + +** part of the SQLite library. + +** + +** $Id: CppSQLite3.cpp,v 1.4 2010/03/16 15:46:13 cervenansky Exp $ + +*/ + + + +/* + +** How This Encoder Works + +** + +** The output is allowed to contain any character except 0x27 (') and + +** 0x00. This is accomplished by using an escape character to encode + +** 0x27 and 0x00 as a two-byte sequence. The escape character is always + +** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The + +** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally, + +** the escape character itself is encoded as the two-character sequence + +** 0x01 0x02. + +** + +** To summarize, the encoder works by using an escape sequences as follows: + +** + +** 0x00 -> 0x01 0x01 + +** 0x01 -> 0x01 0x02 + +** 0x27 -> 0x01 0x03 + +** + +** If that were all the encoder did, it would work, but in certain cases + +** it could double the size of the encoded string. For example, to + +** encode a string of 100 0x27 characters would require 100 instances of + +** the 0x01 0x03 escape sequence resulting in a 200-character output. + +** We would prefer to keep the size of the encoded string smaller than + +** this. + +** + +** To minimize the encoding size, we first add a fixed offset value to each + +** byte in the sequence. The addition is modulo 256. (That is to say, if + +** the sum of the original character value and the offset exceeds 256, then + +** the higher order bits are truncated.) The offset is chosen to minimize + +** the number of characters in the string that need to be escaped. For + +** example, in the case above where the string was composed of 100 0x27 + +** characters, the offset might be 0x01. Each of the 0x27 characters would + +** then be converted into an 0x28 character which would not need to be + +** escaped at all and so the 100 character input string would be converted + +** into just 100 characters of output. Actually 101 characters of output - + +** we have to record the offset used as the first byte in the sequence so + +** that the string can be decoded. Since the offset value is stored as + +** part of the output string and the output string is not allowed to contain + +** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27. + +** + +** Here, then, are the encoding steps: + +** + +** (1) Choose an offset value and make it the first character of + +** output. + +** + +** (2) Copy each input character into the output buffer, one by + +** one, adding the offset value as you copy. + +** + +** (3) If the value of an input character plus offset is 0x00, replace + +** that one character by the two-character sequence 0x01 0x01. + +** If the sum is 0x01, replace it with 0x01 0x02. If the sum + +** is 0x27, replace it with 0x01 0x03. + +** + +** (4) Put a 0x00 terminator at the end of the output. + +** + +** Decoding is obvious: + +** + +** (5) Copy encoded characters except the first into the decode + +** buffer. Set the first encoded character aside for use as + +** the offset in step 7 below. + +** + +** (6) Convert each 0x01 0x01 sequence into a single character 0x00. + +** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27. + +** + +** (7) Subtract the offset value that was the first character of + +** the encoded buffer from all characters in the output buffer. + +** + +** The only tricky part is step (1) - how to compute an offset value to + +** minimize the size of the output buffer. This is accomplished by testing + +** all offset values and picking the one that results in the fewest number + +** of escapes. To do that, we first scan the entire input and count the + +** number of occurances of each character value in the input. Suppose + +** the number of 0x00 characters is N(0), the number of occurances of 0x01 + +** is N(1), and so forth up to the number of occurances of 0xff is N(255). + +** An offset of 0 is not allowed so we don't have to test it. The number + +** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number + +** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth. + +** In this way we find the offset that gives the minimum number of escapes, + +** and thus minimizes the length of the output string. + +*/ + + + +/* + +** Encode a binary buffer "in" of size n bytes so that it contains + +** no instances of characters '\'' or '\000'. The output is + +** null-terminated and can be used as a string value in an INSERT + +** or UPDATE statement. Use sqlite3_decode_binary() to convert the + +** string back into its original binary. + +** + +** The result is written into a preallocated output buffer "out". + +** "out" must be able to hold at least 2 +(257*n)/254 bytes. + +** In other words, the output will be expanded by as much as 3 + +** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. + +** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) + +** + +** The return value is the number of characters in the encoded + +** string, excluding the "\000" terminator. + +*/ + +int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out){ + + int i, j, e, m; + + int cnt[256]; + + if( n<=0 ){ + + out[0] = 'x'; + + out[1] = 0; + + return 1; + + } + + memset(cnt, 0, sizeof(cnt)); + + for(i=n-1; i>=0; i--){ cnt[in[i]]++; } + + m = n; + + for(i=1; i<256; i++){ + + int sum; + + if( i=='\'' ) continue; + + sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff]; + + if( sum