Skip to content

Commit b5a567f

Browse files
Ilya Gurovlarkee
andauthored
feat(db_api): make rowcount property NotImplemented (#603)
Co-authored-by: larkee <[email protected]>
1 parent a6adbcb commit b5a567f

File tree

2 files changed

+16
-27
lines changed

2 files changed

+16
-27
lines changed

google/cloud/spanner_dbapi/cursor.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@
4444

4545
from google.rpc.code_pb2 import ABORTED, OK
4646

47-
_UNSET_COUNT = -1
48-
4947
ColumnDetails = namedtuple("column_details", ["null_ok", "spanner_type"])
5048
Statement = namedtuple("Statement", "sql, params, param_types, checksum, is_insert")
5149

@@ -60,7 +58,6 @@ class Cursor(object):
6058
def __init__(self, connection):
6159
self._itr = None
6260
self._result_set = None
63-
self._row_count = _UNSET_COUNT
6461
self.lastrowid = None
6562
self.connection = connection
6663
self._is_closed = False
@@ -119,12 +116,15 @@ def description(self):
119116

120117
@property
121118
def rowcount(self):
122-
"""The number of rows produced by the last `.execute()`.
119+
"""The number of rows produced by the last `execute()` call.
123120
124-
:rtype: int
125-
:returns: The number of rows produced by the last .execute*().
121+
:raises: :class:`NotImplemented`.
126122
"""
127-
return self._row_count
123+
raise NotImplementedError(
124+
"The `rowcount` property is non-operational. Request "
125+
"resulting rows are streamed by the `fetch*()` methods "
126+
"and can't be counted before they are all streamed."
127+
)
128128

129129
def _raise_if_closed(self):
130130
"""Raise an exception if this cursor is closed.
@@ -153,11 +153,7 @@ def _do_execute_update(self, transaction, sql, params):
153153
result = transaction.execute_update(
154154
sql, params=params, param_types=get_param_types(params)
155155
)
156-
self._itr = None
157-
if type(result) == int:
158-
self._row_count = result
159-
160-
return result
156+
self._itr = iter([result])
161157

162158
def _do_batch_update(self, transaction, statements, many_result_set):
163159
status, res = transaction.batch_update(statements)
@@ -421,9 +417,6 @@ def _handle_DQL_with_snapshot(self, snapshot, sql, params):
421417
# Read the first element so that the StreamedResultSet can
422418
# return the metadata after a DQL statement. See issue #155.
423419
self._itr = PeekIterator(self._result_set)
424-
# Unfortunately, Spanner doesn't seem to send back
425-
# information about the number of rows available.
426-
self._row_count = _UNSET_COUNT
427420

428421
def _handle_DQL(self, sql, params):
429422
if self.connection.read_only and not self.connection.autocommit:

tests/unit/spanner_dbapi/test_cursor.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,10 @@ def test_property_description(self):
6262
self.assertIsInstance(cursor.description[0], ColumnInfo)
6363

6464
def test_property_rowcount(self):
65-
from google.cloud.spanner_dbapi.cursor import _UNSET_COUNT
66-
6765
connection = self._make_connection(self.INSTANCE, self.DATABASE)
6866
cursor = self._make_one(connection)
69-
self.assertEqual(cursor.rowcount, _UNSET_COUNT)
67+
with self.assertRaises(NotImplementedError):
68+
cursor.rowcount
7069

7170
def test_callproc(self):
7271
from google.cloud.spanner_dbapi.exceptions import InterfaceError
@@ -94,26 +93,25 @@ def test_close(self, mock_client):
9493
cursor.execute("SELECT * FROM database")
9594

9695
def test_do_execute_update(self):
97-
from google.cloud.spanner_dbapi.cursor import _UNSET_COUNT
96+
from google.cloud.spanner_dbapi.checksum import ResultsChecksum
9897

9998
connection = self._make_connection(self.INSTANCE, self.DATABASE)
10099
cursor = self._make_one(connection)
100+
cursor._checksum = ResultsChecksum()
101101
transaction = mock.MagicMock()
102102

103103
def run_helper(ret_value):
104104
transaction.execute_update.return_value = ret_value
105-
res = cursor._do_execute_update(
105+
cursor._do_execute_update(
106106
transaction=transaction, sql="SELECT * WHERE true", params={},
107107
)
108-
return res
108+
return cursor.fetchall()
109109

110110
expected = "good"
111-
self.assertEqual(run_helper(expected), expected)
112-
self.assertEqual(cursor._row_count, _UNSET_COUNT)
111+
self.assertEqual(run_helper(expected), [expected])
113112

114113
expected = 1234
115-
self.assertEqual(run_helper(expected), expected)
116-
self.assertEqual(cursor._row_count, expected)
114+
self.assertEqual(run_helper(expected), [expected])
117115

118116
def test_execute_programming_error(self):
119117
from google.cloud.spanner_dbapi.exceptions import ProgrammingError
@@ -706,7 +704,6 @@ def test_setoutputsize(self):
706704

707705
def test_handle_dql(self):
708706
from google.cloud.spanner_dbapi import utils
709-
from google.cloud.spanner_dbapi.cursor import _UNSET_COUNT
710707

711708
connection = self._make_connection(self.INSTANCE, mock.MagicMock())
712709
connection.database.snapshot.return_value.__enter__.return_value = (
@@ -718,7 +715,6 @@ def test_handle_dql(self):
718715
cursor._handle_DQL("sql", params=None)
719716
self.assertEqual(cursor._result_set, ["0"])
720717
self.assertIsInstance(cursor._itr, utils.PeekIterator)
721-
self.assertEqual(cursor._row_count, _UNSET_COUNT)
722718

723719
def test_context(self):
724720
connection = self._make_connection(self.INSTANCE, self.DATABASE)

0 commit comments

Comments
 (0)