DBD::Informix test failure for lvarchar - bug found!
am 13.06.2007 08:01:46 von jonathan.leffler------=_Part_4736_26645297.1181714506993
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Finally, after about 30 hours of hair tearing and hard work, I've isolated a
pure ESQL/C version of this bug and demonstrated that it primarily afflicts
32-bit ports of Informix ESQL/C that come with CSDK 2.90 and 2.81 . It does
not afflict most 64-bit ports (though 2.81.FC2 crashed with a core dump, as
did 2.80.UC2), and it does not affect older ports (2.80.UC1 was OK). It did
not reproduce on RHEL 4 running on Linux PPC-64 with CSDK
3.00.FC1(actually, a nightly build prior to that release). Finally,
it only affects
LVARCHAR NOT NULL, and only if the table is not a temporary table. Since I
kept the server constant (IDS 10.00.UC5 running on Solaris 10), the problem
is most likely in ESQL/C rather than IDS itself; if it is perchance in IDS,
it is in the code that recognizes different versions of ESQL/C and somehow
reacts differently.
This bug is now idsdb00139040 in the IBM/Informix CQ database.
Sadly, as yet, I do not have a workaround or fix for this -- that comes
next.
The reproduction code follows:
/*
** @(#)$Id: bug-lvcnn.ec,v 1.3 2007/06/13 05:35:27 jleffler Exp $
**
** Demonstration of bug originally reported in DBD::Informix
** as RT#13708 at http://rt.cpan.org/ and ignored for a couple of years.
** Primarily seems to afflict 32-bit ports of CSDK (ESQL/C).
** And primarily the more recent versions - 2.90 and maybe 2.81.
**
** Bug: SQL DESCRIPTOR does not handle LVARCHAR NOT NULL properly.
**
** Demonstrated on Solaris 10 with CSDK 2.90.UC4.
** No bug with 64-bit on Solaris 10 with CSDK 2.90.FC4.
** No bug with 64-bit on Linux PPC 64 with CSDK 3.00.FN125 (nightly build).
** No bug with 32-bit on Solaris 10 with CSDK 2.80.UC1
** Core dump on 64-bit on Solaris 10 with CSDK 2.81.FC2
** Core dump on 32-bit on Solaris 10 with CSDK 2.81.UC2
** In each of the above cases, the test DBMS is IDS 10.00.UC5 running on
Solaris 10.
** Also seen by customers on various platforms - primarily 32-bit.
*/
#include
#include
#include
$ static char lvc1[] = "This is row 1";
$ static char lvc2[] = "And this is in row 1 too";
static int num_bugs = 0;
static void print_descriptor(const char *p_name, int p_index)
{
$ int index = p_index;
$ const char *name = p_name;
$ long coltype;
$ long collength;
$ long colind;
$ char colname[129];
$ int nullable;
$ whenever error stop;
$ get descriptor :name VALUE :index
:coltype = TYPE, :collength = LENGTH,
:nullable = NULLABLE,
:colind = INDICATOR, :colname = NAME;
colname[byleng(colname, strlen(colname))] = '\0';
printf("%s:%02d: type = %2d, length = %4d, nulls = %2d, indicator = %2d,
name = %s\n",
name, index, coltype, collength, nullable, colind, colname);
}
static void check_data(void)
{
$ lvarchar *lv1 = 0;
$ lvarchar *lv2 = 0;
$ int row;
$ short ind;
$ prepare p from "select row_number, lvc_with_null, lvc_wout_null from
lvarchar_test order by ro
w_number";
$ declare c cursor for p;
$ allocate descriptor "d" with max 3;
$ describe p using sql descriptor "d";
/* Print allocator description */
print_descriptor("d", 1);
print_descriptor("d", 2);
print_descriptor("d", 3);
ifx_var_flag(&lv1, 1);
ifx_var_flag(&lv2, 1);
$ open c;
while (sqlca.sqlcode == 0)
{
$ fetch c using sql descriptor "d";
if (sqlca.sqlcode != 0)
break;
$ get descriptor "d" VALUE 1 :row = DATA, :ind = INDICATOR;
if (ind == 0)
printf("row_number = %d:\n", row);
else
printf("row_number IS NULL (ind = %d)\n", ind);
$ get descriptor "d" VALUE 2 :lv1 = DATA, :ind = INDICATOR;
if (ind != 0)
printf(" lvc_with_null IS NULL (ind = %d)\n", ind);
else
{
char *result = (char *)ifx_var_getdata(&lv1);
int length = ifx_var_getlen(&lv1);
if (length < 0)
{
printf("Length of lvarchar < 0\n");
length = 0;
}
if (result == 0)
{
printf("Result of lvarchar == 0x00000000\n");
}
printf(" lvc_with_null = <<%.*s>>\n", length, result);
if (strcmp(result, lvc1) != 0)
{
printf("**BUG** wanted = <<%s>>\n", lvc1);
num_bugs++;
}
}
$ get descriptor "d" VALUE 3 :lv2 = DATA, :ind = INDICATOR;
if (ind != 0)
printf(" lvc_wout_null IS NULL (ind = %d)\n", ind);
else
{
char *result = (char *)ifx_var_getdata(&lv2);
int length = ifx_var_getlen(&lv2);
if (length < 0)
{
printf("Length of lvarchar < 0\n");
length = 0;
}
if (result == 0)
{
printf("Result of lvarchar == 0x00000000\n");
}
printf(" lvc_wout_null = <<%.*s>>\n", length, result);
if (strcmp(result, lvc2) != 0)
{
printf("**BUG** wanted = <<%s>>\n", lvc2);
num_bugs++;
}
}
}
ifx_var_freevar(&lv1);
ifx_var_freevar(&lv2);
$ close c;
$ free c;
$ free p;
$ deallocate descriptor "d";
}
int main(int argc, char **argv)
{
$ char *dbname = "stores";
if (argc > 1)
dbname = argv[1];
$ database :dbname;
$ whenever error continue;
$ drop table lvarchar_test;
$ whenever error stop;
printf("\nTest 1: LVARCHAR(128) - with NOT NULL\n");
$ create table lvarchar_test
(
row_number serial not null primary key,
lvc_with_null lvarchar(128),
lvc_wout_null lvarchar(128) not null
);
$ insert into lvarchar_test values(1, :lvc1, :lvc2);
check_data();
printf("\nTest 2: LVARCHAR - with NOT NULL\n");
$ drop table lvarchar_test;
$ create table lvarchar_test
(
row_number serial not null primary key,
lvc_with_null lvarchar,
lvc_wout_null lvarchar not null
);
$ insert into lvarchar_test values(1, :lvc1, :lvc2);
check_data();
printf("\nTest 3: LVARCHAR(128) - without NOT NULL\n");
$ drop table lvarchar_test;
$ create table lvarchar_test
(
row_number serial not null primary key,
lvc_with_null lvarchar(128),
lvc_wout_null lvarchar(128)
);
$ insert into lvarchar_test values(1, :lvc1, :lvc2);
check_data();
printf("\nTest 4: LVARCHAR - without NOT NULL\n");
$ drop table lvarchar_test;
$ create table lvarchar_test
(
row_number serial not null primary key,
lvc_with_null lvarchar,
lvc_wout_null lvarchar
);
$ insert into lvarchar_test values(1, :lvc1, :lvc2);
check_data();
printf("\nTest 5: LVARCHAR(128) - with NOT NULL in TEMP TABLE\n");
$ drop table lvarchar_test;
$ create temp table lvarchar_test
(
row_number serial not null primary key,
lvc_with_null lvarchar(128),
lvc_wout_null lvarchar(128) not null
);
$ insert into lvarchar_test values(1, :lvc1, :lvc2);
check_data();
$ close database;
if (num_bugs == 0)
printf("== PASSED ==\n");
else
printf("** FAILED ** %d bugs detected\n", num_bugs);
return(num_bugs > 0); /* 0 on no bugs; 1 otherwise */
}
To say that the circumstances under which it fails are obscure is to be
excessively polite.
I tried to release an update to DBD::Informix, but the release process goes
through the test suite, and since the test t/t93lvarchar.t was failing, it
was not possible to make the release automatically, so I haven't made the
update. I may decide to cheat and make the release using a 64-bit Perl and
64-bit ESQL/C, like I did with the DBD::Informix 2007.0226 (though that was
released completely unaware of the issue - this one would be released
despite knowing the test fails). The temptation to modify the test (eg to
use a temp table instead of a permanent one) is also quite considerable.
--
Jonathan Leffler
Guardian of DBD::Informix - v2007.0226 - http://dbi.perl.org
"Blessed are we who can laugh at ourselves, for we shall never cease to be
amused."
------=_Part_4736_26645297.1181714506993--