SourceForge: pywin32/pywin32: changeset 4143:427c3e914293
Call PyType_Ready for Cursor_Type and Connection_Type
authorrupole@users.sourceforge.net
Fri Jun 24 02:57:51 2011 -0400 (2011-06-24)
changeset 4143427c3e914293
parent 4142 ade1cd374722
child 4144 067097188a61
Call PyType_Ready for Cursor_Type and Connection_Type
Fix a memory problem when fetching successive large columns
win32/src/odbc.cpp
     1.1 --- a/win32/src/odbc.cpp	Tue Jun 21 22:23:45 2011 -0400
     1.2 +++ b/win32/src/odbc.cpp	Fri Jun 24 02:57:51 2011 -0400
     1.3 @@ -1168,7 +1168,7 @@
     1.4  			PyObject *sitem = PyObject_Str(item);
     1.5  			if (sitem==NULL)
     1.6  				rv = 0;
     1.7 -			else if PyString_Check(sitem)
     1.8 +			else if (PyString_Check(sitem))
     1.9  				rv = ibindString(cur, iCol, sitem);
    1.10  			else if PyUnicode_Check(sitem)
    1.11  				rv = ibindUnicode(cur, iCol, sitem);
    1.12 @@ -1595,51 +1595,46 @@
    1.13  		return NULL;
    1.14  
    1.15  	while (ob) {
    1.16 -		SQLLEN cbRequired;
    1.17 -		RETCODE rc;
    1.18 -		SQLLEN cbRead = 0;
    1.19 -        /* Use SQLGetData to retrieve data for blob (or long varchar) type columns. */
    1.20 -        if (ob->bGetData)
    1.21 -        {
    1.22 -            /* Initialize memory (offsets, etc.)
    1.23 -	       (use bind_area for buffer, and dynamically allocate in the loop below) */
    1.24 -            cbRequired = ob->vsize;
    1.25 -            ob->rcode = 0;
    1.26 -            cbRead = 0;      /* Count of bytes read (running total and offset into buffer). */
    1.27 -
    1.28 -            /* Loop until SQLGetData tells us that there are no more chunks
    1.29 -	       of the blob to retrieve. */
    1.30 -            do
    1.31 -            {
    1.32 -                /* Check to see if bind_area is big enough
    1.33 -                      if cbRequired > vsize
    1.34 -                         re-allocate bind_area to cbRequired
    1.35 -                         set ob->vsize = cbRequired */
    1.36 -                if (cbRequired > ob->vsize)
    1.37 -                {
    1.38 -                    void *pTemp = ob->bind_area;
    1.39 +		if (ob->bGetData)
    1.40 +		{
    1.41 +			/* Use SQLGetData to retrieve data for blob (or long varchar) type columns.
    1.42 +				Loop until return code indicates all remaining data fit into buffer.
    1.43 +			*/
    1.44 +			RETCODE rc;
    1.45 +			SQLLEN cbRead = 0;
    1.46 +			ob->rcode = 0;
    1.47 +			do
    1.48 +			{
    1.49 +				/* Increase buffer size by cursor chunk size on second and subsequent calls
    1.50 +					If not for the SQL Anywhere 5.0 problem (driver version
    1.51 +					5.05.041867), we could probably grow by 50% each time
    1.52 +					or the remaining size (as determined by ob->rcode).
    1.53 +					Regarding above note, caller can now use cursor.setoutputsize
    1.54 +					to work around any such bug in a driver */
    1.55 +				if (ob->rcode){
    1.56 +					void *pTemp = ob->bind_area;
    1.57 +					ob->vsize += cur->max_width;
    1.58  					/* Some BLOBs can be huge, be paranoid about allowing
    1.59  					   other threads to run. */
    1.60  					Py_BEGIN_ALLOW_THREADS
    1.61 -                    ob->bind_area = realloc (ob->bind_area, cbRequired);
    1.62 +					ob->bind_area = realloc (ob->bind_area, ob->vsize);
    1.63  					Py_END_ALLOW_THREADS
    1.64  					if (ob->bind_area == NULL){
    1.65  						PyErr_NoMemory();
    1.66 +						ob->vsize -= cur->max_width;
    1.67  						ob->bind_area = pTemp;
    1.68  						Py_DECREF(row);
    1.69  						return NULL;
    1.70  						}
    1.71 -                    ob->vsize = cbRequired;
    1.72                  }
    1.73  
    1.74 -                /* rc = GetData( ... , bind_area + offset, vsize - offset, &rcode ) */
    1.75  				Py_BEGIN_ALLOW_THREADS
    1.76 -                rc = SQLGetData(cur->hstmt,
    1.77 -                                       ob->pos,
    1.78 -                                       ob->vtype,
    1.79 -                                       (char *)ob->bind_area + cbRead,
    1.80 -                                       ob->vsize - cbRead,
    1.81 -                                       &ob->rcode);
    1.82 +				rc = SQLGetData(cur->hstmt,
    1.83 +								ob->pos,
    1.84 +								ob->vtype,
    1.85 +								(char *)ob->bind_area + cbRead,
    1.86 +								ob->vsize - cbRead,
    1.87 +								&ob->rcode);
    1.88  				Py_END_ALLOW_THREADS
    1.89  				if (unsuccessful(rc))
    1.90  				{
    1.91 @@ -1647,35 +1642,31 @@
    1.92  	 				cursorError(cur, _T("SQLGetData"));
    1.93  					return NULL;
    1.94  				}
    1.95 -				/* Returned length is total length remaining including current read.
    1.96 -					If length is not known, returns SQL_NO_TOTAL */
    1.97 -                if ((ob->rcode != SQL_NO_TOTAL) && (ob->rcode <= cur->max_width))
    1.98 +				/* Return code can be a negative status code:
    1.99 +					SQL_NO_TOTAL if length is not known, SQL_NULL_DATA if nothing to retreive
   1.100 +					Otherwise will be total bytes remaining including current read.
   1.101 +				*/
   1.102 +                if (ob->rcode >= 0 && ob->rcode <= ob->vsize - cbRead)
   1.103                  {
   1.104 -                    /* If we get here, then this should be the last iteration through the loop. */
   1.105 -                    cbRead += ob->rcode;
   1.106 +					/* If we get here, then this should be the last iteration through the loop. */
   1.107 +					ob->rcode += cbRead;
   1.108                  }
   1.109                  else
   1.110                  {
   1.111 -                    /* Grow buffer by cursor chunk size for each read.
   1.112 -			       If not for the SQL Anywhere 5.0 problem (driver version
   1.113 -			       5.05.041867), we could probably grow by 50% each time
   1.114 -				   or the remaining size (as determined by ob->rcode).
   1.115 -					Regarding above note, caller can now use cursor.setoutputsize
   1.116 -					to work around any such bug in a driver */
   1.117 -                    cbRequired += cur->max_width;
   1.118 +					cbRead = ob->vsize;
   1.119  					/* We want to ignore the intermediate
   1.120  						  NULL characters SQLGetData() gives us.
   1.121  						   (silly, silly) */
   1.122  					if (ob->vtype == SQL_C_CHAR)
   1.123 -						cbRead = ob->vsize - 1;
   1.124 +						cbRead--;
   1.125  					else if (ob->vtype == SQL_C_WCHAR)
   1.126 -						cbRead = ob->vsize - sizeof(WCHAR);
   1.127 -					else
   1.128 -						cbRead = ob->vsize;
   1.129 -                }
   1.130 +						/* Buffer is not guaranteed to be an exact multiple of sizeof(WCHAR),
   1.131 +							leaving an extra byte and throwing the next get off by 1. */
   1.132 +						cbRead -= sizeof(WCHAR) + ob->vsize%sizeof(WCHAR);
   1.133 +				}
   1.134  
   1.135 -            } while (rc == SQL_SUCCESS_WITH_INFO); 
   1.136 -        }
   1.137 +			} while (rc == SQL_SUCCESS_WITH_INFO); 
   1.138 +		}
   1.139  		
   1.140  		PyObject *v;
   1.141  		if (ob->rcode == SQL_NULL_DATA)
   1.142 @@ -1696,7 +1687,7 @@
   1.143  			{
   1.144  				v = ob->copy_fcn(
   1.145  					ob->bind_area,
   1.146 -					cbRead);
   1.147 +					ob->rcode);
   1.148  			}
   1.149  			if (!v)
   1.150  			{
   1.151 @@ -1749,7 +1740,11 @@
   1.152  			}
   1.153  			else
   1.154  			{
   1.155 -				PyList_Append(list, entry);
   1.156 +				if (PyList_Append(list, entry) == -1){
   1.157 +					Py_DECREF(list);
   1.158 +					Py_DECREF(entry);
   1.159 +					return NULL;
   1.160 +					}
   1.161  				Py_DECREF(entry);
   1.162  			}
   1.163  		}
   1.164 @@ -1995,6 +1990,11 @@
   1.165  	PYWIN_MODULE_INIT_PREPARE(odbc, globalMethods,
   1.166  				  "A Python wrapper around the ODBC API.");
   1.167  
   1.168 +	if (PyType_Ready(&Cursor_Type) == -1)
   1.169 +		PYWIN_MODULE_INIT_RETURN_ERROR;
   1.170 +	if (PyType_Ready(&Connection_Type) == -1)
   1.171 +		PYWIN_MODULE_INIT_RETURN_ERROR;
   1.172 +
   1.173  	// Sql dates are now returned as python's datetime object.
   1.174  	//	C Api for datetime didn't exist in 2.3, stick to dynamic semantics for now.
   1.175  	datetime_module=PyImport_ImportModule("datetime");