From 7e2c9393dcdbf1620c39650cce6e0d21a7c1bf44 Mon Sep 17 00:00:00 2001
From: surendrasignalwire <surendra.tiwari@siganlwire.com>
Date: Wed, 10 Jun 2020 20:09:01 +0400
Subject: [PATCH] [core] Fix ODBC column size performance issue

---
 src/switch_odbc.c | 57 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/src/switch_odbc.c b/src/switch_odbc.c
index 31161ee158..9ea062d997 100644
--- a/src/switch_odbc.c
+++ b/src/switch_odbc.c
@@ -605,17 +605,66 @@ SWITCH_DECLARE(switch_odbc_status_t) switch_odbc_handle_callback_exec_detailed(c
 		for (x = 1; x <= c; x++) {
 			SQLSMALLINT NameLength = 0, DataType = 0, DecimalDigits = 0, Nullable = 0;
 			SQLULEN ColumnSize = 0;
+			SQLLEN numRecs = 0;
+			SQLCHAR SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
+			SQLINTEGER NativeError;
+			SQLSMALLINT diagCount, MsgLen;
 			names[y] = malloc(name_len);
 			switch_assert(names[y]);
 			memset(names[y], 0, name_len);
 
 			SQLDescribeCol(stmt, x, (SQLCHAR *) names[y], (SQLSMALLINT) name_len, &NameLength, &DataType, &ColumnSize, &DecimalDigits, &Nullable);
 
-			if (!ColumnSize) {
+			if (ColumnSize <= 16383 || ColumnSize == 2147483647) {
 				SQLCHAR val[16384] = { 0 };
+				SQLLEN StrLen_or_IndPtr;
+				SQLRETURN rc;
 				ColumnSize = 16384;
-				SQLGetData(stmt, x, SQL_C_CHAR, val, ColumnSize, NULL);
-				vals[y] = strdup((char *)val);
+
+				/* check diag record and see if we can get real size
+				 * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/using-sqlgetdiagrec-and-sqlgetdiagfield?view=sql-server-ver15
+				 * szSqlState = "01004" and StrLen_or_IndPtr=15794 
+				*/
+				rc = SQLGetData(stmt, x, SQL_C_CHAR, val, ColumnSize, &StrLen_or_IndPtr); 
+
+				if (rc == SQL_SUCCESS_WITH_INFO) {
+					int truncated = 0;
+					diagCount = 1;
+
+					SQLGetDiagField(SQL_HANDLE_STMT, stmt, 0, SQL_DIAG_NUMBER, &numRecs, 0, 0);
+
+					while (diagCount <= numRecs) {
+						SQLGetDiagRec(SQL_HANDLE_STMT, stmt, diagCount, SqlState, &NativeError,Msg, sizeof(Msg), &MsgLen);
+						if (!strcmp((char*)SqlState,"01004")){
+							truncated = 1;
+							break;
+						}
+
+						diagCount++;
+					}
+
+					if (truncated) {
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sql data truncated - %s\n",SqlState);
+						if (StrLen_or_IndPtr && StrLen_or_IndPtr <= 268435456) {
+							ColumnSize = StrLen_or_IndPtr + 1;
+							vals[y] = malloc(ColumnSize);
+							switch_assert(vals[y]);
+							memset(vals[y], 0, ColumnSize);
+							SQLGetData(stmt, x, SQL_C_CHAR, (SQLCHAR *) vals[y], ColumnSize, NULL);
+						} else {
+							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQLGetData failed");
+							vals[y] = NULL;
+						}
+					} else {
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQLGetData failed");
+						vals[y] = NULL;
+					}
+				} else if (rc == SQL_SUCCESS){
+					vals[y] = strdup((char *)val);
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQLGetData failed");
+					vals[y] = NULL;
+				}
 			} else {
 				ColumnSize++;
 
@@ -633,7 +682,7 @@ SWITCH_DECLARE(switch_odbc_status_t) switch_odbc_handle_callback_exec_detailed(c
 
 		for (x = 0; x < y; x++) {
 			free(names[x]);
-			free(vals[x]);
+			switch_safe_free(vals[x]);
 		}
 		free(names);
 		free(vals);