Skip to content

Commit 3b10860

Browse files
Fixed bug when attempting to execute an empty statement (#525).
1 parent 0489c7e commit 3b10860

File tree

8 files changed

+53
-4
lines changed

8 files changed

+53
-4
lines changed

doc/src/release_notes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Thin Mode Changes
2222
Thick Mode Changes
2323
++++++++++++++++++
2424

25+
#) Executed statements are normalized by removing leading and trailing spaces
26+
before being sent to Oracle Database.
27+
2528
Common Changes
2629
++++++++++++++
2730

@@ -32,6 +35,8 @@ Common Changes
3235
:attr:`oracledb.defaults.fetch_lobs <Defaults.fetch_lobs>` and
3336
:attr:`oracledb.defaults.fetch_decimals <Defaults.fetch_decimals>` are now
3437
stored with the operation and used during pipeline execution.
38+
#) Fixed bug when attempting to execute an empty statement
39+
(`issue 525 <https://github.com/oracle/python-oracledb/issues/525>`__).
3540
#) API documentation is now generated from the source code.
3641

3742

src/oracledb/cursor.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ def _call_get_execute_args(
126126
statement = "".join(statement_parts)
127127
return (statement, bind_values)
128128

129+
def _normalize_statement(self, statement: Optional[str]) -> Optional[str]:
130+
"""
131+
Normalizes a statement by stripping leading and trailing spaces. If the
132+
result is an empty string, an error is raised immediately.
133+
"""
134+
if statement is not None:
135+
statement = statement.strip()
136+
if not statement:
137+
errors._raise_err(errors.ERR_EMPTY_STATEMENT)
138+
return statement
139+
129140
def _prepare(
130141
self, statement: str, tag: str = None, cache_statement: bool = True
131142
) -> None:
@@ -142,7 +153,10 @@ def _prepare_for_execute(
142153
"""
143154
self._verify_open()
144155
self._impl._prepare_for_execute(
145-
self, statement, parameters, keyword_parameters
156+
self,
157+
self._normalize_statement(statement),
158+
parameters,
159+
keyword_parameters,
146160
)
147161

148162
def _verify_fetch(self) -> None:
@@ -891,7 +905,7 @@ def executemany(
891905
"""
892906
self._verify_open()
893907
num_execs = self._impl._prepare_for_executemany(
894-
self, statement, parameters
908+
self, self._normalize_statement(statement), parameters
895909
)
896910
self._impl.suspend_on_success = suspend_on_success
897911
if num_execs > 0:
@@ -1227,7 +1241,7 @@ async def executemany(
12271241
"""
12281242
self._verify_open()
12291243
num_execs = self._impl._prepare_for_executemany(
1230-
self, statement, parameters
1244+
self, self._normalize_statement(statement), parameters
12311245
)
12321246
self._impl.suspend_on_success = suspend_on_success
12331247
if num_execs > 0:

src/oracledb/errors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ def _raise_not_supported(feature: str) -> None:
289289
ERR_SCROLL_OUT_OF_RESULT_SET = 2063
290290
ERR_POOL_MAX_LESS_THAN_MIN = 2064
291291
ERR_ARROW_SPARSE_VECTOR_NOT_ALLOWED = 2065
292+
ERR_EMPTY_STATEMENT = 2066
292293

293294
# error numbers that result in NotSupportedError
294295
ERR_TIME_NOT_SUPPORTED = 3000
@@ -587,6 +588,7 @@ def _raise_not_supported(feature: str) -> None:
587588
ERR_DUPLICATED_PARAMETER: (
588589
'"{deprecated_name}" and "{new_name}" cannot be specified together'
589590
),
591+
ERR_EMPTY_STATEMENT: ("an empty statement cannot be executed"),
590592
ERR_EXCEEDED_IDLE_TIME: (
591593
"the database closed the connection because the connection's idle "
592594
"time has been exceeded"

src/oracledb/impl/thin/cursor.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ cdef class BaseThinCursorImpl(BaseCursorImpl):
169169
if self._statement is not None:
170170
self._conn_impl._return_statement(self._statement)
171171
self._statement = None
172-
self._statement = self._conn_impl._get_statement(statement.strip(),
172+
self._statement = self._conn_impl._get_statement(statement,
173173
cache_statement)
174174
self.fetch_metadata = self._statement._fetch_metadata
175175
self.fetch_vars = self._statement._fetch_vars

tests/test_3900_cursor_execute.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,13 @@ def test_3935(self):
562562
with self.assertRaisesFullCode("ORA-01403"):
563563
self.cursor.execute("begin raise no_data_found; end;")
564564

565+
def test_3936(self):
566+
"3936 - test executing an empty statement"
567+
with self.assertRaisesFullCode("DPY-2066"):
568+
self.cursor.execute("")
569+
with self.assertRaisesFullCode("DPY-2066"):
570+
self.cursor.execute(" ")
571+
565572

566573
if __name__ == "__main__":
567574
test_env.run_test_cases()

tests/test_4000_cursor_executemany.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,13 @@ def test_4028(self):
441441
"4028 - test executemany with empty parameter set"
442442
self.cursor.executemany("insert into TestTempTable values (:1)", [])
443443

444+
def test_4029(self):
445+
"4029 - test executemany with an empty statement"
446+
with self.assertRaisesFullCode("DPY-2066"):
447+
self.cursor.executemany("", 5)
448+
with self.assertRaisesFullCode("DPY-2066"):
449+
self.cursor.executemany(" ", 5)
450+
444451

445452
if __name__ == "__main__":
446453
test_env.run_test_cases()

tests/test_5400_cursor_execute_async.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,13 @@ async def test_5436(self):
593593
with self.assertRaisesFullCode("ORA-01403"):
594594
await self.cursor.execute("begin raise no_data_found; end;")
595595

596+
async def test_5437(self):
597+
"5437 - test executing an empty statement"
598+
with self.assertRaisesFullCode("DPY-2066"):
599+
await self.cursor.execute("")
600+
with self.assertRaisesFullCode("DPY-2066"):
601+
await self.cursor.execute(" ")
602+
596603

597604
if __name__ == "__main__":
598605
test_env.run_test_cases()

tests/test_6100_cursor_executemany_async.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,13 @@ async def test_6124(self):
380380
sql = "insert into TestTempTable values (:1)"
381381
await self.cursor.executemany(sql, [])
382382

383+
async def test_6125(self):
384+
"6125 - test executemany with an empty statement"
385+
with self.assertRaisesFullCode("DPY-2066"):
386+
await self.cursor.executemany("", 5)
387+
with self.assertRaisesFullCode("DPY-2066"):
388+
await self.cursor.executemany(" ", 5)
389+
383390

384391
if __name__ == "__main__":
385392
test_env.run_test_cases()

0 commit comments

Comments
 (0)