[ODBC-126] Core dump when procedure returns more than 1 result set Created: 2017-12-18  Updated: 2018-06-26  Resolved: 2018-05-15

Status: Closed
Project: MariaDB Connector/ODBC
Component/s: General
Affects Version/s: 3.0.2
Fix Version/s: 3.0.4, 2.0.17

Type: Bug Priority: Critical
Reporter: Anders Karlsson Assignee: Lawrin Novitsky
Resolution: Fixed Votes: 0
Labels: None
Environment:

Linux Centos 7.4.1708



 Description   

When a procedure returns more than 1 result set, fetching from the second result fails in SQLGetData().
The following SQL is used to set up the test:

CREATE OR REPLACE TABLE odbcbug(col1 INT, col2 INT);
INSERT INTO odbcbug VALUES(1,2),(3,4),(5,6);
delimiter //
CREATE OR REPLACE PROCEDURE odbcbugp1()
BEGIN
  SELECT col1, col2 FROM odbcbug;
  SELECT col1 FROM odbcbug;
END;
//
CREATE OR REPLACE PROCEDURE odbcbugp2()
BEGIN
  SELECT col1 FROM odbcbug;
  SELECT col1, col2 FROM odbcbug;
END;
//

And the following cod is used for testing:

#include <stdio.h>
#include <string.h>
#include <sqlext.h>
 
int main(int argc, char *argv[])
   {
   SQLHANDLE hEnv, hConn, hStmt;
   SQLRETURN nRet;
   SQLCHAR szConnStr[2048];
   SQLCHAR szErrMsg[256];
   SQLLEN nInd;
   SQLSMALLINT nCols;
   int i;
   SQLCHAR szSQLState[5];
   SQLINTEGER nNativeErr;
   SQLSMALLINT sLen;
   SQLCHAR szDriverVer[256];
   SQLCHAR *pSQL;
   SQLCHAR szBuf[1024];
 
   if(argc < 2)
      {
      fprintf(stderr, "Usage: %s <SQL>\n", argv[0]);
      return 1;
      }
   pSQL = argv[1];
   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
   SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
   SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hConn);
 
   strcpy(szConnStr, "DSN=MariaDB;UID=root;");
   if((nRet = SQLDriverConnect(hConn, NULL, szConnStr, SQL_NTS, NULL, 0,
     &sLen, SQL_DRIVER_NOPROMPT)) != SQL_SUCCESS)
      {
      SQLError(hEnv, hConn, SQL_NULL_HSTMT, NULL, NULL, szErrMsg, sizeof(szErrMsg), NULL);
      fprintf(stderr, "Error in connecting to MariaDB:\n%s\n", szErrMsg);
 
      return 1;
      }
 
   SQLAllocHandle(SQL_HANDLE_STMT, hConn, &hStmt);
   if((nRet = SQLExecDirect(hStmt, pSQL, SQL_NTS)) != SQL_SUCCESS)
      {
      SQLError(hEnv, hConn, hStmt, NULL, NULL, szErrMsg, sizeof(szErrMsg), NULL);
      fprintf(stderr, "Error in connecting to MariaDB:\n%s\nin\n%s\n", szErrMsg, pSQL);
 
      return 1;
      }
 
   for(;;)
      {
      SQLNumResultCols(hStmt, &nCols);
      printf("Num cols: %d\n", nCols);
 
      while((nRet = SQLFetch(hStmt)) == SQL_SUCCESS)
         {
         for(i = 0; i < nCols; i++)
            {
            if((nRet = SQLGetData(hStmt, i + 1, SQL_C_CHAR, szBuf, sizeof(szBuf),
              &nInd)) != SQL_SUCCESS)
               {
               SQLError(hEnv, hConn, hStmt, NULL, NULL, szErrMsg, sizeof(szErrMsg), NULL);
               fprintf(stderr, "Error in SQLGetData:\n%s\n", szErrMsg);
 
               return 1;
               }
            printf("Col: %d = /%s/\n", i + 1, nInd == SQL_NULL_DATA ? "null" : ((char *) szBuf));
            }
         }
 
      if(nRet != SQL_NO_DATA)
         {
         SQLError(hEnv, hConn, hStmt, NULL, NULL, szErrMsg, sizeof(szErrMsg), NULL);
         fprintf(stderr, "Error in SQLFetch:\n%s\n", szErrMsg);
 
         return 1;
         }
 
      if((nRet = SQLMoreResults(hStmt)) != SQL_SUCCESS)
         {
         if(nRet == SQL_NO_DATA)
            {
            printf("No more results\n");
            break;
            }
         SQLError(hEnv, hConn, hStmt, szSQLState, &nNativeErr, szErrMsg, sizeof(szErrMsg),
           &sLen)  ;
         fprintf(stderr, "Err: %d SQL State: %5.5s\n", nRet, szSQLState);
         fprintf(stderr, "Native Error: %d\n", nNativeErr);
         fprintf(stderr, "Error in SQLMoreResults:\n%s\n", szErrMsg);
 
         return 1;
         }
     }
   return 0;
   }

With the above program compiled:

$ ./odbcbug "call odbcbugp1"
Num cols: 2
Col: 1 = /1/
Col: 2 = /2/
Col: 1 = /3/
Col: 2 = /4/
Col: 1 = /5/
Col: 2 = /6/
Num cols: 1
Col: 1 = /1/
Col: 1 = /3/
Col: 1 = /5/
No more results
$ ./odbcbug "call odbcbugp2"
Num cols: 1
Col: 1 = /1/
Col: 1 = /3/
Col: 1 = /5/
Num cols: 2
Col: 1 = /1/
Segmentation fault (core dumped)

Note that this only fails when using ODBC 3.0.2, when running with 2.0.15 is works as expected.



 Comments   
Comment by Lawrin Novitsky [ 2018-01-10 ]

Some of internal structures weren't reallocated in accordance with the next resultset fields count. Beyond the case described in the report, fixed the case of statements batch. It had similar and own problems. Testcase covers it as well.
Pushed to both odbc-3.0 and odbc-2.0, commits acdd5cd1f9e3c9cbc2a91f165c899141876e6231 and a22bf42cf79c550d9e28bb61f052dfd9a205c595, respectively(cherry-pick-ing)

Comment by Lawrin Novitsky [ 2018-05-15 ]

Re-opening it, as I have just merged additional fix, that completes the fix of this bug. Unfortunately the testcase did not cover the case, when columns hadn't been unbind when moved to the next resultset. In such case the connector would crash.

Comment by Lawrin Novitsky [ 2018-05-15 ]

The additional fix has been pushed into odbc-3.0 and master as 9eb8370a. Yet to be merged into odbc-2.0

Generated at Thu Feb 08 03:26:24 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.