/*------------------------------------------------------------------------------*
 * File Name:	oSQLite.h	 													*
 * Creation: 	Sophy 12/10/2009												*
 * Purpose: OriginC Class		 to Access SQLite API							*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
#ifndef	_OSQLITE_H_
#define	_OSQLITE_H_

#include <oc_SQLite.h>

//error code
enum {
	OSQLite_OK = 0,
	OSQLite_UNKNOWN_ERR = -1,
	OSQLite_INVALID_DB_FILE = -2,
	OSQLite_INVALID_RESULT_SET = -3,
	OSQLite_UPDATE_RESULT_SET_FAIL = -4,
	OSQLite_INVALID_ORIGIN_OBJECT = -5,
	OSQLite_INVALID_INDEX = -6,
};

#define	STR_SQLITE_TYPE_TEXT	"TEXT"
#define	STR_SQLITE_TYPE_REAL	"REAL"
#define	STR_SQLITE_TYPE_BLOB	"BLOB"
#define	STR_SQLITE_TYPE_INTEGER	"INTEGER"
#define	STR_SQLITE_TYPE_NUMERIC	"NUMERIC"
#define	STR_SQLITE_TYPE_NONE	"NONE"

#define	MAX_FIELD_LEN	1024
#define	BLOB_VALUE_SEP	','
#define	WKS_RESIZE_STEP	20
#define	MAX_BUFFER_SIZE	256

enum {
	SQLITE_INTEGER_AS_64BITS = 0x0001, //if set, will get integer data as 64 bits integer
	SQLITE_MATCH_COLUMN_BY_LONGNAME = 0x0002, //if set, will try to find target column by longname first
	SQLITE_CLEAR_DATA_BEFORE_EXPORT	= 0x0004, //if set, will clear the target table before export worksheet data, if not set, will append new record to the table
};

static LPCSTR _get_field_type_name(int nTypeID)
{
	switch(nTypeID)
	{
	case SQLITE_TEXT:
		return STR_SQLITE_TYPE_TEXT;
	case SQLITE_FLOAT:
		return STR_SQLITE_TYPE_REAL;
	case SQLITE_INTEGER:
		return STR_SQLITE_TYPE_INTEGER;
	case SQLITE_BLOB:
		return STR_SQLITE_TYPE_BLOB;
	case SQLITE_NULL:
		return STR_SQLITE_TYPE_NONE;
	default:
		ASSERT(FALSE);
		break;
	}
	return "Unknown";
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////// Declaration of OSQLite class /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**#
	This class is a wrapper of SQLite APIs.
*/
class OSQLite
{
public:
	/**#
		Constructor.
		Parameters:
			lpcszFileName = [input] sqlite database file name.
	*/
	OSQLite(LPCSTR lpcszFileName);
	/**#
		Destructor. This function will release resources after database is closed.
		Parameters:
			No parameters.
	*/
	~OSQLite();
	
public:
	/**#
		This function is used to check whether the database is open for further transactions.
		Parameters:
			No parameters.
		Return:
			return TRUE if database is open, else return FALSE.
	*/
	BOOL	IsOpen(); //check whether database file is open
	/**#
		This function execute a sql query statement.
		Parameters:
			lpcszSQL = [input] the sql statement to be executed.
		Return:
			return OSQLite_OK is successfully executed, else return minus error code.
	*/
	int		Select(LPCSTR lpcszSQL); //execute query statement.
	/**#
		This function import result set into a worksheet.
		Parameters:
			wks = [input] the worksheet where the result data will be put.
			dwCtrl = [input] options of how to import data.
		Return:
			return OSQLite_OK is success, else return minus error code.
	*/
	int		Import(Worksheet& wks, DWORD dwCtrl = 0); //import whole worksheet
	/**#
		This function import specified field of result set into a column.
		Parameters:
			wks = [input] the worksheet which contains the target column.
			iCol = [input] the column index which into which data will be put.
			iField = [input] the field index of the result(set) table.
			dwCtrl = [input] options of how to import data.
		Return:
			return OSQLite_OK on success, else return minus error code.
	*/
	int		Import(Worksheet& wks, int iCol, int iField, DWORD dwCtrl = 0); //import one field to one column
	/**#
		This function is used to export worksheet data into specified table.
		Parameters:
			lpcszTable = [input] the target table name in the database.
			wks = [input] the worksheet which contains source data.
			iColStart = [input] the column index whose data will be put into the first field of the result table.
			iColEnd = [input] the last column to be exported.
			iRowStart = [input] the first row to be exported.
			iRowEnd = [input] the last row to be exported.
			dwCtrl = [input] options to indicates how to export data to database, if set as SQLITE_CLEAR_DATA_BEFORE_EXPORT, will clear data in target table before exporting, else just append at the end.
		Return:
			return OSQLite_OK on success, else return minus error code.
	*/
	int		Export(LPCSTR lpcszTable, Worksheet& wks, int iColStart = 0, int iColEnd = -1, int iRowStart = 0, int iRowEnd = -1, DWORD dwCtrl = 0);
protected:
	BOOL	Open(LPCSTR lpcszFileName); //execute a sql statement and open the result set.
	void	Close(); //release resources used by this class.
	BOOL	InitFields(); //for import from sqlite.
	BOOL	InitWorksheet(Worksheet& wks); //set worksheet properties to put data.
	int		DetectFieldTypes(LPCSTR lpcszTableName); //detect fields' datatype as to export data into sqlite table.
	int		FindColumnForField(Worksheet& wks, int iField); //find the target column for specified field
	int		ShowError(int nRetCode);
	
private:
	vector<string>	m_vsFieldNames; //contains fields' name on import.
	vector<int>		m_vnFieldTypes; //contains fields' datatype
	vector<int>		m_vnColumnIndex; //column index corresponding to field
	sqlite3*		m_pDB; //database object pointer.
	sqlite3_stmt*	m_pStmt; //statement pointer.
	BOOL			m_bIsOpen; //indicates whether database file is open.
	DWORD			m_dwCtrl; //options of how to import & export data
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////// Implementation of OSQLite class /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

OSQLite::OSQLite(LPCSTR lpcszFileName)
{
	m_bIsOpen = Open(lpcszFileName);
}

OSQLite::~OSQLite()
{
	Close();
}

BOOL	OSQLite::Open(LPCSTR lpcszFileName)
{
	m_bIsOpen = FALSE;
	int nRet = sqlite3_open(lpcszFileName, &m_pDB);
	if ( SQLITE_OK == nRet )
	{
		m_bIsOpen = true;
	}
	return m_bIsOpen;
}

void	OSQLite::Close()
{
	if ( IsOpen() )
	{
		if ( m_pStmt )
		{
			sqlite3_finalize(m_pStmt);
			m_pStmt = NULL;
		}
		if ( m_pDB )
		{
			sqlite3_close(m_pDB);
			m_pDB = NULL;
		}
	}
}

BOOL	OSQLite::IsOpen()
{
	return m_bIsOpen;
}


int 	OSQLite::Select(LPCSTR lpcszSQL)
{
	if ( !IsOpen() )
		return OSQLite_INVALID_DB_FILE;
	
	char* pEnd;
	int nRet = sqlite3_prepare(m_pDB, lpcszSQL, strlen(lpcszSQL), &m_pStmt, &pEnd);
	if ( SQLITE_OK != nRet )
		return OSQLite_INVALID_RESULT_SET;
	
	if ( InitFields() )
		return OSQLite_OK;
	
	return OSQLite_UNKNOWN_ERR;
}

int		OSQLite::Import(Worksheet& wks, DWORD dwCtrl)
{
	m_dwCtrl = dwCtrl;
	if ( !IsOpen() )
		return OSQLite_INVALID_DB_FILE;
	
	if ( m_pStmt == NULL )
		return OSQLite_INVALID_RESULT_SET;
	
	if ( !wks )
		OSQLite_INVALID_ORIGIN_OBJECT;
	
	if ( !InitWorksheet(wks) )
		return OSQLite_UNKNOWN_ERR;
	
	int nRet = sqlite3_reset(m_pStmt);
	if ( SQLITE_OK != nRet )
		return OSQLite_UNKNOWN_ERR;
	
	int iRow = 0, iField, nFields = m_vnFieldTypes.GetSize();
	int nRowsOld = wks.GetNumRows();
	while ( (nRet = sqlite3_step(m_pStmt)) == SQLITE_ROW ) //has more rows
	{
		if ( iRow >= wks.GetNumRows() )
		{
			wks.SetSize(iRow + WKS_RESIZE_STEP, -1);
		}
		
		for ( iField = 0; iField < nFields; iField++ )
		{
			switch(m_vnFieldTypes[iField])
			{
			case SQLITE_INTEGER:
				if ( m_dwCtrl & SQLITE_INTEGER_AS_64BITS ) //get integer as 64bits
				{
					sqlite3_int64 nVal = sqlite3_column_int64(m_pStmt, iField);
					char buffer[MAX_BUFFER_SIZE];
					sprintf(buffer, "%I64d", nVal);
					wks.SetCell(iRow, m_vnColumnIndex[iField], buffer, false);
				}
				else //get as 32bits
				{
					int nVal = sqlite3_column_int(m_pStmt, iField);
					wks.SetCell(iRow, m_vnColumnIndex[iField], nVal);
				}
				break;
				
			case SQLITE_FLOAT:
				double dVal = sqlite3_column_double(m_pStmt, iField);
				wks.SetCell(iRow, m_vnColumnIndex[iField], dVal);
				break;
				
			case SQLITE_BLOB:
 				int nSize = sqlite3_column_bytes(m_pStmt, iField);
				byte* bVals = (byte*)sqlite3_column_blob(m_pStmt, iField);
				vector<byte> vb(nSize);
				memcpy(vb, bVals, sizeof(byte) * nSize);
				vector<string> vs;
				convert_byte_vector_to_string_vector(vb, vs);
				string strVal;
				strVal.SetTokens(vs, BLOB_VALUE_SEP);
				wks.SetCell(iRow, m_vnColumnIndex[iField], strVal, false);
				break;
				
			case SQLITE_NULL:
				wks.SetCell(iRow, m_vnColumnIndex[iField], get_missing_value());
				break;
				
			case SQLITE_TEXT:
				LPCSTR pVal = sqlite3_column_text(m_pStmt, iField);
				wks.SetCell(iRow, m_vnColumnIndex[iField], pVal, false);
				break;
				
			default:
				ASSERT(FALSE);
				break;
			}
		}
		iRow++;
	}	
	if ( iRow > nRowsOld )
	{
		wks.SetSize(iRow, -1);
	}
	return OSQLite_OK;
}

int		OSQLite::Import(Worksheet& wks, int iCol, int iField, DWORD dwCtrl)
{
	m_dwCtrl = dwCtrl;
	if ( !IsOpen() )
		return OSQLite_INVALID_DB_FILE;
	
	if ( m_pStmt == NULL )
		return OSQLite_INVALID_RESULT_SET;
	
	if ( !wks )
		OSQLite_INVALID_ORIGIN_OBJECT;
	
	Column colObj(wks, iCol);
	if ( !colObj || iField >= m_vnFieldTypes.GetSize() )
		return OSQLite_INVALID_INDEX;
	
	int nRet = sqlite3_reset(m_pStmt);
	if ( SQLITE_OK != nRet )
		return ShowError(OSQLite_UNKNOWN_ERR);
	
	if ( SQLITE_FLOAT == m_vnFieldTypes[iField] )
	{
		colObj.SetFormat(OKCOLTYPE_NUMERIC);
		colObj.SetInternalDataType(FSI_DOUBLE);
	}
	else if ( SQLITE_INTEGER == m_vnFieldTypes[iField] )
	{
		colObj.SetFormat(OKCOLTYPE_TEXT);
	}
	
	int iRow = 0;
	int nRowsOld = wks.GetNumRows();
	while ( (nRet = sqlite3_step(m_pStmt)) == SQLITE_ROW )
	{
		if ( iRow >= wks.GetNumRows() )
		{
			wks.SetSize(iRow + WKS_RESIZE_STEP, -1);
		}
		
		switch(m_vnFieldTypes[iField])
		{
		case SQLITE_INTEGER:
			if ( m_dwCtrl & SQLITE_INTEGER_AS_64BITS )
			{
				sqlite3_int64 nVal = sqlite3_column_int64(m_pStmt, iField);
				char buffer[MAX_BUFFER_SIZE];
				sprintf(buffer, "%I64d", nVal);
				wks.SetCell(iRow, iCol, buffer);
			}
			else
			{
				int nVal = sqlite3_column_int(m_pStmt, iField);
				wks.SetCell(iRow, iCol, nVal);
			}
			break;
			
		case SQLITE_FLOAT:
			double dVal = sqlite3_column_double(m_pStmt, iField);
			wks.SetCell(iRow, iCol, dVal);
			break;
			
		case SQLITE_BLOB:
			int nSize = sqlite3_column_bytes(m_pStmt, iField);
			byte* bVals = (byte*)sqlite3_column_blob(m_pStmt, iField);
			vector<byte> vb(nSize);
			memcpy(vb, bVals, sizeof(byte) * nSize);
			vector<string> vs;
			convert_byte_vector_to_string_vector(vb, vs);
			string strVal;
			strVal.SetTokens(vs, BLOB_VALUE_SEP);
			wks.SetCell(iRow, iCol, strVal);
			break;
			
		case SQLITE_NULL:
			wks.SetCell(iRow, iCol, get_missing_value());
			break;
			
		case SQLITE_TEXT:
			LPCSTR pVal = sqlite3_column_text(m_pStmt, iField);
			wks.SetCell(iRow, iCol, pVal);
			break;
			
		default:
			ASSERT(FALSE);
			break;
		}
		iRow++;
	}
	if ( iRow > nRowsOld )
	{
		wks.SetSize(iRow, -1);
	}
	return OSQLite_OK;
}

#define	STR_SQL_INSERT	"insert into "
#define	STR_SQL_VALUES	" values("
#define	STR_PARAM_TOKEN	"?,"
#define	STR_SQL_END		")"
bool	_construct_insert_sql_statement(string& strSQL, LPCSTR lpcszTable, int nParams)
{
	strSQL = STR_SQL_INSERT;
	strSQL += lpcszTable;
	strSQL += STR_SQL_VALUES;
	for ( int iPara = 0; iPara < nParams; iPara++ )
	{
		strSQL += STR_PARAM_TOKEN;
	}
	strSQL.TrimRight(','); //remove last ','
	strSQL += STR_SQL_END;
	return true;
}

#define	STR_SQL_DELETE	"delete from "
bool	_construct_delete_sql_statement(string& strSQL, LPCSTR lpcszTable)
{
	strSQL = STR_SQL_DELETE;
	strSQL += lpcszTable;
	return true;
}
static bool	_memory_alloc(char*** pppVal, int nCols, int nBufferSize)
{
	*pppVal = (char**)malloc(sizeof(DWORD) * nCols);
	char** pTemp = *pppVal;
	for ( int ii = 0; ii < nCols; ii++ )
	{
		pTemp[ii] = (char*)malloc(sizeof(char) * nBufferSize);
	}
	return true;
}

static bool _memory_free(char*** pppVal, int nCols)
{
	char** pTemp = *pppVal;
	for ( int ii = 0; ii < nCols; ii++ )
		free(pTemp[ii]);
	free(pTemp);
	return true;
}

static bool	_strncpy(char* Dst, const char* Src, int nSize)
{
	strncpy(Dst, Src, nSize);
	Dst[nSize] = '\0';
	return true;
}
int		OSQLite::Export(LPCSTR lpcszTable, Worksheet& wks, int iColStart, int iColEnd, int iRowStart, int iRowEnd, DWORD dwCtrl) //0, -1, 0, -1, 0
{
	if ( !IsOpen() )
		return OSQLite_INVALID_DB_FILE;
	
	if ( !wks )
		return OSQLite_INVALID_ORIGIN_OBJECT;
	
	if ( iColEnd < 0 )
		iColEnd = wks.GetNumCols() - 1;
	if ( iRowEnd < 0 )
	{
		//iRowEnd = wks.GetNumRows() - 1;
		int nJunkR1;
		wks.GetRange(nJunkR1, iRowEnd); //don't get empty rows at the end of the worksheet.
	}
	if ( iColEnd < iColStart || iRowEnd < iRowStart || iColStart < 0 || iRowStart < 0 || iColEnd >= wks.GetNumCols() || iRowEnd >= wks.GetNumRows() )
		return OSQLite_INVALID_INDEX;
	
	m_dwCtrl = dwCtrl;
	if ( m_dwCtrl & SQLITE_CLEAR_DATA_BEFORE_EXPORT )
	{
		string strDelSQL;
		_construct_delete_sql_statement(strDelSQL, lpcszTable);
		int nRet = sqlite3_exec(m_pDB, strDelSQL, NULL, NULL, NULL);
		if ( nRet != SQLITE_OK )
			return ShowError(OSQLite_UNKNOWN_ERR);
	}
	
	const int nCols = iColEnd - iColStart + 1;
	string strSQL;
	_construct_insert_sql_statement(strSQL, lpcszTable, nCols);
	
	if ( m_pStmt )
	{
		sqlite3_finalize(m_pStmt);
		m_pStmt = NULL;
	}
	
	char* pEnd;
	int nRet = sqlite3_prepare(m_pDB, strSQL, strSQL.GetLength(), &m_pStmt, &pEnd);
	if ( nRet != SQLITE_OK )
		return OSQLite_UNKNOWN_ERR;

	if ( DetectFieldTypes(lpcszTable) != OSQLite_OK )
		return OSQLite_UNKNOWN_ERR;
	
	sqlite3_exec(m_pDB, "BEGIN", NULL, NULL, NULL);
	char** ppVals;
	_memory_alloc(&ppVals, nCols, MAX_FIELD_LEN);
	while ( iRowStart <= iRowEnd )
	{
		int iField = 0;
		string strVal;
		for ( int iCol = iColStart; iCol <= iColEnd; iCol++ )
		{
			wks.GetCell(iRowStart, iCol, strVal);
			_strncpy(ppVals[iField], strVal, strVal.GetLength());
			if ( m_vnFieldTypes[iField] == SQLITE_BLOB ) //values separated by ',' in cell
			{
				vector<string> vs;
				strVal.GetTokens(vs, BLOB_VALUE_SEP);
				vector<byte> vb;
				if ( vs.GetSize() == 1 ) //only one token, no need to cvt
					vs[0].GetBytes(vb);
				else
					convert_string_vector_to_byte_vector(vs, vb);
				
				memcpy(ppVals[iField], vb, vb.GetSize());
				nRet = sqlite3_bind_blob(m_pStmt, iField + 1, ppVals[iField], vb.GetSize(), SQLITE_STATIC);
			}
			else if ( strVal.GetLength() == 0 )
			{
				nRet = sqlite3_bind_null(m_pStmt, iField + 1);
			}
			else	//m_vnFieldTypes[iField] == SQLITE_TEXT, SQLITE_INTEGER, SQLITE_FLOAT
			{
				//thanks to sqlite's dynamic type support, we can set any type of value as text
				nRet = sqlite3_bind_text(m_pStmt, iField + 1, ppVals[iField], strVal.GetLength(), SQLITE_STATIC);
			}
			if ( nRet != SQLITE_OK )
				return OSQLite_UNKNOWN_ERR;
			
			iField++;
		}
		sqlite3_step(m_pStmt);
		nRet = sqlite3_reset(m_pStmt);
		if ( nRet != SQLITE_OK )
			return ShowError(OSQLite_UNKNOWN_ERR);
		
		iRowStart++;
	}
	sqlite3_exec(m_pDB, "COMMIT", NULL, NULL, NULL);
	_memory_free(&ppVals, nCols);
	return OSQLite_OK;
}

int		OSQLite::DetectFieldTypes(LPCSTR lpcszTableName)
{
	string strQuery = "select * from ";
	strQuery += lpcszTableName;
	strQuery += " limit 1";
	
	sqlite3_stmt* pStmt; //temporary statement handle.
	char* pEnd;
	int nRet = sqlite3_prepare(m_pDB, strQuery, strQuery.GetLength(), &pStmt, &pEnd);
	if ( nRet != SQLITE_OK )
		return OSQLite_UNKNOWN_ERR;
	
	int nFields = sqlite3_column_count(pStmt);
	m_vnFieldTypes.SetSize(nFields);
	for ( int iField = 0; iField < nFields; iField++ )
	{
		LPCSTR lpType = sqlite3_column_decltype(pStmt, iField);
		if ( strcmp(lpType, STR_SQLITE_TYPE_TEXT) == 0 )
			m_vnFieldTypes[iField] = SQLITE_TEXT;
		else if ( strcmp(lpType, STR_SQLITE_TYPE_REAL) == 0 )
			m_vnFieldTypes[iField] = SQLITE_FLOAT;
		else if ( strcmp(lpType, STR_SQLITE_TYPE_INTEGER) == 0 )
			m_vnFieldTypes[iField] = SQLITE_INTEGER;
		else if ( strcmp(lpType, STR_SQLITE_TYPE_NONE) == 0 )
			m_vnFieldTypes[iField] = SQLITE_NULL;
		else if ( strcmp(lpType, STR_SQLITE_TYPE_BLOB) == 0 )
			m_vnFieldTypes[iField] = SQLITE_BLOB;
		else if ( strcmp(lpType, STR_SQLITE_TYPE_NUMERIC) == 0 )
			m_vnFieldTypes[iField] = SQLITE_TEXT;	
	}
	sqlite3_finalize(pStmt);
	return OSQLite_OK;
}

BOOL	OSQLite::InitFields()
{
	if ( m_pStmt == NULL )
		return FALSE;
	
	int nFields = sqlite3_column_count(m_pStmt);
	m_vnFieldTypes.SetSize(nFields);
	m_vsFieldNames.SetSize(nFields);
	
	if ( sqlite3_step(m_pStmt) != SQLITE_ROW ) //fetch one row as to get field type
		return FALSE;
	
	for ( int iField = 0; iField < nFields; iField++ )
	{
		m_vnFieldTypes[iField] = sqlite3_column_type(m_pStmt, iField);
		m_vsFieldNames[iField] = sqlite3_column_name(m_pStmt, iField);
	}
	return TRUE;
}

BOOL	OSQLite::InitWorksheet(Worksheet& wks)
{
	if ( !wks )
		return FALSE;
	
	int nFields = m_vnFieldTypes.GetSize();
	m_vnColumnIndex.SetSize(nFields);
	
	wks.SetSize(-1, nFields);
	for ( int iField = 0; iField < nFields; iField++ )
	{
		m_vnColumnIndex[iField] = FindColumnForField(wks, iField);
		Column colObj(wks, m_vnColumnIndex[iField]);
		if ( m_vnFieldTypes[iField] == SQLITE_FLOAT )
		{
			colObj.SetFormat(OKCOLTYPE_NUMERIC);
			colObj.SetInternalDataType(FSI_DOUBLE);
		}
		else if ( m_vnFieldTypes[iField] == SQLITE_INTEGER )
		{
			if ( m_dwCtrl & SQLITE_INTEGER_AS_64BITS )
			{
				colObj.SetFormat(OKCOLTYPE_TEXT);
			}
			else
			{
				colObj.SetFormat(OKCOLTYPE_NUMERIC);
				colObj.SetInternalDataType(FSI_LONG);
			}
		}
		colObj.SetLongName(m_vsFieldNames[iField]);
	}
	wks.SetName(sqlite3_column_table_name(m_pStmt, 0));
	return TRUE;
}

int		OSQLite::FindColumnForField(Worksheet& wks, int iField)
{
	if ( iField >= m_vnFieldTypes.GetSize() )
		return OSQLite_INVALID_INDEX;
	int iCol, nCols = wks.GetNumCols();
	int iFirstEmptyLongName = -1;
	for ( iCol = 0; iCol < nCols; iCol++ )
	{
		Column col(wks, iCol);
		string strLN = col.GetLongName();
		if ( (m_dwCtrl & SQLITE_MATCH_COLUMN_BY_LONGNAME) && strLN.CompareNoCase(m_vsFieldNames[iField]) == 0 )
			return iCol;
		else if ( strLN.IsEmpty() && iFirstEmptyLongName < 0 )
			iFirstEmptyLongName = iCol;
	}
	if ( iFirstEmptyLongName < 0 ) //need to add new columns
	{
		wks.SetSize(-1, nCols + 1);
		iFirstEmptyLongName = nCols; 
	}
	return iFirstEmptyLongName;
}

int		OSQLite::ShowError(int nRetCode)
{
	LPCSTR err = sqlite3_errmsg(m_pDB);
	printf("Error : %s\n", err);
	return nRetCode;
}
//Usage:
//This example import data from PINGVELOCITES and export to Originlab, which is a new table created with the following sql statement:
//CREATE TABLE OriginLab(ID INTEGER NOT NULL, NUMBER INTEGER NOT NULL, SALARY INTE
//GER NOT NULL, Data BLOB NOT NULL);
//#define	STR_DATABASE_FILE	"E:\\DataSet1.1.db"
//#define	STR_QUERY_STRING	"select * from PINGVELOCITIES limit 80"
//void test_OSQLite()
//{
	//OSQLite sqlObj(STR_DATABASE_FILE);
	//
	//LPCSTR lpSQL = STR_QUERY_STRING;
	//sqlObj.Select(lpSQL);
	//Worksheet wks;
	//wks.Create("Origin");
	//sqlObj.Import(wks);
	//sqlObj.Export("OriginLab", wks);
//}
#endif	//_OSQLITE_H_