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