pyodbc
pyodbc copied to clipboard
TypeError when trying to unpickle results with equal column names
Environment
- Python: 3.7.4
- pyodbc: 4.0.27
- OS: win10 64bit
- DB: SQLITE and MSSQL
- driver: sqliteodbc_w64.exe from here
Issue
When trying to unpickle a result from a query with some equal column names, a TypeError is raised: TypeError: cannot create 'pyodbc.Row' instances
Code to reproduce
I noticed that at work where we have a MS SQL Server, but it is possible to reproduce the error with sqlite.
import sqlite3
import pickle
import pyodbc
# create a database
conn = sqlite3.connect("db.db")
c = conn.cursor()
# create table and fill it
c.execute("""CREATE TABLE IF NOT EXISTS test (value1 int, value2 int,value3 int)""")
c.execute("INSERT INTO test VALUES (1,1,1),(2,2,2),(3,3,3)")
conn.commit()
conn.close()
# create pyodbc connection
cnxn = pyodbc.connect("Driver=SQLite3 ODBC Driver;Database=db.db")
cursor = cnxn.cursor()
# select unique column names
cursor.execute("SELECT value1, value2, value3 from test")
result = cursor.fetchall()
# we get the actual values
print(f"actual result1 {result}")
# pickle the results
with open("result_ok.pkl", "wb") as file:
pickle.dump(result, file, protocol=pickle.HIGHEST_PROTOCOL)
# unpickle them
with open("result_ok.pkl", "rb") as file:
unpickled = pickle.load(file)
# we can load the pickled values
print(f"unpickled result1 {unpickled}")
# same thing again, but select a column twice
cursor = cnxn.cursor()
cursor.execute("SELECT value1, value2, value3, value1 from test")
result = cursor.fetchall()
# we get the actual values again, everything seems fine
print(f"actual result2 {result}")
# pickle the results
with open("result_error.pkl", "wb") as file:
pickle.dump(result, file, protocol=pickle.HIGHEST_PROTOCOL)
# unpickle them
with open("result_error.pkl", "rb") as file:
unpickled = pickle.load(file) # code breaks here
print(f"unpickled result2 {unpickled}")
I will try to reproduce this, just to confirm, this happens on SQL Server as well, with "ODBC Driver 17 for SQL Server"?
Yes.
Possibly related: https://github.com/mkleehammer/pyodbc/issues/446
@tweakimp I am able to repro the issue, and I will into it over the next few days
Great, thank you for your time
I've located the code that throws that error, but I still don't quite understand the logic behind it, in the file row.cpp around line 110, there's this:
if (PyDict_Size(map) != cols || PyTuple_GET_SIZE(args) - 2 != cols)
return 0;
Which will cause the early return if you select different number of columns, because then PyDict_Size(map) != cols
will be true. I tried running the repro with that check removed and I got correct looking results back after unpickling (with 4 columns), but that check was put there for a reason, I'm just not sure what it is @mkleehammer?
Also bitten by this on SQL Server 2014 + msodbcsql17; @v-makouz did you manage to find a work around?
@piskvorky I'm afraid I don't really know anything new, other then if the above code is changed to
if (PyTuple_GET_SIZE(args) - 2 != cols)
return 0;
The above repro will work correctly, but I'm not sure if that'll cause issues for anything else. I'm just not sure why that check is there.