Eseguire una stored procedure mediante la sintassi dell'istruzione ODBC RPC ed elaborare i codici restituiti e i parametri di output (OLE DB)

Le stored procedure di SQL Server possono includere parametri di output e codici restituiti di tipo integer. I codici restituiti e i parametri di output vengono inviati nell'ultimo pacchetto dal server e non sono pertanto disponibili all'applicazione fino al completo rilascio del set di righe. Se il comando restituisce più risultati, i dati dei parametri di output sono disponibili quando IMultipleResults::GetResult restituisce DB_S_NORESULT o l'interfaccia IMultipleResults viene completamente rilasciata, a seconda dell'evento che si verifica per primo.

Nota sulla sicurezzaNota sulla sicurezza

Se possibile, utilizzare l'autenticazione di Windows. Se l'autenticazione di Windows non è disponibile, agli utenti verrà richiesto di immettere le credenziali in fase di esecuzione. Evitare di archiviare le credenziali in un file. Se è necessario rendere persistenti le credenziali, è consigliabile crittografarle utilizzando l'API di crittografia Win32.

Per elaborare i codici restituiti e i parametri di output

  1. Costruire un'istruzione SQL in cui venga utilizzata la sequenza di escape ODBC CALL. Nell'istruzione devono essere utilizzati marcatori di parametro per ogni parametro di input, input/output e output e per il valore restituito della procedura, se disponibile. Per i parametri di input, è possibile utilizzare marcatori di parametro o specificare valori a livello di codice.

  2. Creare un set di associazioni (uno per ogni marcatore di parametro) tramite una matrice di strutture DBBINDING.

  3. Creare una funzione di accesso per i parametri definiti tramite il metodo IAccessor::CreateAccessor. CreateAccessor consente di creare una funzione di accesso da un set di associazioni.

  4. Completare la struttura DBPARAMS.

  5. Chiamare il comando Execute, rappresentato in questo caso da una chiamata a una stored procedure.

  6. Elaborare il set di righe e rilasciarlo utilizzando il metodo IRowset::Release.

  7. Elaborare il codice restituito e i valori del parametro di output ricevuti dalla stored procedure.

Esempio

In questo esempio viene illustrata l'elaborazione di un set di righe, di un codice restituito e di un parametro di output. I set di risultati non vengono elaborati. Questo esempio non è supportato in IA64.

Per l'esempio è necessario il database di esempio AdventureWorks, che è possibile scaricare dalla home page del sito relativo a progetti della community ed esempi per Microsoft SQL Server.

Eseguire il primo listato di codice (Transact-SQL) per creare la stored procedure utilizzata dall'applicazione.

Compilare il secondo listato di codice (C++) con ole32.lib oleaut32.lib ed eseguirlo. In questa applicazione viene eseguita la connessione all'istanza predefinita di SQL Server nel computer in uso. In alcuni sistemi operativi Windows sarà necessario modificare (local) o (localhost) impostando il valore sul nome dell'istanza di SQL Server. Per connettersi a un'istanza denominata, modificare la stringa di connessione da L"(local)" in L"(local)\\nome", dove nome rappresenta l'istanza denominata. Per impostazione predefinita, SQL Server Express viene installato in un'istanza denominata. Verificare che nella variabile di ambiente INCLUDE sia presente la directory che contiene sqlncli.h.

Eseguire il terzo listato di codice (Transact-SQL) per eliminare la stored procedure utilizzata dall'applicazione.

USE AdventureWorks
if exists (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[myProc]'))
   DROP PROCEDURE myProc
GO

CREATE PROCEDURE myProc 
    @inparam nvarchar(5),,
    @outparam int OUTPUT

AS
SELECT Color, ListPrice 
FROM Production.Product WHERE Size > @inparam
SELECT @outparam = 100

IF  (@outparam > 0)
    RETURN 999
ELSE
    RETURN 888
GO

// compile with: ole32.lib oleaut32.lib
void InitializeAndEstablishConnection();

#define UNICODE
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250   // to include correct interfaces

#include <windows.h>
#include <stdio.h>
#include <stddef.h>
#include <iostream>
#include <oledb.h>
#include <oledberr.h>
#include <SQLNCLI.h>

using namespace std;

IDBInitialize* pIDBInitialize = NULL;
IDBCreateSession* pIDBCreateSession = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
ICommandText* pICommandText = NULL;

IRowset* pIRowset = NULL;
ICommandWithParameters* pICommandWithParams = NULL;
IAccessor* pIAccessor = NULL;
IDBProperties* pIDBProperties = NULL;

WCHAR* pStringsBuffer;
DBBINDING* pBindings;
const ULONG nInitProps = 4;
DBPROP InitProperties[nInitProps];

const ULONG nPropSet = 1;
DBPROPSET rgInitPropSet[nPropSet];
HRESULT hr;
HACCESSOR hAccessor;

const ULONG nParams = 3;   // Number of parameters in the command
DBPARAMBINDINFO ParamBindInfo[nParams];
ULONG i;
ULONG cbColOffset = 0;

ULONG ParamOrdinals[nParams];
DBROWCOUNT cNumRows = 0;
DBPARAMS Params;

// Declare an array of DBBINDING structures, one for each parameter in the command.
DBBINDING acDBBinding[nParams];
DBBINDSTATUS acDBBindStatus[nParams];

// The following buffer is used to store parameter values.
typedef struct tagSPROCPARAMS {
   long lReturnValue;
   long outParam;
   long inParam;
} SPROCPARAMS;

int main() {
   // The command to execute.
   WCHAR* wCmdString = L"{? = call myProc(?,?)}";

   SPROCPARAMS sprocparams = {0,0,14};

   // All the initialization activities in a separate function.
   InitializeAndEstablishConnection();

   // Create a new activity from the data source object.
   if ( FAILED(pIDBInitialize->QueryInterface( IID_IDBCreateSession, 
      (void**) &pIDBCreateSession))) {
         cout << "Failed to access IDBCreateSession interface.\n";
         goto EXIT;
   }
   if (FAILED(pIDBCreateSession->CreateSession( NULL, IID_IDBCreateCommand, 
      (IUnknown**) &pIDBCreateCommand))) {
         cout << "pIDBCreateSession->CreateSession failed.\n";
      goto EXIT;
   }

   // Create a Command object.
   if (FAILED(pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, 
      (IUnknown**) &pICommandText))) {
         cout << "Failed to access ICommand interface.\n";
         goto EXIT;
   }

   // Set the command text.
   if (FAILED(pICommandText->SetCommandText(DBGUID_DBSQL, wCmdString))) {
      cout << "Failed to set command text.\n";
      goto EXIT;
   }
   // No need to describe command parameters (parameter name, data type
   // etc) in DBPARAMBINDINFO structure and then SetParameterInfo(). The
   // provider obtains this information by calling appropriate helper
   // function.

   // Describe the consumer buffer by filling in the array of DBBINDING structures.  
   // Each binding associates a single parameter to the consumer's buffer.
   for ( i = 0 ; i < nParams ; i++ ) {
      acDBBinding[i].obLength = 0;
      acDBBinding[i].obStatus = 0;
      acDBBinding[i].pTypeInfo = NULL;
      acDBBinding[i].pObject = NULL;
      acDBBinding[i].pBindExt = NULL;
      acDBBinding[i].dwPart = DBPART_VALUE;
      acDBBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
      acDBBinding[i].dwFlags = 0;
      acDBBinding[i].bScale = 0;
   }   // end for

   acDBBinding[0].iOrdinal = 1;
   acDBBinding[0].obValue = offsetof(SPROCPARAMS, lReturnValue);
   acDBBinding[0].eParamIO = DBPARAMIO_OUTPUT;
   acDBBinding[0].cbMaxLen = sizeof(long);
   acDBBinding[0].wType = DBTYPE_I4;
   acDBBinding[0].bPrecision = 11;

   acDBBinding[1].iOrdinal = 2;
   acDBBinding[1].obValue = offsetof(SPROCPARAMS, inParam);
   acDBBinding[1].eParamIO = DBPARAMIO_INPUT;
   acDBBinding[1].cbMaxLen = sizeof(long);
   acDBBinding[1].wType = DBTYPE_I4;
   acDBBinding[1].bPrecision = 11;

   acDBBinding[2].iOrdinal = 3;
   acDBBinding[2].obValue = offsetof(SPROCPARAMS, outParam);
   acDBBinding[2].eParamIO = DBPARAMIO_OUTPUT;
   acDBBinding[2].cbMaxLen = sizeof(long);
   acDBBinding[2].wType = DBTYPE_I4;
   acDBBinding[2].bPrecision = 11;

   // Create an accessor from the above set of bindings.
   hr = pICommandText->QueryInterface( IID_IAccessor, (void**)&pIAccessor);
   if (FAILED(hr))
      cout << "Failed to get IAccessor interface.\n";

   hr = pIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA, 
                                    nParams,       
                                    acDBBinding, 
                                    sizeof(SPROCPARAMS), 
                                    &hAccessor,
                                    acDBBindStatus);
   if (FAILED(hr))
      cout << "Failed to create accessor for the defined parameters.\n";

   // Fill in DBPARAMS structure for the command execution. This structure 
   // specifies the parameter values in the command and is then passed to Execute.
   Params.pData = &sprocparams;
   Params.cParamSets = 1;
   Params.hAccessor = hAccessor;

   // Execute the command.
   if ( FAILED(hr = pICommandText->Execute( NULL, 
                                            IID_IRowset, 
                                            &Params, 
                                            &cNumRows, 
                                            (IUnknown **) &pIRowset))) {
      cout << "Failed to execute command.\n";
      goto EXIT;
   }

   printf("After command execution but before rowset processing.\n\n");
   printf("  Return value = %d\n", sprocparams.lReturnValue);
   printf("  Output parameter value = %d\n", sprocparams.outParam);
   printf("  These are the same default values set in the application.\n\n\n");

   // Result set is not important in this example; release it without processing.
   pIRowset->Release();

   printf("After processing the result set...\n");
   printf("  Return value = %d\n", sprocparams.lReturnValue);
   printf("  Output parameter value = %d\n\n", sprocparams.outParam);

   // Release memory.
   pIAccessor->ReleaseAccessor(hAccessor, NULL);
   pIAccessor->Release();
   pICommandText->Release();
   pIDBCreateCommand->Release();
   pIDBCreateSession->Release();    
   if (FAILED(pIDBInitialize->Uninitialize()))
      // Uninitialize is not required, but it fails if an interface
      // has not been released.  This can be used for debugging.
      cout << "Problem uninitializing.\n";

   pIDBInitialize->Release();

   CoUninitialize();
   return 0;

EXIT:
   if (pIAccessor != NULL)
      pIAccessor->Release();
   if (pICommandText != NULL)
      pICommandText->Release();
   if (pIDBCreateCommand != NULL)
      pIDBCreateCommand->Release();
   if (pIDBCreateSession != NULL)
      pIDBCreateSession->Release();
   if (pIDBInitialize != NULL)
      if (FAILED(pIDBInitialize->Uninitialize()))
         // Uninitialize is not required, but it fails if an
         // interface has not been released.  This can be used for debugging.
         cout << "Problem in uninitializing.\n";
      pIDBInitialize->Release();

   CoUninitialize();
};

void InitializeAndEstablishConnection() {    
   // Initialize the COM library.
   CoInitialize(NULL);

   // Obtain access to the SQL Server Native Client OLE DB provider.    
   hr = CoCreateInstance( CLSID_SQLNCLI11, 
                          NULL, 
                          CLSCTX_INPROC_SERVER,
                          IID_IDBInitialize, 
                          (void **) &pIDBInitialize);
   if (FAILED(hr))
      cout << "Failed in CoCreateInstance().\n";

   // Initialize the property values needed to establish the connection.
   for ( i = 0 ; i < nInitProps ; i++ )
      VariantInit(&InitProperties[i].vValue);

   // Specify server name.
   InitProperties[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
   InitProperties[0].vValue.vt = VT_BSTR;

   // Replace "MySqlServer" with proper value.
   InitProperties[0].vValue.bstrVal = SysAllocString(L"(local)");
   InitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[0].colid = DB_NULLID;

   // Specify database name.
   InitProperties[1].dwPropertyID = DBPROP_INIT_CATALOG;
   InitProperties[1].vValue.vt = VT_BSTR;
   InitProperties[1].vValue.bstrVal = SysAllocString(L"AdventureWorks");
   InitProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[1].colid = DB_NULLID;

   InitProperties[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
   InitProperties[2].vValue.vt = VT_BSTR;
   InitProperties[2].vValue.bstrVal = SysAllocString(L"SSPI");
   InitProperties[2].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[2].colid = DB_NULLID;

   // Now that properties are set, construct the DBPROPSET structure
   // (rgInitPropSet).  The DBPROPSET structure is used to pass an array
   // of DBPROP structures (InitProperties) to the SetProperties method.
   rgInitPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
   rgInitPropSet[0].cProperties = 4;
   rgInitPropSet[0].rgProperties = InitProperties;

   // Set initialization properties.
   hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties);
   if (FAILED(hr))
      cout << "Failed to obtain IDBProperties interface.\n";

   hr = pIDBProperties->SetProperties(nPropSet, rgInitPropSet);
   if (FAILED(hr))
      cout << "Failed to set initialization properties.\n";

   pIDBProperties->Release();

   // Now establish a connection to the data source.
   if (FAILED(pIDBInitialize->Initialize()))
      cout << "Problem in initializing.\n";
}

USE AdventureWorks
DROP PROCEDURE myProc
GO

Vedere anche

Concetti

Procedure relative all'elaborazione dei risultati (OLE DB)