blob: 67653aeeb9f9897f92543acd8e410da0844dbd36 [file] [log] [blame]
Petri Lehtinenf8547992012-02-02 17:17:36 +02001#-*- coding: iso-8859-1 -*-
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00002# pysqlite2/test/hooks.py: tests for various SQLite-specific hooks
3#
Gerhard Häringe7ea7452008-03-29 00:45:29 +00004# Copyright (C) 2006-2007 Gerhard Häring <gh@ghaering.de>
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00005#
6# This file is part of pysqlite.
7#
8# This software is provided 'as-is', without any express or implied
9# warranty. In no event will the authors be held liable for any damages
10# arising from the use of this software.
11#
12# Permission is granted to anyone to use this software for any purpose,
13# including commercial applications, and to alter it and redistribute it
14# freely, subject to the following restrictions:
15#
16# 1. The origin of this software must not be misrepresented; you must not
17# claim that you wrote the original software. If you use this software
18# in a product, an acknowledgment in the product documentation would be
19# appreciated but is not required.
20# 2. Altered source versions must be plainly marked as such, and must not be
21# misrepresented as being the original software.
22# 3. This notice may not be removed or altered from any source distribution.
23
Christian Heimes05e8be12008-02-23 18:30:17 +000024import unittest
Thomas Wouters477c8d52006-05-27 19:21:47 +000025import sqlite3 as sqlite
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000026
27class CollationTests(unittest.TestCase):
28 def setUp(self):
29 pass
30
31 def tearDown(self):
32 pass
33
34 def CheckCreateCollationNotCallable(self):
35 con = sqlite.connect(":memory:")
Berker Peksag1003b342016-06-12 22:34:49 +030036 with self.assertRaises(TypeError) as cm:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000037 con.create_collation("X", 42)
Berker Peksag1003b342016-06-12 22:34:49 +030038 self.assertEqual(str(cm.exception), 'parameter must be callable')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000039
40 def CheckCreateCollationNotAscii(self):
41 con = sqlite.connect(":memory:")
Berker Peksag1003b342016-06-12 22:34:49 +030042 with self.assertRaises(sqlite.ProgrammingError):
Mark Dickinsona56c4672009-01-27 18:17:45 +000043 con.create_collation("collä", lambda x, y: (x > y) - (x < y))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044
R David Murray3f7beb92013-01-10 20:18:21 -050045 @unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 1),
46 'old SQLite versions crash on this test')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047 def CheckCollationIsUsed(self):
48 def mycoll(x, y):
49 # reverse order
Mark Dickinsona56c4672009-01-27 18:17:45 +000050 return -((x > y) - (x < y))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000051
52 con = sqlite.connect(":memory:")
53 con.create_collation("mycoll", mycoll)
54 sql = """
55 select x from (
56 select 'a' as x
57 union
58 select 'b' as x
59 union
60 select 'c' as x
61 ) order by x collate mycoll
62 """
63 result = con.execute(sql).fetchall()
64 if result[0][0] != "c" or result[1][0] != "b" or result[2][0] != "a":
65 self.fail("the expected order was not returned")
66
67 con.create_collation("mycoll", None)
Berker Peksag1003b342016-06-12 22:34:49 +030068 with self.assertRaises(sqlite.OperationalError) as cm:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000069 result = con.execute(sql).fetchall()
Berker Peksag1003b342016-06-12 22:34:49 +030070 self.assertEqual(str(cm.exception), 'no such collation sequence: mycoll')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000071
Serhiy Storchaka3cf96ac2013-02-07 17:01:47 +020072 def CheckCollationReturnsLargeInteger(self):
73 def mycoll(x, y):
74 # reverse order
75 return -((x > y) - (x < y)) * 2**32
76 con = sqlite.connect(":memory:")
77 con.create_collation("mycoll", mycoll)
78 sql = """
79 select x from (
80 select 'a' as x
81 union
82 select 'b' as x
83 union
84 select 'c' as x
85 ) order by x collate mycoll
86 """
87 result = con.execute(sql).fetchall()
88 self.assertEqual(result, [('c',), ('b',), ('a',)],
89 msg="the expected order was not returned")
90
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000091 def CheckCollationRegisterTwice(self):
92 """
93 Register two different collation functions under the same name.
94 Verify that the last one is actually used.
95 """
96 con = sqlite.connect(":memory:")
Mark Dickinsona56c4672009-01-27 18:17:45 +000097 con.create_collation("mycoll", lambda x, y: (x > y) - (x < y))
98 con.create_collation("mycoll", lambda x, y: -((x > y) - (x < y)))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000099 result = con.execute("""
100 select x from (select 'a' as x union select 'b' as x) order by x collate mycoll
101 """).fetchall()
Berker Peksag1003b342016-06-12 22:34:49 +0300102 self.assertEqual(result[0][0], 'b')
103 self.assertEqual(result[1][0], 'a')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000104
105 def CheckDeregisterCollation(self):
106 """
107 Register a collation, then deregister it. Make sure an error is raised if we try
108 to use it.
109 """
110 con = sqlite.connect(":memory:")
Mark Dickinsona56c4672009-01-27 18:17:45 +0000111 con.create_collation("mycoll", lambda x, y: (x > y) - (x < y))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000112 con.create_collation("mycoll", None)
Berker Peksag1003b342016-06-12 22:34:49 +0300113 with self.assertRaises(sqlite.OperationalError) as cm:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000114 con.execute("select 'a' as x union select 'b' as x order by x collate mycoll")
Berker Peksag1003b342016-06-12 22:34:49 +0300115 self.assertEqual(str(cm.exception), 'no such collation sequence: mycoll')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000116
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000117class ProgressTests(unittest.TestCase):
118 def CheckProgressHandlerUsed(self):
119 """
120 Test that the progress handler is invoked once it is set.
121 """
122 con = sqlite.connect(":memory:")
123 progress_calls = []
124 def progress():
125 progress_calls.append(None)
126 return 0
127 con.set_progress_handler(progress, 1)
128 con.execute("""
129 create table foo(a, b)
130 """)
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000131 self.assertTrue(progress_calls)
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000132
133
134 def CheckOpcodeCount(self):
135 """
136 Test that the opcode argument is respected.
137 """
138 con = sqlite.connect(":memory:")
139 progress_calls = []
140 def progress():
141 progress_calls.append(None)
142 return 0
143 con.set_progress_handler(progress, 1)
144 curs = con.cursor()
145 curs.execute("""
146 create table foo (a, b)
147 """)
148 first_count = len(progress_calls)
149 progress_calls = []
150 con.set_progress_handler(progress, 2)
151 curs.execute("""
152 create table bar (a, b)
153 """)
154 second_count = len(progress_calls)
Benjamin Peterson966f2fc2014-03-12 21:51:52 -0500155 self.assertGreaterEqual(first_count, second_count)
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000156
157 def CheckCancelOperation(self):
158 """
159 Test that returning a non-zero value stops the operation in progress.
160 """
161 con = sqlite.connect(":memory:")
162 progress_calls = []
163 def progress():
164 progress_calls.append(None)
165 return 1
166 con.set_progress_handler(progress, 1)
167 curs = con.cursor()
168 self.assertRaises(
169 sqlite.OperationalError,
170 curs.execute,
171 "create table bar (a, b)")
172
173 def CheckClearHandler(self):
174 """
175 Test that setting the progress handler to None clears the previously set handler.
176 """
177 con = sqlite.connect(":memory:")
178 action = 0
179 def progress():
Petri Lehtinenc86d9e22012-02-17 21:30:55 +0200180 nonlocal action
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000181 action = 1
182 return 0
183 con.set_progress_handler(progress, 1)
184 con.set_progress_handler(None, 1)
185 con.execute("select 1 union select 2 union select 3").fetchall()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000186 self.assertEqual(action, 0, "progress handler was not cleared")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000187
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200188class TraceCallbackTests(unittest.TestCase):
189 def CheckTraceCallbackUsed(self):
190 """
191 Test that the trace callback is invoked once it is set.
192 """
193 con = sqlite.connect(":memory:")
194 traced_statements = []
195 def trace(statement):
196 traced_statements.append(statement)
197 con.set_trace_callback(trace)
198 con.execute("create table foo(a, b)")
199 self.assertTrue(traced_statements)
200 self.assertTrue(any("create table foo" in stmt for stmt in traced_statements))
201
202 def CheckClearTraceCallback(self):
203 """
204 Test that setting the trace callback to None clears the previously set callback.
205 """
206 con = sqlite.connect(":memory:")
207 traced_statements = []
208 def trace(statement):
209 traced_statements.append(statement)
210 con.set_trace_callback(trace)
211 con.set_trace_callback(None)
212 con.execute("create table foo(a, b)")
213 self.assertFalse(traced_statements, "trace callback was not cleared")
214
215 def CheckUnicodeContent(self):
216 """
217 Test that the statement can contain unicode literals.
218 """
219 unicode_value = '\xf6\xe4\xfc\xd6\xc4\xdc\xdf\u20ac'
220 con = sqlite.connect(":memory:")
221 traced_statements = []
222 def trace(statement):
223 traced_statements.append(statement)
224 con.set_trace_callback(trace)
225 con.execute("create table foo(x)")
Antoine Pitrouf4e18102011-04-04 01:50:50 +0200226 # Can't execute bound parameters as their values don't appear
227 # in traced statements before SQLite 3.6.21
228 # (cf. http://www.sqlite.org/draft/releaselog/3_6_21.html)
229 con.execute('insert into foo(x) values ("%s")' % unicode_value)
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200230 con.commit()
231 self.assertTrue(any(unicode_value in stmt for stmt in traced_statements),
Antoine Pitrou43b21682011-04-04 00:50:01 +0200232 "Unicode data %s garbled in trace callback: %s"
233 % (ascii(unicode_value), ', '.join(map(ascii, traced_statements))))
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200234
235
236
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000237def suite():
238 collation_suite = unittest.makeSuite(CollationTests, "Check")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000239 progress_suite = unittest.makeSuite(ProgressTests, "Check")
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200240 trace_suite = unittest.makeSuite(TraceCallbackTests, "Check")
241 return unittest.TestSuite((collation_suite, progress_suite, trace_suite))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000242
243def test():
244 runner = unittest.TextTestRunner()
245 runner.run(suite())
246
247if __name__ == "__main__":
248 test()