bpo-34052: Prevent SQLite functions from setting callbacks on excepti… · python/cpython@fdf5050

@@ -256,24 +256,6 @@ def CheckPragmaAutocommit(self):

256256

cur.execute("pragma page_size")

257257

row = 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-277259

def CheckConnectionCall(self):

278260

"""

279261

Call a connection with a non-string SQL request: check error handling

@@ -398,9 +380,72 @@ def callback(*args):

398380

support.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+401443

def suite():

402444

regression_suite = unittest.makeSuite(RegressionTests, "Check")

403-

return unittest.TestSuite((regression_suite,))

445+

return unittest.TestSuite((

446+

regression_suite,

447+

unittest.makeSuite(UnhashableCallbacksTestCase),

448+

))

404449405450

def test():

406451

runner = unittest.TextTestRunner()