Преглед на файлове

db_unixodbc: allow more than 1024 bytes in a database field

- closes FS#443
Alex Villacis Lasso преди 11 години
родител
ревизия
3e6e4cc03f
променени са 5 файла, в които са добавени 232 реда и са изтрити 138 реда
  1. 131 3
      modules/db_unixodbc/connection.c
  2. 18 1
      modules/db_unixodbc/connection.h
  3. 27 28
      modules/db_unixodbc/dbase.c
  4. 41 87
      modules/db_unixodbc/list.c
  5. 15 19
      modules/db_unixodbc/res.c

+ 131 - 3
modules/db_unixodbc/connection.c

@@ -1,4 +1,4 @@
-/* 
+/*
  * $Id$
  *
  * UNIXODBC module
@@ -26,7 +26,7 @@
  * History:
  * --------
  *  2005-12-01  initial commit (chgen)
- *  2006-01-10  UID (username) and PWD (password) attributes added to 
+ *  2006-01-10  UID (username) and PWD (password) attributes added to
  *              connection string (bogdan)
  *  2006-05-05  extract_error passes back last error state on return (sgupta)
  */
@@ -216,10 +216,138 @@ void db_unixodbc_extract_error(const char *fn, const SQLHANDLE handle, const SQL
 		ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
 			sizeof(text), &len );
 		if (SQL_SUCCEEDED(ret)) {
-			LM_ERR("unixodbc:%s=%s:%ld:%ld:%s\n", fn, state, (long)i, 
+			LM_ERR("unixodbc:%s=%s:%ld:%ld:%s\n", fn, state, (long)i,
 					(long)native, text);
 			if(stret) strcpy( stret, (char*)state );
 		}
 	}
 	while( ret == SQL_SUCCESS );
 }
+
+/*
+ * Allocate a new row of cells, without any data
+ */
+strn * db_unixodbc_new_cellrow(size_t ncols)
+{
+	strn * temp_row;
+
+	temp_row = (strn *)pkg_malloc(ncols * sizeof(strn));
+	if (temp_row) memset(temp_row, 0, ncols * sizeof(strn));
+	return temp_row;
+}
+
+/*
+ * Free row of cells and all associated memory
+ */
+void db_unixodbc_free_cellrow(size_t ncols, strn * row)
+{
+	size_t i;
+
+	for (i = 0; i < ncols; i++) {
+		if (row[i].s != NULL) pkg_free(row[i].s);
+	}
+	pkg_free(row);
+}
+
+/*
+ * Load ODBC cell data into a single cell
+ */
+int db_unixodbc_load_cell(const db1_con_t* _h, int colindex, strn * cell, const db_type_t _t)
+{
+	SQLRETURN ret = 0;
+	unsigned int truesize = 0;
+	unsigned char hasnull = (_t != DB1_BLOB) ? 1 : 0;
+
+	do {
+		SQLLEN indicator;
+		int chunklen;
+		char * s;	/* Pointer to available area for next chunk */
+		char * ns;
+
+		if (cell->buflen > 0) {
+			ns = (char *)pkg_realloc(cell->s, cell->buflen + STRN_LEN);
+			if (ns == NULL) {
+				LM_ERR("no memory left\n");
+				return 0;
+			}
+			cell->s = ns;
+
+			/* Overwrite the previous null terminator */
+			s = cell->s + cell->buflen - hasnull;
+			chunklen = STRN_LEN + hasnull;
+		} else {
+			ns = (char *)pkg_malloc(STRN_LEN);
+			if (ns == NULL) {
+				LM_ERR("no memory left\n");
+				return 0;
+			}
+			cell->s = ns;
+			s = cell->s;
+			chunklen = STRN_LEN;
+		}
+		cell->buflen += STRN_LEN;
+
+		ret = SQLGetData(CON_RESULT(_h), colindex, hasnull ? SQL_C_CHAR : SQL_C_BINARY,
+					s, chunklen, &indicator);
+		LM_DBG("SQLGetData returned ret=%d indicator=%d\n", (int)ret, (int)indicator);
+		if (ret == SQL_SUCCESS) {
+			if (indicator == SQL_NULL_DATA) {
+			    /* TODO: set buffer pointer to NULL instead of string "NULL" */
+			    strcpy(cell->s, "NULL");
+			    truesize = 4 + (1 - hasnull);
+			} else {
+			    /* Get length of data that was available before last SQLGetData call */
+			    if (truesize == 0) truesize = indicator;
+			}
+		} else if (ret == SQL_SUCCESS_WITH_INFO) {
+			SQLINTEGER   i = 0;
+			SQLINTEGER   native;
+			SQLCHAR  state[ 7 ];
+			SQLCHAR  text[256];
+			SQLSMALLINT  len;
+			SQLRETURN	ret2;
+
+			/* Check whether field data was truncated */
+			do
+			{
+				ret2 = SQLGetDiagRec(SQL_HANDLE_STMT, CON_RESULT(_h), ++i, state, &native, text,
+					sizeof(text), &len );
+				if (SQL_SUCCEEDED(ret2)) {
+					if (!strcmp("00000", (const char*)state)) break;
+					if (strcmp("01004", (const char*)state) != 0) {
+						/* Not a string truncation */
+						LM_ERR("SQLGetData failed unixodbc:  =%s:%ld:%ld:%s\n", state, (long)i,
+							(long)native, text);
+						return 0;
+					}
+
+					/* Get length of data that was available before last SQLGetData call */
+					if (truesize == 0) truesize = indicator;
+				} else if (ret2 == SQL_NO_DATA) {
+				    /* Reached end of diagnostic records */
+					break;
+				} else {
+					/* Failed to get diagnostics */
+					LM_ERR("SQLGetData failed, failed to get diagnostics (ret2=%d i=%d)\n",
+						ret2, i);
+					return 0;
+				}
+			}
+			while( ret2 == SQL_SUCCESS );
+		} else {
+			LM_ERR("SQLGetData failed\n");
+		}
+	} while (ret == SQL_SUCCESS_WITH_INFO);
+
+	LM_DBG("Total allocated for this cell (before resize): %d bytes\n", cell->buflen);
+
+	if (cell->buflen > truesize) {
+		cell->s[truesize] = '\0'; /* guarantee strlen() will terminate */
+	}
+	cell->buflen = truesize + hasnull;
+
+	LM_DBG("Total allocated for this cell (after resize): %d bytes\n", cell->buflen);
+	LM_DBG("strlen() reports for this cell: %d bytes\n", (int)strlen(cell->s));
+
+	return 1;
+}

+ 18 - 1
modules/db_unixodbc/connection.h

@@ -34,6 +34,7 @@
 
 #include "../../lib/srdb1/db_pool.h"
 #include "../../lib/srdb1/db_id.h"
+#include "../../lib/srdb1/db_val.h"
 
 #include <time.h>
 #include <stdio.h>
@@ -47,7 +48,8 @@
 
 typedef struct strn
 {
-	char s[STRN_LEN];
+	unsigned int buflen;
+	char *s;
 } strn;
 
 
@@ -90,4 +92,19 @@ char *db_unixodbc_build_conn_str(const struct db_id* id, char *buf);
 
 void db_unixodbc_extract_error(const char *fn, const SQLHANDLE handle, const SQLSMALLINT type, char* stret);
 
+/*
+ * Allocate a new row of cells, without any data
+ */
+strn * db_unixodbc_new_cellrow(size_t ncols);
+
+/*
+ * Free row of cells and all associated memory
+ */
+void db_unixodbc_free_cellrow(size_t ncols, strn * row);
+
+/*
+ * Load ODBC cell data into a single cell
+ */
+int db_unixodbc_load_cell(const db1_con_t* _h, int colindex, strn * cell, const db_type_t _t);
+
 #endif  /* MY_CON_H */

+ 27 - 28
modules/db_unixodbc/dbase.c

@@ -29,7 +29,7 @@
  *  2006-04-03  fixed invalid handle to extract error (sgupta)
  *  2006-04-04  removed deprecated ODBC functions, closed cursors on error
  *              (sgupta)
- *  2006-05-05  Fixed reconnect code to actually work on connection loss 
+ *  2006-05-05  Fixed reconnect code to actually work on connection loss
  *              (sgupta)
  */
 
@@ -115,7 +115,7 @@ static int db_unixodbc_submit_query(const db1_con_t* _h, const str* _s)
 				(int)(long)CON_CONNECTION(_h));
 		db_unixodbc_extract_error("SQLAllocStmt", CON_CONNECTION(_h), SQL_HANDLE_DBC,
 			(char*)sqlstate);
-		
+
 		/* Connection broken */
 		if( !strncmp((char*)sqlstate,"08003",5) ||
 		!strncmp((char*)sqlstate,"08S01",5) ) {
@@ -136,7 +136,7 @@ static int db_unixodbc_submit_query(const db1_con_t* _h, const str* _s)
 
 		/* Connection broken */
 		if( !strncmp((char*)sqlstate,"08003",5) ||
-		    !strncmp((char*)sqlstate,"08S01",5) 
+		    !strncmp((char*)sqlstate,"08S01",5)
 		    )
 		{
 			ret = reconnect(_h);
@@ -155,7 +155,7 @@ static int db_unixodbc_submit_query(const db1_con_t* _h, const str* _s)
 
 		}
 		else {
-			/* Close the cursor */ 
+			/* Close the cursor */
 			SQLCloseCursor(CON_RESULT(_h));
 			SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h));
 		}
@@ -321,18 +321,10 @@ int db_unixodbc_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrow
 
 	SQLNumResultCols(CON_RESULT(_h), (SQLSMALLINT *)&columns);
 
-	/* Allocate a temporary row */
-	temp_row = (strn*)pkg_malloc( columns*sizeof(strn) );
-	if(!temp_row) {
-		LM_ERR("no private memory left\n");
-		return -1;
-	}
-
 	/* Now fetch nrows at most */
 	len = sizeof(db_row_t) * nrows;
 	RES_ROWS(*_r) = (struct db_row*)pkg_malloc(len);
 	if (!RES_ROWS(*_r)) {
-		pkg_free(temp_row);
 		LM_ERR("no memory left\n");
 		return -5;
 	}
@@ -340,19 +332,25 @@ int db_unixodbc_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrow
 
 	LM_DBG("Now fetching %i rows at most\n", nrows);
 	while(SQL_SUCCEEDED(ret = SQLFetch(CON_RESULT(_h)))) {
+		/* Allocate a temporary row */
+		temp_row = db_unixodbc_new_cellrow(columns);
+		if (!temp_row) {
+			LM_ERR("no private memory left\n");
+			pkg_free(RES_ROWS(*_r));
+			pkg_free(*_r);
+			*_r = 0;
+			return -1;
+		}
+
 		LM_DBG("fetching %d columns for row %d...\n",columns, row_n);
 		for(i=0; i < columns; i++) {
-			SQLLEN indicator;
 			LM_DBG("fetching column %d\n",i);
-
-			ret = SQLGetData(CON_RESULT(_h), i+1, SQL_C_CHAR,
-					temp_row[i].s, STRN_LEN, &indicator);
-
-			if (SQL_SUCCEEDED(ret)) {
-				if (indicator == SQL_NULL_DATA)
-					strcpy(temp_row[i].s, "NULL");
-			} else {
-				LM_ERR("SQLGetData failed\n");
+			if (!db_unixodbc_load_cell(_h, i+1, temp_row + i, RES_TYPES(*_r)[i])) {
+			    pkg_free(RES_ROWS(*_r));
+			    db_unixodbc_free_cellrow(columns, temp_row);
+			    pkg_free(*_r);
+			    *_r = 0;
+			    return -5;
 			}
 		}
 
@@ -361,22 +359,23 @@ int db_unixodbc_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrow
 		if (db_unixodbc_list_insert(&rowstart, &rows, columns, temp_row) < 0) {
 			LM_ERR("SQL result row insert failed\n");
 			pkg_free(RES_ROWS(*_r));
-			pkg_free(temp_row);
-			temp_row= NULL;
+			db_unixodbc_free_cellrow(columns, temp_row);
 			pkg_free(*_r);
 			*_r = 0;
 			return -5;
 		}
 
+		/* Free temporary row data */
+		LM_DBG("freeing temp_row at %p\n", temp_row);
+		db_unixodbc_free_cellrow(columns, temp_row);
+		temp_row = NULL;
+
 		row_n++;
 		if (row_n == nrows) {
 			break;
 		}
 	}
-	
-	/* Free temporary row data */
-	LM_DBG("freeing temp_row at %p\n", temp_row);
-	pkg_free(temp_row);
+
 	CON_ROW(_h) = NULL;
 
 	RES_ROW_N(*_r) = row_n;

+ 41 - 87
modules/db_unixodbc/list.c

@@ -1,6 +1,4 @@
-/* 
- * $Id$ 
- *
+/*
  * UNIXODBC module
  *
  * Copyright (C) 2005-2006 Marco Lorrai
@@ -18,8 +16,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  *
@@ -46,101 +44,57 @@
 int db_unixodbc_list_insert(list** start, list** link, int n, strn* value)
 {
 	int i = 0;
+	list* nlink;
 
-	if(!(*start)) {
-		*link = (list*)pkg_malloc(sizeof(list));
-		if(!(*link)) {
-			LM_ERR("no more pkg memory (1)\n");
-			return -1;
-		}
-		(*link)->rownum = n;
-		(*link)->next = NULL;
+	if (!(*start)) *link = NULL;
 
-		(*link)->lengths = (unsigned long*)pkg_malloc(sizeof(unsigned long)*n);
-		if(!(*link)->lengths) {
-			LM_ERR("no more pkg memory (2)\n");
-			pkg_free(*link);
-			*link = NULL;
-			return -1;
-		}
-		for(i=0; i<n; i++)
-			(*link)->lengths[i] = strlen(value[i].s) + 1;
-
-		(*link)->data = (char**)pkg_malloc(sizeof(char*)*n);
-		if(!(*link)->data) {
-			LM_ERR("no more pkg memory (3)\n");
-			pkg_free( (*link)->lengths );
-			pkg_free(*link);
-			*link = NULL;
-			return -1;
-		}
-
-		for(i=0; i<n; i++) {
-			(*link)->data[i] = pkg_malloc(sizeof(char) * (*link)->lengths[i]);
-			if(!(*link)->data[i]) {
-				LM_ERR("no more pkg memory (4)\n");
-				pkg_free( (*link)->lengths );
-				pkg_free( (*link)->data );
-				pkg_free(*link);
-				*link = NULL;
-				return -1;
-			}
-			strncpy((*link)->data[i], value[i].s, (*link)->lengths[i]);
-		}
-	
-		*start = *link;
-		return 0;
+	nlink=(list*)pkg_malloc(sizeof(list));
+	if(!nlink) {
+		LM_ERR("no more pkg memory (1)\n");
+		return -1;
+	}
+	nlink->rownum = n;
+	nlink->next = NULL;
+
+	nlink->lengths = (unsigned long*)pkg_malloc(sizeof(unsigned long)*n);
+	if(!nlink->lengths) {
+		LM_ERR("no more pkg memory (2)\n");
+		pkg_free(nlink);
+		return -1;
+	}
+	for(i=0; i<n; i++)
+		nlink->lengths[i] = value[i].buflen;
+
+	nlink->data = (char**)pkg_malloc(sizeof(char*)*n);
+	if(!nlink->data) {
+		LM_ERR("no more pkg memory (3)\n");
+		pkg_free( nlink->lengths );
+		pkg_free(nlink);
+		return -1;
 	}
-	else
-	{
-		list* nlink;
-		nlink=(list*)pkg_malloc(sizeof(list));
-		if(!nlink) {
-			LM_ERR("no more pkg memory (5)\n");
-			return -1;
-		}
-		nlink->rownum = n;
-
-		nlink->lengths = (unsigned long*)pkg_malloc(sizeof(unsigned long)*n);
-		if(!nlink->lengths) {
-			LM_ERR("no more pkg memory (6)\n");
-			pkg_free(nlink);
-			nlink = NULL;
-			return -1;
-		}
-		for(i=0; i<n; i++)
-			nlink->lengths[i] = strlen(value[i].s) + 1;
 
-		nlink->data = (char**)pkg_malloc(sizeof(char*)*n);
-		if(!nlink->data) {
-			LM_ERR("no more pkg memory (7)\n");
+	for(i=0; i<n; i++) {
+		nlink->data[i] = pkg_malloc(sizeof(char) * nlink->lengths[i]);
+		if(!nlink->data[i]) {
+			LM_ERR("no more pkg memory (4)\n");
 			pkg_free( nlink->lengths );
+			pkg_free( nlink->data );
 			pkg_free(nlink);
-			nlink = NULL;
 			return -1;
 		}
+		memcpy(nlink->data[i], value[i].s, nlink->lengths[i]);
+	}
 
-		for(i=0; i<n; i++) {
-			nlink->data[i] = pkg_malloc(sizeof(char) * nlink->lengths[i]);
-			if(!nlink->data[i]) {
-				LM_ERR("no more pkg memory (8)\n");
-				pkg_free( nlink->lengths );
-				pkg_free( nlink->data );
-				pkg_free(nlink);
-				nlink = NULL;
-				return -1;
-			}
-			strncpy(nlink->data[i], value[i].s, nlink->lengths[i]);
-		}
-
-		nlink->next = NULL;
+	if (!(*start)) {
+		*link = nlink;
+		*start = *link;
+	} else {
 		(*link)->next = nlink;
 		*link = (*link)->next;
-
-		return 0;
 	}
-}
 
+	return 0;
+}
 
 /*!
  * \brief Destroy a list

+ 15 - 19
modules/db_unixodbc/res.c

@@ -1,4 +1,4 @@
-/* 
+/*
  * $Id$
  *
  * UNIXODBC module result related functions
@@ -178,38 +178,34 @@ static inline int db_unixodbc_convert_rows(const db1_con_t* _h, db1_res_t* _r)
 	}
 
 	SQLNumResultCols(CON_RESULT(_h), (SQLSMALLINT *)&columns);
-	temp_row = (strn*)pkg_malloc( columns*sizeof(strn) );
-	if(!temp_row) {
-		LM_ERR("no private memory left\n");
-		return -1;
-	}
 
 	while(SQL_SUCCEEDED(ret = SQLFetch(CON_RESULT(_h))))
 	{
+		temp_row = db_unixodbc_new_cellrow(columns);
+		if (!temp_row) {
+			LM_ERR("no private memory left\n");
+			return -1;
+		}
+
 		for(i=0; i < columns; i++)
 		{
-			SQLLEN indicator;
-			ret = SQLGetData(CON_RESULT(_h), i+1, SQL_C_CHAR,
-				temp_row[i].s, STRN_LEN, &indicator);
-			if (SQL_SUCCEEDED(ret)) {
-				if (indicator == SQL_NULL_DATA)
-					strcpy(temp_row[i].s, "NULL");
-			}
-			else {
-				LM_ERR("SQLGetData failed\n");
+			if (!db_unixodbc_load_cell(_h, i+1, temp_row + i, RES_TYPES(_r)[i])) {
+			    db_unixodbc_free_cellrow(columns, temp_row);
+			    return -5;
 			}
 		}
 
 		if (db_unixodbc_list_insert(&rowstart, &rows, columns, temp_row) < 0) {
 			LM_ERR("insert failed\n");
-			pkg_free(temp_row);
-			temp_row= NULL;
+			db_unixodbc_free_cellrow(columns, temp_row);
 			return -5;
 		}
 		RES_ROW_N(_r)++;
+
+		/* free temporary row data */
+		db_unixodbc_free_cellrow(columns, temp_row);
+		temp_row = NULL;
 	}
-	/* free temporary row data */
-	pkg_free(temp_row);
 	CON_ROW(_h) = NULL;
 
 	if (!RES_ROW_N(_r)) {