Mini Update #2 -- final fixes for buffer lengths, null buffers, truncation
authorByron Nikolaidis
Tue, 5 Jan 1999 00:32:21 +0000 (00:32 +0000)
committerByron Nikolaidis
Tue, 5 Jan 1999 00:32:21 +0000 (00:32 +0000)
src/interfaces/odbc/connection.h
src/interfaces/odbc/drvconn.c
src/interfaces/odbc/environ.c
src/interfaces/odbc/execute.c
src/interfaces/odbc/results.c

index c076bc2926ec55d910e21dbf4afb7244c8dfcd4f..d07fd60362a3b9bca1ce269da6272bc8b3da1aae 100644 (file)
@@ -65,6 +65,8 @@ typedef enum {
 #define CONN_OPTION_VALUE_CHANGED 213
 #define CONN_VALUE_OUT_OF_RANGE 214
 
+#define CONN_TRUNCATED 215
+
 /* Conn_status defines */
 #define CONN_IN_AUTOCOMMIT 0x01
 #define CONN_IN_TRANSACTION 0x02
index 7959ed998cc20d497f9703e329734e6353e2a8cd..cfb514c7fc5c2f49010c4830daf471ec0cb5aca8 100644 (file)
@@ -79,12 +79,14 @@ static char *func = "SQLDriverConnect";
 ConnectionClass *conn = (ConnectionClass *) hdbc;
 ConnInfo *ci;
 #ifdef WIN32
-RETCODE dialog_result;
+RETCODE dialog_result, result;
 #endif
 char connStrIn[MAX_CONNECT_STRING];
 char connStrOut[MAX_CONNECT_STRING];
 int retval;
 char password_required = FALSE;
+int len = 0;
+
 
    mylog("%s: entering...\n", func);
 
@@ -166,22 +168,6 @@ dialog:
        return SQL_NO_DATA_FOUND;
    }
 
-   if(szConnStrOut) {
-
-       /*  Return the completed string to the caller.
-           Only construct the connect string if a dialog was put up,
-           otherwise, just copy the connection input string to the output.
-       */
-       makeConnectString(connStrOut, ci);
-
-       if(pcbConnStrOut) {
-           *pcbConnStrOut = strlen(connStrOut);
-       }
-       strncpy_null(szConnStrOut, connStrOut, cbConnStrOutMax);
-   }
-
-   mylog("szConnStrOut = '%s'\n", szConnStrOut);
-   qlog("conn=%u, SQLDriverConnect(out)='%s'\n", conn, szConnStrOut);
 
    // do the actual connect
    retval = CC_connect(conn, password_required);
@@ -205,8 +191,41 @@ dialog:
        return SQL_ERROR;
    }
 
-   mylog("SQLDRiverConnect: returning success\n");
-   return SQL_SUCCESS;
+   /*********************************************/
+   /*     Create the Output Connection String   */
+   /*********************************************/
+   result = SQL_SUCCESS;
+
+   makeConnectString(connStrOut, ci);
+   len = strlen(connStrOut);
+
+   if(szConnStrOut) {
+
+       /*  Return the completed string to the caller. The correct method is to 
+           only construct the connect string if a dialog was put up, otherwise, 
+           it should just copy the connection input string to the output.  
+           However, it seems ok to just always construct an output string.  There
+           are possible bad side effects on working applications (Access) by 
+           implementing the correct behavior, anyway. 
+       */
+       strncpy_null(szConnStrOut, connStrOut, cbConnStrOutMax);
+
+       if (len >= cbConnStrOutMax) {
+           result = SQL_SUCCESS_WITH_INFO;
+           conn->errornumber = CONN_TRUNCATED;
+           conn->errormsg = "The buffer was too small for the result.";
+       }
+   }
+
+   if(pcbConnStrOut)
+       *pcbConnStrOut = len;
+
+   mylog("szConnStrOut = '%s'\n", szConnStrOut);
+   qlog("conn=%u, SQLDriverConnect(out)='%s'\n", conn, szConnStrOut);
+
+
+   mylog("SQLDRiverConnect: returning %d\n", result);
+   return result;
 }
 
 #ifdef WIN32
index bf99135133c3b070fdc2a1cd04250de159206198..1fa005b4c46f4e2e4e6df400138f573d5fc8c372 100644 (file)
@@ -242,6 +242,7 @@ int status;
                     strcpy(szSqlState, "01S02");
                    break;
                 case STMT_TRUNCATED:
+               case CONN_TRUNCATED:
                     strcpy(szSqlState, "01004");
                     // data truncated
                     break;
index 59cfa450a10cb4f109672d41e73a9d71d7caa4bc..87b6a31162f380c3b75ce38c487e1759a67e63bd 100644 (file)
@@ -431,7 +431,8 @@ FARPROC addr;
 //      -       -       -       -       -       -       -       -       -
 
 //      Returns the SQL string as modified by the driver.
-
+//     Currently, just copy the input string without modification
+//     observing buffer limits and truncation.
 RETCODE SQL_API SQLNativeSql(
         HDBC      hdbc,
         UCHAR FAR *szSqlStrIn,
@@ -441,12 +442,40 @@ RETCODE SQL_API SQLNativeSql(
         SDWORD FAR *pcbSqlStr)
 {
 static char *func="SQLNativeSql";
+int len = 0;
+char *ptr;
+ConnectionClass *conn = (ConnectionClass *) hdbc;
+RETCODE result;
 
-   mylog( "%s: entering...\n", func);
+   mylog( "%s: entering...cbSqlStrIn=%d\n", func, cbSqlStrIn);
+
+   ptr = (cbSqlStrIn == 0) ? "" : make_string(szSqlStrIn, cbSqlStrIn, NULL);
+   if ( ! ptr) {
+       conn->errornumber = CONN_NO_MEMORY_ERROR;
+       conn->errormsg = "No memory available to store native sql string";
+       CC_log_error(func, "", conn);
+       return SQL_ERROR;
+   }
+
+   result = SQL_SUCCESS;
+   len = strlen(ptr);
+
+   if (szSqlStr) {
+       strncpy_null(szSqlStr, ptr, cbSqlStrMax);
+
+       if (len >= cbSqlStrMax)  {
+           result = SQL_SUCCESS_WITH_INFO;
+           conn->errornumber = STMT_TRUNCATED;
+           conn->errormsg = "The buffer was too small for the result.";
+       }
+   }
+
+   if (pcbSqlStr)
+       *pcbSqlStr = len;
 
-    strncpy_null(szSqlStr, szSqlStrIn, cbSqlStrMax);
+   free(ptr);
 
-    return SQL_SUCCESS;
+    return result;
 }
 
 //      -       -       -       -       -       -       -       -       -
index 7595fd5e3ba1ccc5f3749671f1f1116d737b704f..7b2a0dc04975c517e48b4bc9f68e5015e956b4f1 100644 (file)
@@ -175,13 +175,16 @@ RETCODE SQL_API SQLDescribeCol(
 static char *func="SQLDescribeCol";
     /* gets all the information about a specific column */
 StatementClass *stmt = (StatementClass *) hstmt;
-QResultClass *result;
+QResultClass *res;
 char *col_name = NULL;
 Int4 fieldtype = 0;
 int precision = 0;
 ConnInfo *ci;
 char parse_ok;
 char buf[255];
+int len = 0;
+RETCODE result;
+
 
    mylog("%s: entering...\n", func);
 
@@ -239,10 +242,10 @@ char buf[255];
    if ( ! parse_ok) {
        SC_pre_execute(stmt);
    
-       result = SC_get_Result(stmt);
+       res = SC_get_Result(stmt);
 
-       mylog("**** SQLDescribeCol: result = %u, stmt->status = %d, !finished=%d, !premature=%d\n", result, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
-       if ( (NULL == result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) {
+       mylog("**** SQLDescribeCol: res = %u, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
+       if ( (NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE))) {
            /* no query has been executed on this statement */
            stmt->errornumber = STMT_SEQUENCE_ERROR;
            stmt->errormsg = "No query has been assigned to this statement.";
@@ -250,16 +253,16 @@ char buf[255];
            return SQL_ERROR;
        }
 
-       if (icol >= QR_NumResultCols(result)) {
+       if (icol >= QR_NumResultCols(res)) {
            stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
            stmt->errormsg = "Invalid column number in DescribeCol.";
-           sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(result));
+           sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(res));
            SC_log_error(func, buf, stmt);
            return SQL_ERROR;
        }
 
-       col_name = QR_get_fieldname(result, icol);
-        fieldtype = QR_get_field_type(result, icol);
+       col_name = QR_get_fieldname(res, icol);
+        fieldtype = QR_get_field_type(res, icol);
 
        precision = pgtype_precision(stmt, fieldtype, icol, globals.unknown_sizes);  // atoi(ci->unknown_sizes)
    }
@@ -268,28 +271,40 @@ char buf[255];
    mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
    mylog("describeCol: col %d precision = %d\n", icol, precision);
 
-    if (cbColNameMax >= 1) {
-        if (pcbColName)  {
-            if (col_name) 
-                *pcbColName = strlen(col_name);
-            else
-                *pcbColName = 0;
-        }
-        if (szColName) {
-            if (col_name) 
-                strncpy_null(szColName, col_name, cbColNameMax);
-            else
-                szColName[0] = '\0';
-        }
+
+   result = SQL_SUCCESS;
+
+   /************************/
+   /*      COLUMN NAME     */
+   /************************/
+   len = strlen(col_name);
+
+   if (pcbColName)
+       *pcbColName = len;
+
+   if (szColName) {
+       strncpy_null(szColName, col_name, cbColNameMax);
+
+       if (len >= cbColNameMax)  {
+           result = SQL_SUCCESS_WITH_INFO;
+           stmt->errornumber = STMT_TRUNCATED;
+           stmt->errormsg = "The buffer was too small for the result.";
+       }
     }
 
 
+   /************************/
+   /*      SQL TYPE        */
+   /************************/
     if (pfSqlType) {
         *pfSqlType = pgtype_to_sqltype(stmt, fieldtype);
 
        mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
    }
 
+   /************************/
+   /*      PRECISION       */
+   /************************/
     if (pcbColDef) {
 
        if ( precision < 0)
@@ -300,6 +315,9 @@ char buf[255];
        mylog("describeCol: col %d  *pcbColDef = %d\n", icol, *pcbColDef);
    }
 
+   /************************/
+   /*      SCALE           */
+   /************************/
     if (pibScale) {
         Int2 scale;
         scale = pgtype_scale(stmt, fieldtype);
@@ -309,16 +327,16 @@ char buf[255];
        mylog("describeCol: col %d  *pibScale = %d\n", icol, *pibScale);
     }
 
+   /************************/
+   /*      NULLABILITY     */
+   /************************/
     if (pfNullable) {
-       if (parse_ok)
-           *pfNullable = stmt->fi[icol]->nullable;
-       else
-           *pfNullable = pgtype_nullable(stmt, fieldtype);
+       *pfNullable = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype);
 
        mylog("describeCol: col %d  *pfNullable = %d\n", icol, *pfNullable);
     }
 
-    return SQL_SUCCESS;
+    return result;
 }
 
 //      Returns result column descriptor information for a result set.
@@ -334,12 +352,14 @@ RETCODE SQL_API SQLColAttributes(
 {
 static char *func = "SQLColAttributes";
 StatementClass *stmt = (StatementClass *) hstmt;
-char *value;
 Int4 field_type = 0;
 ConnInfo *ci;
 int unknown_sizes;
 int cols = 0;
 char parse_ok;
+RETCODE result;
+char *p = NULL;
+int len = 0, value = 0;
 
    mylog("%s: entering...\n", func);
 
@@ -434,17 +454,14 @@ char parse_ok;
 
    switch(fDescType) {
    case SQL_COLUMN_AUTO_INCREMENT:
-       if (pfDesc) {
-           *pfDesc = pgtype_auto_increment(stmt, field_type);
-           if (*pfDesc == -1)  /*  non-numeric becomes FALSE (ODBC Doc) */
-               *pfDesc = FALSE;
+       value  = pgtype_auto_increment(stmt, field_type);
+       if (value == -1)  /*  non-numeric becomes FALSE (ODBC Doc) */
+           value = FALSE;
                
-       }
        break;
 
    case SQL_COLUMN_CASE_SENSITIVE:
-       if (pfDesc)    
-           *pfDesc = pgtype_case_sensitive(stmt, field_type);
+       value = pgtype_case_sensitive(stmt, field_type);
        break;
 
    /*  This special case is handled above.
@@ -453,151 +470,127 @@ char parse_ok;
    */
 
     case SQL_COLUMN_DISPLAY_SIZE:
-       if (pfDesc) {
-           if (parse_ok)
-               *pfDesc = stmt->fi[icol]->display_size;
-           else
-               *pfDesc = pgtype_display_size(stmt, field_type, icol, unknown_sizes);
-       }
+       value = (parse_ok) ? stmt->fi[icol]->display_size : pgtype_display_size(stmt, field_type, icol, unknown_sizes);
 
-       mylog("SQLColAttributes: col %d, display_size= %d\n", icol, *pfDesc);
+       mylog("SQLColAttributes: col %d, display_size= %d\n", icol, value);
 
         break;
 
    case SQL_COLUMN_LABEL:
        if (parse_ok && stmt->fi[icol]->alias[0] != '\0') {
-           strncpy_null((char *)rgbDesc, stmt->fi[icol]->alias, cbDescMax);
-           if (pcbDesc)
-               *pcbDesc = strlen(stmt->fi[icol]->alias);
+           p = stmt->fi[icol]->alias;
 
+           mylog("SQLColAttr: COLUMN_LABEL = '%s'\n", p);
            break;
 
-           mylog("SQLColAttr: COLUMN_LABEL = '%s'\n", rgbDesc);
-       }   // otherwise same as column name
+       }   // otherwise same as column name -- FALL THROUGH!!!
 
    case SQL_COLUMN_NAME:
 
-       if (parse_ok)
-           value = stmt->fi[icol]->name;
-       else
-           value = QR_get_fieldname(stmt->result, icol);
-
-       strncpy_null((char *)rgbDesc, value, cbDescMax);
-
-       if (pcbDesc)
-           *pcbDesc = strlen(value);
+       p = (parse_ok) ? stmt->fi[icol]->name : QR_get_fieldname(stmt->result, icol);
 
-       mylog("SQLColAttr: COLUMN_NAME = '%s'\n", rgbDesc);
+       mylog("SQLColAttr: COLUMN_NAME = '%s'\n", p);
        break;
 
    case SQL_COLUMN_LENGTH:
-       if (pfDesc) {
-           if (parse_ok)
-               *pfDesc = stmt->fi[icol]->length;
-           else
-               *pfDesc = pgtype_length(stmt, field_type, icol, unknown_sizes); 
-       }
-       mylog("SQLColAttributes: col %d, length = %d\n", icol, *pfDesc);
+       value = (parse_ok) ? stmt->fi[icol]->length :  pgtype_length(stmt, field_type, icol, unknown_sizes); 
+
+       mylog("SQLColAttributes: col %d, length = %d\n", icol, value);
         break;
 
    case SQL_COLUMN_MONEY:
-       if (pfDesc)    
-           *pfDesc = pgtype_money(stmt, field_type);
+       value = pgtype_money(stmt, field_type);
        break;
 
    case SQL_COLUMN_NULLABLE:
-       if (pfDesc) {
-           if (parse_ok)
-               *pfDesc = stmt->fi[icol]->nullable;
-           else
-               *pfDesc = pgtype_nullable(stmt, field_type);
-       }
+       value = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, field_type);
        break;
 
    case SQL_COLUMN_OWNER_NAME:
-       strncpy_null((char *)rgbDesc, "", cbDescMax);
-       if (pcbDesc)        
-           *pcbDesc = 0;
+       p = "";
        break;
 
    case SQL_COLUMN_PRECISION:
-       if (pfDesc) {
-           if (parse_ok)
-               *pfDesc = stmt->fi[icol]->precision;
-           else
-               *pfDesc = pgtype_precision(stmt, field_type, icol, unknown_sizes);
-       }
-       mylog("SQLColAttributes: col %d, precision = %d\n", icol, *pfDesc);
+       value = (parse_ok) ? stmt->fi[icol]->precision : pgtype_precision(stmt, field_type, icol, unknown_sizes);
+
+       mylog("SQLColAttributes: col %d, precision = %d\n", icol, value);
         break;
 
    case SQL_COLUMN_QUALIFIER_NAME:
-       strncpy_null((char *)rgbDesc, "", cbDescMax);
-       if (pcbDesc)        
-           *pcbDesc = 0;
+       p = "";
        break;
 
    case SQL_COLUMN_SCALE:
-       if (pfDesc)    
-           *pfDesc = pgtype_scale(stmt, field_type);
+       value = pgtype_scale(stmt, field_type);
        break;
 
    case SQL_COLUMN_SEARCHABLE:
-       if (pfDesc)    
-           *pfDesc = pgtype_searchable(stmt, field_type);
+       value = pgtype_searchable(stmt, field_type);
        break;
 
     case SQL_COLUMN_TABLE_NAME:
-       if (parse_ok && stmt->fi[icol]->ti) {
-           strncpy_null((char *)rgbDesc, stmt->fi[icol]->ti->name, cbDescMax);
-           if (pcbDesc)        
-               *pcbDesc = strlen(stmt->fi[icol]->ti->name);
-       }
-       else {
-           strncpy_null((char *)rgbDesc, "", cbDescMax);
-           if (pcbDesc)        
-               *pcbDesc = 0;
-       }
 
-       mylog("SQLColAttr: TABLE_NAME = '%s'\n", rgbDesc);
+       p = (parse_ok && stmt->fi[icol]->ti) ? stmt->fi[icol]->ti->name : "";
+
+       mylog("SQLColAttr: TABLE_NAME = '%s'\n", p);
         break;
 
    case SQL_COLUMN_TYPE:
-       if (pfDesc) {
-           *pfDesc = pgtype_to_sqltype(stmt, field_type);
-       }
+       value = pgtype_to_sqltype(stmt, field_type);
        break;
 
    case SQL_COLUMN_TYPE_NAME:
-       value = pgtype_to_name(stmt, field_type);
-       strncpy_null((char *)rgbDesc, value, cbDescMax);
-       if (pcbDesc)        
-           *pcbDesc = strlen(value);
+       p = pgtype_to_name(stmt, field_type);
        break;
 
    case SQL_COLUMN_UNSIGNED:
-       if (pfDesc) {
-           *pfDesc = pgtype_unsigned(stmt, field_type);
-           if(*pfDesc == -1)   /* non-numeric becomes TRUE (ODBC Doc) */
-               *pfDesc = TRUE;
-       }
+       value = pgtype_unsigned(stmt, field_type);
+       if(value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
+           value = TRUE;
+
        break;
 
    case SQL_COLUMN_UPDATABLE:
-       if (pfDesc)    {
-           /*  Neither Access or Borland care about this.
+       /*  Neither Access or Borland care about this.
 
-           if (field_type == PG_TYPE_OID)
-               *pfDesc = SQL_ATTR_READONLY;
-           else
-           */
+       if (field_type == PG_TYPE_OID)
+           *pfDesc = SQL_ATTR_READONLY;
+       else
+       */
 
-           *pfDesc = SQL_ATTR_WRITE;
-           mylog("SQLColAttr: UPDATEABLE = %d\n", *pfDesc);
-       }
+       value = SQL_ATTR_WRITE;
+
+       mylog("SQLColAttr: UPDATEABLE = %d\n", value);
        break;
     }
 
-    return SQL_SUCCESS;
+   result = SQL_SUCCESS;
+
+   if (p) {  /* char/binary data */
+       len = strlen(p);
+
+       if (rgbDesc) {
+           strncpy_null((char *)rgbDesc, p, (size_t)cbDescMax);
+
+           if (len >= cbDescMax)  {
+               result = SQL_SUCCESS_WITH_INFO;
+               stmt->errornumber = STMT_TRUNCATED;
+               stmt->errormsg = "The buffer was too small for the result.";
+           }
+       }
+
+       if (pcbDesc) 
+           *pcbDesc = len;
+   }
+   else {  /* numeric data */
+
+       if (pfDesc)
+           *pfDesc = value;
+
+   }
+
+
+    return result;
 }
 
 //      Returns result data for a single column in the current row.
@@ -1162,14 +1155,15 @@ mylog("SQLSetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCurs
    }
 
    len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor;
-   mylog("cursor len = %d\n", len);
+
    if (len <= 0 || len > sizeof(stmt->cursor_name) - 1) {
        stmt->errornumber = STMT_INVALID_CURSOR_NAME;
        stmt->errormsg = "Invalid Cursor Name";
        SC_log_error(func, "", stmt);
        return SQL_ERROR;
    }
-   strncpy_null(stmt->cursor_name, szCursor, cbCursor);
+
+   strncpy_null(stmt->cursor_name, szCursor, len+1);
    return SQL_SUCCESS;
 }
 
@@ -1183,6 +1177,8 @@ RETCODE SQL_API SQLGetCursorName(
 {
 static char *func="SQLGetCursorName";
 StatementClass *stmt = (StatementClass *) hstmt;
+int len = 0;
+RETCODE result;
 
 mylog("SQLGetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor);
 
@@ -1191,7 +1187,6 @@ mylog("SQLGetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n",
        return SQL_INVALID_HANDLE;
    }
 
-
    if ( stmt->cursor_name[0] == '\0') {
        stmt->errornumber = STMT_NO_CURSOR_NAME;
        stmt->errormsg = "No Cursor name available";
@@ -1199,12 +1194,23 @@ mylog("SQLGetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n",
        return SQL_ERROR;
    }
 
-   strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);
+   result = SQL_SUCCESS;
+   len = strlen(stmt->cursor_name);
+
+   if (szCursor) {
+       strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);
+
+       if (len >= cbCursorMax)  {
+           result = SQL_SUCCESS_WITH_INFO;
+           stmt->errornumber = STMT_TRUNCATED;
+           stmt->errormsg = "The buffer was too small for the result.";
+       }
+   }
 
    if (pcbCursor)
-       *pcbCursor = strlen(szCursor);
+       *pcbCursor = len;
 
-   return SQL_SUCCESS;
+   return result;
 }