From bf79d28cded50e91111966c952619138460940a7 Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 27 Mar 2021 12:09:37 +0800 Subject: [PATCH] :bug: Fixed Bug #80908 PDO::lastInsertId() return wrong when table's id bigger than the maximum value of int64. --- ext/pdo/pdo.c | 34 +++++++++++++++++++++ ext/pdo/php_pdo_driver.h | 1 + ext/pdo_mysql/mysql_driver.c | 2 +- ext/pdo_mysql/tests/bug80908.phpt | 49 +++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_mysql/tests/bug80908.phpt diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index 1120f9a3269a8..f5686161e8f5d 100644 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -281,6 +281,40 @@ PDO_API zend_string *php_pdo_int64_to_str(int64_t i64) /* {{{ */ } /* }}} */ +/* Convert uint64 to zend_string */ +PDO_API zend_string *php_pdo_uint64_to_str(uint64_t i64) /* {{{ */ +{ + char buffer[65]; + char outbuf[65] = ""; + register char *p; + zend_long long_val; + char *dst = outbuf; + + if (i64 == 0) { + return ZSTR_CHAR('0'); + } + + p = &buffer[sizeof(buffer)-1]; + *p = '\0'; + + while ((uint64_t)i64 > (uint64_t)ZEND_LONG_MAX) { + uint64_t quo = (uint64_t)i64 / (unsigned int)10; + unsigned int rem = (unsigned int)(i64 - quo*10U); + *--p = digit_vec[rem]; + i64 = (int64_t)quo; + } + long_val = (zend_long)i64; + while (long_val != 0) { + zend_long quo = long_val / 10; + *--p = digit_vec[(unsigned int)(long_val - quo * 10)]; + long_val = quo; + } + while ((*dst++ = *p++) != 0) + ; + *dst = '\0'; + return zend_string_init(outbuf, strlen(outbuf), 0); +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(pdo) { diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index c1a01b3400754..d4186fa54189f 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -27,6 +27,7 @@ typedef struct _pdo_row_t pdo_row_t; struct pdo_bound_param_data; PDO_API zend_string *php_pdo_int64_to_str(int64_t i64); +PDO_API zend_string *php_pdo_uint64_to_str(uint64_t ui64); #ifndef TRUE # define TRUE 1 diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index 7bc0cbcf0a54e..ba2dd318e94d4 100644 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -289,7 +289,7 @@ static zend_string *pdo_mysql_last_insert_id(pdo_dbh_t *dbh, const zend_string * { pdo_mysql_db_handle *H = (pdo_mysql_db_handle *)dbh->driver_data; PDO_DBG_ENTER("pdo_mysql_last_insert_id"); - PDO_DBG_RETURN(php_pdo_int64_to_str(mysql_insert_id(H->server))); + PDO_DBG_RETURN(php_pdo_uint64_to_str(mysql_insert_id(H->server))); } /* }}} */ diff --git a/ext/pdo_mysql/tests/bug80908.phpt b/ext/pdo_mysql/tests/bug80908.phpt new file mode 100644 index 0000000000000..35cba1f89101d --- /dev/null +++ b/ext/pdo_mysql/tests/bug80908.phpt @@ -0,0 +1,49 @@ +--TEST-- +Bug #80908: pdo_mysql lastInsertId() return wrong, when table id bigger than the maximum value of int64 +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + return $db; +} + +$db = createDB(); +$db->exec('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE test (`id` bigint(20) unsigned AUTO_INCREMENT, `name` varchar(5), primary key (`id`)) ENGINE = InnoDB AUTO_INCREMENT=10376293541461622799'); + +function testLastInsertId(PDO $db) { + echo "Running test lastInsertId\n"; + $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); + try { + $db->exec("insert into test (`name`) values ('bar')"); + $id = $db->lastInsertId(); + echo "Last insert id is " . $id . "\n"; + } catch (PDOException $e) { + echo $e->getMessage()."\n"; + } +} + +testLastInsertId($db); +unset($db); +echo "\n"; + +?> +--CLEAN-- + +--EXPECT-- +Running test lastInsertId +Last insert id is 10376293541461622799