Hello.

At 14:32 2/2/2007 +0000, you wrote:
lReturnedRows is one of the function arguments, so that is fine.

I have made some progress though and that is that this problem only occurs
with this particular table, called SQL. It is a table that logs all the SQL
statements that run in my app. When I instead make this for example
sqlite_master there is no problem ever.
No idea though why this table would cause a problem. Could it be that
one of the items in that table is a reserved word?

CREATE TABLE 'SQL'
([STATEMENT_COUNT] INTEGER,
[DB] TEXT,
[QUERY_TIME] TEXT,
[QUERY_LENGTH] REAL,
[QUERY] TEXT)

Any other ideas what could be wrong with this table?

It is possible that the result set is too large to fit in the memory constraints you have, so when one of the memory allocations in the sqlite_get_table fails, the application crashes. Can you run the same statement from the sqlite3 command line application and see if it returns the expected results? and if it's very long?

In any case, I've worked a bit on the function, and here's the result:

VBSQL.h ---------------------------------

#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <oleauto.h>
#include <wtypes.h>
#include "sqlite3.h"

SAFEARRAY * __stdcall sqlite_get_table(sqlite3 * , const char *, BSTR * , long*, VARIANT);
BSTR __stdcall sqlite_libversion(void);
int __stdcall sqlite_exec(sqlite3*, const char*, BSTR*);

VBSQL.c ---------------------------------
#include "vbsqlite.h"

#define MEMORY_ERROR    "Memory allocation error"

BOOL FillVariantFromString(VARIANT* pVariant, const char* pString)
{
        int nLen;
        LPOLESTR pOLEString;

        VariantClear(pVariant);

        nLen = MultiByteToWideChar( CP_ACP, 0, pString, -1, NULL, 0);
        if (nLen == 0)
                return FALSE;
        nLen++;
        pOLEString = CoTaskMemAlloc(nLen * sizeof(WCHAR));
        if (pOLEString == NULL)
                return FALSE;
if (MultiByteToWideChar( CP_ACP, 0, pString, -1, pOLEString, nLen) == 0)
        {
                CoTaskMemFree(pOLEString);
                return FALSE;
        }
        pVariant->bstrVal = SysAllocString(pOLEString);
        V_VT(pVariant) = VT_BSTR;
        CoTaskMemFree(pOLEString);
        return TRUE;
}

SAFEARRAY * __stdcall sqlite_get_table(
  sqlite3 *db,      /* The database on which the SQL executes */
  const char *zSql, /* The SQL to be executed */
  BSTR *ErrMsg,       /* Write error messages here */
long* pNumberOfRows, /* Number of rows in the returned array (ALWAYS without the headers!) */
  VARIANT varIncludeHeaders /* TRUE to include headers, FALSE not to */
)                   /* Return the SAFEARRAY */
{
     // Temp result fields
        char **pSQL_Results;
        char *pErrMessage= 0;
        int nNumberOfColumns;
        int nResult;
        SAFEARRAY* pResult = NULL;

nResult = sqlite3_get_table(db, zSql, &pSQL_Results, pNumberOfRows, &nNumberOfColumns, &pErrMessage);

    if (nResult == SQLITE_OK)
        {
                SAFEARRAYBOUND SA_Bounds[2];

                sqlite3_free(pErrMessage);
                if (nNumberOfColumns == 0)
                {
                        sqlite3_free_table(pSQL_Results);
// Return an empty array - to make sure nothing happens:
                        SA_Bounds[0].cElements = 0;
                        SA_Bounds[0].lLbound = 0;
                        pResult = SafeArrayCreate(VT_VARIANT, 1, SA_Bounds);
                        if (pResult == NULL)
                        {
*ErrMsg = SysAllocStringByteLen(MEMORY_ERROR,strlen(MEMORY_ERROR) ); return NULL; // Don't know what this will do. NEVER USE AN ARRAY when an error was returned!
                        }
                }
                else
                {
//We have a resultset so transform this into a SAFEARRAY
                        // Create SAFEARRAY
                        //SAFEARRAY FAR* resultp = NULL;
                        BSTR bstrTemporyStringHolder = NULL;
                        VARIANT tmpVariant;
                        BSTR bstr1 = NULL;
                        HRESULT hr;
                        LPOLESTR pTempWideDataHolder = NULL;
                        int intCurrentRow ; // Tempory counter for looping
                        long indices[] = {0,0};
                        VariantInit(&tmpVariant);

                        // Set up array bounds
                        SA_Bounds[0].cElements = *pNumberOfRows + 1;
                        SA_Bounds[0].lLbound = 0;
                        SA_Bounds[1].cElements = nNumberOfColumns;
                        SA_Bounds[1].lLbound = 0;

                        //Create array
                        pResult = SafeArrayCreate(VT_VARIANT, 2, SA_Bounds);
                        if (pResult == NULL)
                        {
                                *pNumberOfRows = 0;
                                sqlite3_free_table(pSQL_Results);
*ErrMsg = SysAllocStringByteLen(MEMORY_ERROR,strlen(MEMORY_ERROR) ); return NULL; // Don't know what this will do. NEVER USE AN ARRAY when an error was returned!
                        }

                        // Put header, if so requested
if ((varIncludeHeaders.vt != VT_BOOL) || varIncludeHeaders.bVal)
                        {
                                // Start Place column headers in first row
for (indices[1]=0; indices[1]<nNumberOfColumns; indices[1]++)
                                {
if (!FillVariantFromString(&tmpVariant, pSQL_Results[indices[1]]))
                                        {
                                                *pNumberOfRows = 0;
                                                SafeArrayDestroy(pResult);
                                                
sqlite3_free_table(pSQL_Results);
*ErrMsg = SysAllocStringByteLen(MEMORY_ERROR,strlen(MEMORY_ERROR) ); return NULL; // Don't know what this will do. NEVER USE AN ARRAY when an error was returned!
                                        }

                                        // Safearray Column headers
hr = SafeArrayPutElement(pResult, indices, &tmpVariant);
                                        if (hr != S_OK)
                                        {
                                                *pNumberOfRows = 0;
                                                SafeArrayDestroy(pResult);
                                                
sqlite3_free_table(pSQL_Results);
*ErrMsg = SysAllocStringByteLen(MEMORY_ERROR,strlen(MEMORY_ERROR) ); return NULL; // Don't know what this will do. NEVER USE AN ARRAY when an error was returned!
                                        }
                                }
                                VariantClear(&tmpVariant);
                        }

                        // Start Loop through array and fill SAFEARRAY
for (intCurrentRow=1; intCurrentRow<=*pNumberOfRows; intCurrentRow++)
                        {
for (indices[1]=0; indices[1]<nNumberOfColumns; indices[1]++)
                                {
const char* pString = pSQL_Results[(intCurrentRow * nNumberOfColumns) + indices[1]];
                                        if (pString == NULL)
                                                continue;

if (!FillVariantFromString(&tmpVariant, pSQL_Results[(intCurrentRow * nNumberOfColumns) + indices[1]]))
                                        {
                                                *pNumberOfRows = 0;
                                                SafeArrayDestroy(pResult);
                                                
sqlite3_free_table(pSQL_Results);
*ErrMsg = SysAllocStringByteLen(MEMORY_ERROR,strlen(MEMORY_ERROR) ); return NULL; // Don't know what this will do. NEVER USE AN ARRAY when an error was returned!
                                        }
hr = SafeArrayPutElement(pResult, indices, &tmpVariant);
                                        if (hr != S_OK)
                                        {
                                                *pNumberOfRows = 0;
                                                SafeArrayDestroy(pResult);
                                                
sqlite3_free_table(pSQL_Results);
*ErrMsg = SysAllocStringByteLen(MEMORY_ERROR,strlen(MEMORY_ERROR) ); return NULL; // Don't know what this will do. NEVER USE AN ARRAY when an error was returned!
                                        }
                                }
                        }
                        VariantClear(&tmpVariant);
                }
                // END Loop through array and fill SAFEARRAY
                sqlite3_free_table(pSQL_Results);
        }
        else
        {
                *pNumberOfRows = 0;
*ErrMsg = SysAllocStringByteLen(pErrMessage, strlen(pErrMessage) );
                sqlite3_free(pErrMessage);
        }

        return pResult;
}

BSTR __stdcall sqlite_libversion()
{
        return SysAllocStringByteLen(SQLITE_VERSION, strlen(SQLITE_VERSION) );
}

BSTR __stdcall sqlite3_libversion_number()
{
        return SysAllocStringByteLen(SQLITE_VERSION, strlen(SQLITE_VERSION) );
}

int __stdcall sqlite_exec(
        sqlite3* pDB,
        const char *sql,              /* SQL to be executed */
        BSTR *errmsg                 /* Error msg written here */
)
{
        char *pErrMessage = NULL;
        int nResult = sqlite3_exec(pDB, sql, NULL, NULL, &pErrMessage);

        if (nResult != SQLITE_OK)
        {
*errmsg = SysAllocStringByteLen(pErrMessage, strlen(pErrMessage) );
        }

        sqlite3_free(pErrMessage);
        return nResult;
}
---------------------------

And the VB definitions for sqlite_get_table and sqlite_exec are:
Private Declare Function sqlite_get_table Lib "SQLite3VB.dll" (ByVal DB_Handle As Long, ByVal SQLString As String, ByRef ErrStr As String, ByRef rows As Long, ByVal bIncludeHeader As Boolean) As Variant() Private Declare Function sqlite_exec Lib "SQLite3VB.dll" (ByVal DB_Handle As Long, ByVal SQLString As String, ByRef ErrStr As String) As Long

Notes for sqlite_get_table:
1. Added rows which will return filled with the number of rows in the result (without the headers!) 2. Added bIncludeHeader, which, if set to False, will not include the column headers in the result 3. If you want, you can make it safer by returning a Long (error code), and sending the array back as one of the variables (like rows). It's safer as it won't be used upon error, unlike the current situation

Notes for sqlite_exec:
1. Use like you have used sqlite_get_table, but for situations when you don't need a result set (like Update queries or Pragma commands).
2. The return value is one of the SQLITE_ constants (SQLITE_OK is 0)


Guy



-----------------------------------------------------------------------------
To unsubscribe, send email to [EMAIL PROTECTED]
-----------------------------------------------------------------------------

Reply via email to