Skip to content

Commit 4abe8ca

Browse files
author
clach04
committed
decimal type crash on select, decimal truncation, unable to use decimal with precision of 31 with Ingres pre 9.2.
Change is in two parts: 1) use the correct (string) length along with precision/scale for decimal data retrieved from the DBMS 2) use the correct (string) length along with precision/scale for decimal data sent to the DBMS Most of the change is on removing the need for arithmetic on "fetch" from database (and send), arithmetic is done once and the same value is used to get data from the DBMS by puting the correct values into the IIDBI_DESCRIPTOR. The IIDBI_DESCRIPTOR is set for Bind (send) and fetch (get) operations. The IIDBI_DESCRIPTOR values is then used at the time the ODBC call is made, no math is issued at this point in time. The new get/send code that was added for the Decimal type does not have any code that is specific to decimals. The decimal specific code is in the describe code. This is stage 1 of a multi stage code re-factor, to use the cType and internal size for all types and then use it everywhere where that information is needed. All types will set the size and type and then the get code will be a single block of code that uses the descriptor information. Testing Done: All decimal tests in tets suite now pass. I.e.: test_decimalCrash passes (test suite no longer crashes) test_decimalLongValues passes test_decimalBindLongValues now passes (and does not generate ADF errors) with pre Ingres 9.2 versions of Ingres (i.e. versions where 31 is the maximum precision for decimal type). git-svn-id: http://code.ingres.com/ingres/drivers/python/main@1880 45b5d43f-8932-4c86-835a-3654e5842839
1 parent af8f961 commit 4abe8ca

File tree

3 files changed

+133
-12
lines changed

3 files changed

+133
-12
lines changed

dbi/iidbicurs.c

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
** vim:filetype=c:ts=4:sw=4:et:nowrap
23
** Copyright (c) 2008 Ingres Corporation
34
**
45
** This program is free software; you can redistribute it and/or modify
@@ -134,8 +135,32 @@
134135
** trace files.
135136
** 05-Aug-2009 (Chris.Clark@ingres.com)
136137
** Removed unused dbi_cursorFetchall()
138+
** 19-Aug-2009 (Chris.Clark@ingres.com)
139+
** Added vim hints.
140+
** Fixed bug with select of decimal values.
141+
** Leave "precision" alone, precision means something and should
142+
** not be altered. Now set internalSize to the size of the buffer
143+
** space for decimal on fetch. Ensure the correct (max) string
144+
** length is used for fetching decimal values from the DBMS. Added
145+
** new macro MAX_STRING_LEN_FOR_DECIMAL_PRECISION for calculating
146+
** string buffer length.
137147
**/
138148

149+
/*
150+
** Decimal values are sent/received as strings.
151+
** Define macro that calculates the largest string needed for
152+
** a decimal with a given precision.
153+
**
154+
** max string length/size of decimal value is based on:
155+
** precision (not the precision/scale encoded combo) for digits
156+
** +1 for NULL terminator
157+
** +1 for period/dot/comma
158+
** +1 for possible negative sign.
159+
** +1 for possible leading zero where precision=scale,
160+
** e.g Ingres literal Decimal(0.1, 1, 1)
161+
*/
162+
#define MAX_STRING_LEN_FOR_DECIMAL_PRECISION(x) (x+4)
163+
139164
RETCODE BindParameters(IIDBI_STMT *pstmt, unsigned char isProc);
140165

141166
/*{
@@ -1147,6 +1172,38 @@ dbi_cursorFetchone( IIDBI_STMT *pstmt )
11471172
}
11481173
break;
11491174

1175+
case SQL_DECIMAL:
1176+
/* Start of generic get code */
1177+
rc = SQLGetData(pstmt->hdr.handle, i+1,
1178+
pstmt->descriptor[i]->cType,
1179+
pstmt->descriptor[i]->data,
1180+
pstmt->descriptor[i]->internalSize,
1181+
&orind);
1182+
if (orind == SQL_NULL_DATA)
1183+
pstmt->descriptor[i]->isNull = 1;
1184+
else
1185+
pstmt->descriptor[i]->isNull = 0;
1186+
if (rc == SQL_NO_DATA)
1187+
pstmt->fetchDone = TRUE;
1188+
if (SQL_SUCCEEDED(rc) || rc == SQL_NO_DATA)
1189+
{
1190+
rc = DBI_SQL_SUCCESS;
1191+
break;
1192+
}
1193+
else
1194+
{
1195+
return_code = IIDBI_ERROR( rc, NULL, NULL,
1196+
pstmt, &pstmt->hdr.err );
1197+
DBPRINTF(DBI_TRC_STAT)
1198+
( "%d = dbi_cursorFetchone (%d) %s %s %x\n\n",
1199+
rc, __LINE__, pstmt->hdr.err.sqlState,
1200+
pstmt->hdr.err.messageText,
1201+
pstmt->hdr.err.native);
1202+
return return_code;
1203+
}
1204+
break;
1205+
1206+
11501207
default:
11511208
rc = SQLGetData(pstmt->hdr.handle, i+1, SQL_C_CHAR,
11521209
pstmt->descriptor[i]->data,
@@ -1605,6 +1662,8 @@ BindParameters(IIDBI_STMT *pstmt, unsigned char isProc)
16051662
void *data;
16061663
SQLUINTEGER precision;
16071664
SQLSMALLINT scale;
1665+
SQLSMALLINT cType;
1666+
SQLLEN internalSize;
16081667
unsigned char isNull;
16091668

16101669
DBPRINTF(DBI_TRC_ENTRY)("%p: Begin BindParameters\n", pstmt);
@@ -1626,6 +1685,8 @@ BindParameters(IIDBI_STMT *pstmt, unsigned char isProc)
16261685
precision = pstmt->parameter[i]->precision;
16271686
scale = pstmt->parameter[i]->scale;
16281687
isNull = pstmt->parameter[i]->isNull;
1688+
cType = pstmt->parameter[i]->cType;
1689+
internalSize = pstmt->parameter[i]->internalSize;
16291690
/*
16301691
** make orind persistent beyond this call by using the field
16311692
** in the parameter descriptor structure.
@@ -1700,9 +1761,30 @@ BindParameters(IIDBI_STMT *pstmt, unsigned char isProc)
17001761
}
17011762
break;
17021763

1764+
case SQL_DECIMAL:
1765+
/* Start of generic BindParameters code */
1766+
if (isNull)
1767+
*orind = SQL_NULL_DATA;
1768+
else
1769+
*orind = SQL_NTS;
1770+
rc = SQLBindParameter(pstmt->hdr.handle, i+1, SQL_PARAM_INPUT,
1771+
cType, type, precision, scale, data, internalSize , orind);
1772+
1773+
if (rc != SQL_SUCCESS)
1774+
{
1775+
return_code = IIDBI_ERROR( rc, NULL, NULL, pstmt->hdr.handle,
1776+
&pstmt->hdr.err );
1777+
1778+
DBPRINTF(DBI_TRC_STAT)
1779+
( "%d = SQLBindParameter (%d) %s %s %x\n %s\n",
1780+
rc, __LINE__, pstmt->hdr.err.sqlState,
1781+
pstmt->hdr.err.messageText, pstmt->hdr.err.native, 0 );
1782+
return return_code;
1783+
}
1784+
break;
1785+
17031786
case SQL_CHAR:
17041787
case SQL_VARCHAR:
1705-
case SQL_DECIMAL:
17061788
DBPRINTF(DBI_TRC_STAT)("CHAR or VARCHAR\n");
17071789
if (isNull)
17081790
*orind = SQL_NULL_DATA;
@@ -1921,11 +2003,13 @@ RETCODE dbi_describeColumns (IIDBI_STMT *pstmt)
19212003
SQLINTEGER internalSize;
19222004
SQLINTEGER displaySize;
19232005
SQLUINTEGER precision;
2006+
int cType;
19242007

19252008
numCols = pstmt->descCount;
19262009

19272010
for (i = 0; i < numCols; i++)
19282011
{
2012+
cType=SQL_C_CHAR; /* Default */
19292013
rc = SQLDescribeCol(hstmt, i+1, colName, 256, &cbColName, &type,
19302014
&precision, &scale, &nullable);
19312015
if (!SQL_SUCCEEDED(rc))
@@ -1949,11 +2033,7 @@ RETCODE dbi_describeColumns (IIDBI_STMT *pstmt)
19492033
case SQL_DATE:
19502034
case SQL_TIME:
19512035
pstmt->descriptor[i]->precision = 26;
1952-
break;
1953-
1954-
case SQL_NUMERIC:
1955-
case SQL_DECIMAL:
1956-
pstmt->descriptor[i]->precision = 34;
2036+
cType=SQL_C_TIMESTAMP;
19572037
break;
19582038

19592039
case SQL_LONGVARCHAR:
@@ -1977,6 +2057,15 @@ RETCODE dbi_describeColumns (IIDBI_STMT *pstmt)
19772057
}
19782058
if (displaySize == MAX_DISPLAY_SIZE) /* if set at max, default to -1 */
19792059
displaySize = -1;
2060+
switch (pstmt->descriptor[i]->type)
2061+
{
2062+
case SQL_NUMERIC:
2063+
case SQL_DECIMAL:
2064+
internalSize = MAX_STRING_LEN_FOR_DECIMAL_PRECISION(precision);
2065+
cType = SQL_C_CHAR;
2066+
break;
2067+
2068+
default:
19802069
rc = SQLColAttribute(hstmt, i+1, SQL_DESC_OCTET_LENGTH, 0, 0, NULL,
19812070
&internalSize);
19822071
if (!SQL_SUCCEEDED(rc))
@@ -1989,13 +2078,16 @@ RETCODE dbi_describeColumns (IIDBI_STMT *pstmt)
19892078
pstmt->hdr.err.native);
19902079
return return_code;
19912080
}
1992-
DBPRINTF(DBI_TRC_STAT)("For Col %d, type is %d, name is %s, precision is %d, scale is %d display size is %d internal size is %d and nullable is %d\n", i, type, colName, precision, scale, displaySize, internalSize, nullable);
2081+
break;
2082+
}
2083+
DBPRINTF(DBI_TRC_STAT)("For Col %d, type is %d (cType is %d), name is %s, precision is %d, scale is %d display size is %d internal size is %d and nullable is %d\n", i, type, cType, colName, precision, scale, displaySize, internalSize, nullable);
19932084
pstmt->descriptor[i]->scale = scale;
19942085
pstmt->descriptor[i]->nullable = nullable;
19952086
pstmt->descriptor[i]->columnName = calloc(1,cbColName + 1);
19962087
strcpy(pstmt->descriptor[i]->columnName, (char *)colName);
19972088
pstmt->descriptor[i]->displaySize = displaySize;
19982089
pstmt->descriptor[i]->internalSize = internalSize;
2090+
pstmt->descriptor[i]->cType = cType;
19992091
}
20002092
return DBI_SQL_SUCCESS;
20012093
}

dbi/ingresdbi.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
** vim:filetype=c:ts=4:sw=4:et:nowrap
23
** Copyright (c) 2008 Ingres Corporation
34
**
45
** This program is free software; you can redistribute it and/or modify
@@ -214,6 +215,12 @@
214215
** Added correct counter increment to Py_None
215216
** simplified NULL logic into one place by
216217
** removing duplicate code.
218+
** 19-Aug-2009 (Chris.Clark@ingres.com)
219+
** Compile warnings clean up of "unreferenced local variable" (win32
220+
** specific).
221+
** Set accurate decimal precision/scale/internalSize information
222+
** for bind parameters.
223+
** Added trace information for bind parameters as they are processed.
217224
**/
218225

219226
static PyObject *IIDBI_Warning;
@@ -1151,9 +1158,11 @@ static PyObject * IIDBI_connect(PyObject *self, PyObject *args,
11511158
RETCODE rc=DBI_SQL_SUCCESS;
11521159
int i;
11531160
int result = FALSE;
1161+
#ifndef WIN32
11541162
struct stat buf;
11551163
char odbcconfig[MAX_PATH];
11561164
char *ii_system;
1165+
#endif /* WIN32 */
11571166
static char *kwlist[] =
11581167
{
11591168
"dsn", "database", "vnode", "uid", "pwd", "autocommit", "selectloops",
@@ -6200,17 +6209,35 @@ int IIDBI_sendParameters(IIDBI_CURSOR *self, PyObject *params)
62006209
else if (PyObject_IsInstance(elem, decimalType))
62016210
{
62026211
char *decimal = NULL;
6212+
char *tmp_str = NULL;
62036213
parameter[i]->data =
62046214
strdup(PyString_AsString(PyObject_Str(elem)));
6215+
if (parameter[i]->data == NULL)
6216+
{
6217+
Py_XDECREF(params);
6218+
exception = IIDBI_InternalError;
6219+
errMsg = "strdup returned NULL";
6220+
result = IIDBI_handleError((PyObject *)self, exception, errMsg);
6221+
goto errorExit;
6222+
}
62056223
parameter[i]->type = SQL_DECIMAL;
62066224
parameter[i]->cType = SQL_C_CHAR;
6207-
parameter[i]->precision =
6208-
(int)strlen((char *)parameter[i]->data);
6225+
parameter[i]->internalSize = (int)strlen((char *)parameter[i]->data) + 1; /* +1 for NULL terminator */
6226+
tmp_str = (char *)parameter[i]->data;
6227+
/* Ignore leading signs and leading zeros */
6228+
while (*tmp_str!='\0' && (*tmp_str=='-' || *tmp_str=='0')) /* check for '+' too? Not seen one before */
6229+
{
6230+
tmp_str++;
6231+
}
6232+
parameter[i]->precision = (int)strlen(tmp_str);
62096233
decimal = strstr(parameter[i]->data,".");
62106234
if (!decimal)
62116235
decimal = strstr(parameter[i]->data, ",");
62126236
if (decimal)
6237+
{
62136238
decimal++;
6239+
parameter[i]->precision--; /* do not count decimal seperator as part of precision */
6240+
}
62146241
if (decimal)
62156242
parameter[i]->scale = (int)strlen(decimal);
62166243
else
@@ -6227,6 +6254,7 @@ int IIDBI_sendParameters(IIDBI_CURSOR *self, PyObject *params)
62276254
goto errorExit;
62286255
}
62296256
Py_DECREF(elem);
6257+
DBPRINTF(DBI_TRC_STAT)("For bind %d, type is %d (cType is %d), precision is %d, scale is %d internal size is %d and nullable is %d\n", i, parameter[i]->type, parameter[i]->cType, parameter[i]->precision, parameter[i]->scale, parameter[i]->internalSize, parameter[i]->isNull);
62306258
} /* for (i = 0; i < parmCount; i++) */
62316259

62326260
DBPRINTF(DBI_TRC_RET)("%p: IIDBI_sendParameters }}}1\n", self);

hdr/iidbi.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
** vim:filetype=c:ts=4:sw=4:et:nowrap
23
** Copyright (c) 2008 Ingres Corporation
34
**
45
** This program is free software; you can redistribute it and/or modify
@@ -267,14 +268,14 @@ typedef struct
267268
typedef struct _IIDBI_DESCRIPTOR
268269
{
269270
char *columnName;
270-
int type;
271-
int cType;
271+
int type; /* ODBC DBMS type */
272+
int cType; /* The ODBC C type used for "transport" */
272273
int precision;
273274
int scale;
274275
void *data;
275276
int nullable;
276277
int displaySize;
277-
int internalSize;
278+
int internalSize; /* Used for internal memory malloc */
278279
unsigned char isNull;
279280
long orInd;
280281
} IIDBI_DESCRIPTOR;

0 commit comments

Comments
 (0)