diff --git a/ChangeLog.txt b/ChangeLog.txt index 99237045fe4..397bcf84ad1 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,15 @@ Entries may not always be in chronological/commit order. See license at the end of file. */ +2023-11-15 18:04 UTC+0100 Phil Krylov (phil a t krylov.eu) + * contrib/hbsqlit3/core.c + * contrib/hbsqlit3/hbsqlit3.ch + * contrib/hbsqlit3/hbsqlit3.hbx + * Implemented SQLITE3_TRACE_V2() on SQLite 3.14.0+. + * contrib/hbsqlit3/tests/backup.prg + * contrib/hbsqlit3/tests/demo.prg + * Updated examples to use SQLITE3_TRACE_V2() on SQLite 3.14.0+ + 2023-11-15 15:57 UTC+0100 Phil Krylov (phil a t krylov.eu) * contrib/hbsqlit3/tests/backup.prg + Simple change in test to provoke access to dangling pointer saved diff --git a/contrib/hbsqlit3/core.c b/contrib/hbsqlit3/core.c index 05efa6f67b3..2aee282a42b 100644 --- a/contrib/hbsqlit3/core.c +++ b/contrib/hbsqlit3/core.c @@ -82,6 +82,9 @@ static int progress_handler( void * ); static int hook_commit( void * ); static void hook_rollback( void * ); static void func( sqlite3_context *, int, sqlite3_value ** ); +#if SQLITE_VERSION_NUMBER >= 3014000 +static int trace_handler( unsigned, void *, void *, void * ); +#endif typedef struct { @@ -92,8 +95,12 @@ typedef struct PHB_ITEM cbHookCommit; PHB_ITEM cbHookRollback; PHB_ITEM cbFunc; +#if SQLITE_VERSION_NUMBER >= 3014000 + PHB_ITEM cbTraceHandler; +#else PHB_ITEM sProfileFileName; PHB_ITEM sTraceFileName; +#endif } HB_SQLITE3, * PHB_SQLITE3; typedef struct @@ -156,6 +163,13 @@ static HB_GARBAGE_FUNC( hb_sqlite3_destructor ) pStructHolder->hbsqlite3->cbFunc = NULL; } +#if SQLITE_VERSION_NUMBER >= 3014000 + if( pStructHolder->hbsqlite3->cbTraceHandler ) + { + hb_itemRelease( pStructHolder->hbsqlite3->cbTraceHandler ); + pStructHolder->hbsqlite3->cbTraceHandler = NULL; + } +#else if( pStructHolder->hbsqlite3->sProfileFileName ) { hb_itemRelease( pStructHolder->hbsqlite3->sProfileFileName ); @@ -166,6 +180,7 @@ static HB_GARBAGE_FUNC( hb_sqlite3_destructor ) hb_itemRelease( pStructHolder->hbsqlite3->sTraceFileName ); pStructHolder->hbsqlite3->sTraceFileName = NULL; } +#endif hb_xfree( pStructHolder->hbsqlite3 ); pStructHolder->hbsqlite3 = NULL; @@ -196,6 +211,12 @@ static HB_GARBAGE_FUNC( hb_sqlite3_mark ) if( pStructHolder->hbsqlite3->cbFunc ) hb_gcMark( pStructHolder->hbsqlite3->cbFunc ); +#if SQLITE_VERSION_NUMBER >= 3014000 + if( pStructHolder->hbsqlite3->cbTraceHandler ) + { + hb_gcMark( pStructHolder->hbsqlite3->cbTraceHandler ); + } +#else if( pStructHolder->hbsqlite3->sProfileFileName ) { hb_gcMark( pStructHolder->hbsqlite3->sProfileFileName ); @@ -204,6 +225,7 @@ static HB_GARBAGE_FUNC( hb_sqlite3_mark ) { hb_gcMark( pStructHolder->hbsqlite3->sTraceFileName ); } +#endif } } @@ -1776,14 +1798,101 @@ HB_FUNC( SQLITE3_ENABLE_SHARED_CACHE ) hb_retni( sqlite3_enable_shared_cache( hb_parl( 1 ) ) ); } -/* TODO: implement sqlite3_trace_v2(), that replaces both of these deprecated functions */ /** Tracing And Profiling Functions + sqlite3_trace_v2( db, nMask, [ bCallback ] ) // Starting with 3.14.0 + sqlite3_trace( db, lOnOff, [ filename ] ) // Deprecated in 3.14.0 sqlite3_profile( db, lOnOff, [ filename ] ) // Deprecated in 3.14.0 */ + +#if SQLITE_VERSION_NUMBER >= 3014000 +static int trace_handler( unsigned uType, void *cbTraceHandler, void * p, void * x ) +{ + PHB_ITEM pCallback = ( PHB_ITEM ) cbTraceHandler; + int iRes = 0; + + if( pCallback && hb_vmRequestReenter() ) + { + hb_vmPushEvalSym(); + hb_vmPush( pCallback ); + hb_vmPushNumInt( uType ); + switch( uType ) + { + case SQLITE_TRACE_STMT: + hb_vmPushPointer( p ); + hb_vmPushString( x, strlen( x ) ); + hb_vmSend( 3 ); + break; + case SQLITE_TRACE_PROFILE: + hb_vmPushPointer( p ); + hb_vmPushNumInt( *( sqlite3_uint64 * ) x ); + hb_vmSend( 3 ); + break; + case SQLITE_TRACE_ROW: + { + HB_SYMBOL_UNUSED( x ); + hb_vmPushPointer( p ); + hb_vmSend( 2 ); + break; + } + case SQLITE_TRACE_CLOSE: + { + PHB_ITEM pItem = hb_itemNew( NULL ); + hb_sqlite3_itemPut( pItem, p, HB_SQLITE3_DB ); + HB_SYMBOL_UNUSED( x ); + hb_vmPush( pItem ); + hb_vmSend( 2 ); + break; + } + } + iRes = hb_parni( -1 ); + hb_vmRequestRestore(); + } + return iRes; +} +#endif + +HB_FUNC( SQLITE3_TRACE_V2 ) +{ +#if SQLITE_VERSION_NUMBER >= 3014000 + HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE ); + + if( pHbSqlite3 && pHbSqlite3->db ) + { + unsigned uMask = ( unsigned int ) hb_parnint( 2 ); + int iRes; + + if( pHbSqlite3->cbTraceHandler ) + { + hb_itemRelease( pHbSqlite3->cbTraceHandler ); + pHbSqlite3->cbTraceHandler = NULL; + } + + if( HB_ISEVALITEM( 3 ) ) + { + pHbSqlite3->cbTraceHandler = hb_itemNew( hb_param( 3, HB_IT_EVALITEM ) ); + hb_gcUnlock( pHbSqlite3->cbTraceHandler ); + + iRes = sqlite3_trace_v2( pHbSqlite3->db, uMask, + uMask ? trace_handler : NULL, + uMask ? ( void * ) pHbSqlite3->cbTraceHandler : NULL ); + } + else + iRes = sqlite3_trace_v2( pHbSqlite3->db, 0, NULL, NULL ); + hb_retni( iRes ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#else + hb_errRT_BASE_SubstR( EG_UNSUPPORTED, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#endif +} + + +#if SQLITE_VERSION_NUMBER < 3014000 static void SQL3ProfileLog( void * sFile, const char * sProfileMsg, sqlite3_uint64 uint64 ) { if( sProfileMsg ) @@ -1811,9 +1920,11 @@ static void SQL3TraceLog( void * sFile, const char * sTraceMsg ) } } } +#endif HB_FUNC( SQLITE3_PROFILE ) { +#if SQLITE_VERSION_NUMBER < 3014000 HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE ); if( pHbSqlite3 && pHbSqlite3->db ) @@ -1835,10 +1946,14 @@ HB_FUNC( SQLITE3_PROFILE ) } else hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#else + hb_errRT_BASE_SubstR( EG_UNSUPPORTED, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#endif } HB_FUNC( SQLITE3_TRACE ) { +#if SQLITE_VERSION_NUMBER < 3014000 HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE ); if( pHbSqlite3 && pHbSqlite3->db ) @@ -1860,8 +1975,12 @@ HB_FUNC( SQLITE3_TRACE ) } else hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#else + hb_errRT_BASE_SubstR( EG_UNSUPPORTED, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#endif } + /** BLOB Import/export */ @@ -2360,3 +2479,26 @@ HB_FUNC( SQLITE3_CREATE_FUNCTION ) else hb_retni( SQLITE_ERROR ); } + + +/** + Get expanded SQL for a prepared statement + + sqlite3_expanded_sql( pPreparedStatement ) + */ +HB_FUNC( SQLITE3_EXPANDED_SQL ) +{ +#if SQLITE_VERSION_NUMBER >= 3014000 + psqlite3_stmt pStmt = ( psqlite3_stmt ) hb_parptr( 1 ); + if( pStmt ) + { + char *sql = sqlite3_expanded_sql( pStmt ); + hb_retstr_utf8( sql ); + sqlite3_free( sql ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#else + hb_errRT_BASE_SubstR( EG_UNSUPPORTED, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +#endif +} diff --git a/contrib/hbsqlit3/hbsqlit3.ch b/contrib/hbsqlit3/hbsqlit3.ch index 7088073cac8..c1408704c69 100644 --- a/contrib/hbsqlit3/hbsqlit3.ch +++ b/contrib/hbsqlit3/hbsqlit3.ch @@ -174,4 +174,10 @@ #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 +/* Trace Event Codes */ +#define SQLITE_TRACE_STMT 0x01 +#define SQLITE_TRACE_PROFILE 0x02 +#define SQLITE_TRACE_ROW 0x04 +#define SQLITE_TRACE_CLOSE 0x08 + #endif diff --git a/contrib/hbsqlit3/hbsqlit3.hbx b/contrib/hbsqlit3/hbsqlit3.hbx index 68095f12d3f..451bb529cfa 100644 --- a/contrib/hbsqlit3/hbsqlit3.hbx +++ b/contrib/hbsqlit3/hbsqlit3.hbx @@ -79,6 +79,7 @@ DYNAMIC sqlite3_errcode DYNAMIC sqlite3_errmsg DYNAMIC sqlite3_errstr DYNAMIC sqlite3_exec +DYNAMIC sqlite3_expanded_sql DYNAMIC sqlite3_extended_errcode DYNAMIC sqlite3_extended_result_codes DYNAMIC sqlite3_file_to_buff @@ -116,6 +117,7 @@ DYNAMIC sqlite3_temp_directory DYNAMIC sqlite3_threadsafe DYNAMIC sqlite3_total_changes DYNAMIC sqlite3_trace +DYNAMIC sqlite3_trace_v2 #if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBSQLIT3__REQUEST ) #uncommand DYNAMIC => EXTERNAL diff --git a/contrib/hbsqlit3/tests/backup.prg b/contrib/hbsqlit3/tests/backup.prg index a4cfe4a7215..7cf6a997045 100644 --- a/contrib/hbsqlit3/tests/backup.prg +++ b/contrib/hbsqlit3/tests/backup.prg @@ -60,8 +60,26 @@ #require "hbsqlit3" +#include "fileio.ch" + + PROCEDURE init_trace( pDbDest, cPrefix ) - sqlite3_trace( pDbDest, .T., cPrefix + ".log" ) + LOCAL hFile + IF sqlite3_libversion_number() < 3014000 + sqlite3_trace( pDbDest, .T., cPrefix + ".log" ) + ELSE + hFile := FOpen( cPrefix + ".log1", FO_READWRITE + HB_FO_CREAT ) + FSeek( hFile, 0, FS_END ) + sqlite3_trace_v2( pDbDest, SQLITE_TRACE_STMT, {| nMask, pPreparedStatement, cOriginalSql | + HB_SYMBOL_UNUSED( nMask ) + IF hb_LeftEq( cOriginalSql, "--" ) + FWrite( hFile, cOriginalSql + hb_eol() ) + ELSE + FWrite( hFile, sqlite3_expanded_sql( pPreparedStatement ) + hb_eol() ) + ENDIF + RETURN 0 + } ) + ENDIF RETURN PROCEDURE Main() @@ -69,6 +87,8 @@ PROCEDURE Main() LOCAL cFileSource := ":memory:", cFileDest := "backup.db", cSQLTEXT LOCAL pDbSource, pDbDest, pBackup, cb, nDbFlags + ? "Using SQLite3 version " + hb_NToS( sqlite3_libversion_number() ) + IF sqlite3_libversion_number() < 3006011 ErrorLevel( 1 ) RETURN @@ -167,7 +187,7 @@ STATIC FUNCTION PrepareDB( cFile ) RETURN NIL ENDIF - sqlite3_trace( pDb, .T., "backup.log" ) + init_trace( pDb, "backup" ) cSQLTEXT := "CREATE TABLE person( name TEXT, age INTEGER )" IF sqlite3_exec( pDb, cSQLTEXT ) != SQLITE_OK diff --git a/contrib/hbsqlit3/tests/demo.prg b/contrib/hbsqlit3/tests/demo.prg index f51ffb47194..38471336d09 100644 --- a/contrib/hbsqlit3/tests/demo.prg +++ b/contrib/hbsqlit3/tests/demo.prg @@ -46,6 +46,8 @@ #require "hbsqlit3" +#include "fileio.ch" + #define TRACE #define TABLE_SQL "CREATE TABLE t1( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER )" @@ -86,12 +88,34 @@ PROCEDURE t2() LOCAL nCCount, nCType, nI, nJ LOCAL aCType := { "SQLITE_INTEGER", "SQLITE_FLOAT", "SQLITE_TEXT", "SQLITE_BLOB", "SQLITE_NULL" } LOCAL aTable +#ifdef TRACE + LOCAL hTraceFile, hProfileFile +#endif IF ! Empty( db ) #ifdef TRACE - sqlite3_profile( db, .T. ) - sqlite3_trace( db, .T. ) + IF sqlite3_libversion_number() < 3014000 + sqlite3_profile( db, .T. ) + sqlite3_trace( db, .T. ) + ELSE + hTraceFile := FOpen( "hbsq3_tr.log", FO_READWRITE + HB_FO_CREAT ) + FSeek( hTraceFile, 0, FS_END ) + hProfileFile := FOpen( "hbsq3_pr.log", FO_READWRITE + HB_FO_CREAT ) + FSeek( hProfileFile, 0, FS_END ) + sqlite3_trace_v2( db, SQLITE_TRACE_STMT + SQLITE_TRACE_PROFILE, {| nMask, pStmt, x | + IF nMask == SQLITE_TRACE_STMT + IF hb_LeftEq( x, "--" ) + FWrite( hTraceFile, x + hb_eol() ) + ELSE + FWrite( hTraceFile, sqlite3_expanded_sql( pStmt ) + hb_eol() ) + ENDIF + ELSEIF nMask == SQLITE_TRACE_PROFILE + FWrite( hProfileFile, sqlite3_expanded_sql( pStmt ) + " - " + hb_NToS( x ) + hb_eol() ) + ENDIF + RETURN 0 + } ) + ENDIF #endif sqlite3_exec( db, "PRAGMA auto_vacuum=0" ) sqlite3_exec( db, "PRAGMA page_size=4096" )