Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contrib/hbsqlit3: Implemented SQLITE3_TRACE_V2(), SQLITE3_DB_FILENAME(), SQLITE3_EXPANDED_SQL() #343

Merged
merged 1 commit into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@
Entries may not always be in chronological/commit order.
See license at the end of file. */

2023-11-30 18:12 UTC+0100 Phil Krylov (phil a t krylov.eu)
* contrib/hbsqlit3/core.c
* contrib/hbsqlit3/hbsqlit3.ch
* contrib/hbsqlit3/hbsqlit3.hbx
* Implemented SQLITE3_DB_FILENAME() on SQLite 3.7.10+.
* Implemented SQLITE3_EXPANDED_SQL(), SQLITE3_TRACE_V2() on SQLite 3.14.0+.
; Thanks to Mindaugas Kavaliauskas for thorough reviewing!
* contrib/hbsqlit3/tests/backup.prg
* contrib/hbsqlit3/tests/demo.prg
* Updated examples to use SQLITE3_TRACE_V2() on SQLite 3.14.0+.

2023-11-22 09:06 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
* contrib/hbssl/hbssl.hbm
* contrib/hbssl/hbssl.hbx
Expand Down
171 changes: 170 additions & 1 deletion contrib/hbsqlit3/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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
Expand Down Expand Up @@ -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 );
Expand All @@ -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;
Expand Down Expand Up @@ -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 );
Expand All @@ -204,6 +225,7 @@ static HB_GARBAGE_FUNC( hb_sqlite3_mark )
{
hb_gcMark( pStructHolder->hbsqlite3->sTraceFileName );
}
#endif
}
}

Expand Down Expand Up @@ -1776,14 +1798,108 @@ 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 * hbsqlite3 = ( HB_SQLITE3 * ) hb_xgrabz( sizeof( HB_SQLITE3 ) );
HB_SYMBOL_UNUSED( x );

hbsqlite3->db = p;
hb_sqlite3_itemPut( pItem, hbsqlite3, HB_SQLITE3_DB );
hb_vmPush( pItem );
hb_vmSend( 2 );

/* We don't want sqlite3_close() called recursively
* and don't want to implement a weak reference engine yet
* so we just clear the pointer before hb_itemRelease(). */
hbsqlite3->db = NULL;
hb_itemRelease( pItem );
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 )
Expand Down Expand Up @@ -1811,9 +1927,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 )
Expand All @@ -1835,10 +1953,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 )
Expand All @@ -1860,8 +1982,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
*/
Expand Down Expand Up @@ -2360,3 +2486,46 @@ HB_FUNC( SQLITE3_CREATE_FUNCTION )
else
hb_retni( SQLITE_ERROR );
}


/**
Get database filename for given connection and database name
sqlite3_db_filename( pDb, sDbName )
*/
HB_FUNC( SQLITE3_DB_FILENAME )
{
#if SQLITE_VERSION_NUMBER >= 3007010
HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE );

if( pHbSqlite3 && pHbSqlite3->db && HB_ISCHAR( 2 ) )
hb_retc( ( const char * ) sqlite3_db_filename( pHbSqlite3->db, hb_parc( 2 ) ) );
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
}


/**
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
}
6 changes: 6 additions & 0 deletions contrib/hbsqlit3/hbsqlit3.ch
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions contrib/hbsqlit3/hbsqlit3.hbx
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ DYNAMIC sqlite3_compileoption_get
DYNAMIC sqlite3_compileoption_used
DYNAMIC sqlite3_complete
DYNAMIC sqlite3_create_function
DYNAMIC sqlite3_db_filename
DYNAMIC sqlite3_db_status
DYNAMIC sqlite3_enable_load_extension
DYNAMIC sqlite3_enable_shared_cache
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
Expand Down Expand Up @@ -116,6 +118,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 <fncs,...> => EXTERNAL <fncs>
Expand Down
29 changes: 26 additions & 3 deletions contrib/hbsqlit3/tests/backup.prg
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,38 @@

#require "hbsqlit3"

PROCEDURE init_trace( pDbDest, cPrefix )
sqlite3_trace( pDbDest, .T., cPrefix + ".log" )
#include "fileio.ch"


PROCEDURE init_trace( pDb, cPrefix )
LOCAL hFile
IF sqlite3_libversion_number() < 3014000
sqlite3_trace( pDb, .T., cPrefix + ".log" )
ELSE
hFile := FOpen( cPrefix + ".log", FO_READWRITE + HB_FO_CREAT )
FSeek( hFile, 0, FS_END )
sqlite3_trace_v2( pDb, SQLITE_TRACE_STMT + SQLITE_TRACE_CLOSE, {| nMask, p, x |
IF nMask == SQLITE_TRACE_STMT /* p is pPreparedStatement, x is cOriginalSql */
IF hb_LeftEq( x, "--" )
FWrite( hFile, x + hb_eol() )
ELSE
FWrite( hFile, sqlite3_expanded_sql( p ) + hb_eol() )
ENDIF
ELSEIF nMask == SQLITE_TRACE_CLOSE /* p is the database connection */
FWrite( hFile, "Closing the database connection: " + sqlite3_db_filename( p, "main" ) + hb_eol() )
ENDIF
RETURN 0
} )
ENDIF
RETURN

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
Expand Down Expand Up @@ -167,7 +190,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
Expand Down
Loading
Loading