[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [rt.cpan.org #118823] libdbd-mysql-perl: FTBFS on some architectures



On Fri, Nov 18, 2016 at 01:45:59AM +0100, gregor herrmann wrote:

> But as https://buildd.debian.org/status/package.php?p=libdbd-mysql-perl
> shows this is not only about mips but about various architectures,
> and the quick summary on IRC was "looks like a big-endian issue".

FWIW it goes away if I revert 97c8c22998e67e7c6cfe5635578e0464e9ac9513 
("Add support for 64bit types when perl is compiled with 64bit support").

We're configuring Perl with -Duse64bitint on both 32-bit and 64-bit
platforms.

I think the 64-bit integer support is not quite working yet; even on
little-endian (regular x86_64), if I test for them:

--- a/t/40server_prepare.t
+++ b/t/40server_prepare.t
@@ -59,11 +59,11 @@ ok($sth2 = $dbh->prepare('INSERT INTO dbd_mysql_t40serverprepare2 VALUES (?,?,?,
 ok($sth2->bind_param(1, 101, DBI::SQL_INTEGER), "binding int");
 ok($sth2->bind_param(2, 102, DBI::SQL_SMALLINT), "binding smallint");
 ok($sth2->bind_param(3, 103, DBI::SQL_TINYINT), "binding tinyint");
-ok($sth2->bind_param(4, 104, DBI::SQL_INTEGER), "binding bigint");
+ok($sth2->bind_param(4, (2<<32)+4, DBI::SQL_INTEGER), "binding bigint");
 
 ok($sth2->execute(), "inserting data");
 
-is_deeply($dbh->selectall_arrayref('SELECT * FROM dbd_mysql_t40serverprepare2'), [[101, 102, 103, 104]]);
+is_deeply($dbh->selectall_arrayref('SELECT * FROM dbd_mysql_t40serverprepare2'), [[101, 102, 103, (2<<32)+4]]);
 
 ok ($dbh->do(qq{DROP TABLE dbd_mysql_t40serverprepare2}), "cleaning up");
 
I get

not ok 20
#   Failed test at t/40server_prepare.t line 66.
#     Structures begin differing at:
#          $got->[0][3] = '4'
#     $expected->[0][3] = '8589934596'

It looks like it's coercing 64-bit integers into 32-bit ones, which mostly
works for small integers on little endian, but zeros out the result on
big endian.

I gather MYSQL_TYPE_LONG is an int (32-bit) despite the name and it
seems to be the default everywhere in dbdimp.c. Blindly sprinkling
MYSQL_TYPE_LONGLONG in the code fixes it; see the attached patch. I've
tested it on amd64 (64-bit LE), s390x (64-bit BE), armhf (32-bit LE)
and powerpc (32-bit BE).

But maybe MYSQL_TYPE_LONGLONG should be limited to just SQL_BIGINTs?
Hope this helps a bit anyway.
-- 
Niko Tyni   ntyni@debian.org
>From 5b2c77e9e0836963d505138b7c99a959adf32538 Mon Sep 17 00:00:00 2001
From: Niko Tyni <ntyni@debian.org>
Date: Fri, 18 Nov 2016 10:53:29 +0200
Subject: [PATCH] Use MYSQL_TYPE_LONGLONG (64-bit) by default

This is possibly overkill, but it fixes test failures on
big endian platforms with 64-bit integers.

Bug-Debian: https://bugs.debian.org/844538
Bug: https://rt.cpan.org/Ticket/Display.html?id=118823
---
 dbdimp.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/dbdimp.c b/dbdimp.c
index c1ef3ff..c5b4f22 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -361,7 +361,7 @@ static enum enum_field_types mysql_to_perl_type(enum enum_field_types type)
 #if IVSIZE >= 8
   case MYSQL_TYPE_LONGLONG:
 #endif
-    enum_type= MYSQL_TYPE_LONG;
+    enum_type= MYSQL_TYPE_LONGLONG;
     break;
 
 #if MYSQL_VERSION_ID > NEW_DATATYPE_VERSION
@@ -3508,7 +3508,7 @@ my_ulonglong mysql_st_internal_execute41(
   {
     for (i = mysql_stmt_field_count(stmt) - 1; i >=0; --i) {
         enum_type = mysql_to_perl_type(stmt->fields[i].type);
-        if (enum_type != MYSQL_TYPE_DOUBLE && enum_type != MYSQL_TYPE_LONG && enum_type != MYSQL_TYPE_BIT)
+        if (enum_type != MYSQL_TYPE_DOUBLE && enum_type != MYSQL_TYPE_LONG && enum_type != MYSQL_TYPE_LONGLONG && enum_type != MYSQL_TYPE_BIT)
         {
             /* mysql_stmt_store_result to update MYSQL_FIELD->max_length */
             my_bool on = 1;
@@ -3776,6 +3776,7 @@ int dbd_describe(SV* sth, imp_sth_t* imp_sth)
         break;
 
       case MYSQL_TYPE_LONG:
+      case MYSQL_TYPE_LONGLONG:
         buffer->buffer_length= sizeof(fbh->ldata);
         buffer->buffer= (char*) &fbh->ldata;
         buffer->is_unsigned= (fields[i].flags & UNSIGNED_FLAG) ? 1 : 0;
@@ -4016,6 +4017,7 @@ process:
           break;
 
         case MYSQL_TYPE_LONG:
+        case MYSQL_TYPE_LONGLONG:
           if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
             PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tst_fetch int data %"IVdf", unsigned? %d\n",
                           fbh->ldata, buffer->is_unsigned);
@@ -4178,6 +4180,7 @@ process:
           break;
 
         case MYSQL_TYPE_LONG:
+        case MYSQL_TYPE_LONGLONG:
           /* Coerce to integer and set scalar as UV resp. IV */
           if (fields[i].flags & UNSIGNED_FLAG)
           {
@@ -4884,7 +4887,7 @@ int dbd_bind_ph(SV *sth, imp_sth_t *imp_sth, SV *param, SV *value,
       case SQL_SMALLINT:
       case SQL_BIGINT:
       case SQL_TINYINT:
-          buffer_type= MYSQL_TYPE_LONG;
+          buffer_type= MYSQL_TYPE_LONGLONG;
           break;
       case SQL_DOUBLE:
       case SQL_DECIMAL: 
@@ -4910,6 +4913,7 @@ int dbd_bind_ph(SV *sth, imp_sth_t *imp_sth, SV *param, SV *value,
     if (! buffer_is_null) {
       switch(buffer_type) {
         case MYSQL_TYPE_LONG:
+        case MYSQL_TYPE_LONGLONG:
           /* INT */
           if (!SvIOK(imp_sth->params[idx].value) && DBIc_TRACE_LEVEL(imp_xxh) >= 2)
             PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tTRY TO BIND AN INT NUMBER\n");
-- 
2.10.2


Reply to: