Partilhar via


Recuperando parâmetros de saída usando SQLGetData

Antes do ODBC 3.8, uma aplicação só podia recuperar os parâmetros de saída de uma consulta com um buffer de saída limitado. No entanto, é difícil alocar um buffer muito grande quando o tamanho do valor do parâmetro é muito grande (por exemplo, uma imagem grande). O ODBC 3.8 introduz uma nova forma de recuperar parâmetros de saída em partes. Uma aplicação pode agora chamar SQLGetData com um pequeno buffer várias vezes para recuperar um valor de parâmetro elevado. Isto é semelhante a recuperar grandes volumes de dados de coluna.

Para associar um parâmetro de saída ou um parâmetro de entrada/saída a recuperar em partes, chame SQLBindParameter com o argumento InputOutputType definido como SQL_PARAM_OUTPUT_STREAM ou SQL_PARAM_INPUT_OUTPUT_STREAM. Com SQL_PARAM_INPUT_OUTPUT_STREAM, uma aplicação pode usar SQLPutData para introduzir dados no parâmetro e depois usar SQLGetData para recuperar o parâmetro de saída. Os dados de entrada devem estar na forma data-at-execution (DAE), usando SQLPutData em vez de os associar a um buffer pré-alocado.

Esta funcionalidade pode ser usada por aplicações ODBC 3.8 ou por aplicações ODBC 3.x e ODBC 2.x recompiladas, e estas aplicações devem ter um driver ODBC 3.8 que suporte a recuperação de parâmetros de saída usando SQLGetData e ODBC 3.8 Driver Manager. Para informações sobre como permitir que uma aplicação antiga utilize novas funcionalidades ODBC, consulte Matriz de Compatibilidade.

Exemplo de uso

Por exemplo, considere executar um procedimento armazenado, {CALL sp_f(?,?)}, onde ambos os parâmetros são ligados como SQL_PARAM_OUTPUT_STREAM e o procedimento armazenado não retorna conjunto de resultados (mais adiante neste tópico encontrará um cenário mais complexo):

  1. Para cada parâmetro, chame SQLBindParameter com InputOutputType definido para SQL_PARAM_OUTPUT_STREAM e ParameterValuePtr definido para um token, como um número de parâmetro, um ponteiro para dados ou um ponteiro para uma estrutura que a aplicação usa para associar parâmetros de entrada. Este exemplo usará o ordinal do parâmetro como token.

  2. Executa a consulta com SQLExecDirect ou SQLExecute. SQL_PARAM_DATA_AVAILABLE será devolvido, indicando que existem parâmetros de saída em streaming disponíveis para recuperação.

  3. Chame SQLParamData para obter o parâmetro disponível para recuperar. O SQLParamData devolverá SQL_PARAM_DATA_AVAILABLE com o token do primeiro parâmetro disponível, que está definido em SQLBindParameter (passo 1). O token é devolvido no buffer para o qual o ValuePtrPtr aponta.

  4. Chame SQLGetData com o argumento Col_or_Param_Num definido para o ordinal do parâmetro para recuperar os dados do primeiro parâmetro disponível. Se o SQLGetData devolve SQL_SUCCESS_WITH_INFO e SQLState 01004 (dados truncados), e o tipo for de comprimento variável tanto no cliente como no servidor, há mais dados para recuperar do primeiro parâmetro disponível. Podes continuar a chamar SQLGetData até que ele retorne SQL_SUCCESS ou SQL_SUCCESS_WITH_INFO com um SQLState diferente.

  5. Repita o passo 3 e o passo 4 para recuperar o parâmetro atual.

  6. Chame SQLParamData novamente. Se devolver algo que não seja SQL_PARAM_DATA_AVAILABLE, não há mais dados de parâmetros transmitidos para recuperar, e o código de retorno será o código de retorno da próxima instrução executada.

  7. Chame SQLMoreResults para processar o próximo conjunto de parâmetros até que este retorne SQL_NO_DATA. O SQLMoreResults devolverá SQL_NO_DATA neste exemplo se o atributo da instrução SQL_ATTR_PARAMSET_SIZE estiver definido como 1. Caso contrário, o SQLMoreResults devolverá SQL_PARAM_DATA_AVAILABLE para indicar que existem parâmetros de saída transmitidos disponíveis para o próximo conjunto de parâmetros a recuperar.

Semelhante a um parâmetro de entrada DAE, o token usado no argumento ParameterValuePtr em SQLBindParameter (passo 1) pode ser um ponteiro que aponta para uma estrutura de dados de aplicação, que contém o ordinal do parâmetro e informações mais específicas da aplicação, se necessário.

A ordem dos parâmetros de saída ou entrada/saída devolvidos é específica do driver e pode nem sempre ser a mesma que a ordem especificada na consulta.

Se a aplicação não chamar SQLGetData no passo 4, o valor do parâmetro é descartado. De forma semelhante, se a aplicação chamar SQLParamData antes de todo o valor de um parâmetro ter sido lido pelo SQLGetData, o restante valor é descartado e a aplicação pode processar o parâmetro seguinte.

Se a aplicação chamar SQLMoreResults antes de todos os parâmetros de saída transmitidos serem processados (o SQLParamData ainda devolve SQL_PARAM_DATA_AVAILABLE), todos os parâmetros restantes são descartados. De forma semelhante, se a aplicação chamar SQLMoreResults antes de todo o valor de um parâmetro ter sido lido pelo SQLGetData, o restante valor e todos os parâmetros restantes são descartados, e a aplicação pode continuar a processar o próximo conjunto de parâmetros.

Note que uma aplicação pode especificar o tipo de dado C tanto em SQLBindParameter como em SQLGetData. O tipo de dado C especificado com SQLGetData sobrepõe-se ao tipo de dado C especificado em SQLBindParameter, a menos que o tipo de dado C especificado em SQLGetData seja SQL_APD_TYPE.

Embora um parâmetro de saída em fluxo seja mais útil quando o tipo de dado do parâmetro de saída é do tipo BLOB, esta funcionalidade também pode ser usada com qualquer tipo de dado. Os tipos de dados suportados pelos parâmetros de saída em fluxo são especificados no driver.

Se houver SQL_PARAM_INPUT_OUTPUT_STREAM parâmetros a processar, SQLExecute ou SQLExecDirect devolverão SQL_NEED_DATA primeiro. Uma aplicação pode chamar SQLParamData e SQLPutData para enviar dados dos parâmetros DAE. Quando todos os parâmetros de entrada do DAE são processados, o SQLParamData devolve SQL_PARAM_DATA_AVAILABLE para indicar que os parâmetros de saída transmitidos estão disponíveis.

Quando existem parâmetros de saída transmitidos e parâmetros de saída limitados a processar, o driver determina a ordem para processar os parâmetros de saída. Assim, se um parâmetro de saída estiver ligado a um buffer (o SQLBindParameterInputOutputType está definido como SQL_PARAM_INPUT_OUTPUT ou SQL_PARAM_OUTPUT), o buffer pode não ser preenchido até que SQLParamData retorne SQL_SUCCESS ou SQL_SUCCESS_WITH_INFO. Uma aplicação deve ler um buffer limitado apenas depois de o SQLParamData devolver SQL_SUCCESS ou SQL_SUCCESS_WITH_INFO ou seja, depois de todos os parâmetros de saída transmitidos serem processados.

A fonte de dados pode devolver um conjunto de avisos e resultados, além do parâmetro de saída transmitido. Em geral, avisos e conjuntos de resultados são processados separadamente de um parâmetro de saída em streaming, chamando SQLMoreResults. Avisos de processo e conjunto de resultados antes de processar o parâmetro de saída em streaming.

A tabela seguinte descreve diferentes cenários de um único comando enviado ao servidor e como a aplicação deve funcionar.

Scenario Valor retornado por SQLExecute ou SQLExecDirect O que fazer a seguir
Os dados incluem apenas parâmetros de saída transmitidos em streaming SQL_PARAM_DATA_AVAILABLE Use SQLParamData e SQLGetData para recuperar parâmetros de saída transmitidos.
Os dados incluem um conjunto de resultados e parâmetros de saída transmitidos em fluxo contínuo. SQL_SUCCESS Recuperar o conjunto de resultados com SQLBindCol e SQLGetData.

Chame SQLMoreResults para começar a processar os parâmetros de saída em streaming. Deve devolver SQL_PARAM_DATA_AVAILABLE.

Use SQLParamData e SQLGetData para recuperar parâmetros de saída transmitidos.
Os dados incluem uma mensagem de aviso e parâmetros de saída transmitidos em streaming SQL_SUCCESS_WITH_INFO Use SQLGetDiagRec e SQLGetDiagField para processar mensagens de aviso.

Chame SQLMoreResults para começar a processar os parâmetros de saída em streaming. Deve devolver SQL_PARAM_DATA_AVAILABLE.

Use SQLParamData e SQLGetData para recuperar parâmetros de saída transmitidos.
Os dados incluem uma mensagem de aviso, conjunto de resultados e parâmetros de saída transmitidos SQL_SUCCESS_WITH_INFO Use SQLGetDiagRec e SQLGetDiagField para processar mensagens de aviso. Depois chama SQLMoreResults para começar a processar o conjunto de resultados.

Recuperar um conjunto de resultados com SQLBindCol e SQLGetData.

Chame SQLMoreResults para começar a processar os parâmetros de saída em streaming. O SQLMoreResults deve devolver SQL_PARAM_DATA_AVAILABLE.

Use SQLParamData e SQLGetData para recuperar parâmetros de saída transmitidos.
Consulta com parâmetros de entrada do DAE, por exemplo, um parâmetro de entrada/saída em fluxo (DAE) SQL NEED_DATA Chame SQLParamData e SQLPutData para enviar dados de parâmetros de entrada DAE.

Depois de todos os parâmetros de entrada do DAE serem processados, o SQLParamData pode devolver qualquer código de retorno que o SQLExecute e o SQLExecDirect possam devolver. Os casos desta tabela podem então ser aplicados.

Se o código de retorno for SQL_PARAM_DATA_AVAILABLE, os parâmetros de saída transmitidos estão disponíveis. Uma aplicação deve chamar novamente o SQLParamData para recuperar o token do parâmetro de saída transmitido, conforme descrito na primeira linha desta tabela.

Se o código de retorno for SQL_SUCCESS, ou existe um conjunto de resultados a processar ou o processamento está completo.

Se o código de retorno for SQL_SUCCESS_WITH_INFO, há mensagens de aviso a processar.

Após SQLExecute, SQLExecDirect ou SQLMoreResults retornarem SQL_PARAM_DATA_AVAILABLE, ocorrerá um erro de sequência de funções se uma aplicação chamar uma função que não esteja na lista seguinte:

  • SQLAllocHandle / SQLAllocHandleStd

  • SQLDataSources / SQLDrivers

  • SQLGetInfo / SQLGetFunctions

  • SQLGetConnectAttr / SQLGetEnvAttr / SQLGetDescField / SQLGetDescRec

  • SQLNumParams

  • SQLDescribeParam

  • SQLNativeSQL

  • SQLParamData

  • SQLMoreResults

  • SQLGetDiagField / SQLGetDiagRec

  • SQLCancel

  • SQLCancelHandle (com manipulador de instrução)

  • SQLFreeStmt (com Opção = SQL_CLOSE, SQL_DROP ou SQL_UNBIND)

  • SQLCloseCursor

  • SQLDisconnect

  • SQLFreeHandle (com o tipo de handle = SQL_HANDLE_STMT)

  • SQLGetStmtAttr

As aplicações ainda podem usar SQLSetDescField ou SQLSetDescRec para definir a informação de ligação. O mapeamento de campo não será alterado. No entanto, campos dentro do descritor podem devolver novos valores. Por exemplo, SQL_DESC_PARAMETER_TYPE pode devolver SQL_PARAM_INPUT_OUTPUT_STREAM ou SQL_PARAM_OUTPUT_STREAM.

Cenário de Utilização: Recuperar uma Imagem em Partes a partir de um Conjunto de Resultados

O SQLGetData pode ser usado para obter dados em partes quando um procedimento armazenado devolve um conjunto de resultados que contém uma linha de metadados sobre uma imagem e a imagem é devolvida num parâmetro de saída grande.

// CREATE PROCEDURE SP_TestOutputPara  
//      @ID_of_picture   as int,  
//      @Picture         as varbinary(max) out  
// AS  
//     output the image data through streamed output parameter  
// GO  
BOOL displayPicture(SQLUINTEGER idOfPicture, SQLHSTMT hstmt) {  
   SQLLEN      lengthOfPicture;    // The actual length of the picture.  
   BYTE        smallBuffer[100];   // A very small buffer.  
   SQLRETURN   retcode, retcode2;  
  
   // Bind the first parameter (input parameter)  
   SQLBindParameter(  
         hstmt,  
         1,                         // The first parameter.   
         SQL_PARAM_INPUT,           // Input parameter: The ID_of_picture.  
         SQL_C_ULONG,               // The C Data Type.  
         SQL_INTEGER,               // The SQL Data Type.  
         0,                         // ColumnSize is ignored for integer.  
         0,                         // DecimalDigits is ignored for integer.  
         &idOfPicture,              // The Address of the buffer for the input parameter.  
         0,                         // BufferLength is ignored for integer.  
         NULL);                     // This is ignored for integer.  
  
   // Bind the streamed output parameter.  
   SQLBindParameter(  
         hstmt,   
         2,                         // The second parameter.  
         SQL_PARAM_OUTPUT_STREAM,   // A streamed output parameter.   
         SQL_C_BINARY,              // The C Data Type.    
         SQL_VARBINARY,             // The SQL Data Type.  
         0,                         // ColumnSize: The maximum size of varbinary(max).  
         0,                         // DecimalDigits is ignored for binary type.  
         (SQLPOINTER)2,             // ParameterValuePtr: An application-defined  
                                    // token (this will be returned from SQLParamData).  
                                    // In this example, we used the ordinal   
                                    // of the parameter.  
         0,                         // BufferLength is ignored for streamed output parameters.  
         &lengthOfPicture);         // StrLen_or_IndPtr: The status variable returned.   
  
   retcode = SQLPrepare(hstmt, L"{call SP_TestOutputPara(?, ?)}", SQL_NTS);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   retcode = SQLExecute(hstmt);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   // Assume that the retrieved picture exists.  Use SQLBindCol or SQLGetData to retrieve the result-set.  
  
   // Process the result set and move to the streamed output parameters.  
   retcode = SQLMoreResults( hstmt );  
  
   // SQLGetData retrieves and displays the picture in parts.  
   // The streamed output parameter is available.  
   while (retcode == SQL_PARAM_DATA_AVAILABLE) {  
      SQLPOINTER token;   // Output by SQLParamData.  
      SQLLEN cbLeft;      // #bytes remained  
      retcode = SQLParamData(hstmt, &token);   // returned token is 2 (according to the binding)  
      if ( retcode == SQL_PARAM_DATA_AVAILABLE ) {  
         // A do-while loop retrieves the picture in parts.  
         do {  
            retcode2 = SQLGetData(   
               hstmt,   
               (UWORD) token,          // the value of the token is the ordinal.   
               SQL_C_BINARY,           // The C-type.  
               smallBuffer,            // A small buffer.   
               sizeof(smallBuffer),    // The size of the buffer.  
               &cbLeft);               // How much data we can get.  
         }  
         while ( retcode2 == SQL_SUCCESS_WITH_INFO );  
      }  
   }  
  
   return TRUE;  
}  

Cenário de Utilização: Enviar e Receber um Objeto Grande como Parâmetro de Entrada/Saída Transmitido

O SQLGetData pode ser usado para obter e enviar dados em partes quando um procedimento armazenado passa um objeto grande como parâmetro de entrada/saída, transmitindo o valor para e da base de dados. Não é necessário armazenar todos os dados na memória.

// CREATE PROCEDURE SP_TestInOut  
//       @picture as varbinary(max) out  
// AS  
//    output the image data through output parameter   
// go  
  
BOOL displaySimilarPicture(BYTE* image, ULONG lengthOfImage, SQLHSTMT hstmt) {  
   BYTE smallBuffer[100];   // A very small buffer.  
   SQLRETURN retcode, retcode2;  
   SQLLEN statusOfPicture;  
  
   // First bind the parameters, before preparing the statement that binds the output streamed parameter.  
   SQLBindParameter(  
      hstmt,   
      1,                                 // The first parameter.  
      SQL_PARAM_INPUT_OUTPUT_STREAM,     // I/O-streamed parameter: The Picture.  
      SQL_C_BINARY,                      // The C Data Type.  
      SQL_VARBINARY,                     // The SQL Data Type.  
      0,                                 // ColumnSize: The maximum size of varbinary(max).  
      0,                                 // DecimalDigits is ignored.   
      (SQLPOINTER)1,                     // An application defined token.   
      0,                                 // BufferLength is ignored for streamed I/O parameters.  
      &statusOfPicture);                 // The status variable.  
  
   statusOfPicture = SQL_DATA_AT_EXEC;   // Input data in parts (DAE parameter at input).  
  
   retcode = SQLPrepare(hstmt, L"{call SP_TestInOut(?) }", SQL_NTS);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   // Execute the statement.  
   retcode = SQLExecute(hstmt);  
   if ( retcode == SQL_ERROR )  
      return FALSE;  
  
   if ( retcode == SQL_NEED_DATA ) {  
      // Use SQLParamData to loop through DAE input parameters.  For  
      // each, use SQLPutData to send the data to database in parts.  
  
      // This example uses an I/O parameter with streamed output.  
      // Therefore, the last call to SQLParamData should return  
      // SQL_PARAM_DATA_AVAILABLE to indicate the end of the input phrase   
      // and report that a streamed output parameter is available.  
  
      // Assume retcode is set to the return value of the last call to  
      // SQLParamData, which is equal to SQL_PARAM_DATA_AVAILABLE.  
   }  
  
   // Start processing the streamed output parameters.  
   while ( retcode == SQL_PARAM_DATA_AVAILABLE ) {  
      SQLPOINTER token;   // Output by SQLParamData.  
      SQLLEN cbLeft;     // #bytes remained  
      retcode = SQLParamData(hstmt, &token);  
      if ( retcode == SQL_PARAM_DATA_AVAILABLE ) {  
         do {  
            retcode2 = SQLGetData(   
               hstmt,   
               (UWORD) token,          // the value of the token is the ordinal.   
               SQL_C_BINARY,           // The C-type.  
               smallBuffer,            // A small buffer.   
               sizeof(smallBuffer),    // The size of the buffer.  
               &cbLeft);               // How much data we can get.  
         }  
         while ( retcode2 == SQL_SUCCESS_WITH_INFO );  
      }  
   }   
  
   return TRUE;  
}  

Ver também

Parâmetros da Declaração