A little DDT program to show how to interrupt long running queries
when using SQLite databases.
Also shows how to map the SQLITE results set directly onto a listview
No dependencies except for the SQLITE3.dll from www.sqlite.org and the latest PB translation of the SQLITE3.inc wrappers from http://www.jose.it-berater.org.
when using SQLite databases.
Also shows how to map the SQLITE results set directly onto a listview
No dependencies except for the SQLITE3.dll from www.sqlite.org and the latest PB translation of the SQLITE3.inc wrappers from http://www.jose.it-berater.org.
Code:
' ' a little DDT program to show how to interrupt long running queries ' when using SQLite databases. ' Also shows how to map the SQLITE results set directly onto a listview ' ' Chris Holbrook May 2008. Use at your own risk!. ' Pl3ase post f33dback in PB forums or 3mail m3: [email protected] ' ' #COMPILE EXE #DIM ALL #INCLUDE "WIN32API.INC" #INCLUDE "COMMCTRL.INC" ' get the DLL from http://www.sqlite.org/download.html, tested with v3.5.7 ' members of the Jose Roca software forums can get the 3.5.6 include file ' from http://www.jose.it-berater.org/smfforum/index.php?topic=1604.msg5547 #INCLUDE "sqlite3.inc" ' #INCLUDE "commctrl.inc" #INCLUDE "comdlg32.inc" %IDD_DIALOG1 = 101 %IDC_BUTTON1 = 1001 %IDC_BUTTON2 = 1002 %IDC_TEXTBOX1 = 1003 %IDC_LV1 = 1006 %DEFAULT_LV_STYLES = %ws_child OR %WS_TABSTOP OR %WS_VISIBLE OR %LVS_SHOWSELALWAYS OR %LVS_REPORT %DEFAULT_LV_EX_STYLES = %WS_EX_CLIENTEDGE OR %LVS_EX_GRIDLINES OR %WS_EX_LEFT OR %WS_EX_RIGHTSCROLLBAR GLOBAL ghDB AS DWORD ' mustn't call sqlite3_close on a closed DB, hence this Global: GLOBAL gDBOPEN AS LONG ' 0 = closed 1 = open '------------------------------------------------------------------------------- ' the thread function can receieve only a single dword so put SQLite calling ' params in a UDT and send its address. ' TYPE sqliteExecParms hDB AS DWORD ' database handle psSQL AS ASCIZ PTR ' pointer to string containing SQL statement pSQLiteResults AS LONG PTR ' pointer to SQLite's results array hD AS DWORD ' dialog handle lCtl AS LONG ' control id for listview control pzerms AS ASCIZ PTR ' pointer to error msg populated by sqlite rows AS LONG ' row count returned by sqlite cols AS LONG ' column count returned by sqlite END TYPE GLOBAL gptable() AS LONG PTR' raw results table pointer to map the query results ' in a way that PB can handle. GLOBAL SEP AS SQLiteExecParms '------------------------------------------------------------------------------------ FUNCTION MakeFont(BYVAL fName AS STRING, BYVAL ptSize AS LONG, _ OPT BYVAL attr AS STRING) AS DWORD '-------------------------------------------------------------------- ' Create a desired font and return its handle. ' attr = "biu" for bold, italic, and underlined (any order) '-------------------------------------------------------------------- LOCAL hDC AS DWORD, CharSet AS LONG, CyPixels AS LONG LOCAL bold, italic, uLine AS LONG IF LEN(attr) THEN IF INSTR(LCASE$(attr), "b") THEN bold = %FW_BOLD IF INSTR(LCASE$(attr), "i") THEN italic = 1 IF INSTR(LCASE$(attr), "u") THEN uLine = 1 END IF hDC = GetDC(%HWND_DESKTOP) CyPixels = GetDeviceCaps(hDC, %LOGPIXELSY) ReleaseDC %HWND_DESKTOP, hDC PtSize = 0 - (ptSize * CyPixels) \ 72 FUNCTION = CreateFont(ptSize, 0, 0, 0, bold, italic, uLine, _ %FALSE, CharSet, %OUT_TT_PRECIS, _ %CLIP_DEFAULT_PRECIS, %DEFAULT_QUALITY, _ %FF_DONTCARE , BYCOPY fName) END FUNCTION '-------------------------------------------------------------- FUNCTION SelDBFile (hD AS DWORD) AS STRING LOCAL buf, spath, sfile AS STRING LOCAL dwstyle AS DWORD LOCAL hFile AS LONG '------------------------ get database file dwStyle = %OFN_EXPLORER OR %OFN_FILEMUSTEXIST OR %OFN_HIDEREADONLY Buf = "Database files (*.SDB)|*.SDB|Database files (*.DB3)|*.DB3|" IF OpenFileDialog (hD, "Locate SDB file ", sfile, spath, buf, "SDB", dwstyle) = 0 THEN EXIT FUNCTION ' returning null END IF FUNCTION = sfile END FUNCTION '------------------------------------------------------------------------------ ' open database SUB Open_Database ( hd AS DWORD ) LOCAL sz AS ASCIZ * 512 LOCAL lresult AS LONG ' can only call sqlite3_close once! IF gDBOPEN = 1 THEN sqlite3_close(ghDB) ' in case one is already open ' choose a DB sz = SelDBFile(hD) IF sqlite3_open( sz, BYREF ghDB) <> %SQLITE_OK THEN ? "unable to open database", %mb_applmodal,"Warning" gDBOPEN = 0 EXIT SUB END IF gDBOPEN = 1 END SUB ' -------------------------------------------------- FUNCTION sqlErrMsg( BYVAL hDB AS LONG ) AS STRING 'Thanks to Don Dickinson DIM pzErr AS ASCIIZ PTR ' get the SQLite error message ' ? sqlErrMsg(hDB) pzErr = sqlite3_errmsg(hDB) IF pzErr = 0 THEN FUNCTION = "" ELSE FUNCTION = @pzErr END IF END FUNCTION '--------------------------------------------------------------------------------- 'FUNCTION q2lv(ghDB AS DWORD, BYVAL hDlg AS DWORD, BYVAL lLVID AS LONG, lERMS AS LONG, sql AS STRING ) AS LONG FUNCTION q2lvthread ( BYVAL dwSEP AS DWORD PTR) AS LONG LOCAL i, j, lresult, n, nmaxrows, nmaxcols AS LONG LOCAL pSEP AS SqliteEXecParms PTR LOCAL p AS DWORD LOCAL pcols, ps AS STRING PTR LOCAL pzfield AS ASCIZ PTR LOCAL hCtl AS DWORD LOCAL tLVC AS LV_COLUMN LOCAL tLVI AS LV_ITEM LOCAL lStyle AS LONG LOCAL pzerms AS ASCIZ PTR pSEP = dwSEP FUNCTION = -1 ' Get ListView Handle CONTROL HANDLE @pSEP.hD, @pSEP.lCtl TO hCtl ' clear down any existing columns DO WHILE ListView_DeleteColumn(hCtl, 0) LOOP Listview_DeleteAllItems(hCtl) ps = @pSEP.psSQL lresult = SQLite3_Get_Table( ghDB, BYVAL @pSEP.psSQL, p, nMaxRows, nMaxCols, BYVAL pzerms ) IF lresult <> 0 THEN ? "SQLite error! :" + sqlErrMsg(@pSEP.hDB) EXIT FUNCTION END IF REDIM gptable ( 0 TO nmaxcols * nmaxrows ) AT p @pSEP.rows = nMaxRows @pSEP.cols = nMaxCols @pSEP.pSQLiteResults = p 'Load column headers. tLVC.mask = %LVCF_FMT OR %LVCF_TEXT OR %LVCF_SUBITEM OR %LVCF_WIDTH tLVC.fmt = %LVCFMT_LEFT 'redim preserve ptable(0 to nmaxcols * nmaxrows) FOR i = 0 TO nmaxcols -1 tLVC.pszText = gptable(i) tLVC.isubitem = i ListView_InsertColumn(hCtl, i , tLVC) NEXT listview_setItemCount ( hCtl, nmaxrows) FOR i = 0 TO nmaxcols - 1 ListView_SetColumnWidth(hCtl, i, %LVSCW_AUTOSIZE_USEHEADER ) NEXT FUNCTION = nmaxrows END FUNCTION '------------------------------------------------------------ ' execute query and show recset in listview ' params: database handle, dialog handle, LV ctl id, Error Msg Ctl id, SQL text ' returns -1 in the event of failure, rowcount if sucessful ' caller passes in a pointer to a SQLiteExecParms structure which is populated ' in order to pass parameters in to threaded function running the query ' and to contain the query results - a pointer to sqlite's results table, ' the row and column counts. ' ' NB the query and populating the LV will not be synchronised with the main thread ' so the main thread can issue a sqlite3_interrupt to abandon the query! ' FUNCTION Q2Lv (ghDB AS DWORD, BYVAL hDlg AS DWORD, BYVAL lLVID AS LONG, lERMS AS LONG, BYVAL psql AS ASCIZ PTR) AS LONG LOCAL lresult AS LONG SEP.hDB = ghdb SEP.psSQL = psql SEP.pSQLiteResults = 0 ' returned by SQLITE and must be freed using sqlite3_free_table() SEP.hd = hDlg SEP.lCtl = %IDC_LV1 SEP.pzerms = 0 ' returned by SQLITE THREAD CREATE q2lvthread(BYVAL VARPTR (SEP)) TO lresult FUNCTION = lresult END FUNCTION '------------------------------------------------------------------------------ FUNCTION QLVCustomDraw ( BYVAL ptlvcd AS NMLVCUSTOMDRAW PTR ) AS LONG LOCAL szItem AS ASCIIZ * %MAX_PATH ' item text LOCAL trcItem AS RECT ' bounding rectangle of item/subitem LOCAL tlv_item AS LV_ITEM ' listview item information LOCAL tlb AS LOGBRUSH ' specifies information used to create background brush LOCAL hWndHdr AS DWORD ' handle of header child control LOCAL hBrush AS DWORD LOCAL hBrushOld AS DWORD LOCAL hPenOld AS DWORD LOCAL dwBackColor AS DWORD LOCAL nBkModeOld AS INTEGER LOCAL hwin AS DWORD LOCAL lresult AS LONG ' Get the item or subitem info tlv_item.mask = %LVIF_TEXT OR %LVIF_IMAGE OR %LVIF_STATE tlv_item.stateMask = %LVIS_FOCUSED OR %LVIS_SELECTED tlv_item.iItem = @ptlvcd.nmcd.dwItemSpec tlv_item.iSubItem = @ptlvcd.iSubItem tlv_item.pszText = VARPTR(szItem) tlv_item.cchTextMax = %MAX_PATH SendMessage @ptlvcd.nmcd.hdr.hwndFrom, %LVM_GETITEM, 0, BYVAL VARPTR(tlv_item) ' Get the bounding rectangle of the subitem(cell) trcItem.nLeft = %LVIR_BOUNDS trcItem.nTop = @ptlvcd.iSubItem lresult = SendMessage( @ptlvcd.nmcd.hdr.hwndFrom, %LVM_GETSUBITEMRECT, @ptlvcd.nmcd.dwItemSpec, BYVAL VARPTR(trcItem)) ' Color the background IF @ptlvcd.nmcd.dwItemSpec MOD 2 = 0 THEN dwBackColor = RGB(255,127,127) ' background ELSE dwBackColor = %WHITE ' white background END IF tlb.lbStyle = %BS_SOLID tlb.lbColor = dwBackColor tlb.lbHatch = 0 hBrush = CreateBrushIndirect(tlb) FillRect @ptlvcd.nmcd.hdc, trcItem, hBrush DeleteObject hBrush ' Draw the text nBkModeOld = SetBkMode(@ptlvcd.nmcd.hdc, %TRANSPARENT) DrawTextEx @ptlvcd.nmcd.hdc, szItem, LEN(szItem), trcItem, %DT_SINGLELINE OR %DT_LEFT OR %DT_VCENTER OR %DT_END_ELLIPSIS, BYVAL %NULL SetBkMode @ptlvcd.nmcd.hdc, nBkModeOld FUNCTION = %CDRF_SKIPDEFAULT END FUNCTION '------------------------------------------------------------------------------- CALLBACK FUNCTION ShowDIALOG1Proc() LOCAL sSQL AS STRING LOCAL lcols, lresult, n AS LONG LOCAL pzerms AS ASCIZ PTR STATIC hwthread AS LONG LOCAL sz AS ASCIZ * 512 LOCAL pnmh AS NMHDR PTR LOCAL pnm AS NMLVCUSTOMDRAW PTR LOCAL p AS POINTAPI LOCAL lpLVDispInfo AS LV_DISPINFO PTR LOCAL lpia AS NMITEMACTIVATE PTR SELECT CASE AS LONG CBMSG CASE %WM_INITDIALOG open_database(CBHNDL) CASE %WM_NOTIFY pnmh = CBLPARAM SELECT CASE @pnmh.code IF LOWRD(CBWPARAM) <> %IDC_lv1 THEN EXIT SELECT lpia=CBLPARAM CASE %NM_CUSTOMDRAW ' Here you can specify font and color. pnm = CBLPARAM SELECT CASE @pnm.nmcd.dwDrawStage CASE %CDDS_PREPAINT ' The following statement ensures that the relevant ' messages are coming back to Windows, so Windows ' can respond appropriately. ' The following statement makes Windows happy: SetWindowLong CBHNDL,%DWL_MSGRESULT,%CDRF_NOTIFYITEMDRAW ' This statement makes the DDT engine happy: FUNCTION = 1: EXIT FUNCTION CASE %CDDS_ITEMPREPAINT SetWindowLong CBHNDL,%DWL_MSGRESULT,%CDRF_NOTIFYSUBITEMDRAW FUNCTION = 1: EXIT FUNCTION CASE %CDDS_SUBITEM OR %CDDS_ITEMPREPAINT FUNCTION = QLVCustomDraw(BYVAL pnm) EXIT FUNCTION END SELECT CASE %LVN_GETDISPINFO 'Virtual ListView ask for Item text lpLVDispInfo = CBLPARAM @lpLVDispInfo.item.pszText = VARPTR(sz) IF (@lpLVDispInfo.item.mask AND %LVIF_TEXT) THEN @lpLVDispInfo.item.pszText = _ gptable(((@lpLVDispInfo.item.iitem + 1)* sep.cols) + @lpLVDispInfo.item.iSubItem) END IF END SELECT CASE %WM_COMMAND SELECT CASE AS LONG CBCTL CASE %IDC_BUTTON1 IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN listview_deleteAllItems(getdlgitem(CBHNDL,%IDC_LV1)) sqlite3_free_table(SEP.pSqliteResults) ' clear last sqlite results if any CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX1 TO sSQL q2lv(ghDB, CBHNDL, %IDC_LV1, 0, BYVAL STRPTR (sSQL)) END IF CASE %IDC_BUTTON2 IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN sqlite3_interrupt(ghdb) THREAD CLOSE hwthread TO lresult listview_deleteAllItems(getdlgitem(CBHNDL,%IDC_LV1)) sqlite3_free_table(SEP.pSqliteResults) END IF END SELECT CASE %WM_DESTROY THREAD CLOSE hwthread TO lresult sqlite3_free_table(SEP.pSqliteResults) listview_deleteAllItems(getdlgitem(CBHNDL,%IDC_LV1)) sqlite3_close(ghDB) END SELECT END FUNCTION '----------------------------------------------------------------------- FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG LOCAL lRslt AS LONG LOCAL hDlg AS DWORD DIALOG NEW hParent, "SQLITE3 example - interruptable query using threads", 175, 118, 305, 252, %WS_POPUP OR %WS_BORDER OR _ %WS_DLGFRAME OR %WS_SYSMENU OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _ %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Start Query", 5, 230, 65, 15 CONTROL ADD BUTTON, hDlg, %IDC_BUTTON2, "Stop Query", 80, 230, 65, 15 CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "Enter query here", 5, 5, 295, 60, %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR _ %ES_LEFT OR %ES_MULTILINE OR %ES_AUTOHSCROLL OR %ES_WANTRETURN, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR _ %WS_EX_RIGHTSCROLLBAR CONTROL ADD "SysListView32", hDlg, %IDC_LV1, "SysListView32_1", 5, 70, 295, 155, _ %DEFAULT_LV_STYLES OR %LVS_OWNERDATA, %DEFAULT_LV_EX_STYLES 'OR %LVS_OWNERDATA DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt FUNCTION = lRslt END FUNCTION '============================================================================ FUNCTION PBMAIN() InitCommonControls ShowDIALOG1 %HWND_DESKTOP END FUNCTION