home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Internet Business Development Kit / PRODUCT_CD.iso / sqlsvr / odbcsdk / samples / admndemo / execute.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-07  |  35.7 KB  |  1,192 lines

  1. //*---------------------------------------------------------------------------------
  2. //|  ODBC System Administrator
  3. //|
  4. //|  This code is furnished on an as-is basis as part of the ODBC SDK and is
  5. //|  intended for example purposes only.
  6. //|
  7. //|    Title:    EXECUTE.C
  8. //|        This file contains the actual code to execute SQL Statements and
  9. //|            display them.  This file is dependent on the SA Tool data structures
  10. //|            and the independent module RESULTS.
  11. //*---------------------------------------------------------------------------------
  12. #include "admndemo.h"
  13. #include "execute.h"
  14. #include "strings.h"
  15.  
  16. VSZFile;
  17.  
  18. #define MAXRECORDS 1000
  19.  
  20.  
  21.  
  22. //*---------------------------------------------------------------------------------
  23. //|    Global variables
  24. //*---------------------------------------------------------------------------------
  25. lpRESULTSINFO                lpActiveResults=NULL;            // Tracks the active results window
  26. extern lpCHILDINFO         lpActiveConn;
  27. extern HWND                    hwndCurMDIChild;
  28. extern HINSTANCE             hInst;
  29. extern HWND                    hwndFrame;
  30.  
  31. extern char OutStr[MAXBUFF];
  32. extern char szDirName[_MAX_PATH];
  33. extern char szDftFileFilter[MAXBUFF];
  34. extern LPSTR szOPENFILE;
  35.  
  36. dCSEG(char) szNullString[]                        =    "<null>";
  37. dCSEG(char) szDash[]                                =    " - ";
  38. dCSEG(char)    szResults[]                            =    "Results ";
  39. dCSEG(char) szErrorVal[]                        =    "#Error";
  40. dCSEG(char) sz1000[]                                =    "1000";
  41. dCSEG(char) szDlgTitle[]                        =    "Execute File";
  42.  
  43.  
  44. typedef struct tagEXECUTEFILE {
  45.     BOOL            fExecute;                        // TRUE if ok to proceede
  46.     HINSTANCE    hInst;                            // Instance handle
  47.     LPSTR            szFile;                            // File name to execute
  48.     char            szCharacter[4];                // Character to stop on
  49.     int            cbMaxLength;                    // Maximum buffer size
  50.     } EXECUTEFILE;
  51.  
  52.  
  53. //*---------------------------------------------------------------------------------
  54. //|    Local function prototypes
  55. //*---------------------------------------------------------------------------------
  56. BOOL INTFUN FindTerminator(LPSTR str, LPSTR term, LPSTR * nxtstr);
  57. BOOL INTFUN NotInQuote(LPSTR str, LPSTR tar);
  58. BOOL INTFUN ValidSQLStmt(LPSTR str);
  59. BOOL EXTFUN ExecuteFileWndProc(HWND hDlg, unsigned msg, WORD wParam, LONG lParam);
  60. void INTFUN InternalDestroyResultsWindow(CHILDINFO FAR * ci, RESULTSSET FAR * rs);
  61. void INTFUN DestroyResultsWindow(CHILDINFO FAR * ci, lpRESULTSINFO lpri);
  62.  
  63.     
  64. //*---------------------------------------------------------------------------------
  65. //| ExecuteFile:
  66. //|    This function will open a file and execute each SQL statement in it.
  67. //| Parms:
  68. //|    ci                            CHILDINFO information
  69. //|    hwnd                        Owner window for prompting
  70. //|    szExeFile                If not NULL, then the name of a file to execute, in
  71. //|                                    which case the following *are* used
  72. //|    szTerm                    Terminator for statement
  73. //|    cbStmt                    Max statement size
  74. //| Returns:              
  75. //|    Nothing.
  76. //*---------------------------------------------------------------------------------
  77. void ExecuteFile(CHILDINFO FAR * ci, HWND hwnd, LPSTR szExeFile,
  78.         LPSTR szTerm, int cbStmt)
  79. {
  80.     char                    szFile[MAXBUFF];
  81.     int                    cbRead, cbNextRead;
  82.     HFILE                    hf;
  83.     LPSTR                    szBuff;
  84.     DLGPROC              dlgproc;
  85.     EXECUTEFILE            ef;
  86.     HWND                    fHwnd=GetFocus();
  87.     
  88.  
  89.     //
  90.     // Display dialog to get execute parameters
  91.     //
  92.     memset(szFile, 0, MAXBUFF);
  93.     ef.hInst = ci->hInst;
  94.     ef.szFile = szFile;
  95.     
  96.     // If the caller doesn't supply a file, then ask them for it
  97.     if(!szExeFile) {
  98.         dlgproc = MakeProcInstance(ExecuteFileWndProc, ef.hInst);
  99.         if(-1 == DialogBoxParam(ci->hInst, 
  100.                 MAKEINTRESOURCE(IDD_EXECUTE_FILE),
  101.                 hwnd, 
  102.                 dlgproc, (LPARAM)(EXECUTEFILE FAR *)&ef))
  103.             MessageBox(NULL, "Could not open dialog box.",
  104.                 "Execute File", MB_ICONEXCLAMATION);
  105.         FreeProcInstance((FARPROC)dlgproc);
  106.         
  107.         if(fHwnd)
  108.             SetFocus(fHwnd);
  109.             
  110.         if(!ef.fExecute)
  111.             return;
  112.         }
  113.     // If they do, then grab the options
  114.     else {
  115.         lstrcpy(ef.szFile, szExeFile); 
  116.         lstrcpy(ef.szCharacter, szTerm);
  117.         ef.cbMaxLength = cbStmt;
  118.         }
  119.  
  120.     // Now execute the file 
  121.     szBuff = (LPSTR)GetMemory(ef.cbMaxLength);
  122.     if(!szBuff)
  123.         return;
  124.     
  125.     if((hf = _lopen(ef.szFile, OF_READ)) != HFILE_ERROR) {
  126.         LPSTR nxtstr, nxtread;
  127.         szWrite(ci->hwndOut, 
  128.                     GetidsString(idsExecutingFile, OutStr, MAXBUFF), 
  129.                     (LPSTR)ef.szFile);
  130.         nxtread = szBuff;
  131.         cbNextRead = ef.cbMaxLength;
  132.         while((cbRead = _lread(hf, nxtread, cbNextRead)) ||
  133.                 *szBuff) {
  134.             if(FindTerminator(szBuff, ef.szCharacter, &nxtstr)) {
  135.                 if(ValidSQLStmt(szBuff))
  136.                     ExecuteCmds(ci, szBuff);
  137.                 }
  138.             else {
  139.                 szWrite(ci->hwndOut, GetidsString(idsTerminatorNotFound, OutStr, MAXBUFF));
  140.                 goto exit01;
  141.                 }
  142.             cbNextRead = ef.cbMaxLength;
  143.             if(nxtstr) {
  144.                 lstrcpy(szBuff, nxtstr);
  145.                 cbNextRead -= lstrlen(szBuff);
  146.                 nxtread = szBuff + lstrlen(szBuff) + 1;
  147.                 }
  148.             }
  149.         }
  150.     else {        // Couldn't open file   
  151.         szMessageBox(GetActiveWindow(),
  152.                         MB_ICONEXCLAMATION,
  153.                         szOPENFILE,
  154.                         GetidsString(idsOpenFileFailed, szBuff, sizeof(szBuff)),
  155.                         (LPSTR)ef.szFile);
  156.         }
  157.  
  158. exit01:
  159.     _lclose(hf);
  160.  
  161.     ReleaseMemory(szBuff);
  162. }
  163.  
  164.  
  165. //*------------------------------------------------------------------------
  166. //| FindTerminator:
  167. //|    Looks for a statement terminator, clears out carriage returns,
  168. //|    and finds the next statement.
  169. //| Parms:
  170. //|    str        Starting location to look
  171. //|    term        The terminator string
  172. //|    nxtstr    The next string if there is one, NULL otherwise
  173. //| Returns:              
  174. //|    TRUE if a valid statement was found, FALSE on error
  175. //*------------------------------------------------------------------------
  176. BOOL INTFUN FindTerminator(LPSTR str, LPSTR term, LPSTR * nxtstr)
  177. {
  178.     LPSTR        cstr=str;
  179.     int        len;
  180.     LPSTR        next=*nxtstr;
  181.     
  182.     next = str;
  183.     while(next) {
  184.         if(!(next = strstr(cstr, term))) {
  185.             *nxtstr = NULL;
  186.             return FALSE;
  187.             }
  188.         if(NotInQuote(cstr, next)) {
  189.             len = lstrlen(term);
  190.             while(len--)
  191.                 *next++ = '\0';
  192.             *nxtstr = next;
  193.             return TRUE;
  194.             }
  195.         }
  196.         
  197.     return FALSE;
  198. }
  199.  
  200.  
  201. //*------------------------------------------------------------------------
  202. //| NotInQuote:
  203. //|    Given a starting position and a target, this function determines
  204. //|    if the target is within a quoted string.  If so, then both
  205. //|    pointers are advaned one character after the closing quote.
  206. //| Parms:
  207. //|    str        Starting location to look
  208. //|    tar        Target location
  209. //| Returns:              
  210. //|    TRUE if the value is not in a quote, FALSE if it is
  211. //*------------------------------------------------------------------------
  212. BOOL INTFUN NotInQuote(LPSTR str, LPSTR tar)
  213. {
  214.     static char        apost = '\'';        
  215.     LPSTR                instr;
  216.     LPSTR                tstr=str;
  217.     
  218.     
  219.     while((instr = strchr(tstr, apost))) {
  220.         if(instr < tar) {
  221.             instr = strchr(instr+1, apost);
  222.             if(instr > tar) {
  223.                 str = tar = instr + 1;
  224.                 return FALSE;            // Target in quoted string
  225.                 }
  226.             else
  227.                 tstr = instr + 1;
  228.             }
  229.         else
  230.             return TRUE;            // Target not between quotes
  231.         }
  232.  
  233.     return TRUE;
  234. }
  235.  
  236.  
  237. //*------------------------------------------------------------------------
  238. //| ValidSQLStmt:
  239. //|    Takes a buffer and makes sure it is valid.  Essentially it removes
  240. //|    all non-embedded carriage returns and looks for a statement which
  241. //|    has nothing but blanks.
  242. //| Parms:
  243. //|    str        The string to parse
  244. //| Returns:              
  245. //|    TRUE if the statement is valid, FALSE otherwise
  246. //*------------------------------------------------------------------------
  247. BOOL INTFUN ValidSQLStmt(LPSTR str)
  248. {
  249.     LPSTR    tmpstr=str;
  250.     
  251.     RemoveCrLf(str);
  252.     while(*tmpstr)
  253.         if(*tmpstr++ != ' ')
  254.             return TRUE;
  255.     return FALSE;
  256. }    
  257.  
  258.  
  259. //*------------------------------------------------------------------------
  260. //| ExecuteFileWndProc:
  261. //|    This window procedure is for executing a file.
  262. //| Parms:
  263. //|    in            Standard window parms
  264. //| Returns:              
  265. //|    Depends on message
  266. //*------------------------------------------------------------------------
  267. BOOL EXTFUN ExecuteFileWndProc(HWND hDlg, unsigned msg, WORD wParam, LONG lParam)
  268. {
  269.     static EXECUTEFILE FAR *        ef;
  270.  
  271.     switch(msg) {
  272.         case WM_INITDIALOG:
  273.             {
  274.             ef = (EXECUTEFILE FAR *)lParam;
  275.             ef->fExecute = FALSE;                                    // Assume we close
  276.             CenterDialog(hDlg);
  277.             if(!*szDirName)
  278.                 GetWindowsDirectory(szDirName, MAXBUFF);
  279.             SendMessage(GetDlgItem(hDlg, IDE_CHARACTER), EM_LIMITTEXT, 3, 0L);
  280.             SendMessage(GetDlgItem(hDlg, IDE_MAXLENGTH), EM_LIMITTEXT, 4, 0L);
  281.             CheckRadioButton(hDlg, IDR_CARRIAGE,
  282.                         IDR_CHARACTER, IDR_CARRIAGE);
  283.             EnableWindow(GetDlgItem(hDlg, IDE_CHARACTER), FALSE);
  284.             SetWindowText(GetDlgItem(hDlg, IDT_FILE), szDirName);
  285.             SetWindowText(GetDlgItem(hDlg, IDE_MAXLENGTH), (LPSTR)sz1000);
  286.             }
  287.             return TRUE;        
  288.  
  289.  
  290.         case WM_COMMAND:
  291.             switch(GET_WM_COMMAND_ID(wParam, lParam)) {
  292.                 // Handle radio buttons
  293.                 case IDR_CARRIAGE:
  294.                     EnableWindow(GetDlgItem(hDlg, IDE_CHARACTER), FALSE);
  295.                     return TRUE;
  296.                     
  297.                 case IDR_CHARACTER:
  298.                     EnableWindow(GetDlgItem(hDlg, IDE_CHARACTER), TRUE);
  299.                     SetFocus(GetDlgItem(hDlg, IDE_CHARACTER));
  300.                     return TRUE;
  301.  
  302.                 // Get file to execute, use last directory name
  303.                 case IDB_FILE:
  304.                     {
  305.                     OPENFILENAME    lpofn;
  306.                     char                szFileTitle[MAXBUFF];
  307.                     
  308.                     _fmemset(&lpofn, 0, sizeof(OPENFILENAME));
  309.                     lpofn.hInstance = ef->hInst;
  310.                     lpofn.lStructSize = sizeof(OPENFILENAME);
  311.                     lpofn.hwndOwner = hDlg;
  312.                     lpofn.lpstrFilter = (LPSTR)szDftFileFilter;
  313.                     lpofn.nFilterIndex = 1;
  314.                     lpofn.lpstrFile = ef->szFile;
  315.                     lpofn.nMaxFile = MAXBUFF;
  316.                     lpofn.lpstrFileTitle = szFileTitle;
  317.                     lpofn.nMaxFileTitle = sizeof(szFileTitle);
  318.                     lpofn.lpstrInitialDir = szDirName;
  319.                     lpofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  320.                     if(GetOpenFileName(&lpofn)) {
  321.                         lstrcpy(ef->szFile, lpofn.lpstrFile);
  322.                         SetWindowText(GetDlgItem(hDlg, IDT_FILE), ef->szFile);
  323.                         GetNewDirectory(szDirName, lpofn.lpstrFile);
  324.                         }
  325.                     }
  326.                     return TRUE;
  327.                     
  328.                 // User has clicked OK
  329.                 case IDOK:
  330.                     {
  331.                     char  szNum[5];
  332.                GetText(GetDlgItem(hDlg, IDE_MAXLENGTH), szNum);
  333.                ef->cbMaxLength = atoi(szNum);
  334.                if(ef->cbMaxLength < MINSTMTSIZE) {
  335.                    MessageBox(hDlg, GetidsString(idsNumTooSmall, OutStr, MAXBUFF), 
  336.                                szDlgTitle, MB_OK);
  337.                    SetFocus(GetDlgItem(hDlg, IDE_MAXLENGTH));
  338.                    return TRUE;
  339.                    }
  340.                     GetText(GetDlgItem(hDlg, IDT_FILE), ef->szFile);
  341.                if(IsRadioButtonOn(GetDlgItem(hDlg, IDR_CARRIAGE)))
  342.                    lstrcpy((LPSTR)ef->szCharacter, (LPSTR)"\r\n");
  343.                else
  344.                    GetText(GetDlgItem(hDlg, IDE_CHARACTER), (LPSTR)ef->szCharacter);
  345.                if(*ef->szCharacter == ' ' ||
  346.                    !*ef->szCharacter) {
  347.                    MessageBox(GetActiveWindow(), 
  348.                                GetidsString(idsInvalidTerminator, OutStr, MAXBUFF),
  349.                                szErrTitle, MB_OK);
  350.                    SetFocus(GetDlgItem(hDlg, IDE_CHARACTER));
  351.                    return TRUE;
  352.                    }
  353.                ef->fExecute = TRUE;
  354.                EndDialog(hDlg, IDOK);
  355.                     }
  356.                     return TRUE;
  357.             
  358.             case IDCANCEL:
  359.                 ef->fExecute = FALSE;
  360.                 EndDialog(hDlg, IDCANCEL);
  361.                     return TRUE;
  362.                 }
  363.             return TRUE;
  364.  
  365.         default:
  366.             return FALSE;
  367.         }
  368.     return FALSE;
  369. }
  370.  
  371.     
  372. //*---------------------------------------------------------------------------------
  373. //| DoCommitRollback:
  374. //|    This function will use SQLTransact to do either a commit or a rollback
  375. //|    on the current HDBC.
  376. //| Parms:
  377. //|    in            ci                            CHILDINFO information
  378. //|    in            type                        Which action identified by menu item
  379. //| Returns:              
  380. //|    Nothing.
  381. //*---------------------------------------------------------------------------------
  382. void DoCommitRollback(CHILDINFO FAR * ci, int type)
  383. {
  384.     RETCODE retcode;
  385.     
  386.     //-------------------------------------------------------------------------------
  387.     //    This tool has only one HSTMT per HDBC, and therefore we need only specify
  388.     //    the HDBC we want to COMMIT.  Note that some drivers may allow the user to
  389.     //    Execute a COMMIT or ROLLBACK and thereby would not even require a call to
  390.     //    this function.
  391.     //-------------------------------------------------------------------------------
  392.     switch(type) {
  393.         case IDM_COMMIT:
  394.             retcode = SQLTransact(ci->henv, ci->hdbc, SQL_COMMIT);
  395.             break;
  396.             
  397.         case IDM_ROLLBACK:
  398.             retcode = SQLTransact(ci->henv, ci->hdbc, SQL_ROLLBACK);
  399.             break;
  400.         }
  401.     
  402.     if(retcode != SQL_SUCCESS)
  403.         PrintErrors(ci);
  404.     else
  405.         szWrite(ci->hwndOut, GetidsString(idsStmtProcessed, OutStr, MAXBUFF));
  406. }
  407.  
  408.     
  409. //*---------------------------------------------------------------------------------
  410. //| ExecuteCmds:
  411. //|    This function will take a command from the given input window and execute
  412. //|        it.  The order of execution is to use the selcted text, and if none
  413. //|        is selected, to use the entire text from the input window.
  414. //| Parms:
  415. //|    ci                    CHILDINFO information
  416. //|    stmt                A null-terminated statement to execute, NULL
  417. //|                                                if the value should be taken from input window.
  418. //| Returns:              
  419. //|    Nothing.
  420. //*---------------------------------------------------------------------------------
  421. void ExecuteCmds(CHILDINFO FAR * ci, LPSTR stmt)
  422. {
  423.     RETCODE                    retcode;
  424.     LPSTR                        sqlstmt, tmpstr;
  425.     DWORD                        size;
  426.     DWORD                        len;
  427.  
  428.  
  429.     // The user may pass in their own SQL statement to be executed.  In
  430.     // this case simply do so.
  431.     Busy(TRUE);
  432.     if(stmt)
  433.         sqlstmt = stmt;
  434.     // If stmt is NULL, however, we must get the statement from the input
  435.     //    window of the current connection window.
  436.     else {
  437.         len = SendMessage(ci->hwndIn, WM_GETTEXTLENGTH, 0, 0L);
  438.         sqlstmt = (LPSTR)GetMemory(len+1);
  439.         if(!sqlstmt) {
  440.             Busy(FALSE);
  441.             return;
  442.             }
  443.         SendMessage(ci->hwndIn, WM_GETTEXT, (WPARAM)len + 1, (LPARAM)sqlstmt);
  444.         size = SendMessage(ci->hwndIn, EM_GETSEL, 0, 0L);
  445.         if(HIWORD(size) - LOWORD(size) > 0) {            // A selection has been made
  446.             len = HIWORD(size) - LOWORD(size);
  447.             memmove(sqlstmt, &sqlstmt[LOWORD(size)],
  448.                         HIWORD(size) - LOWORD(size));
  449.             tmpstr = sqlstmt + len;
  450.             *tmpstr = '\0';
  451.             }
  452.         RemoveCrLf(sqlstmt);
  453.        }
  454.    
  455.     // Execute the statement
  456.     SQLFreeStmt(ci->hstmt, SQL_CLOSE);
  457.     retcode = SQLExecDirect(ci->hstmt, sqlstmt, SQL_NTS);
  458.     if(retcode != SQL_SUCCESS) {
  459.         PrintErrors(ci);
  460.         Busy(FALSE);
  461.         goto exit00;
  462.         }
  463.         
  464.     // Check for results
  465.     CheckForResults(ci);
  466.  
  467. exit00:   
  468.     SQLFreeStmt(ci->hstmt, SQL_CLOSE);
  469.     if(!stmt)
  470.        ReleaseMemory(sqlstmt);
  471.     Busy(FALSE);
  472. }
  473.  
  474.     
  475. //*---------------------------------------------------------------------------------
  476. //| CheckForResults:
  477. //|    Call this function after successful execution of an SQL statement.  This
  478. //|    function will attempt to fetch the results from the statement, if they
  479. //|    exist.  It is not an error if they do not.
  480. //| Parms:
  481. //|    ci                    CHILDINFO information
  482. //| Returns:              
  483. //|    Nothing.
  484. //*---------------------------------------------------------------------------------
  485. void CheckForResults(CHILDINFO FAR * ci)
  486. {
  487.     RESULTSSET FAR *         rs;
  488.     SWORD                        cbCols;
  489.     SDWORD                    cbRowCount;
  490.     char                        szStr[MAXBUFF];
  491.     char                        tmpbuff[30];
  492.     RETCODE                    retcode;
  493.  
  494.     //
  495.     // At this point we have executed the statement successfully.  If there is
  496.     // a results set, fetch it to a results window.  Otherwise simply tell the
  497.     // user how many rows were affected, if possible.
  498.     //
  499.     retcode = SQLFetch(ci->hstmt);
  500.     if(retcode == SQL_ERROR) {
  501.         cbRowCount = PrintAffectedRows(ci->hstmt, ci->hwndOut);
  502.         SQLFreeStmt(ci->hstmt, SQL_CLOSE);
  503.         return;
  504.         }
  505.     else if (retcode == SQL_NO_DATA_FOUND) {
  506.         szWrite(ci->hwndOut, GetidsString(idsNoDataFound, szStr, sizeof(szStr)));
  507.         return;
  508.         }
  509.  
  510.     //
  511.     // If we made it this far, then we have a results set to work with.  The
  512.     //        following loop will go through each results set (if there are more
  513.     //        than one) and place their contents in a results window.
  514.     //
  515.     while(RC_SUCCESSFUL(retcode)) {
  516.         lstrcpy((LPSTR)szStr, (LPSTR)ci->szClientTitle);
  517.         lstrcat((LPSTR)szStr, (LPSTR)szDash);
  518.         lstrcat((LPSTR)szStr, (LPSTR)szResults);
  519.         wsprintf(tmpbuff, "%u", ++ci->cbResultCount);
  520.         lstrcat((LPSTR)szStr, (LPSTR)tmpbuff);
  521.  
  522.         if(!(cbCols = GetNumResultsCols(ci->hstmt)))
  523.             return;
  524.  
  525.         rs = GetConnectWindowResultsNode(ci);
  526.         if(!CreateResultsSet(rs, ci->hwndClient, ci->hInst, cbCols, (LPSTR)szStr))
  527.             return;    
  528.  
  529.         //
  530.         // Set the meta data
  531.         //
  532.         SetMetaDataFromSql(ci->hwndOut, ci->hstmt, rs, cbCols);
  533.  
  534.         //
  535.         // Now create the MDI child window which will hold the results.
  536.         //
  537.         if(!CreateResultsWindow(ci, rs)) 
  538.             return;
  539.  
  540.    
  541.         //
  542.         // Loop through each data source and add it to the results set.
  543.         //
  544.         cbRowCount = FetchAllRecordsToResults(ci->hwndOut, ci->hstmt, rs, cbCols, FALSE);
  545.         szWrite(ci->hwndOut, 
  546.                     GetidsString(idsAffectedRows, szStr, sizeof(szStr)),
  547.                     cbRowCount);
  548.         
  549.         //
  550.         //    Now see if there are any more results to fetch.
  551.         //
  552.         retcode = SQLMoreResults(ci->hstmt);
  553.         if(RC_SUCCESSFUL(retcode))
  554.             retcode = SQLFetch(ci->hstmt);
  555.         }        // End of loop through results sets
  556.  
  557.     SQLFreeStmt(ci->hstmt, SQL_CLOSE);    
  558.  
  559.     return;
  560. }
  561.  
  562.  
  563.  
  564. //*---------------------------------------------------------------------------------
  565. //| FreeConnectWindowResults:
  566. //|    This function will free all present results sets (if there are any) and then
  567. //|        free up the memory they occupied.
  568. //| Parms:
  569. //|    in            ci                            Pointer to connection window
  570. //| Returns:              
  571. //|    Nothing.
  572. //*---------------------------------------------------------------------------------
  573. void FreeConnectWindowResults(lpCHILDINFO lpci)
  574. {
  575.     while(lpci->lprihead)
  576.         DestroyResultsWindow(lpci,
  577.                     (lpRESULTSINFO)lpci->lprihead);
  578.  
  579.     return;
  580. }
  581.  
  582.  
  583.  
  584. //*---------------------------------------------------------------------------------
  585. //| GetConnectWindowResultsNode:
  586. //|    This function will return a results set pointer which describes only
  587. //|    the graphical portion of a results set.
  588. //| Parms:
  589. //|    lpci                            Pointer to connection window
  590. //| Returns:              
  591. //|    Pointer to Results set
  592. //*---------------------------------------------------------------------------------
  593. lpRESULTSSET GetConnectWindowResultsNode(lpCHILDINFO lpci)
  594. {
  595.     lpRESULTSSET lprs;
  596.     
  597.     lprs = (lpRESULTSSET)GetMemory(sizeof(RESULTSSET));
  598.     return lprs;
  599. }
  600.  
  601.  
  602. //*---------------------------------------------------------------------------------
  603. //| AddResultsInfoNode:
  604. //|    Associate a child MDI window structure with a results set.
  605. //| Parms:
  606. //|    lpci                            Pointer to connection window
  607. //|    lprs                            The results set to put into a window
  608. //| Returns:              
  609. //|    Pointer to Results set
  610. //*---------------------------------------------------------------------------------
  611. lpRESULTSINFO AddResultsInfoNode(lpCHILDINFO lpci, lpRESULTSSET lprs)
  612. {
  613.     lpRESULTSINFO        lpri;
  614.     lpRESULTSINFO        head=(lpRESULTSINFO)lpci->lprihead;
  615.     lpRESULTSINFO        tail=(lpRESULTSINFO)lpci->lpritail;
  616.     
  617.     lpri = (lpRESULTSINFO)GetMemory(sizeof(RESULTSINFO));
  618.     if(!lpri)
  619.         return NULL;
  620.  
  621.     if(!head) {
  622.         lpci->lprihead = lpri;
  623.         lpri->next = 
  624.         lpri->prev = NULL;
  625.         }
  626.     else {
  627.         tail->next = lpri;
  628.         lpri->prev = tail;
  629.         lpri->next = NULL;
  630.         }
  631.     lpci->lpritail = lpri;
  632.     ++lpci->cbResults;
  633.  
  634.     return lpri;
  635. }
  636.  
  637.  
  638.  
  639. //*---------------------------------------------------------------------------------
  640. //| CreateResultsWindow:
  641. //|    This function will create an MDI client window of type Results which can
  642. //|        be used to display a results set.  Call this function with a CHILDINFO
  643. //|        structure which is the owner.  A spot will be found in the array of
  644. //|        results a child can hold.  FIFO is used if there are no empty array
  645. //|        locations.
  646. //| Parms:
  647. //|    cs                            Pointer to connection window
  648. //|    rs                            Pointer to the results set to use for creation
  649. //| Returns:              
  650. //|    TRUE on success, FALSE on failure
  651. //*---------------------------------------------------------------------------------
  652. BOOL INTFUN CreateResultsWindow(CHILDINFO FAR * ci, lpRESULTSSET rs)
  653. {
  654.     MDICREATESTRUCT                mdicreate;
  655.     lpRESULTSINFO                    lpri;
  656.     
  657.     
  658.     //
  659.     // Allocate memory for the RESULTSINFO which holds all the control
  660.     //        structures needed for creating a window.
  661.     //
  662.     lpri = AddResultsInfoNode(ci, rs);
  663.     if(!lpri)
  664.         return FALSE;
  665.     lpri->ci = ci;
  666.     lpri->rs = rs;
  667.     
  668.  
  669.     //
  670.     //  User must have a valid pointer to a results set which was create via
  671.     //        CreateResultsSet.  This function will simply create a results set window
  672.     //        for the user based on this value.
  673.     //
  674.     mdicreate.szClass = szResultsClass;
  675.     mdicreate.szTitle = (LPSTR)rs->szTitle;
  676.     mdicreate.hOwner  = rs->hInst;
  677.     mdicreate.x            = CW_USEDEFAULT;
  678.     mdicreate.y            = CW_USEDEFAULT;
  679.     mdicreate.cx        = CW_USEDEFAULT;
  680.     mdicreate.cy        = CW_USEDEFAULT;
  681.     mdicreate.style    = (hwndCurMDIChild) ? ((IsZoomed(hwndCurMDIChild)) ? WS_MAXIMIZE : 0) : 0;
  682.     mdicreate.lParam    = (LPARAM)lpri;
  683.     if(SendMessage(rs->hwndClient, WM_MDICREATE, 0,
  684.             (LONG)(LPMDICREATESTRUCT)&mdicreate))
  685.         return TRUE;
  686.     else
  687.         return FALSE;
  688. }
  689.  
  690.  
  691.  
  692.  
  693. //*---------------------------------------------------------------------------------
  694. //| DestroyResultsWindow:
  695. //|    This function will free all memory for a specified results set.  It must
  696. //|        remove the results set from the array of results sets and collapse the
  697. //|        array to maintain FIFO order.
  698. //| Parms:
  699. //|    lpci                        Pointer to Child info
  700. //|    lpri                        The results to drop
  701. //| Returns:              
  702. //|    Nothing.
  703. //*---------------------------------------------------------------------------------
  704. void INTFUN DestroyResultsWindow(lpCHILDINFO lpci, lpRESULTSINFO lpri)
  705. {
  706.     //
  707.     // First destroy the contents of the results set, then the memory for
  708.     //        the results set itself.
  709.     //    
  710.     SendMessage(lpri->rs->hwndList, LB_RESETCONTENT, 0, 0L);
  711.     SendMessage(lpci->hwndClient, WM_MDIDESTROY, 
  712.                         (WPARAM)(HWND)lpri->rs->hwndResults, 0L);
  713.     FreeResultsSet(lpri->rs);
  714.  
  715.     if(lpci->lprihead == lpri)
  716.         lpci->lprihead = lpri->next;
  717.     if(lpci->lpritail == lpri)
  718.         lpci->lpritail = lpri->prev;
  719.     if(lpri->next)
  720.         lpri->next->prev = lpri->prev;
  721.     if(lpri->prev)
  722.         lpri->prev->next = lpri->next;
  723.  
  724.     --lpci->cbResults;
  725. }
  726.  
  727.  
  728. //*---------------------------------------------------------------------------------
  729. //| ResultsWndProc:
  730. //|    This function will handle all messages which are received by a results
  731. //|        window.
  732. //| Parms:
  733. //|    in            hwnd                        Window to work with
  734. //|    in            msg                        Message we need to handle
  735. //|    in            wParam                    First param
  736. //|    in            lParam                    Second param
  737. //| Returns:              
  738. //|    Depends on message.
  739. //*---------------------------------------------------------------------------------
  740. long EXTFUN ResultsWndProc(HWND hwnd, unsigned msg, WORD wParam, LONG lParam)
  741. {
  742.     switch(msg) {
  743.         //
  744.         // WM_CREATE is received when the new window is created.  We need to create
  745.         //        the listbox for the results set when this message is received.
  746.         //
  747.         case WM_CREATE:
  748.             //
  749.             // First get information on the font being used for the display.  Declare
  750.             //        all variables which are only needed once on stack for this message only.
  751.             //
  752.             {
  753.             CREATESTRUCT FAR *         cs;
  754.             MDICREATESTRUCT FAR *     mdi;
  755.             lpRESULTSINFO                rwi;
  756.             lpRESULTSSET                rs;
  757.             
  758.  
  759.             cs = (CREATESTRUCT FAR *)lParam;
  760.             mdi = (MDICREATESTRUCT FAR *)cs->lpCreateParams;
  761.             lpActiveResults = rwi = (lpRESULTSINFO)mdi->lParam;
  762.             rs = rwi->rs;
  763.  
  764.             SETRWPOINTER(hwnd, lpActiveResults);
  765.             CreateResultsFont(rs, hwnd, NULL);
  766.             
  767.             //
  768.             // Now get the window handle values and then create the components of the
  769.             //        results window.  These include the title which has the column names,
  770.             //        and the actual owner drawn list box which has the results set in it.
  771.             //
  772.             rs->hwndResults = hwnd;
  773.             rs->hwndClient = GetParent(hwnd);
  774.             if(!(rs->hwndList = CreateWindow("listbox", NULL,
  775.                         WS_CHILD | WS_VISIBLE | WS_VSCROLL |
  776.                         LBS_MULTIPLESEL | LBS_OWNERDRAWFIXED | 
  777.                         LBS_NOTIFY | LBS_EXTENDEDSEL | LBS_NOINTEGRALHEIGHT,
  778.                         0, 0, 0, 0,
  779.                         hwnd, (HMENU)(2), hInst, NULL)))
  780.                 return -1;
  781.             if(!(rs->hwndHScroll = CreateWindow("scrollbar", NULL,
  782.                         WS_CHILD | WS_VISIBLE,
  783.                         0, 0, 0, 0,
  784.                         hwnd, (HMENU)(3), hInst, NULL)))
  785.                 return -1;
  786.  
  787.             //
  788.             // Get scroll bar stats and set scroll range
  789.             //
  790.             SetScrollRange(rs->hwndHScroll, SB_CTL, 0, rs->cbColumns - 1, TRUE);
  791.             }
  792.             return 0;
  793.             
  794.  
  795.       //
  796.         // When WM_SIZE is received, we need to move the child windows including the
  797.         //        title information and the listbox to fit the new area.  If all of the
  798.         //        columns will fit in one display, hide our horizontal scroll bar.
  799.         // NOTE:  We must break to return DefMDIWndProc in order for the menu to
  800.         //        be redrawn when we maximize the child window.
  801.         //
  802.         case WM_SIZE:
  803.             {
  804.             lpRESULTSINFO            rwi = GETRWPOINTER(hwnd);
  805.             lpRESULTSSET            rs=rwi->rs;
  806.             int                        cScroll, dex;
  807.             
  808.             rwi->dx = LOWORD(lParam);
  809.             rwi->dy = HIWORD(lParam);
  810.             rwi->xRightCol = FindRightCol(rs, rwi->xLeftCol, rwi->dx);
  811.             for(cScroll=0, dex=rwi->xLeftCol;  dex<=rwi->xRightCol;  dex++)
  812.                 cScroll += rs->md[dex].cColWidth;
  813.             rwi->fScrollPresent = FALSE;
  814.             if(cScroll > rwi->dx) {
  815.                 rwi->fScrollPresent = TRUE;
  816.                 MoveWindow(rs->hwndHScroll, 0, rwi->dy - GetSystemMetrics(SM_CYHSCROLL),
  817.                     rwi->dx - GetSystemMetrics(SM_CXVSCROLL), GetSystemMetrics(SM_CYHSCROLL), TRUE);
  818.                 }
  819.             MoveWindow(rs->hwndList, 0, rs->cTitleHeight, rwi->dx, 
  820.                 (rwi->fScrollPresent) ? rwi->dy - rs->cTitleHeight - GetSystemMetrics(SM_CYHSCROLL) : rwi->dy - rs->cTitleHeight, TRUE);
  821.             ShowWindow(rs->hwndHScroll, (rwi->fScrollPresent) ? SW_SHOW : SW_HIDE);
  822.             rwi->tRect.left = rwi->tRect.top = 0;
  823.             rwi->tRect.bottom = rs->cTitleHeight;
  824.             rwi->tRect.right = min(rwi->dx, cScroll);
  825.             }
  826.             break;
  827.  
  828.  
  829.         // 
  830.         // Hande the WM_INITMENUPOPUP message so that when the user selects
  831.         //        a menu, we enable/disable each item based on our current state.
  832.         //
  833.         case WM_INITMENUPOPUP:
  834.             if(!(BOOL)HIWORD(lParam))            // Not the system menu
  835.                 ResetMenu((HMENU)wParam, (int)LOWORD(lParam));
  836.             break;
  837.  
  838.  
  839.         //
  840.         // A WM_HSCROLL message is received when the user does something with our
  841.         //        scroll bar.  We must handle horizontal scroll since a listbox does
  842.         //        does inherintly support it.
  843.         //
  844.         case WM_HSCROLL:
  845.             {
  846.             lpRESULTSINFO    rwi = GETRWPOINTER(hwnd);
  847.             lpRESULTSSET            rs=rwi->rs;
  848.  
  849.             HandleHScroll(wParam, rs, hwnd, rs->hwndHScroll, &rwi->xLeftCol, &rwi->xRightCol, 
  850.                     rs->hwndList, rs->cbColumns, rwi->dx, &rwi->tRect);
  851.             }
  852.             return 0;
  853.             
  854.         
  855.         // In order to enable the keyboard to run the scroll bars (for those users
  856.         //        without a mouse), we must look for the cursor keys etc... and cause
  857.         //        them to do scrolling.
  858.         case WM_KEYDOWN:
  859.             {
  860.             lpRESULTSINFO    rwi = GETRWPOINTER(hwnd);
  861.             lpRESULTSSET            rs=rwi->rs;
  862.  
  863.             HandleVirtualHScroll(wParam, rs->hwndList, rs->hwndResults);
  864.             }
  865.             break;                                        // Let it pass through to the app
  866.             
  867.         
  868.         //
  869.         // WM_MEASUREITEM is received when the listbox is first being drawn.  We tell
  870.         //        Windows what each row is going to look like.
  871.         //
  872.         case WM_MEASUREITEM:
  873.             {
  874.             MEASUREITEMSTRUCT FAR * ms;
  875.             lpRESULTSINFO    rwi = GETRWPOINTER(hwnd);
  876.             lpRESULTSSET            rs=rwi->rs;
  877.  
  878.             if((int)wParam != 2)                      // Not our list box
  879.                 return FALSE;
  880.             ms = (MEASUREITEMSTRUCT FAR *)lParam;
  881.             ms->itemHeight = rs->cTitleHeight;
  882.             }
  883.             return TRUE;
  884.  
  885.  
  886.         //
  887.         // WM_DRAWITEM is received whenever we must draw a record in the results set
  888.         //     list box.  We will recieve a pointer to the ROWDATA structure.
  889.         //
  890.         case WM_DRAWITEM:
  891.             {
  892.             DRAWITEMSTRUCT FAR * dwitem;
  893.             lpRESULTSINFO    rwi = GETRWPOINTER(hwnd);
  894.             lpRESULTSSET            rs=rwi->rs;
  895.  
  896.             dwitem = (DRAWITEMSTRUCT FAR *)lParam;
  897.             DrawRowData(rs, dwitem, rwi->xLeftCol, rwi->xRightCol);
  898.             }
  899.             return TRUE;
  900.  
  901.  
  902.         //
  903.         // The WM_DELETEITEM message is received whenever a row is to be
  904.         //        deleted from the listbox.  We will take the opportunity to
  905.         //        free the storage for the row.
  906.         //
  907.         case WM_DELETEITEM:
  908.             {
  909.             DELETEITEMSTRUCT FAR *    dlt;
  910.             lpRESULTSINFO    rwi = GETRWPOINTER(hwnd);
  911.             lpRESULTSSET            rs=rwi->rs;
  912.             ROWDATA FAR *             rd;
  913.  
  914.             dlt = (DELETEITEMSTRUCT FAR *)lParam;
  915.               rd = (ROWDATA FAR *)dlt->itemData;
  916.             FreeRowData(rs, rd);
  917.             }
  918.             return 0;
  919.  
  920.  
  921.         //
  922.         //    WM_PAINT means it's time for us to paint our columns titles which are
  923.         //        simply drawn in the client area for speed.
  924.         //
  925.         case WM_PAINT: 
  926.             {
  927.             HDC                hdc;
  928.             PAINTSTRUCT        ps;
  929.             lpRESULTSINFO    rwi = GETRWPOINTER(hwnd);
  930.             lpRESULTSSET    rs=rwi->rs;
  931.  
  932.             hdc = BeginPaint(hwnd, &ps);
  933.             if(hdc) {
  934.                 DrawColumnTitles(hdc, rs, &rwi->tRect, rwi->xLeftCol, rwi->xRightCol);
  935.                 if(rwi->fScrollPresent) {
  936.                     RECT                 rct;
  937.                     rct.left = rwi->dx - GetSystemMetrics(SM_CXVSCROLL);
  938.                     rct.top = rwi->dy - GetSystemMetrics(SM_CYHSCROLL);
  939.                     rct.right = rwi->dx;
  940.                     rct.bottom = rwi->dy;
  941.                     MoveTo(hdc, rct.left, rct.top);
  942.                     LineTo(hdc, rct.right, rct.top);
  943.                     ++rct.top;
  944.                     FillRect(hdc, &rct, GetStockObject(LTGRAY_BRUSH));
  945.                     }
  946.                 EndPaint(hwnd, &ps);
  947.                 }
  948.             }
  949.             break;
  950.  
  951.         //
  952.         // All messages are handled in the main wnd proc, so pass them back
  953.         //
  954.         case WM_COMMAND:
  955.             {
  956.             UINT            id=GET_WM_COMMAND_ID(wParam, lParam);
  957.             
  958.             if(id >= IDM_CONNECT &&
  959.                 id <= IDM_MOVE_WINDOW)
  960.                 SendMessage(hwndFrame, WM_COMMAND, wParam, lParam);
  961.             }
  962.             break;
  963.  
  964.  
  965.         //
  966.         // The WM_MDIACTIVATE message is received first by the child window
  967.         //        which is losing focus, then by the window which is receiving
  968.         //        focus. If we're changing windows, get the RESULTSSET.
  969.         //
  970.         case WM_MDIACTIVATE:
  971.             {
  972. #ifndef WIN32
  973.             if(wParam) {
  974.                 lpActiveResults = GETRWPOINTER((HWND)LOWORD(lParam));
  975.                 }
  976. #else
  977.             if((HWND)lParam == hwnd) {
  978.                 lpActiveResults = GETRWPOINTER((HWND)lParam);
  979.                 }
  980. #endif
  981.             else
  982.                 lpActiveResults = NULL;
  983.                 
  984.             if(lpActiveResults) {
  985.                 RECT         rect;
  986.                 
  987.                 hwndCurMDIChild = lpActiveResults->rs->hwndResults;
  988.                 lpActiveConn = lpActiveResults->ci;
  989.                 GetClientRect(hwndCurMDIChild, &rect);
  990.                 InvalidateRect(hwndCurMDIChild, &rect, TRUE);
  991.                 }
  992.             }
  993.             return 0;
  994.  
  995.  
  996.         //
  997.         // If the user selects close from the system menu for the window, we need
  998.         //        to be able to backtrack and delete our results set.  Since our
  999.         //        list of results set is not kept by us, we will pass in our rs
  1000.         //        pointer and let InternalDestroyResultsWindow find the correct
  1001.         //        node to destroy. 
  1002.         //
  1003.         case WM_SYSCOMMAND:
  1004.             {
  1005.             lpRESULTSINFO    rwi = lpActiveResults;
  1006.  
  1007.             if(wParam == SC_CLOSE)
  1008.                 DestroyResultsWindow(lpActiveConn, rwi);
  1009.             }
  1010.             break;
  1011.  
  1012.         default:
  1013.             break;
  1014.         }
  1015.  
  1016.     //
  1017.     // If we haven't already processed the message, do default behavior.
  1018.     //
  1019.     return DefMDIChildProc(hwnd, msg, wParam, lParam);
  1020. }
  1021.  
  1022.  
  1023. //*------------------------------------------------------------------------
  1024. //| SetMetaDataFromSql:
  1025. //|    This function will walk through the columns of a results set and
  1026. //|        set the meta data accordingly.  If the caller so chooses, they
  1027. //|        may update the default values after calling this function.
  1028. //| Parms:
  1029. //|    in            hwndOut                    Where to write output
  1030. //|    in            hstmt                        Statement handle with results set
  1031. //|    in            rs                            Pointer to results set
  1032. //|    in            cbCols                    Number of results cols
  1033. //| Returns:
  1034. //|    TRUE if successful, FALSE otherwise
  1035. //*------------------------------------------------------------------------
  1036. BOOL SetMetaDataFromSql(HWND hwndOut, HSTMT hstmt, RESULTSSET FAR * rs, int cbCols)
  1037. {
  1038.     int            dex;
  1039.     char            szColumnName[MAXBUFF];
  1040.     SWORD            fSqlType;
  1041.     UDWORD        precision;
  1042.     SWORD            scale;
  1043.     SWORD            fNullable;
  1044.     RETCODE        retcode;
  1045.     
  1046.     for(dex=0;  dex<cbCols;  dex++) {
  1047.         retcode = SQLDescribeCol(hstmt, 
  1048.                 (UWORD)(dex+1), (LPSTR)szColumnName, 
  1049.                 sizeof(szColumnName),
  1050.                 NULL, 
  1051.                 &fSqlType, &precision, &scale, &fNullable);
  1052.         if(!*szColumnName)            // Some drivers don't return names for computed columns
  1053.             wsprintf(szColumnName, GetidsString(idsExpression, OutStr, MAXBUFF), dex);
  1054.         if(retcode != SQL_SUCCESS) 
  1055.             PrintErrorsHwnd(hwndOut, NULL, NULL, hstmt);
  1056.         else 
  1057.             SetMetaDataColumn(rs, dex, (LPSTR)szColumnName, 
  1058.                     GetTypeName(SQL_TYPE, fSqlType), fSqlType, 
  1059.                     (UDWORD)precision, (SWORD)scale,
  1060.                     (int)(min(MAXBYTES, precision)), TA_LEFT);
  1061.         }
  1062.     return TRUE;
  1063. }
  1064.  
  1065.  
  1066. //*------------------------------------------------------------------------
  1067. //| FetchAllRecordsToResults:
  1068. //|    This function will fetch each row, convert it to char, then add it
  1069. //|        to the results set we're querying.
  1070. //| Parms:
  1071. //|    in            hwnd                        Where to write output
  1072. //|    in            hstmt                        Statement handle with results set
  1073. //|    in            rs                            Pointer to results set
  1074. //|    in            cbCols                    Number of columns
  1075. //|    in            fFetch                    TRUE if need to fetch first record
  1076. //| Returns:
  1077. //|    Number of records which were fetched.
  1078. //*------------------------------------------------------------------------
  1079. SDWORD FetchAllRecordsToResults(HWND hwndOut, HSTMT hstmt,
  1080.                 RESULTSSET FAR * rs, int cbCols, BOOL fFetch)
  1081. {
  1082. #define MAXRSLTSIZE 65535
  1083.     int                        dex;
  1084.     ROWDATA FAR *            rd;
  1085.     LPSTR                        inbuff, outbuff;
  1086.     SDWORD                    cNull;
  1087.     RETCODE                    retcode;
  1088.     SDWORD                    cnt=0;
  1089.     COLORREF                    rgbDft=GetDefaultRGB();
  1090.  
  1091.     //
  1092.     // First get some memory to work with
  1093.     //
  1094.     inbuff = (LPSTR)GetMemory(MAXRSLTSIZE);
  1095.     outbuff = (LPSTR)GetMemory(MAXRSLTSIZE);
  1096.     if(!inbuff ||
  1097.         !outbuff) 
  1098.         return FALSE;
  1099.             
  1100.     //
  1101.     //    Now fetch each row, do a getdata on each column, convert it to char, then
  1102.     //        add it to the results set.  The caller has the option of fetching the 
  1103.     //        first row.  This is required, as the caller may have needed to find
  1104.     //        out if there was a results set.
  1105.     //
  1106.     if(fFetch)
  1107.         retcode = SQLFetch(hstmt);
  1108.     else
  1109.         retcode = SQL_SUCCESS;
  1110.     while(retcode != SQL_NO_DATA_FOUND) {
  1111.         // Increment count and enforce max records
  1112.         if(cnt > MAXRECORDS) {
  1113.             szMessageBox(hwndOut,
  1114.                         MB_ICONEXCLAMATION + MB_OK,
  1115.                         "Limit",
  1116.                         GetidsString(idsMaxRecords, OutStr, MAXBUFF),
  1117.                         MAXRECORDS);
  1118.             SQLFreeStmt(hstmt, SQL_CLOSE);
  1119.             goto exit00;
  1120.             }
  1121.         ++cnt;
  1122.             
  1123.         if(retcode != SQL_SUCCESS)
  1124.             PrintErrorsHwnd(hwndOut, NULL, NULL, hstmt);
  1125.         else {
  1126.             //
  1127.             //    Loop through each column and retrieve it's value
  1128.             //
  1129.             rd = AllocateRowData(rs, rgbDft, RDATA_DEFAULT_BKGRND);
  1130.             for(dex=0;  dex<cbCols;  dex++) {
  1131.                 retcode = SQLGetData(hstmt, (UWORD)(dex+1), 
  1132.                                 SQL_C_DEFAULT, 
  1133.                                 (PTR)inbuff, 
  1134.                                 rs->md[dex].precision + 1, 
  1135.                                 &cNull);
  1136.                 if(retcode != SQL_SUCCESS) {
  1137.                     PrintErrorsHwnd(hwndOut, NULL, NULL, hstmt);
  1138.                     SetColumnData(dex, rd, (LPSTR)szErrorVal);
  1139.                     }
  1140.                 else {
  1141.                     if(cNull != SQL_NULL_DATA)
  1142.                         ConvertSqlTypeToChar(rs, dex, inbuff, outbuff, cNull);
  1143.                     SetColumnData(dex, rd, 
  1144.                             (cNull == SQL_NULL_DATA) ? (LPSTR)szNullString : outbuff);
  1145.                     }
  1146.                 }        // End of loop through columns
  1147.             if(AddRowData(rs, rd) == LB_ERRSPACE)
  1148.                 goto exit00;
  1149.             }
  1150.         
  1151.         retcode = SQLFetch(hstmt);
  1152.         }                // End of fetch loop
  1153.  
  1154. exit00:    
  1155.     ReleaseMemory(inbuff);
  1156.     ReleaseMemory(outbuff);
  1157.     
  1158.     return cnt;
  1159. }
  1160.  
  1161.  
  1162. //*------------------------------------------------------------------------
  1163. //| PrintAffectedRows:
  1164. //|    This function will print out the "Affected rows" message based on
  1165. //|    row count.
  1166. //| Parms:
  1167. //|    in            hstmt                        Statement handle to use
  1168. //|    in            hwnd                        Where to write output
  1169. //| Returns:
  1170. //|    Number of records which were affected
  1171. //*------------------------------------------------------------------------
  1172. SDWORD PrintAffectedRows(HSTMT hstmt, HWND hwnd)
  1173. {
  1174.     SDWORD     cbRowCount;
  1175.     RETCODE     retcode;
  1176.     
  1177.     //
  1178.     // Use SQLRowCount to see how many rows were affected.  It is possible
  1179.     //        that the driver does not know, in which case -1 will be returned.
  1180.     //        in this case, we assume success.
  1181.     //
  1182.     retcode = SQLRowCount(hstmt, &cbRowCount);
  1183.     if(cbRowCount > 0) 
  1184.         szWrite(hwnd, 
  1185.                     GetidsString(idsAffectedRows, OutStr, MAXBUFF),
  1186.                     cbRowCount);
  1187.     else
  1188.         szWrite(hwnd, GetidsString(idsStmtProcessed, OutStr, MAXBUFF));
  1189.         
  1190.     return cbRowCount;
  1191. }
  1192.