Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

SQLite interrupt queries, results direct to listview

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • SQLite interrupt queries, results direct to listview

    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.

    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
Working...
X