Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0eec627

Browse files
serhiy-storchakaErlend Egeberg Aasland
and
Erlend Egeberg Aasland
authored
bpo-44859: Improve error handling in sqlite3 and and raise more accurate exceptions. (pythonGH-27654)
* MemoryError is now raised instead of sqlite3.Warning when memory is not enough for encoding a statement to UTF-8 in Connection.__call__() and Cursor.execute().* UnicodEncodeError is now raised instead of sqlite3.Warning when the statement contains surrogate characters in Connection.__call__() and Cursor.execute().* TypeError is now raised instead of ValueError for non-string script argument in Cursor.executescript().* ValueError is now raised for script containing the null character instead of truncating it in Cursor.executescript().* Correctly handle exceptions raised when getting boolean value of the result of the progress handler.* Add many tests covering different corner cases.Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
1 parentebecffd commit0eec627

File tree

10 files changed

+226
-52
lines changed

10 files changed

+226
-52
lines changed

‎Lib/sqlite3/test/dbapi.py‎

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
importthreading
2727
importunittest
2828

29-
fromtest.supportimportcheck_disallow_instantiation,threading_helper
29+
fromtest.supportimportcheck_disallow_instantiation,threading_helper,bigmemtest
3030
fromtest.support.os_helperimportTESTFN,unlink
3131

3232

@@ -758,9 +758,35 @@ def test_script_error_normal(self):
758758
deftest_cursor_executescript_as_bytes(self):
759759
con=sqlite.connect(":memory:")
760760
cur=con.cursor()
761-
withself.assertRaises(ValueError)ascm:
761+
withself.assertRaises(TypeError):
762762
cur.executescript(b"create table test(foo); insert into test(foo) values (5);")
763-
self.assertEqual(str(cm.exception),'script argument must be unicode.')
763+
764+
deftest_cursor_executescript_with_null_characters(self):
765+
con=sqlite.connect(":memory:")
766+
cur=con.cursor()
767+
withself.assertRaises(ValueError):
768+
cur.executescript("""
769+
create table a(i);\0
770+
insert into a(i) values (5);
771+
""")
772+
773+
deftest_cursor_executescript_with_surrogates(self):
774+
con=sqlite.connect(":memory:")
775+
cur=con.cursor()
776+
withself.assertRaises(UnicodeEncodeError):
777+
cur.executescript("""
778+
create table a(s);
779+
insert into a(s) values ('\ud8ff');
780+
""")
781+
782+
@unittest.skipUnless(sys.maxsize>2**32,'requires 64bit platform')
783+
@bigmemtest(size=2**31,memuse=3,dry_run=False)
784+
deftest_cursor_executescript_too_large_script(self,maxsize):
785+
con=sqlite.connect(":memory:")
786+
cur=con.cursor()
787+
forsizein2**31-1,2**31:
788+
withself.assertRaises(sqlite.DataError):
789+
cur.executescript("create table a(s);".ljust(size))
764790

765791
deftest_connection_execute(self):
766792
con=sqlite.connect(":memory:")
@@ -969,6 +995,7 @@ def suite():
969995
CursorTests,
970996
ExtensionTests,
971997
ModuleTests,
998+
OpenTests,
972999
SqliteOnConflictTests,
9731000
ThreadTests,
9741001
UninitialisedConnectionTests,

‎Lib/sqlite3/test/hooks.py‎

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
importsqlite3assqlite
2525

2626
fromtest.support.os_helperimportTESTFN,unlink
27-
27+
from .userfunctionsimportwith_tracebacks
2828

2929
classCollationTests(unittest.TestCase):
3030
deftest_create_collation_not_string(self):
@@ -145,7 +145,6 @@ def progress():
145145
""")
146146
self.assertTrue(progress_calls)
147147

148-
149148
deftest_opcode_count(self):
150149
"""
151150
Test that the opcode argument is respected.
@@ -198,6 +197,32 @@ def progress():
198197
con.execute("select 1 union select 2 union select 3").fetchall()
199198
self.assertEqual(action,0,"progress handler was not cleared")
200199

200+
@with_tracebacks(['bad_progress','ZeroDivisionError'])
201+
deftest_error_in_progress_handler(self):
202+
con=sqlite.connect(":memory:")
203+
defbad_progress():
204+
1/0
205+
con.set_progress_handler(bad_progress,1)
206+
withself.assertRaises(sqlite.OperationalError):
207+
con.execute("""
208+
create table foo(a, b)
209+
""")
210+
211+
@with_tracebacks(['__bool__','ZeroDivisionError'])
212+
deftest_error_in_progress_handler_result(self):
213+
con=sqlite.connect(":memory:")
214+
classBadBool:
215+
def__bool__(self):
216+
1/0
217+
defbad_progress():
218+
returnBadBool()
219+
con.set_progress_handler(bad_progress,1)
220+
withself.assertRaises(sqlite.OperationalError):
221+
con.execute("""
222+
create table foo(a, b)
223+
""")
224+
225+
201226
classTraceCallbackTests(unittest.TestCase):
202227
deftest_trace_callback_used(self):
203228
"""

‎Lib/sqlite3/test/regression.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# 3. This notice may not be removed or altered from any source distribution.
2222

2323
importdatetime
24+
importsys
2425
importunittest
2526
importsqlite3assqlite
2627
importweakref
@@ -273,7 +274,7 @@ def test_connection_call(self):
273274
Call a connection with a non-string SQL request: check error handling
274275
of the statement constructor.
275276
"""
276-
self.assertRaises(TypeError,self.con,1)
277+
self.assertRaises(TypeError,self.con,b"select 1")
277278

278279
deftest_collation(self):
279280
defcollation_cb(a,b):
@@ -344,6 +345,26 @@ def test_null_character(self):
344345
self.assertRaises(ValueError,cur.execute,"\0select 2")
345346
self.assertRaises(ValueError,cur.execute,"select 2\0")
346347

348+
deftest_surrogates(self):
349+
con=sqlite.connect(":memory:")
350+
self.assertRaises(UnicodeEncodeError,con,"select '\ud8ff'")
351+
self.assertRaises(UnicodeEncodeError,con,"select '\udcff'")
352+
cur=con.cursor()
353+
self.assertRaises(UnicodeEncodeError,cur.execute,"select '\ud8ff'")
354+
self.assertRaises(UnicodeEncodeError,cur.execute,"select '\udcff'")
355+
356+
@unittest.skipUnless(sys.maxsize>2**32,'requires 64bit platform')
357+
@support.bigmemtest(size=2**31,memuse=4,dry_run=False)
358+
deftest_large_sql(self,maxsize):
359+
# Test two cases: size+1 > INT_MAX and size+1 <= INT_MAX.
360+
forsizein (2**31,2**31-2):
361+
con=sqlite.connect(":memory:")
362+
sql="select 1".ljust(size)
363+
self.assertRaises(sqlite.DataError,con,sql)
364+
cur=con.cursor()
365+
self.assertRaises(sqlite.DataError,cur.execute,sql)
366+
delsql
367+
347368
deftest_commit_cursor_reset(self):
348369
"""
349370
Connection.commit() did reset cursors, which made sqlite3

‎Lib/sqlite3/test/types.py‎

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323
importdatetime
2424
importunittest
2525
importsqlite3assqlite
26+
importsys
2627
try:
2728
importzlib
2829
exceptImportError:
2930
zlib=None
3031

32+
fromtestimportsupport
33+
3134

3235
classSqliteTypeTests(unittest.TestCase):
3336
defsetUp(self):
@@ -45,14 +48,20 @@ def test_string(self):
4548
row=self.cur.fetchone()
4649
self.assertEqual(row[0],"Österreich")
4750

51+
deftest_string_with_null_character(self):
52+
self.cur.execute("insert into test(s) values (?)", ("a\0b",))
53+
self.cur.execute("select s from test")
54+
row=self.cur.fetchone()
55+
self.assertEqual(row[0],"a\0b")
56+
4857
deftest_small_int(self):
4958
self.cur.execute("insert into test(i) values (?)", (42,))
5059
self.cur.execute("select i from test")
5160
row=self.cur.fetchone()
5261
self.assertEqual(row[0],42)
5362

5463
deftest_large_int(self):
55-
num=2**40
64+
num=123456789123456789
5665
self.cur.execute("insert into test(i) values (?)", (num,))
5766
self.cur.execute("select i from test")
5867
row=self.cur.fetchone()
@@ -78,6 +87,45 @@ def test_unicode_execute(self):
7887
row=self.cur.fetchone()
7988
self.assertEqual(row[0],"Österreich")
8089

90+
deftest_too_large_int(self):
91+
forvaluein2**63,-2**63-1,2**64:
92+
withself.assertRaises(OverflowError):
93+
self.cur.execute("insert into test(i) values (?)", (value,))
94+
self.cur.execute("select i from test")
95+
row=self.cur.fetchone()
96+
self.assertIsNone(row)
97+
98+
deftest_string_with_surrogates(self):
99+
forvaluein0xd8ff,0xdcff:
100+
withself.assertRaises(UnicodeEncodeError):
101+
self.cur.execute("insert into test(s) values (?)", (chr(value),))
102+
self.cur.execute("select s from test")
103+
row=self.cur.fetchone()
104+
self.assertIsNone(row)
105+
106+
@unittest.skipUnless(sys.maxsize>2**32,'requires 64bit platform')
107+
@support.bigmemtest(size=2**31,memuse=4,dry_run=False)
108+
deftest_too_large_string(self,maxsize):
109+
withself.assertRaises(sqlite.InterfaceError):
110+
self.cur.execute("insert into test(s) values (?)", ('x'*(2**31-1),))
111+
withself.assertRaises(OverflowError):
112+
self.cur.execute("insert into test(s) values (?)", ('x'*(2**31),))
113+
self.cur.execute("select 1 from test")
114+
row=self.cur.fetchone()
115+
self.assertIsNone(row)
116+
117+
@unittest.skipUnless(sys.maxsize>2**32,'requires 64bit platform')
118+
@support.bigmemtest(size=2**31,memuse=3,dry_run=False)
119+
deftest_too_large_blob(self,maxsize):
120+
withself.assertRaises(sqlite.InterfaceError):
121+
self.cur.execute("insert into test(s) values (?)", (b'x'*(2**31-1),))
122+
withself.assertRaises(OverflowError):
123+
self.cur.execute("insert into test(s) values (?)", (b'x'*(2**31),))
124+
self.cur.execute("select 1 from test")
125+
row=self.cur.fetchone()
126+
self.assertIsNone(row)
127+
128+
81129
classDeclTypesTests(unittest.TestCase):
82130
classFoo:
83131
def__init__(self,_val):
@@ -163,7 +211,7 @@ def test_small_int(self):
163211

164212
deftest_large_int(self):
165213
# default
166-
num=2**40
214+
num=123456789123456789
167215
self.cur.execute("insert into test(i) values (?)", (num,))
168216
self.cur.execute("select i from test")
169217
row=self.cur.fetchone()

‎Lib/sqlite3/test/userfunctions.py‎

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,37 @@
3333
fromtest.supportimportbigmemtest
3434

3535

36-
defwith_tracebacks(strings):
36+
defwith_tracebacks(strings,traceback=True):
3737
"""Convenience decorator for testing callback tracebacks."""
38-
strings.append('Traceback')
38+
iftraceback:
39+
strings.append('Traceback')
3940

4041
defdecorator(func):
4142
@functools.wraps(func)
4243
defwrapper(self,*args,**kwargs):
4344
# First, run the test with traceback enabled.
44-
sqlite.enable_callback_tracebacks(True)
45-
buf=io.StringIO()
46-
withcontextlib.redirect_stderr(buf):
45+
withcheck_tracebacks(self,strings):
4746
func(self,*args,**kwargs)
48-
tb=buf.getvalue()
49-
forsinstrings:
50-
self.assertIn(s,tb)
5147

5248
# Then run the test with traceback disabled.
53-
sqlite.enable_callback_tracebacks(False)
5449
func(self,*args,**kwargs)
5550
returnwrapper
5651
returndecorator
5752

53+
@contextlib.contextmanager
54+
defcheck_tracebacks(self,strings):
55+
"""Convenience context manager for testing callback tracebacks."""
56+
sqlite.enable_callback_tracebacks(True)
57+
try:
58+
buf=io.StringIO()
59+
withcontextlib.redirect_stderr(buf):
60+
yield
61+
tb=buf.getvalue()
62+
forsinstrings:
63+
self.assertIn(s,tb)
64+
finally:
65+
sqlite.enable_callback_tracebacks(False)
66+
5867
deffunc_returntext():
5968
return"foo"
6069
deffunc_returntextwithnull():
@@ -408,9 +417,26 @@ def md5sum(t):
408417
delx,y
409418
gc.collect()
410419

420+
deftest_func_return_too_large_int(self):
421+
cur=self.con.cursor()
422+
forvaluein2**63,-2**63-1,2**64:
423+
self.con.create_function("largeint",0,lambdavalue=value:value)
424+
withcheck_tracebacks(self, ['OverflowError']):
425+
withself.assertRaises(sqlite.DataError):
426+
cur.execute("select largeint()")
427+
428+
deftest_func_return_text_with_surrogates(self):
429+
cur=self.con.cursor()
430+
self.con.create_function("pychr",1,chr)
431+
forvaluein0xd8ff,0xdcff:
432+
withcheck_tracebacks(self,
433+
['UnicodeEncodeError','surrogates not allowed']):
434+
withself.assertRaises(sqlite.OperationalError):
435+
cur.execute("select pychr(?)", (value,))
436+
411437
@unittest.skipUnless(sys.maxsize>2**32,'requires 64bit platform')
412438
@bigmemtest(size=2**31,memuse=3,dry_run=False)
413-
deftest_large_text(self,size):
439+
deftest_func_return_too_large_text(self,size):
414440
cur=self.con.cursor()
415441
forsizein2**31-1,2**31:
416442
self.con.create_function("largetext",0,lambdasize=size:"b"*size)
@@ -419,7 +445,7 @@ def test_large_text(self, size):
419445

420446
@unittest.skipUnless(sys.maxsize>2**32,'requires 64bit platform')
421447
@bigmemtest(size=2**31,memuse=2,dry_run=False)
422-
deftest_large_blob(self,size):
448+
deftest_func_return_too_large_blob(self,size):
423449
cur=self.con.cursor()
424450
forsizein2**31-1,2**31:
425451
self.con.create_function("largeblob",0,lambdasize=size:b"b"*size)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Improve error handling in:mod:`sqlite3` and raise more accurate exceptions.
2+
3+
*:exc:`MemoryError` is now raised instead of:exc:`sqlite3.Warning` when memory is not enough for encoding a statement to UTF-8 in ``Connection.__call__()`` and ``Cursor.execute()``.
4+
*:exc:`UnicodEncodeError` is now raised instead of:exc:`sqlite3.Warning` when the statement contains surrogate characters in ``Connection.__call__()`` and ``Cursor.execute()``.
5+
*:exc:`TypeError` is now raised instead of:exc:`ValueError` for non-string script argument in ``Cursor.executescript()``.
6+
*:exc:`ValueError` is now raised for script containing the null character instead of truncating it in ``Cursor.executescript()``.
7+
* Correctly handle exceptions raised when getting boolean value of the result of the progress handler.
8+
* Add many tests covering different corner cases.

‎Modules/_sqlite/clinic/cursor.c.h‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,35 @@ PyDoc_STRVAR(pysqlite_cursor_executescript__doc__,
119119
#definePYSQLITE_CURSOR_EXECUTESCRIPT_METHODDEF \
120120
{"executescript", (PyCFunction)pysqlite_cursor_executescript, METH_O, pysqlite_cursor_executescript__doc__},
121121

122+
staticPyObject*
123+
pysqlite_cursor_executescript_impl(pysqlite_Cursor*self,
124+
constchar*sql_script);
125+
126+
staticPyObject*
127+
pysqlite_cursor_executescript(pysqlite_Cursor*self,PyObject*arg)
128+
{
129+
PyObject*return_value=NULL;
130+
constchar*sql_script;
131+
132+
if (!PyUnicode_Check(arg)) {
133+
_PyArg_BadArgument("executescript","argument","str",arg);
134+
gotoexit;
135+
}
136+
Py_ssize_tsql_script_length;
137+
sql_script=PyUnicode_AsUTF8AndSize(arg,&sql_script_length);
138+
if (sql_script==NULL) {
139+
gotoexit;
140+
}
141+
if (strlen(sql_script)!= (size_t)sql_script_length) {
142+
PyErr_SetString(PyExc_ValueError,"embedded null character");
143+
gotoexit;
144+
}
145+
return_value=pysqlite_cursor_executescript_impl(self,sql_script);
146+
147+
exit:
148+
returnreturn_value;
149+
}
150+
122151
PyDoc_STRVAR(pysqlite_cursor_fetchone__doc__,
123152
"fetchone($self, /)\n"
124153
"--\n"
@@ -270,4 +299,4 @@ pysqlite_cursor_close(pysqlite_Cursor *self, PyTypeObject *cls, PyObject *const
270299
exit:
271300
returnreturn_value;
272301
}
273-
/*[clinic end generated code: output=7b216aba2439f5cf input=a9049054013a1b77]*/
302+
/*[clinic end generated code: output=ace31a7481aa3f41 input=a9049054013a1b77]*/

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp