10000 BUG#34689812: Fix datetime conversion when using prepared cursors · mysql/mysql-connector-python@6594d25 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6594d25

Browse files
committed
BUG#34689812: Fix datetime conversion when using prepared cursors
When using a prepared cursor, if a datetime column contains 00:00:00 as time, a Python date object is returned instead of datetime. This patch inspects the column type instead of relying on the packet length and returns the proper object type. Thank you for the contribution. Change-Id: Ie2313e5cf26ce457ae7d9479f3b710d59b511d99
1 parent cd77637 commit 6594d25

File tree

4 files changed

+75
-9
lines changed

4 files changed

+75
-9
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ v8.0.32
1313

1414
- WL#15348: Support MIT Kerberos library on Windows
1515
- WL#15036: Support for type hints
16+
- BUG#34689812: Fix datetime conversion when using prepared cursors
1617
- BUG#34556157: Kerberos authorization fails when using SSPI as security interface
1718
- BUG#30089671: Fix decoding VARBINARY columns when using a prepared cursor
1819

lib/mysql/connector/protocol.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -548,16 +548,19 @@ def _parse_binary_new_decimal(
548548
@sta 10000 ticmethod
549549
def _parse_binary_timestamp(
550550
packet: bytes,
551+
field_type: int,
551552
) -> Tuple[bytes, Optional[Union[datetime.date, datetime.datetime]]]:
552553
"""Parse a timestamp from a binary packet"""
553554
length = packet[0]
554555
value = None
555556
if length == 4:
556-
value = datetime.date(
557-
year=struct.unpack("<H", packet[1:3])[0],
558-
month=packet[3],
559-
day=packet[4],
560-
)
557+
year = struct.unpack("<H", packet[1:3])[0]
558+
month = packet[3]
559+
day = packet[4]
560+
if field_type in (FieldType.DATETIME, FieldType.TIMESTAMP):
561+
value = datetime.datetime(year=year, month=month, day=day)
562+
else:
563+
value = datetime.date(year=year, month=month, day=day)
561564
elif length >= 7:
562565
mcs = 0
563566
if length == 11:
@@ -634,7 +637,7 @@ def _parse_binary_values(
634637
FieldType.DATE,
635638
FieldType.TIMESTAMP,
636639
):
637-
(packet, value) = self._parse_binary_timestamp(packet)
640+
(packet, value) = self._parse_binary_timestamp(packet, field[1])
638641
values.append(value)
639642
elif field[1] == FieldType.TIME:
640643
(packet, value) = self._parse_binary_time(packet)

tests/test_bugs.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7129,3 +7129,59 @@ def text_datatype_handled_as_binary_for_bin_suffix_collation(self):
71297129
finally:
71307130
# always executed
71317131
cur.execute(f"DROP TABLE IF EXISTS {table_name}")
7132+
7133+
7134+
class BugOra34689812(tests.MySQLConnectorTests):
7135+
"""BUG#34689812: Fix datetime conversion when using prepared cursors.
7136+
7137+
When using a prepared cursor, if a datetime column contains 00:00:00 as
7138+
time, a Python date object is returned instead of datetime.
7139+
7140+
This patch inspects the column type instead of relying on the packet
7141+
length and returns the proper object type.
7142+
"""
7143+
7144+
table_name = "BugOra34689812"
7145+
data = (
7146+
date(2022, 10, 13),
7147+
datetime(2022, 10, 13, 0, 0, 0),
7148+
datetime(2022, 10, 13, 0, 0, 0, 0),
7149+
)
7150+
7151+
def setUp(self):
7152+
config = tests.get_mysql_config()
7153+
with mysql.connector.connect(**config) as cnx:
7154+
with cnx.cursor(prepared=True) as cur:
7155+
cur.execute(f"DROP TABLE IF EXISTS {self.table_name}")
7156+
cur.execute(
7157+
f"""
7158+
CREATE TABLE {self.table_name} (
7159+
id INT UNSIGNED NOT NULL AUTO_INCREMENT KEY,
7160+
my_date DATE,
7161+
my_datetime DATETIME,
7162+
my_timestamp TIMESTAMP
7163+
)
7164+
"""
7165+
)
7166+
cur.execute(
7167+
f"""
7168+
INSERT INTO {self.table_name}
7169+
(my_date, my_datetime, my_timestamp) VALUES (?, ?, ?)
7170+
""",
7171+
self.data,
7172+
)
7173+
cnx.commit()
7174+
7175+
def tearDown(self):
7176+
config = tests.get_mysql_config()
7177+
with mysql.connector.connect(**config) as cnx:
7178+
cnx.cmd_query(f"DROP TABLE IF EXISTS {self.table_name}")
7179+
7180+
@foreach_cnx()
7181+
def test_datetime_with_prepared_cursor(self):
7182+
with self.cnx.cursor(prepared=True) as cur:
7183+
cur.execute(
7184+
f"SELECT my_date, my_datetime, my_timestamp FROM {self.table_name}"
7185+
)
7186+
res = cur.fetchall()
7187+
self.assertEqual(res[0], self.data)

tests/test_protocol.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,18 +394,24 @@ def test__parse_binary_timestamp(self):
394394
"""Parse a timestamp from a binary packet"""
395395
# Case = Expected value; data
396396
cases = [
397-
(datetime.date(1977, 6, 14), bytearray(b"\x04\xb9\x07\x06\x0e")),
398397
(
398+
FieldType.DATE,
399+
datetime.date(1977, 6, 14),
400+
bytearray(b"\x04\xb9\x07\x06\x0e"),
401+
),
402+
(
403+
FieldType.DATETIME,
399404
datetime.datetime(1977, 6, 14, 21, 33, 14),
400405
bytearray(b"\x07\xb9\x07\x06\x0e\x15\x21\x0e"),
401406
),
402407
(
408+
FieldType.TIMESTAMP,
403409
datetime.datetime(1977, 6, 14, 21, 33, 14, 345),
404410
bytearray(b"\x0b\xb9\x07\x06\x0e\x15\x21\x0e\x59\x01\x00\x00"),
405411
),
406412
]
407-
for exp, data in cases:
408-
res = self._protocol._parse_binary_timestamp(data + b"\x00\x00")
413+
for field_type, exp, data in cases:
414+
res = self._protocol._parse_binary_timestamp(data + b"\x00\x00", field_type)
409415
self.assertEqual(
410416
(b"\x00\x00", exp),
411417
res,

0 commit comments

Comments
 (0)
0