Skip to content

Commit 93f5a23

Browse files
Added query results to contain data types (ibmdb#1031)
Signed-off-by: Balram Choudhary <bchoudhary@rocketsoftware.com>
1 parent 762c326 commit 93f5a23

File tree

2 files changed

+130
-10
lines changed

2 files changed

+130
-10
lines changed

ibm_db_dbi.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,7 +1889,7 @@ def executemany(self, operation, seq_parameters):
18891889

18901890
def fetchone(self):
18911891
"""This method fetches one row from the database using the ibm_db.fetchone() API."""
1892-
LogMsg("INFO", "entry fetchone()")
1892+
LogMsg(INFO, "entry fetchone()")
18931893
if self.stmt_handler is None:
18941894
LogMsg("ERROR", "Please execute an SQL statement in order to get a row from result set.")
18951895
self.messages.append(
@@ -1898,19 +1898,23 @@ def fetchone(self):
18981898
raise self.messages[-1]
18991899

19001900
if not self._result_set_produced:
1901-
LogMsg("ERROR", "The last call to execute did not produce any result set.")
1901+
LogMsg(ERROR, "The last call to execute did not produce any result set.")
19021902
self.messages.append(
19031903
ProgrammingError("The last call to execute did not produce any result set.")
19041904
)
19051905
raise self.messages[-1]
19061906

19071907
row = ibm_db.fetchone(self.stmt_handler)
19081908
if row is None:
1909-
LogMsg("DEBUG", "No rows fetched.")
1910-
else:
1911-
LogMsg("DEBUG", "Row fetched successfully.")
1909+
LogMsg(DEBUG, "No row fetched.")
1910+
LogMsg(INFO, "exit fetchone()")
1911+
return None
19121912

1913-
LogMsg("INFO", "exit fetchone()")
1913+
if self.FIX_RETURN_TYPE == 1:
1914+
row = self._fix_return_data_type(row)
1915+
1916+
LogMsg(DEBUG, "Row fetched successfully.")
1917+
LogMsg(INFO, "exit fetchone()")
19141918
return row
19151919

19161920
def fetchmany(self, size=0):
@@ -1946,9 +1950,13 @@ def fetchmany(self, size=0):
19461950
raise self.messages[-1]
19471951

19481952
fetch_nrows = ibm_db.fetchmany(self.stmt_handler, size)
1949-
nrows = len(fetch_nrows) if fetch_nrows else 0
1953+
nrows = len(fetch_nrows)
19501954
message = f"Fetched {nrows} rows successfully."
19511955
LogMsg(DEBUG, message)
1956+
1957+
if self.FIX_RETURN_TYPE == 1 and fetch_nrows:
1958+
fetch_nrows = self._fix_return_data_type_batch(fetch_nrows)
1959+
19521960
LogMsg(INFO, "exit fetchmany()")
19531961
return fetch_nrows
19541962

@@ -1970,8 +1978,12 @@ def fetchall(self):
19701978
raise self.messages[-1]
19711979

19721980
rows_fetched = ibm_db.fetchall(self.stmt_handler)
1973-
nrows = len(rows_fetched) if rows_fetched else 0
1981+
nrows = len(rows_fetched)
19741982
LogMsg(DEBUG, f"Fetched {nrows} rows successfully.")
1983+
1984+
if self.FIX_RETURN_TYPE == 1 and rows_fetched:
1985+
rows_fetched = self._fix_return_data_type_batch(rows_fetched)
1986+
19751987
LogMsg(INFO, "exit fetchall()")
19761988
return rows_fetched
19771989

@@ -2030,13 +2042,13 @@ def _fix_return_data_type(self, row):
20302042
for index in range(len(row)):
20312043
if row[index] is not None:
20322044
type = ibm_db.field_type(self.stmt_handler, index)
2033-
type = type.upper()
2045+
type = type.upper() if type else ""
20342046

20352047
try:
20362048
if type == 'BLOB':
20372049
if row_list is None:
20382050
row_list = list(row)
2039-
row_list[index] = buffer(row[index])
2051+
row_list[index] = memoryview(row[index])
20402052

20412053
elif type == 'DECIMAL':
20422054
if row_list is None:
@@ -2055,6 +2067,12 @@ def _fix_return_data_type(self, row):
20552067
LogMsg(INFO, "exit _fix_return_data_type()")
20562068
return tuple(row_list)
20572069

2070+
def _fix_return_data_type_batch(self, rows):
2071+
LogMsg(INFO, "entry _fix_return_data_type_batch()")
2072+
fixed_rows = [self._fix_return_data_type(row) for row in rows] if self.FIX_RETURN_TYPE == 1 else rows
2073+
LogMsg(INFO, "exit _fix_return_data_type_batch()")
2074+
return fixed_rows
2075+
20582076
def __enter__(self):
20592077
return self
20602078

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#
2+
# Licensed Materials - Property of IBM
3+
#
4+
# (c) Copyright IBM Corp. 2025
5+
#
6+
7+
from __future__ import print_function
8+
import sys
9+
import unittest
10+
import ibm_db
11+
import ibm_db_dbi
12+
import config
13+
import decimal
14+
from testfunctions import IbmDbTestFunctions
15+
16+
17+
class IbmDbTestCase(unittest.TestCase):
18+
19+
def test_321_FetchWithDatatypesMethods_DBI(self):
20+
obj = IbmDbTestFunctions()
21+
obj.assert_expect(self.run_test_321)
22+
23+
def _format_blob_rows(self, rows):
24+
"""Convert memoryview BLOBs to strings for consistent output."""
25+
formatted = []
26+
for row in rows:
27+
id_, dec_val, blob_mv = row
28+
blob_bytes = bytes(blob_mv)
29+
blob_str = blob_bytes.decode('utf-8')
30+
formatted.append((id_, dec_val, blob_str))
31+
return formatted
32+
33+
def run_test_321(self):
34+
conn = ibm_db.connect(config.database, config.user, config.password)
35+
ibm_db.autocommit(conn, ibm_db.SQL_AUTOCOMMIT_OFF)
36+
dbi_conn = ibm_db_dbi.Connection(conn)
37+
38+
cur = dbi_conn.cursor()
39+
40+
try:
41+
cur.execute("DROP TABLE dbiEmployeesDatatypes")
42+
except:
43+
pass
44+
45+
create_sql = '''
46+
CREATE TABLE dbiEmployeesDatatypes (
47+
id INT PRIMARY KEY NOT NULL,
48+
SMTH DECIMAL(10,2),
49+
BDATA BLOB
50+
)
51+
'''
52+
cur.execute(create_sql)
53+
dbi_conn.commit()
54+
55+
insert_sql = 'INSERT INTO dbiEmployeesDatatypes (id, SMTH, BDATA) VALUES (?, ?, ?)'
56+
for i in range(1, 6):
57+
dec_val = decimal.Decimal(f"{1000 + i * 0.5:.2f}")
58+
blob_val = f"Blob data {i}".encode("utf-8")
59+
cur.execute(insert_sql, (i, dec_val, blob_val))
60+
61+
dbi_conn.commit()
62+
63+
select_sql = "SELECT * FROM dbiEmployeesDatatypes ORDER BY id"
64+
65+
# fetchall
66+
cur.execute(select_sql)
67+
all_rows = cur.fetchall()
68+
print(self._format_blob_rows(all_rows))
69+
70+
# fetchmany(2)
71+
cur2 = dbi_conn.cursor()
72+
cur2.execute(select_sql)
73+
many_rows = cur2.fetchmany(2)
74+
print(self._format_blob_rows(many_rows))
75+
76+
# fetchone
77+
cur3 = dbi_conn.cursor()
78+
cur3.execute(select_sql)
79+
one_row = cur3.fetchone()
80+
# format fetchone single row as tuple (id, decimal, string)
81+
id_, dec_val, blob_mv = one_row
82+
blob_str = bytes(blob_mv).decode('utf-8')
83+
print((id_, dec_val, blob_str))
84+
85+
86+
#__END__
87+
#__LUW_EXPECTED__
88+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2'), (3, Decimal('1001.50'), 'Blob data 3'), (4, Decimal('1002.00'), 'Blob data 4'), (5, Decimal('1002.50'), 'Blob data 5')]
89+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2')]
90+
#(1, Decimal('1000.50'), 'Blob data 1')
91+
#__ZOS_EXPECTED__
92+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2'), (3, Decimal('1001.50'), 'Blob data 3'), (4, Decimal('1002.00'), 'Blob data 4'), (5, Decimal('1002.50'), 'Blob data 5')]
93+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2')]
94+
#(1, Decimal('1000.50'), 'Blob data 1')
95+
#__SYSTEMI_EXPECTED__
96+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2'), (3, Decimal('1001.50'), 'Blob data 3'), (4, Decimal('1002.00'), 'Blob data 4'), (5, Decimal('1002.50'), 'Blob data 5')]
97+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2')]
98+
#(1, Decimal('1000.50'), 'Blob data 1')
99+
#__IDS_EXPECTED__
100+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2'), (3, Decimal('1001.50'), 'Blob data 3'), (4, Decimal('1002.00'), 'Blob data 4'), (5, Decimal('1002.50'), 'Blob data 5')]
101+
#[(1, Decimal('1000.50'), 'Blob data 1'), (2, Decimal('1001.00'), 'Blob data 2')]
102+
#(1, Decimal('1000.50'), 'Blob data 1')

0 commit comments

Comments
 (0)