/* * SQLite3 Database Driver * * Copyright 2010 Viktor Szakats (vszakats.net/harbour) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file LICENSE.txt. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/). * * As a special exception, the Harbour Project gives permission for * additional uses of the text contained in its release of Harbour. * * The exception is that, if you link the Harbour libraries with other * files to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public License. * Your use of that executable is in no way restricted on account of * linking the Harbour library code into it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU General Public License. * * This exception applies only to the code released by the Harbour * Project under the name Harbour. If you copy code from other * Harbour Project or Free Software Foundation releases into a copy of * Harbour, as the General Public License permits, the exception does * not apply to the code that you add in this way. To avoid misleading * anyone as to the status of such modified files, you must delete * this exception notice from them. * * If you write modifications of your own for Harbour, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. * */ #include "hbrddsql.h" #include "hbapiitm.h" #include "hbapistr.h" #include "hbapicdp.h" #include "hbdate.h" #include "hbset.h" #include "hbvm.h" #include "hbset.h" #include #define S_HB_ARRAYGETSTR( arr, n, phstr, plen ) hb_arrayGetStrUTF8( arr, n, phstr, plen ) #define S_HB_ITEMCOPYSTR( itm, str, len ) hb_itemCopyStrUTF8( itm, str, len ) #define S_HB_ITEMGETSTR( itm, phstr, plen ) hb_itemGetStrUTF8( itm, phstr, plen ) #define S_HB_ITEMPUTSTR( itm, str ) hb_itemPutStrUTF8( itm, str ) #define S_HB_ITEMPUTSTRLEN( itm, str, len ) hb_itemPutStrLenUTF8( itm, str, len ) typedef struct { sqlite3 * pDb; } SDDCONN; typedef struct { sqlite3_stmt * pStmt; } SDDDATA; static HB_ERRCODE sqlite3Connect( SQLDDCONNECTION * pConnection, PHB_ITEM pItem ); static HB_ERRCODE sqlite3Disconnect( SQLDDCONNECTION * pConnection ); static HB_ERRCODE sqlite3Execute( SQLDDCONNECTION * pConnection, PHB_ITEM pItem ); static HB_ERRCODE sqlite3Open( SQLBASEAREAP pArea ); static HB_ERRCODE sqlite3Close( SQLBASEAREAP pArea ); static HB_ERRCODE sqlite3GoTo( SQLBASEAREAP pArea, HB_ULONG ulRecNo ); static SDDNODE s_sqlt3dd = { NULL, "SQLITE3", ( SDDFUNC_CONNECT ) sqlite3Connect, ( SDDFUNC_DISCONNECT ) sqlite3Disconnect, ( SDDFUNC_EXECUTE ) sqlite3Execute, ( SDDFUNC_OPEN ) sqlite3Open, ( SDDFUNC_CLOSE ) sqlite3Close, ( SDDFUNC_GOTO ) sqlite3GoTo, ( SDDFUNC_GETVALUE ) NULL, ( SDDFUNC_GETVARLEN ) NULL }; static void hb_sqlt3dd_init( void * cargo ) { HB_SYMBOL_UNUSED( cargo ); #if SQLITE_VERSION_NUMBER >= 3006000 sqlite3_initialize(); #endif if( ! hb_sddRegister( &s_sqlt3dd ) ) hb_errInternal( HB_EI_RDDINVALID, NULL, NULL, NULL ); } static void hb_sqlt3dd_exit( void * cargo ) { HB_SYMBOL_UNUSED( cargo ); #if SQLITE_VERSION_NUMBER >= 3006000 sqlite3_shutdown(); #endif } HB_FUNC( HB_SDDSQLITE3_REGISTER ) { hb_sqlt3dd_init( NULL ); } /* force SQLBASE linking */ HB_FUNC_TRANSLATE( SDDSQLITE3, SQLBASE ) HB_INIT_SYMBOLS_BEGIN( sqlt3dd__InitSymbols ) { "SDDSQLITE3", { HB_FS_PUBLIC }, { HB_FUNCNAME( SDDSQLITE3 ) }, NULL }, HB_INIT_SYMBOLS_END( sqlt3dd__InitSymbols ) HB_CALL_ON_STARTUP_BEGIN( _hb_sqlt3dd_init_ ) hb_vmAtInit( hb_sqlt3dd_init, NULL ); hb_vmAtExit( hb_sqlt3dd_exit, NULL ); HB_CALL_ON_STARTUP_END( _hb_sqlt3dd_init_ ) #if defined( HB_PRAGMA_STARTUP ) #pragma startup sqlt3dd__InitSymbols #pragma startup _hb_sqlt3dd_init_ #elif defined( HB_DATASEG_STARTUP ) #define HB_DATASEG_BODY \ HB_DATASEG_FUNC( sqlt3dd__InitSymbols ) \ HB_DATASEG_FUNC( _hb_sqlt3dd_init_ ) #include "hbiniseg.h" #endif /* --- */ static HB_USHORT hb_errRT_SQLT3DD( HB_ERRCODE errGenCode, HB_ERRCODE errSubCode, const char * szDescription, const char * szOperation, HB_ERRCODE errOsCode ) { PHB_ITEM pError; HB_USHORT uiAction; pError = hb_errRT_New( ES_ERROR, "SDDSQLITE3", errGenCode, errSubCode, szDescription, szOperation, errOsCode, EF_NONE ); uiAction = hb_errLaunch( pError ); hb_itemRelease( pError ); return uiAction; } static char * sqlite3GetError( sqlite3 * pDb, HB_ERRCODE * pErrCode ) { char * szRet; int iNativeErr; if( pDb ) { PHB_ITEM pRet = S_HB_ITEMPUTSTR( NULL, sqlite3_errmsg( pDb ) ); szRet = hb_strdup( hb_itemGetCPtr( pRet ) ); hb_itemRelease( pRet ); iNativeErr = sqlite3_errcode( pDb ); } else { szRet = hb_strdup( "Could not get the error message" ); iNativeErr = 9999; } if( pErrCode ) *pErrCode = ( HB_ERRCODE ) iNativeErr; return szRet; } static HB_USHORT sqlite3DeclType(sqlite3_stmt * st, HB_USHORT uiIndex ) { const char * szDeclType; szDeclType = sqlite3_column_decltype( st, uiIndex ); /* the order of comparisons below is important to replicate * type precedence used by SQLITE3 */ if( szDeclType != NULL ) { HB_SIZE nLen = strlen( szDeclType ); if( hb_strAtI( "INT", 3, szDeclType, nLen ) != 0 ) return HB_FT_INTEGER; if( hb_strAtI( "CHAR", 4, szDeclType, nLen ) != 0 || hb_strAtI( "TEXT", 4, szDeclType, nLen ) != 0 || hb_strAtI( "CLOB", 4, szDeclType, nLen ) != 0 ) return HB_FT_STRING; if( hb_strAtI( "BLOB", 4, szDeclType, nLen ) != 0 ) return HB_FT_ANY; if( hb_strAtI( "REAL", 4, szDeclType, nLen ) != 0 || hb_strAtI( "FLOA", 4, szDeclType, nLen ) != 0 || hb_strAtI( "DOUB", 4, szDeclType, nLen ) != 0 ) return HB_FT_LONG; /* logically HB_FT_DOUBLE, what was the idea? */ #ifdef HB_SQLT3_MAP_DECLARED_EMULATED /* types not handled in a specific way by SQLITE3 * but anyway we should try to look at declarations */ if( hb_strAtI( "TIME", 4, szDeclType, nLen ) != 0 ) return HB_FT_TIMESTAMP; if( hb_strAtI( "DATE", 4, szDeclType, nLen ) != 0 ) return HB_FT_DATE; if( hb_strAtI( "NUME", 4, szDeclType, nLen ) != 0 || hb_strAtI( "NUMB", 4, szDeclType, nLen ) != 0 ) return HB_FT_LONG; #endif } #ifdef HB_SQLT3_MAP_UNDECLARED_TYPES_AS_ANY return HB_FT_ANY; #else switch( sqlite3_column_type( st, uiIndex ) ) { case SQLITE_TEXT: return HB_FT_STRING; case SQLITE_FLOAT: return HB_FT_LONG; case SQLITE_INTEGER: return HB_FT_INTEGER; case SQLITE_BLOB: return HB_FT_BLOB; case SQLITE_NULL: return HB_FT_ANY; } return HB_FT_NONE; #endif } #ifdef HB_SQLT3_MAP_DECLARED_EMULATED static void sqlite3DeclStru( sqlite3_stmt * st, HB_USHORT uiIndex, HB_USHORT * puiLen, HB_USHORT * puiDec ) { const char * szDeclType = sqlite3_column_decltype( st, uiIndex ); if( szDeclType != NULL ) { HB_SIZE nLen = strlen( szDeclType ); HB_SIZE nAt; int iOverflow; HB_MAXINT iRetLen = 0; /* SQLite doesn't normally have field size limits, * but column declarations are freeform - let's * try some really stupid guesswork on schema... */ if( ( nAt = hb_strAt( "(", 1, szDeclType, nLen ) ) > 0 ) { if( puiLen ) { iRetLen = hb_strValInt( szDeclType + nAt, &iOverflow ); if( ! puiDec || ( iRetLen > 0 && iRetLen < 100 ) ) * puiLen = ( HB_USHORT ) iRetLen; } if( ! puiDec ) return; if( * puiLen < 2 ) * puiDec = 0; else if( puiLen && ( nAt = hb_strAt( ",", 1, szDeclType + nAt, nLen - nAt - 1 ) ) > 0 ) { if( ( iRetLen = hb_strValInt( szDeclType + nAt, &iOverflow ) ) > 0 ) { * puiDec = ( HB_USHORT ) HB_MIN( * puiLen - 1, iRetLen ); /* SQL column declaration doesn't include space for * decimal separator, while xBase stores it. */ * puiLen = ( HB_USHORT ) ++iRetLen; } else if( iRetLen == 0 ) * puiDec = 0; } } } } #endif /* --- SDD METHODS --- */ static HB_ERRCODE sqlite3Connect( SQLDDCONNECTION * pConnection, PHB_ITEM pItem ) { sqlite3 * db; void * hConn; if( sqlite3_open( S_HB_ARRAYGETSTR( pItem, 2, &hConn, NULL ), &db ) == SQLITE_OK ) { pConnection->pSDDConn = hb_xgrab( sizeof( SDDCONN ) ); ( ( SDDCONN * ) pConnection->pSDDConn )->pDb = db; } else sqlite3_close( db ); hb_strfree( hConn ); return db ? HB_SUCCESS : HB_FAILURE; } static HB_ERRCODE sqlite3Disconnect( SQLDDCONNECTION * pConnection ) { HB_ERRCODE errCode; errCode = sqlite3_close( ( ( SDDCONN * ) pConnection->pSDDConn )->pDb ) == SQLITE_OK ? HB_SUCCESS : HB_FAILURE; if( errCode == HB_SUCCESS ) hb_xfree( pConnection->pSDDConn ); return errCode; } static HB_ERRCODE sqlite3Execute( SQLDDCONNECTION * pConnection, PHB_ITEM pItem ) { sqlite3 * pDb = ( ( SDDCONN * ) pConnection->pSDDConn )->pDb; HB_ERRCODE errCode; int iRow, iCol; void * hStatement; char ** pResult = NULL; char * pszErrMsg = NULL; if( sqlite3_get_table( pDb, S_HB_ITEMGETSTR( pItem, &hStatement, NULL ), &pResult, &iRow, &iCol, &pszErrMsg ) != SQLITE_OK ) { hb_strfree( hStatement ); hb_xfree( sqlite3GetError( pDb, &errCode ) ); hb_errRT_SQLT3DD( EG_OPEN, ESQLDD_STMTALLOC, pszErrMsg, hb_itemGetCPtr( pItem ), errCode ); hb_xfree( pszErrMsg ); return HB_FAILURE; } else hb_strfree( hStatement ); sqlite3_free_table( pResult ); /* TODO: new id */ hb_rddsqlSetError( 0, NULL, hb_itemGetCPtr( pItem ), NULL, ( unsigned long ) iRow ); return HB_SUCCESS; } static HB_ERRCODE sqlite3Open( SQLBASEAREAP pArea ) { sqlite3 * pDb = ( ( SDDCONN * ) pArea->pConnection->pSDDConn )->pDb; sqlite3_stmt * st = NULL; SDDDATA * pSDDData; const char * pszQuery; HB_SIZE nQueryLen; void * hQuery; HB_USHORT uiFields, uiIndex; PHB_ITEM pItemEof, pItem, pName = NULL; HB_ERRCODE errCode; char * szError; HB_BOOL bError; int iStatus, result; pArea->pSDDData = memset( hb_xgrab( sizeof( SDDDATA ) ), 0, sizeof( SDDDATA ) ); pSDDData = ( SDDDATA * ) pArea->pSDDData; pItem = hb_itemPutC( NULL, pArea->szQuery ); pszQuery = S_HB_ITEMGETSTR( pItem, &hQuery, &nQueryLen ); #if SQLITE_VERSION_NUMBER >= 3020000 result = sqlite3_prepare_v3( pDb, pszQuery, ( int ) nQueryLen, 0, &st, NULL ); #else result = sqlite3_prepare_v2( pDb, pszQuery, ( int ) nQueryLen, &st, NULL ); #endif if( result != SQLITE_OK ) { hb_strfree( hQuery ); hb_itemRelease( pItem ); szError = sqlite3GetError( pDb, &errCode ); hb_errRT_SQLT3DD( EG_OPEN, ESQLDD_INVALIDQUERY, szError, pArea->szQuery, errCode ); sqlite3_finalize( st ); hb_xfree( szError ); return HB_FAILURE; } else { hb_strfree( hQuery ); hb_itemRelease( pItem ); } if( ( iStatus = sqlite3_step( st ) ) == SQLITE_DONE ) pArea->fFetched = HB_TRUE; else if( iStatus != SQLITE_ROW ) { szError = sqlite3GetError( pDb, &errCode ); hb_errRT_SQLT3DD( EG_OPEN, ESQLDD_INVALIDQUERY, szError, pArea->szQuery, errCode ); sqlite3_finalize( st ); hb_xfree( szError ); return HB_FAILURE; } uiFields = ( HB_USHORT ) sqlite3_column_count( st ); SELF_SETFIELDEXTENT( &pArea->area, uiFields ); errCode = 0; bError = HB_FALSE; pItemEof = hb_itemArrayNew( uiFields ); for( uiIndex = 0; uiIndex < uiFields; ++uiIndex ) { DBFIELDINFO dbFieldInfo; #ifdef HB_SQLT3_FIELDNAME_STRICT HB_SIZE nPos; #endif memset( &dbFieldInfo, 0, sizeof( dbFieldInfo ) ); pName = S_HB_ITEMPUTSTR( pName, sqlite3_column_name( st, uiIndex ) ); #ifdef HB_SQLT3_FIELDNAME_STRICT /* WA->T.FIELD syntax is not valid, but FieldPos("t.field") is OK */ if( ( nPos = hb_strAt( ".", 1, hb_itemGetCPtr( pName ), hb_itemGetCLen( pName ) ) ) != 0 ) dbFieldInfo.atomName = hb_itemGetCPtr( pName ) + nPos; else #endif dbFieldInfo.atomName = hb_itemGetCPtr( pName ); dbFieldInfo.uiType = sqlite3DeclType( st, uiIndex ); pItem = hb_arrayGetItemPtr( pItemEof, uiIndex + 1 ); /* There are no field length limits stored in the SQLite3 database, so we're resorting to setting some arbitrary default values to make apps relying on these (f.e. Browse()/GET) to behave somewhat better. For better results, update apps to untie UI metrics from any database field/value widths. [vszakats] */ switch( dbFieldInfo.uiType ) { case HB_FT_STRING: { HB_SIZE nSize = hb_cdpUTF8StringLength( ( const char * ) sqlite3_column_text( st, uiIndex ), sqlite3_column_bytes( st, uiIndex ) ); /* sqlite3_column_bytes() returns variable lengths for UTF-8 strings - *_bytes16() UTF-16 could do that too, but not for mostly used character sets. Yet seems better to use hb_cdpUTF8StringLength() */ char * pStr; HB_USHORT uiRetLen = 10; #ifdef HB_SQLT3_MAP_DECLARED_EMULATED sqlite3DeclStru( st, uiIndex, &uiRetLen, NULL ); #endif dbFieldInfo.uiLen = ( HB_USHORT ) HB_MAX( nSize, ( HB_SIZE ) uiRetLen ); pStr = ( char * ) hb_xgrab( ( HB_SIZE ) dbFieldInfo.uiLen + 1 ); memset( pStr, ' ', dbFieldInfo.uiLen ); hb_itemPutCLPtr( pItem, pStr, dbFieldInfo.uiLen ); break; } case HB_FT_BLOB: dbFieldInfo.uiLen = 4; hb_itemPutC( pItem, NULL ); break; case HB_FT_INTEGER: dbFieldInfo.uiLen = 8; hb_itemPutNInt( pItem, 0 ); break; case HB_FT_LONG: dbFieldInfo.uiLen = 20; dbFieldInfo.uiDec = ( HB_USHORT ) hb_setGetDecimals(); #ifdef HB_SQLT3_MAP_DECLARED_EMULATED sqlite3DeclStru( st, uiIndex, &dbFieldInfo.uiLen, &dbFieldInfo.uiDec ); #endif hb_itemPutNDDec( pItem, 0.0, dbFieldInfo.uiDec ); break; #ifdef HB_SQLT3_MAP_DECLARED_EMULATED case HB_FT_DATE: dbFieldInfo.uiLen = 8; hb_itemPutDS( pItem, NULL ); break; case HB_FT_TIMESTAMP: dbFieldInfo.uiLen = 8; hb_itemPutTDT( pItem, 0, 0 ); break; #endif case HB_FT_ANY: dbFieldInfo.uiLen = 6; break; default: bError = HB_TRUE; } if( ! bError ) bError = ( SELF_ADDFIELD( &pArea->area, &dbFieldInfo ) == HB_FAILURE ); if( bError ) break; } hb_itemRelease( pName ); if( bError ) { hb_itemRelease( pItemEof ); sqlite3_finalize( st ); hb_errRT_SQLT3DD( EG_CORRUPTION, ESQLDD_INVALIDFIELD, "Invalid field type", pArea->szQuery, errCode ); return HB_FAILURE; } pArea->ulRecCount = 0; pArea->ulRecMax = SQLDD_ROWSET_INIT; pArea->pRow = ( void ** ) hb_xgrab( SQLDD_ROWSET_INIT * sizeof( void * ) ); pArea->pRowFlags = ( HB_BYTE * ) hb_xgrab( SQLDD_ROWSET_INIT * sizeof( HB_BYTE ) ); pArea->pRow[ 0 ] = pItemEof; pArea->pRowFlags[ 0 ] = SQLDD_FLAG_CACHED; pSDDData->pStmt = st; return HB_SUCCESS; } static HB_ERRCODE sqlite3Close( SQLBASEAREAP pArea ) { SDDDATA * pSDDData = ( SDDDATA * ) pArea->pSDDData; if( pSDDData ) { if( pSDDData->pStmt ) sqlite3_finalize( pSDDData->pStmt ); hb_xfree( pSDDData ); pArea->pSDDData = NULL; } return HB_SUCCESS; } static HB_ERRCODE sqlite3GoTo( SQLBASEAREAP pArea, HB_ULONG ulRecNo ) { sqlite3_stmt * st = ( ( SDDDATA * ) pArea->pSDDData )->pStmt; while( ulRecNo > pArea->ulRecCount && ! pArea->fFetched ) { PHB_ITEM pArray; HB_USHORT ui; pArray = hb_itemArrayNew( pArea->area.uiFieldCount ); for( ui = 0; ui < pArea->area.uiFieldCount; ++ui ) { PHB_ITEM pItem = NULL; LPFIELD pField = pArea->area.lpFields + ui; HB_USHORT uiType = pField->uiType; if( uiType == HB_FT_ANY ) { switch( sqlite3_column_type( st, ui ) ) { case SQLITE_TEXT: uiType = HB_FT_STRING; break; case SQLITE_FLOAT: case SQLITE_INTEGER: uiType = HB_FT_LONG; break; case SQLITE_BLOB: uiType = HB_FT_BLOB; break; } } switch( uiType ) { case HB_FT_STRING: pItem = S_HB_ITEMPUTSTR( NULL, ( const char * ) sqlite3_column_text( st, ui ) ); break; case HB_FT_INTEGER: #if HB_VMLONG_MAX > INT32_MAX && ! defined( HB_LONG_LONG_OFF ) pItem = hb_itemPutNInt( NULL, sqlite3_column_int64( st, ui ) ); break; #else /* fallthrough */ #endif case HB_FT_LONG: pItem = hb_itemPutNDDec( NULL, sqlite3_column_double( st, ui ), pField->uiDec ); break; #ifdef HB_SQLT3_MAP_DECLARED_EMULATED case HB_FT_DATE: if( sqlite3_column_bytes( st, ui ) >= 10 ) { char szDate[ 9 ]; const char * pValue = ( const char * ) sqlite3_column_text( st, ui ); szDate[ 0 ] = pValue[ 0 ]; szDate[ 1 ] = pValue[ 1 ]; szDate[ 2 ] = pValue[ 2 ]; szDate[ 3 ] = pValue[ 3 ]; szDate[ 4 ] = pValue[ 5 ]; szDate[ 5 ] = pValue[ 6 ]; szDate[ 6 ] = pValue[ 8 ]; szDate[ 7 ] = pValue[ 9 ]; szDate[ 8 ] = '\0'; pItem = hb_itemPutDS( NULL, szDate ); } else if( sqlite3_column_bytes( st, ui ) == 8 ) pItem = hb_itemPutDS( NULL, ( const char * ) sqlite3_column_text( st, ui ) ); break; case HB_FT_TIMESTAMP: if( sqlite3_column_bytes( st, ui ) >= 10 ) { long lDate, lTime; const char * pValue = ( const char * ) sqlite3_column_text( st, ui ); hb_timeStampStrGetDT( pValue, &lDate, &lTime ); pItem = hb_itemPutTDT( NULL, lDate, lTime ); break; } #endif case HB_FT_BLOB: pItem = hb_itemPutCL( NULL, ( const char * ) sqlite3_column_blob( st, ui ), sqlite3_column_bytes( st, ui ) ); break; } if( pItem ) { hb_arraySetForward( pArray, ui + 1, pItem ); hb_itemRelease( pItem ); } } if( pArea->ulRecCount + 1 >= pArea->ulRecMax ) { pArea->pRow = ( void ** ) hb_xrealloc( pArea->pRow, ( pArea->ulRecMax + SQLDD_ROWSET_RESIZE ) * sizeof( void * ) ); pArea->pRowFlags = ( HB_BYTE * ) hb_xrealloc( pArea->pRowFlags, ( pArea->ulRecMax + SQLDD_ROWSET_RESIZE ) * sizeof( HB_BYTE ) ); pArea->ulRecMax += SQLDD_ROWSET_RESIZE; } pArea->ulRecCount++; pArea->pRow[ pArea->ulRecCount ] = pArray; pArea->pRowFlags[ pArea->ulRecCount ] = SQLDD_FLAG_CACHED; if( sqlite3_step( st ) != SQLITE_ROW ) { pArea->fFetched = HB_TRUE; break; } } if( ulRecNo == 0 || ulRecNo > pArea->ulRecCount ) { pArea->pRecord = pArea->pRow[ 0 ]; pArea->bRecordFlags = pArea->pRowFlags[ 0 ]; pArea->fPositioned = HB_FALSE; } else { pArea->pRecord = pArea->pRow[ ulRecNo ]; pArea->bRecordFlags = pArea->pRowFlags[ ulRecNo ]; pArea->fPositioned = HB_TRUE; } return HB_SUCCESS; }