PyMySQL/mysqlclient

mysqlclient returning wrong lastrowid when calling insert with auto_increment with negative value

matejsp opened this issue · 3 comments

Describe the bug

lastrowid returns 18446744073709551615 instead of -1 or 0 (in case auto increment is not used).
https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-lastrowid.html

https://dev.mysql.com/doc/c-api/5.7/en/mysql-insert-id.html

The return value of [mysql_insert_id()](https://dev.mysql.com/doc/c-api/5.7/en/mysql-insert-id.html) is always zero unless explicitly updated under one of the following conditions:

[INSERT](https://dev.mysql.com/doc/refman/5.7/en/insert.html) statements that store a value into an AUTO_INCREMENT column. This is true whether the value is automatically generated by storing the special values NULL or 0 into the column, or is an explicit nonspecial value.

To Reproduce

Schema

CREATE TABLE `auth_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(150) NOT NULL,
  `first_name` varchar(150) NOT NULL,
  `last_name` varchar(150) NOT NULL,
  `email` varchar(254) NOT NULL,
  `password` varchar(128) NOT NULL,
  `is_staff` tinyint(1) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  `is_superuser` tinyint(1) NOT NULL,
  `last_login` datetime DEFAULT NULL,
  `date_joined` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  KEY `email` (`email`),
  KEY `last_name_and_frist_name` (`last_name`,`first_name`),
  FULLTEXT KEY `auth_user_ft_first_last_email_user` (`first_name`,`last_name`,`email`,`username`)
) ENGINE=InnoDB AUTO_INCREMENT=31039988 DEFAULT CHARSET=utf8;

Code

con = MySQLdb.Connection(...)
cur = con.cursor()
cur.execute("INSERT INTO `auth_user` (id, password, `last_login`, `is_superuser`, `username`, `first_name`, "
             "`last_name`, `email`, `is_staff`, `is_active`, `date_joined`) "
             "VALUES (-1, '', NULL, 0, '1111113', 'Some', 'User', '', 0, 1, '2022-11-02 08:29:59.021095')")
print(cur.lastrowid)

Environment

MySQL Server

  • Server OS (e.g. Windows 10, Ubuntu 20.04): Docker mysql:5.7.39
  • Server Version (e.g. MariaDB 10.3.16): 5.7.39

MySQL Client

  • OS (e.g. Windows 10, Ubuntu 20.04): MacOSX 12.6

  • Python (e.g. Homebrew Python 3.7.5): pyenv python 3.8.13

  • Connector/C (e.g. Homebrew mysql-client 8.0.18): brew install mysql@5.7

Additional context

Add any other context about the problem here.

At protocol level, there is no flag to distinguish signed / unsigned.
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html

At API level, it is defined as uint64_t. So it is unsigned.
https://dev.mysql.com/doc/c-api/8.0/en/mysql-insert-id.html

I can do nothing for you. You should avoid using negative id, or convert unsigned int to signed int manually.

I thing that mysql should return value 0 for lastrowid in case auto inc is not used.

The return value of [mysql_insert_id()](https://dev.mysql.com/doc/c-api/5.7/en/mysql-insert-id.html) is always zero unless explicitly updated under one of the following conditions:

[INSERT](https://dev.mysql.com/doc/refman/8.0/en/insert.html) statements that store a value into an AUTO_INCREMENT column. This is true whether the value is automatically generated by storing the special values NULL or 0 into the column, or is an explicit nonspecial value.

I see ... so yes it works according to specs I guess. It is hard to fix this since this change propagates across Django.

I thing that mysql should return value 0 for lastrowid in case auto inc is not used.

I don't know. This project is not developing MySQL. Please use bugs.mysql.com.