blob: cafff932b4da60f0e61e7eb1288a1391ce55480e [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):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000028 def CheckCreateCollationNotCallable(self):
29 con = sqlite.connect(":memory:")
Berker Peksag1003b342016-06-12 22:34:49 +030030 with self.assertRaises(TypeError) as cm:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000031 con.create_collation("X", 42)
Berker Peksag1003b342016-06-12 22:34:49 +030032 self.assertEqual(str(cm.exception), 'parameter must be callable')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033
34 def CheckCreateCollationNotAscii(self):
35 con = sqlite.connect(":memory:")
Berker Peksag1003b342016-06-12 22:34:49 +030036 with self.assertRaises(sqlite.ProgrammingError):
Mark Dickinsona56c4672009-01-27 18:17:45 +000037 con.create_collation("collä", lambda x, y: (x > y) - (x < y))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000038
R David Murray3f7beb92013-01-10 20:18:21 -050039 @unittest.skipIf(sqlite.sqlite_version_info < (3, 2, 1),
40 'old SQLite versions crash on this test')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000041 def CheckCollationIsUsed(self):
42 def mycoll(x, y):
43 # reverse order
Mark Dickinsona56c4672009-01-27 18:17:45 +000044 return -((x > y) - (x < y))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000045
46 con = sqlite.connect(":memory:")
47 con.create_collation("mycoll", mycoll)
48 sql = """
49 select x from (
50 select 'a' as x
51 union
52 select 'b' as x
53 union
54 select 'c' as x
55 ) order by x collate mycoll
56 """
57 result = con.execute(sql).fetchall()
Berker Peksag48b5c982016-06-14 00:42:50 +030058 self.assertEqual(result, [('c',), ('b',), ('a',)],
59 msg='the expected order was not returned')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000060
61 con.create_collation("mycoll", None)
Berker Peksag1003b342016-06-12 22:34:49 +030062 with self.assertRaises(sqlite.OperationalError) as cm:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000063 result = con.execute(sql).fetchall()
Berker Peksag1003b342016-06-12 22:34:49 +030064 self.assertEqual(str(cm.exception), 'no such collation sequence: mycoll')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000065
Serhiy Storchaka3cf96ac2013-02-07 17:01:47 +020066 def CheckCollationReturnsLargeInteger(self):
67 def mycoll(x, y):
68 # reverse order
69 return -((x > y) - (x < y)) * 2**32
70 con = sqlite.connect(":memory:")
71 con.create_collation("mycoll", mycoll)
72 sql = """
73 select x from (
74 select 'a' as x
75 union
76 select 'b' as x
77 union
78 select 'c' as x
79 ) order by x collate mycoll
80 """
81 result = con.execute(sql).fetchall()
82 self.assertEqual(result, [('c',), ('b',), ('a',)],
83 msg="the expected order was not returned")
84
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000085 def CheckCollationRegisterTwice(self):
86 """
87 Register two different collation functions under the same name.
88 Verify that the last one is actually used.
89 """
90 con = sqlite.connect(":memory:")
Mark Dickinsona56c4672009-01-27 18:17:45 +000091 con.create_collation("mycoll", lambda x, y: (x > y) - (x < y))
92 con.create_collation("mycoll", lambda x, y: -((x > y) - (x < y)))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000093 result = con.execute("""
94 select x from (select 'a' as x union select 'b' as x) order by x collate mycoll
95 """).fetchall()
Berker Peksag1003b342016-06-12 22:34:49 +030096 self.assertEqual(result[0][0], 'b')
97 self.assertEqual(result[1][0], 'a')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000098
99 def CheckDeregisterCollation(self):
100 """
101 Register a collation, then deregister it. Make sure an error is raised if we try
102 to use it.
103 """
104 con = sqlite.connect(":memory:")
Mark Dickinsona56c4672009-01-27 18:17:45 +0000105 con.create_collation("mycoll", lambda x, y: (x > y) - (x < y))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000106 con.create_collation("mycoll", None)
Berker Peksag1003b342016-06-12 22:34:49 +0300107 with self.assertRaises(sqlite.OperationalError) as cm:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000108 con.execute("select 'a' as x union select 'b' as x order by x collate mycoll")
Berker Peksag1003b342016-06-12 22:34:49 +0300109 self.assertEqual(str(cm.exception), 'no such collation sequence: mycoll')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000110
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000111class ProgressTests(unittest.TestCase):
112 def CheckProgressHandlerUsed(self):
113 """
114 Test that the progress handler is invoked once it is set.
115 """
116 con = sqlite.connect(":memory:")
117 progress_calls = []
118 def progress():
119 progress_calls.append(None)
120 return 0
121 con.set_progress_handler(progress, 1)
122 con.execute("""
123 create table foo(a, b)
124 """)
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000125 self.assertTrue(progress_calls)
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000126
127
128 def CheckOpcodeCount(self):
129 """
130 Test that the opcode argument is respected.
131 """
132 con = sqlite.connect(":memory:")
133 progress_calls = []
134 def progress():
135 progress_calls.append(None)
136 return 0
137 con.set_progress_handler(progress, 1)
138 curs = con.cursor()
139 curs.execute("""
140 create table foo (a, b)
141 """)
142 first_count = len(progress_calls)
143 progress_calls = []
144 con.set_progress_handler(progress, 2)
145 curs.execute("""
146 create table bar (a, b)
147 """)
148 second_count = len(progress_calls)
Benjamin Peterson966f2fc2014-03-12 21:51:52 -0500149 self.assertGreaterEqual(first_count, second_count)
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000150
151 def CheckCancelOperation(self):
152 """
153 Test that returning a non-zero value stops the operation in progress.
154 """
155 con = sqlite.connect(":memory:")
156 progress_calls = []
157 def progress():
158 progress_calls.append(None)
159 return 1
160 con.set_progress_handler(progress, 1)
161 curs = con.cursor()
162 self.assertRaises(
163 sqlite.OperationalError,
164 curs.execute,
165 "create table bar (a, b)")
166
167 def CheckClearHandler(self):
168 """
169 Test that setting the progress handler to None clears the previously set handler.
170 """
171 con = sqlite.connect(":memory:")
172 action = 0
173 def progress():
Petri Lehtinenc86d9e22012-02-17 21:30:55 +0200174 nonlocal action
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000175 action = 1
176 return 0
177 con.set_progress_handler(progress, 1)
178 con.set_progress_handler(None, 1)
179 con.execute("select 1 union select 2 union select 3").fetchall()
Gregory P. Smith04cecaf2009-07-04 08:32:15 +0000180 self.assertEqual(action, 0, "progress handler was not cleared")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000181
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200182class TraceCallbackTests(unittest.TestCase):
183 def CheckTraceCallbackUsed(self):
184 """
185 Test that the trace callback is invoked once it is set.
186 """
187 con = sqlite.connect(":memory:")
188 traced_statements = []
189 def trace(statement):
190 traced_statements.append(statement)
191 con.set_trace_callback(trace)
192 con.execute("create table foo(a, b)")
193 self.assertTrue(traced_statements)
194 self.assertTrue(any("create table foo" in stmt for stmt in traced_statements))
195
196 def CheckClearTraceCallback(self):
197 """
198 Test that setting the trace callback to None clears the previously set callback.
199 """
200 con = sqlite.connect(":memory:")
201 traced_statements = []
202 def trace(statement):
203 traced_statements.append(statement)
204 con.set_trace_callback(trace)
205 con.set_trace_callback(None)
206 con.execute("create table foo(a, b)")
207 self.assertFalse(traced_statements, "trace callback was not cleared")
208
209 def CheckUnicodeContent(self):
210 """
211 Test that the statement can contain unicode literals.
212 """
213 unicode_value = '\xf6\xe4\xfc\xd6\xc4\xdc\xdf\u20ac'
214 con = sqlite.connect(":memory:")
215 traced_statements = []
216 def trace(statement):
217 traced_statements.append(statement)
218 con.set_trace_callback(trace)
219 con.execute("create table foo(x)")
Antoine Pitrouf4e18102011-04-04 01:50:50 +0200220 # Can't execute bound parameters as their values don't appear
221 # in traced statements before SQLite 3.6.21
222 # (cf. http://www.sqlite.org/draft/releaselog/3_6_21.html)
223 con.execute('insert into foo(x) values ("%s")' % unicode_value)
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200224 con.commit()
225 self.assertTrue(any(unicode_value in stmt for stmt in traced_statements),
Antoine Pitrou43b21682011-04-04 00:50:01 +0200226 "Unicode data %s garbled in trace callback: %s"
227 % (ascii(unicode_value), ', '.join(map(ascii, traced_statements))))
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200228
229
230
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000231def suite():
232 collation_suite = unittest.makeSuite(CollationTests, "Check")
Gerhard Häringe7ea7452008-03-29 00:45:29 +0000233 progress_suite = unittest.makeSuite(ProgressTests, "Check")
Antoine Pitrou5bfa0622011-04-04 00:12:04 +0200234 trace_suite = unittest.makeSuite(TraceCallbackTests, "Check")
235 return unittest.TestSuite((collation_suite, progress_suite, trace_suite))
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000236
237def test():
238 runner = unittest.TextTestRunner()
239 runner.run(suite())
240
241if __name__ == "__main__":
242 test()