+////////////////////////////////////////////////////////////////////////////////\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