home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 32 / IOPROG_32.ISO / SOFT / SqlEval7 / devtools / samples / ODS / xp_odbc / xp_odbc.c next >
Encoding:
C/C++ Source or Header  |  1998-10-12  |  14.9 KB  |  496 lines

  1. // This is an example of an extended procedure DLL built with Open Data
  2. // Services. The functions within the DLL can be invoked by using the extended
  3. //      stored procedures support in SQL Server.  To register the functions 
  4. // and allow all users to use them run the ISQL script XP_ODBC.SQL.
  5. //
  6. // For further information on Open Data Services refer to the Microsoft Open 
  7. // Data Services Programmer's Reference.
  8. //
  9. //  The extended procedures implemented in this DLL is:
  10. //
  11. //  XP_GETTABLE_ODBC -- Used to show the creation of a new connection to
  12. //  SQL Server using ODBC that is bound to the initial client connection
  13. #include <windows.h>
  14. #include <tchar.h>
  15. #include <string.h>
  16. #include <sql.h>
  17. #include <sqlext.h>
  18. #include <odbcss.h>
  19. #include <srv.h>
  20.  
  21. // Miscellaneous defines.
  22. #define XP_NOERROR              0
  23. #define XP_ERROR                1
  24.  
  25. // Extended procedure error codes.
  26. #define SRV_MAXERROR            50000
  27. #define GETTABLE_ERROR          SRV_MAXERROR + 1
  28.  
  29. #define REMOTE_FAIL             4002
  30.  
  31. void handle_odbc_err(PSTR szODBCApi,
  32.     SQLRETURN sret,
  33.     DBINT msgnum,
  34.     SQLHANDLE herror,
  35.     SQLSMALLINT htype,
  36.     SRV_PROC* srvproc);
  37.  
  38. // It is highly recommended that all Microsoft« SQL Server (7.0 
  39. // and greater) extended stored procedure DLLs implement and export 
  40. // __GetXpVersion. For more information see SQL Server 
  41. // Books Online
  42. ULONG __GetXpVersion()
  43.  
  44. {
  45.     return ODS_VERSION;
  46. }
  47.  
  48.  
  49. // XP_GETTABLE_ODBC
  50. //    Returns the result of the SQL statement
  51. //              select * from <szTable>
  52. //
  53. // Parameters:
  54. //    srvproc - the handle to the client connection that 
  55. //    got the SRV_CONNECT.
  56. //
  57. // Returns:
  58. //    XP_NOERROR
  59. //    XP_ERROR
  60. //
  61. // Side Effects:
  62. //    Returns messages and/or a result set to client. 
  63. RETCODE xp_gettable_odbc(srvproc)
  64. SRV_PROC *srvproc;
  65. {
  66.  
  67.     HENV        henv        = SQL_NULL_HENV;
  68.     HDBC        hdbc        = SQL_NULL_HDBC;
  69.     HSTMT       hstmt       = SQL_NULL_HSTMT;
  70.     SQLRETURN   sret;
  71.     RETCODE     rc;
  72.  
  73.     char        acBindToken[256];
  74.  
  75.     // ODBC column attributes.
  76.     TCHAR       acColumnName[MAXNAME];
  77.     SQLINTEGER  cbColData;
  78.     SQLSMALLINT eSQLType;
  79.     SQLINTEGER  iNumAttr;
  80.     SQLSMALLINT cbAttr;                // pointer to storage for descriptor info
  81.     PBYTE*      ppData      = NULL;
  82.     SQLINTEGER* pIndicators = NULL;
  83.     DBINT       rows        = 0L;           // number of rows sent
  84.     PTSTR       szDSN       = _T("local");  // for integrated security to work you need to 
  85.                                             // specify a local server in the ODBC setting
  86.                                             // in the Control Panel in Windows
  87.     int         bImpersonated;
  88.  
  89.     TCHAR       acUID[MAXNAME];
  90.     TCHAR       acPWD[MAXNAME];
  91.  
  92.     int         nParams;
  93.     DBINT       paramtype;
  94.  
  95.     TCHAR       szTable[MAXNAME * 3];   // database.owner.table
  96.     TCHAR       szExec[128 + (MAXNAME * 3)];
  97.     SQLSMALLINT nCols;
  98.     SQLSMALLINT nCol;
  99.  
  100.     RETCODE     rcXP = XP_ERROR;        // Assume failure until shown otherwise.
  101.  
  102.     // Get number of parameters.
  103.     nParams = srv_rpcparams(srvproc);   
  104.  
  105.     // Check number of parameters
  106.     if (nParams != 1) {
  107.         // Send error message and return
  108.         srv_sendmsg(srvproc, SRV_MSG_ERROR, GETTABLE_ERROR, SRV_INFO, (DBTINYINT)0,
  109.             NULL, 0, 0, "Error executing extended stored procedure: Invalid Parameter",
  110.             SRV_NULLTERM);
  111.  
  112.         // A SRV_DONE_MORE instead of a SRV_DONE_FINAL must complete the
  113.         // result set of an Extended Stored Procedure.
  114.         srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_MORE), 0, 0);
  115.         return(XP_ERROR);
  116.         }
  117.  
  118.     // If parameter is not varchar (should be a table name), send an
  119.     // error and return.
  120.     paramtype = srv_paramtype(srvproc, nParams);
  121.     if (paramtype != SRVVARCHAR) {
  122.         srv_sendmsg(srvproc, SRV_MSG_ERROR, GETTABLE_ERROR, SRV_INFO, (DBTINYINT)0,
  123.             NULL, 0, 0,
  124.             "Error executing extended stored procedure: Invalid Parameter Type",
  125.             SRV_NULLTERM);
  126.  
  127.         // A SRV_DONE_MORE instead of a SRV_DONE_FINAL must complete the
  128.         // result set of an Extended Stored Procedure.
  129.         srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_MORE), 0, 0);
  130.         return(XP_ERROR);
  131.         }
  132.  
  133.     // Terminate parameter string with NULL.
  134.     memcpy(szTable, srv_paramdata(srvproc, 1),
  135.         srv_paramlen(srvproc, 1));
  136.     szTable[srv_paramlen(srvproc, 1)] = '\0';
  137.     
  138.     // Allocate an ODBC environment handle
  139.     sret = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
  140.     if (sret != SQL_SUCCESS) {
  141.         handle_odbc_err("SQLAllocHandle:Env",
  142.             sret, 
  143.             (DBINT) REMOTE_FAIL,
  144.             henv,
  145.             SQL_HANDLE_ENV,
  146.             srvproc);
  147.         return(XP_ERROR);
  148.         }
  149.     SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3,
  150.         SQL_IS_INTEGER);
  151.  
  152.     // Allocate an ODBC connection handle
  153.     sret = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
  154.     if (sret != SQL_SUCCESS) {
  155.         handle_odbc_err("SQLAllocHandle:Dbc",
  156.             sret, 
  157.             (DBINT)REMOTE_FAIL,
  158.             henv, 
  159.             SQL_HANDLE_ENV, 
  160.             srvproc);
  161.  
  162.         SQLFreeHandle(SQL_HANDLE_ENV, henv);
  163.         return(XP_ERROR);
  164.         }
  165.  
  166.     // Check for integrated security.
  167.     if (strcmp(srv_pfield(srvproc, SRV_LSECURE, (int *)NULL), "TRUE") == 0) {
  168.         // Client has accessed using some form of integrated security
  169.         // Impersonate client and set SQL_INTEGRATED_SECURITY option
  170.         bImpersonated = srv_impersonate_client(srvproc);
  171.  
  172.         // Connect to DSN using integrated security
  173.         SQLSetConnectAttr(hdbc, SQL_INTEGRATED_SECURITY, 
  174.             (SQLPOINTER) SQL_IS_ON, SQL_IS_INTEGER);
  175.         _tcscpy(acUID, _T(""));
  176.         _tcscpy(acPWD, _T(""));
  177.         }
  178.     else {
  179.         // Client used standard login. Set the user name and password.
  180. #ifdef UNICODE
  181.         MultiByteToWideChar(CP_ACP, 0, srv_pfield(srvproc, SRV_USER, NULL),
  182.             -1, acUID, MAXNAME);
  183.         MultiByteToWideChar(CP_ACP, 0, srv_pfield(srvproc, SRV_PWD, NULL),
  184.             -1, acPWD, MAXNAME);
  185. #else
  186.         strncpy(acUID, srv_pfield(srvproc, SRV_USER, NULL),
  187.             MAXNAME);
  188.         strncpy(acPWD, srv_pfield(srvproc, SRV_PWD, NULL),
  189.             MAXNAME);
  190. #endif
  191.         }
  192.  
  193.     if (!SQL_SUCCEEDED(
  194.         sret = SQLConnect(hdbc, (SQLTCHAR*) szDSN, SQL_NTS, 
  195.         (SQLTCHAR*) acUID, SQL_NTS, (SQLTCHAR*) acPWD, SQL_NTS)
  196.         )) {
  197.         handle_odbc_err("SQLConnect",
  198.             sret, 
  199.             (DBINT)REMOTE_FAIL,
  200.             hdbc, 
  201.             SQL_HANDLE_DBC, 
  202.             srvproc);
  203.         goto SAFE_EXIT;
  204.         } 
  205.  
  206.     // Process data after successful connection 
  207.     sret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  208.     if (sret != SQL_SUCCESS) {
  209.         handle_odbc_err("SQLAllocHandle",
  210.             sret, 
  211.             (DBINT)REMOTE_FAIL,
  212.             hdbc, 
  213.             SQL_HANDLE_DBC, 
  214.             srvproc);
  215.         return(XP_ERROR);
  216.         }
  217.  
  218.     // Get the client session token...
  219.     rc = srv_getbindtoken(srvproc, acBindToken);
  220.     if (rc == FAIL) {
  221.         srv_sendmsg(srvproc,
  222.             SRV_MSG_ERROR,
  223.             GETTABLE_ERROR,
  224.             SRV_INFO,
  225.             (DBTINYINT) 0,
  226.             NULL,
  227.             0,
  228.             0,
  229.             "Error with srv_getbindtoken",
  230.             SRV_NULLTERM);
  231.         srv_senddone(srvproc, (SRV_DONE_ERROR | SRV_DONE_MORE), 0, 0);
  232.         return(XP_ERROR);
  233.         }
  234.  
  235.     // ...bind it as an ODBC parameter for the stored procedure call...
  236.     _tcscpy(szExec, _T("{call sp_bindsession(?)}"));
  237.     sret = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
  238.         SQL_VARCHAR, 255, 0, acBindToken, 256, NULL);
  239.     if (sret != SQL_SUCCESS) {
  240.         handle_odbc_err("SQLBindParameter",
  241.             sret, 
  242.             (DBINT)REMOTE_FAIL,
  243.             hstmt,
  244.             SQL_HANDLE_STMT,
  245.             srvproc);
  246.         return(XP_ERROR);
  247.         }
  248.  
  249.     // ...and, using sp_bindsession, bind our session to the client's
  250.     // session so that we share transaction space.
  251.     sret = SQLExecDirect(hstmt, (SQLTCHAR*) szExec, SQL_NTS);
  252.     if (!((sret == SQL_SUCCESS) ||(sret == SQL_SUCCESS_WITH_INFO))) {
  253.         handle_odbc_err("SQLExecDirect",
  254.             sret, 
  255.             (DBINT) GETTABLE_ERROR,
  256.             hstmt,
  257.             SQL_HANDLE_STMT,
  258.             srvproc);
  259.         return(XP_ERROR);
  260.         }
  261.     SQLFreeStmt(hstmt, SQL_RESET_PARAMS);
  262.  
  263.     // SELECT the result set.
  264.     _tcscpy(szExec, _T("SELECT * FROM  "));
  265.     _tcscat(szExec, szTable);
  266.     sret = SQLExecDirect(hstmt, (SQLTCHAR*) szExec, SQL_NTS);
  267.     if (sret != SQL_SUCCESS) {
  268.         handle_odbc_err("SQLExecDirect",
  269.             sret, 
  270.             (DBINT) GETTABLE_ERROR,
  271.             hstmt,
  272.             SQL_HANDLE_STMT,
  273.             srvproc);
  274.         return(XP_ERROR);
  275.         }
  276.  
  277.     // Get the number of columns in the ODBC result set.
  278.     SQLNumResultCols(hstmt, &nCols);
  279.  
  280.     ppData = (PBYTE*) malloc(nCols * sizeof(PBYTE));
  281.     pIndicators = malloc(nCols * sizeof(SQLINTEGER));
  282.  
  283.     if (ppData == NULL || pIndicators == NULL)
  284.         goto SAFE_EXIT;
  285.  
  286.     // Build the column description for this results set.
  287.     for (nCol = 0; nCol < nCols; nCol++) {
  288.         // Get the column name, length and data type.
  289.         SQLColAttribute(hstmt, 
  290.             (SQLSMALLINT) (nCol + 1), 
  291.             SQL_DESC_NAME, 
  292.             (SQLTCHAR*) acColumnName,   // returned column name
  293.             MAXNAME,            // max length of rgbDesc buffer
  294.             &cbAttr,            // number of bytes returned in rgbDesc
  295.             &iNumAttr);
  296.  
  297.         SQLColAttribute(hstmt, 
  298.             (SQLSMALLINT) (nCol + 1), 
  299.             SQL_DESC_OCTET_LENGTH, 
  300.             NULL, 
  301.             0,              
  302.             NULL,           
  303.             &cbColData);     
  304.     
  305.         // Get the column's SQL Server data type, then reset the length
  306.         // of the data retrieved as required.
  307.         SQLColAttribute(hstmt, 
  308.             (SQLSMALLINT) (nCol + 1), 
  309.             SQL_CA_SS_COLUMN_SSTYPE, 
  310.             NULL, 
  311.             0,              
  312.             NULL,           
  313.             &eSQLType);
  314.  
  315.         // Over-write the column length returned by ODBC with the correct value
  316.         //    to be used by ODS
  317.         switch( eSQLType )  {
  318.             case SQLMONEYN:
  319.             case SQLMONEY:
  320.                 cbColData = sizeof(DBMONEY);
  321.                 break;
  322.  
  323.             case SQLDATETIMN:
  324.             case SQLDATETIME:
  325.                 cbColData = sizeof(DBDATETIME);
  326.                 break;
  327.  
  328.             case SQLNUMERIC:
  329.             case SQLDECIMAL:
  330.                 cbColData = sizeof(DBNUMERIC);
  331.                 break;
  332.  
  333.  
  334.             case SQLMONEY4:
  335.                 cbColData = sizeof(DBMONEY4);
  336.                 break;
  337.  
  338.             case SQLDATETIM4:                       //smalldatetime
  339.                 cbColData = sizeof(DBDATETIM4);
  340.                 break;
  341.             }
  342.  
  343.  
  344.         // Allocate memory for row data.
  345.         if ((ppData[nCol] = (PBYTE) malloc(cbColData)) == NULL)
  346.             goto SAFE_EXIT;
  347.  
  348.         memset(ppData[nCol], 0, cbColData);
  349.  
  350.         // Bind column 
  351.         SQLBindCol(hstmt, 
  352.             (SQLSMALLINT) (nCol + 1), 
  353.             SQL_C_BINARY,           // No data conversion.
  354.             ppData[nCol],           
  355.             cbColData,              
  356.             &(pIndicators[nCol]));
  357.     
  358.         
  359.         // Prepare structure that will be sent via ODS back to
  360.         // the caller of the extended procedure
  361.         srv_describe(srvproc, 
  362.             nCol + 1, 
  363.             acColumnName, 
  364.             SRV_NULLTERM,
  365.             eSQLType,               // Dest data type.
  366.             (DBINT) cbColData,      // Dest data length.
  367.             eSQLType,               // Source data type.
  368.             (DBINT) cbColData,      // Source data length.
  369.             (PBYTE) NULL);
  370.         }
  371.  
  372.     // Initialize the row counter
  373.     rows = 0;
  374.  
  375.     // Get each row of data from ODBC until there are no more rows
  376.     while((sret = SQLFetch(hstmt)) != SQL_NO_DATA_FOUND) {
  377.         if (!SQL_SUCCEEDED(sret)) {
  378.             handle_odbc_err("SQLFetch",
  379.                 sret, 
  380.                 (DBINT) GETTABLE_ERROR,
  381.                 hstmt,
  382.                 SQL_HANDLE_STMT,
  383.                 srvproc);
  384.             goto SAFE_EXIT;
  385.             }
  386.  
  387.         // For each data field in the current row, fill the structure
  388.         // that will be sent back to the caller.
  389.         for (nCol = 0; nCol < nCols; nCol++) {
  390.             cbColData = (pIndicators[nCol] == SQL_NULL_DATA ?
  391.                 0 : pIndicators[nCol]);
  392.             srv_setcollen(srvproc, nCol+1, (int) cbColData);
  393.             srv_setcoldata(srvproc, nCol+1, ppData[nCol]);
  394.             }
  395.  
  396.         // Send the data row back to SQL Server via ODS.
  397.         if (srv_sendrow(srvproc) == SUCCEED)
  398.             rows++;
  399.         }
  400.  
  401.     if (rows > 0)
  402.         srv_senddone(srvproc, SRV_DONE_MORE | SRV_DONE_COUNT, (DBUSMALLINT)0, rows);
  403.     else
  404.         srv_senddone(srvproc, SRV_DONE_MORE, (DBUSMALLINT)0, (DBINT)0);
  405.  
  406.  
  407.     // We got here successfully, let the client know.
  408.     rcXP = XP_NOERROR;
  409.  
  410. SAFE_EXIT:
  411.     // Free the data buffers.
  412.     if (ppData != NULL)
  413.         {
  414.         for (nCol = 0; nCol < nCols; nCol++)
  415.             free(ppData[nCol]);
  416.  
  417.         free(ppData);
  418.         }
  419.     if (pIndicators != NULL)
  420.         free(pIndicators);
  421.     
  422.     // Free handles.
  423.     if (hstmt != SQL_NULL_HSTMT)
  424.         SQLFreeStmt(hstmt, SQL_DROP);
  425.  
  426.     if (hdbc != SQL_NULL_HDBC)
  427.         {
  428.         SQLDisconnect(hdbc);
  429.         SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
  430.         }
  431.  
  432.     if (henv != SQL_NULL_HENV)
  433.         SQLFreeEnv(henv);
  434.  
  435.     // Revert back to SQL Server's user account.
  436.     if( bImpersonated )
  437.         srv_revert_to_self(srvproc);
  438.  
  439.     
  440.     return (rcXP);
  441. }
  442.  
  443.  
  444. // HANDLE_ODBC_ERR
  445. //  This routine is called to send messages to clients when an ODBC
  446. //  function returns what could be considered an error (e.g., SQL_ERROR,
  447. //  SQL_INVALID_HANDLE).
  448. //
  449. // Parameters:
  450. //  szODBCApi   - The name of the failing function.
  451. //  srODBAPI    - The SQLRETURN of the failing function.
  452. //  msgnum      - The ODS user message code.
  453. //  herror      - The ODBC handle involved in the error.
  454. //  htype       - The ODBC handle type.
  455. //  srvproc     - Contains additional client information.
  456. //
  457. // Returns:
  458. //      none
  459. //
  460. void handle_odbc_err(PSTR szODBCApi,
  461.     SQLRETURN sret,
  462.     DBINT msgnum,
  463.     SQLHANDLE herror,
  464.     SQLSMALLINT htype,
  465.     SRV_PROC* srvproc)
  466. {
  467.     SQLTCHAR    szErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1];
  468.     SQLSMALLINT cbErrorMsg;
  469.     SQLSMALLINT nRec = 1;
  470.  
  471.     // If sret is SQL_SUCCESS, return without doing anything
  472.     if (sret == SQL_SUCCESS)
  473.         return;
  474.  
  475.     while (
  476.         SQLGetDiagField(htype, herror, nRec++, SQL_DIAG_MESSAGE_TEXT,
  477.         szErrorMsg, SQL_MAX_MESSAGE_LENGTH, &cbErrorMsg) 
  478.         == SQL_SUCCESS)
  479.         {
  480.         // If sret is SUCCESS_WITH_INFO, send as "message" (severity 
  481.         // <= 10, we use zero), else send to client as "error"
  482.         // (severity > 10, we use 11).
  483.         srv_sendmsg(srvproc, 
  484.             SRV_MSG_INFO, 
  485.             msgnum, 
  486.             (DBTINYINT) (sret == SQL_SUCCESS_WITH_INFO ? 0 : 11),
  487.             (DBTINYINT) 1, 
  488.             NULL, 
  489.             0, 
  490.             0, 
  491.             szErrorMsg,
  492.             SRV_NULLTERM);
  493.         }
  494. }
  495.  
  496.