diff --git a/tdclient/cursor.py b/tdclient/cursor.py
index ef4b159..83cc292 100644
--- a/tdclient/cursor.py
+++ b/tdclient/cursor.py
@@ -82,15 +82,22 @@ class Cursor(object):
         return [ (column[0], None, None, None, None, None, None) for column in result_schema ]
 
     def fetchone(self):
+        """
+        Fetch the next row of a query result set, returning a single sequence, or `None` when no more data is available.
+        """
         self._check_executed()
         if self._rownumber < self._rowcount:
             row = self._rows[self._rownumber]
             self._rownumber += 1
             return row
         else:
-            raise errors.InternalError("index out of bound (%d out of %d)" % (self._rownumber, self._rowcount))
+            return None
 
     def fetchmany(self, size=None):
+        """
+        Fetch the next set of rows of a query result, returning a sequence of sequences (e.g. a list of tuples).
+        An empty sequence is returned when no more rows are available.
+        """
         if size is None:
             return self.fetchall()
         else:
@@ -103,13 +110,17 @@ class Cursor(object):
                 raise errors.InternalError("index out of bound (%d out of %d)" % (self._rownumber, self._rowcount))
 
     def fetchall(self):
+        """
+        Fetch all (remaining) rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples).
+        Note that the cursor's arraysize attribute can affect the performance of this operation.
+        """
         self._check_executed()
         if self._rownumber < self._rowcount:
             rows = self._rows[self._rownumber:]
             self._rownumber = self._rowcount
             return rows
         else:
-            raise errors.InternalError("row index out of bound (%d out of %d)" % (self._rownumber, self._rowcount))
+            return []
 
     def nextset(self):
         raise errors.NotSupportedError

[Q] An error occurs when the record count of `read_sql` is 0
I executed the following Python script.

```
pandas.read_sql("SELECT * FROM td_table WHERE 1=0", td)
=> tdclient.errors.InternalError: row index out of bound (0 out of 0)
```

Is this in the specifications?

In the case of `mysql-connector-python`, the result is as follows.
```
pandas.read_sql("SELECT * FROM mysql_table WHERE 1=0", mysql)
=> Empty DataFrame
```
diff --git a/tdclient/test/cursor_test.py b/tdclient/test/cursor_test.py
index 24780e7..0c0e368 100644
--- a/tdclient/test/cursor_test.py
+++ b/tdclient/test/cursor_test.py
@@ -133,8 +133,7 @@ def test_fetchone():
     assert td.fetchone() == ["foo", 1]
     assert td.fetchone() == ["bar", 1]
     assert td.fetchone() == ["baz", 2]
-    with pytest.raises(errors.InternalError) as error:
-        td.fetchone()
+    assert td.fetchone() == None
 
 def test_fetchmany():
     td = cursor.Cursor(mock.MagicMock())
@@ -144,8 +143,9 @@ def test_fetchmany():
     td._rowcount = len(td._rows)
     assert td.fetchmany(2) == [["foo", 1], ["bar", 1]]
     assert td.fetchmany() == [["baz", 2]]
+    assert td.fetchmany() == []
     with pytest.raises(errors.InternalError) as error:
-        td.fetchmany()
+        td.fetchmany(1)
 
 def test_fetchall():
     td = cursor.Cursor(mock.MagicMock())
@@ -154,8 +154,7 @@ def test_fetchall():
     td._rownumber = 0
     td._rowcount = len(td._rows)
     assert td.fetchall() == [["foo", 1], ["bar", 1], ["baz", 2]]
-    with pytest.raises(errors.InternalError) as error:
-        td.fetchall()
+    assert td.fetchall() == []
 
 def test_show_job():
     td = cursor.Cursor(mock.MagicMock())

pip install -e .[dev]
parse_log_pytest
requirements.txt
coveralls mock pytest pytest-cov tox
3.6.5
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
coveralls==3.3.1
distlib==0.3.9
docopt==0.6.2
filelock==3.4.1
idna==3.10
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig==1.1.1
mock==5.2.0
msgpack-python==0.5.6
packaging==21.3
platformdirs==2.4.0
pluggy==1.0.0
py==1.11.0
pyparsing==3.0.7
pytest==7.0.1
pytest-cov==4.0.0
python-dateutil==2.7.3
requests==2.27.1
six==1.17.0
-e git+https://github.com/treasure-data/td-client-python.git@e2d0e830c2b9b8615523d73ee53fb86d98c59c3b#egg=td_client
toml==0.10.2
tomli==1.2.3
tox==3.28.0
typing_extensions==4.1.1
urllib3==1.22
virtualenv==20.17.1
zipp==3.6.0

name: td-client-python
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - libedit=3.1.20230828=h5eee18b_0
  - libffi=3.2.1=hf484d3e_1007
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.0.2u=h7b6447c_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.5=hc3d631a_2
  - readline=7.0=h7b6447c_5
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.33.0=h62c20be_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - charset-normalizer==2.0.12
      - coverage==6.2
      - coveralls==3.3.1
      - distlib==0.3.9
      - docopt==0.6.2
      - filelock==3.4.1
      - idna==3.10
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - mock==5.2.0
      - msgpack-python==0.5.6
      - packaging==21.3
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.0.7
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - python-dateutil==2.7.3
      - requests==2.27.1
      - six==1.17.0
      - toml==0.10.2
      - tomli==1.2.3
      - tox==3.28.0
      - typing-extensions==4.1.1
      - urllib3==1.22
      - virtualenv==20.17.1
      - zipp==3.6.0
prefix: /opt/conda/envs/td-client-python

tdclient/test/cursor_test.py::test_fetchone tdclient/test/cursor_test.py::test_fetchmany tdclient/test/cursor_test.py::test_fetchall
tdclient/test/cursor_test.py::test_cursor tdclient/test/cursor_test.py::test_cursor_close tdclient/test/cursor_test.py::test_cursor_execute tdclient/test/cursor_test.py::test_cursor_execute_format_dict tdclient/test/cursor_test.py::test_cursor_execute_format_tuple tdclient/test/cursor_test.py::test_cursor_executemany tdclient/test/cursor_test.py::test_check_executed tdclient/test/cursor_test.py::test_do_execute_success tdclient/test/cursor_test.py::test_do_execute_error tdclient/test/cursor_test.py::test_do_execute_wait tdclient/test/cursor_test.py::test_result_description tdclient/test/cursor_test.py::test_show_job tdclient/test/cursor_test.py::test_job_status tdclient/test/cursor_test.py::test_job_result tdclient/test/cursor_test.py::test_cursor_callproc tdclient/test/cursor_test.py::test_cursor_nextset tdclient/test/cursor_test.py::test_cursor_setinputsizes tdclient/test/cursor_test.py::test_cursor_setoutputsize
tdclient/cursor.py
tdclient/cursor.py