Skip to content

coercion of UNIQUEIDENTIFIER / uuid values from binary to string only works with fetchone(), not fetchall() or fetchmany() #241

@zzzeek

Description

@zzzeek

There seems to be some built in coercion occurring when fetching UNIQUEIDENTIFIER results, which is fine, however it seems to take place inconsistently, only when the rows were fetched using fetchone(). When using fetchall() or fetchmany(), the values seem to be bytes using little-endian.

from mssql_python import connect
import uuid

conn_str = "UID=scott;PWD=tiger^5HHH;Server=mssql2022;Database=test;Encrypt=No"
conn = connect(conn_str)

cursor = conn.cursor()

cursor.execute("""DROP TABLE IF EXISTS t1""")

cursor.execute(
    """
    CREATE TABLE t1 (
        id UNIQUEIDENTIFIER NULL,
        data VARCHAR(50) NULL
    )
    """
)

for i, uuid_value in enumerate(
    [
        uuid.UUID("110e2700-9d34-44e9-ba0e-bd74401a54a4"),
        uuid.UUID("68be2224-543e-4868-914a-a65f06047593"),
    ]
):

    cursor.execute(
        "INSERT INTO t1 (id, data) VALUES (?, ?)",
        (str(uuid_value), f"value {i}"),
    )

    cursor.execute("SELECT id FROM t1 WHERE data = ?", (f"value {i}",))
    row = cursor.fetchone()
    print(
        f"For fetchone() of UUID {uuid_value}, "
        f"we get from the driver: {row[0]}"
    )

    cursor.execute("SELECT id FROM t1 WHERE data = ?", (f"value {i}",))
    row = cursor.fetchall()
    print(
        f"For fetchall() of UUID {uuid_value}, "
        f"we get from the driver: {row[0][0]}; "
        f"this translates to {uuid.UUID(bytes_le=row[0][0])}"
    )

    cursor.execute("SELECT id FROM t1 WHERE data = ?", (f"value {i}",))
    row = cursor.fetchmany()
    print(
        f"For fetchmany() of UUID {uuid_value}, "
        f"we get from the driver: {row[0][0]}; "
        f"this translates to {uuid.UUID(bytes_le=row[0][0])}"
    )

output we get shows that we get a binary value for fetchall(), fetchmany(), which apparently converts back using UUID(bytes_le):

For fetchone() of UUID 110e2700-9d34-44e9-ba0e-bd74401a54a4, we get from the driver: 110e2700-9d34-44e9-ba0e-bd74401a54a4
For fetchall() of UUID 110e2700-9d34-44e9-ba0e-bd74401a54a4, we get from the driver: b"\x00'\x0e\x114\x9d\xe9D\xba\x0e\xbdt@\x1aT\xa4"; this translates to 110e2700-9d34-44e9-ba0e-bd74401a54a4
For fetchmany() of UUID 110e2700-9d34-44e9-ba0e-bd74401a54a4, we get from the driver: b"\x00'\x0e\x114\x9d\xe9D\xba\x0e\xbdt@\x1aT\xa4"; this translates to 110e2700-9d34-44e9-ba0e-bd74401a54a4

For fetchone() of UUID 68be2224-543e-4868-914a-a65f06047593, we get from the driver: 68be2224-543e-4868-914a-a65f06047593
For fetchall() of UUID 68be2224-543e-4868-914a-a65f06047593, we get from the driver: b'$"\xbeh>ThH\x91J\xa6_\x06\x04u\x93'; this translates to 68be2224-543e-4868-914a-a65f06047593
For fetchmany() of UUID 68be2224-543e-4868-914a-a65f06047593, we get from the driver: b'$"\xbeh>ThH\x91J\xa6_\x06\x04u\x93'; this translates to 68be2224-543e-4868-914a-a65f06047593

Expected behavior

fetchone(), fetchmany(), fetchall() should each return rows in the identical format for a particular value. string is probably best. (many drivers also deliver native Python UUID objects, or offer an option to do so. I dont need that but just something to keep in mind).

Further technical details

Python version: 3.13.5
SQL Server version: (e.g. SQL Server 2022) SQL Server 2022
Operating system: (e.g. Windows Server 2022, Ubuntu 24.04, macOS 14.7.1, Docker container) Fedora running a Fedora 40 container

Additional context

Note this is for development of a SQLAlchemy driver for mssql-python. I'm impressed that at least several hundred tests are running without issue so far. New drivers typically have at least half a dozen bugs though I am getting a lot of passing so far, so that's good.

Metadata

Metadata

Assignees

Labels

Triage DoneIssues that are triaged by dev team and are in investigation.

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions