bpo-34052: Prevent SQLite functions from setting callbacks on excepti… · python/cpython@fdf5050
@@ -256,24 +256,6 @@ def CheckPragmaAutocommit(self):
256256cur.execute("pragma page_size")
257257row = cur.fetchone()
258258259-def CheckSetDict(self):
260-"""
261- See http://bugs.python.org/issue7478
262-263- It was possible to successfully register callbacks that could not be
264- hashed. Return codes of PyDict_SetItem were not checked properly.
265- """
266-class NotHashable:
267-def __call__(self, *args, **kw):
268-pass
269-def __hash__(self):
270-raise TypeError()
271-var = NotHashable()
272-self.assertRaises(TypeError, self.con.create_function, var)
273-self.assertRaises(TypeError, self.con.create_aggregate, var)
274-self.assertRaises(TypeError, self.con.set_authorizer, var)
275-self.assertRaises(TypeError, self.con.set_progress_handler, var)
276-277259def CheckConnectionCall(self):
278260"""
279261 Call a connection with a non-string SQL request: check error handling
@@ -398,9 +380,72 @@ def callback(*args):
398380support.gc_collect()
399381400382383+class UnhashableFunc:
384+__hash__ = None
385+386+def __init__(self, return_value=None):
387+self.calls = 0
388+self.return_value = return_value
389+390+def __call__(self, *args, **kwargs):
391+self.calls += 1
392+return self.return_value
393+394+395+class UnhashableCallbacksTestCase(unittest.TestCase):
396+"""
397+ https://bugs.python.org/issue34052
398+399+ Registering unhashable callbacks raises TypeError, callbacks are not
400+ registered in SQLite after such registration attempt.
401+ """
402+def setUp(self):
403+self.con = sqlite.connect(':memory:')
404+405+def tearDown(self):
406+self.con.close()
407+408+def test_progress_handler(self):
409+f = UnhashableFunc(return_value=0)
410+with self.assertRaisesRegex(TypeError, 'unhashable type'):
411+self.con.set_progress_handler(f, 1)
412+self.con.execute('SELECT 1')
413+self.assertFalse(f.calls)
414+415+def test_func(self):
416+func_name = 'func_name'
417+f = UnhashableFunc()
418+with self.assertRaisesRegex(TypeError, 'unhashable type'):
419+self.con.create_function(func_name, 0, f)
420+msg = 'no such function: %s' % func_name
421+with self.assertRaisesRegex(sqlite.OperationalError, msg):
422+self.con.execute('SELECT %s()' % func_name)
423+self.assertFalse(f.calls)
424+425+def test_authorizer(self):
426+f = UnhashableFunc(return_value=sqlite.SQLITE_DENY)
427+with self.assertRaisesRegex(TypeError, 'unhashable type'):
428+self.con.set_authorizer(f)
429+self.con.execute('SELECT 1')
430+self.assertFalse(f.calls)
431+432+def test_aggr(self):
433+class UnhashableType(type):
434+__hash__ = None
435+aggr_name = 'aggr_name'
436+with self.assertRaisesRegex(TypeError, 'unhashable type'):
437+self.con.create_aggregate(aggr_name, 0, UnhashableType('Aggr', (), {}))
438+msg = 'no such function: %s' % aggr_name
439+with self.assertRaisesRegex(sqlite.OperationalError, msg):
440+self.con.execute('SELECT %s()' % aggr_name)
441+442+401443def suite():
402444regression_suite = unittest.makeSuite(RegressionTests, "Check")
403-return unittest.TestSuite((regression_suite,))
445+return unittest.TestSuite((
446+regression_suite,
447+unittest.makeSuite(UnhashableCallbacksTestCase),
448+ ))
404449405450def test():
406451runner = unittest.TextTestRunner()