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