comparison test/test_sqlite.py @ 6806:bdd28b244839

- issue2551223 - fix timestamp truncation in mysql and postgresql The data types used to represent timestamps in pg and mysql for ephemeral tables: sessions and otks don't have enough signifcant digits to work. As a result the timestamps are rounduped (up/down) rsuling in the stored timestamp being 2 minutes (pg) or 2-3 hours(mysql) off from what it should be. Modify db schema to use a numeric type that preserves more significant figures. Implement schema upgrade. Document need for upgrade in upgrading.txt. Write tests for schema upgrade. Implement test for updateTimestamp method on BasicDatabase that showed this issue in the first place. Write overrides for test for anydbm/memorydb which store timestamp properly or not at all.
author John Rouillard <rouilj@ieee.org>
date Mon, 25 Jul 2022 17:20:20 -0400
parents 044dcf3608a2
children 3f60a71b0812
comparison
equal deleted inserted replaced
6805:09d9c646ca89 6806:bdd28b244839
17 17
18 import unittest, os, shutil, time 18 import unittest, os, shutil, time
19 import sqlite3 as sqlite 19 import sqlite3 as sqlite
20 20
21 from roundup.backends import get_backend, have_backend 21 from roundup.backends import get_backend, have_backend
22 from roundup.backends.sessions_sqlite import Sessions, OneTimeKeys
22 23
23 from .db_test_base import DBTest, ROTest, SchemaTest, ClassicInitTest, config 24 from .db_test_base import DBTest, ROTest, SchemaTest, ClassicInitTest, config
24 from .db_test_base import ConcurrentDBTest, FilterCacheTest 25 from .db_test_base import ConcurrentDBTest, FilterCacheTest
25 from .db_test_base import SpecialActionTest 26 from .db_test_base import SpecialActionTest
26 from .rest_common import TestCase as RestTestCase 27 from .rest_common import TestCase as RestTestCase
39 40
40 # load the database 41 # load the database
41 self.db.issue.create(title="flebble frooz") 42 self.db.issue.create(title="flebble frooz")
42 self.db.commit() 43 self.db.commit()
43 44
44 if self.db.database_schema['version'] != 7: 45 if self.db.database_schema['version'] > 7:
46 # make testUpgrades run the downgrade code only.
47 if hasattr(self, "downgrade_only"):
48 # we are being called by an earlier test
49 self.testUpgrade_7_to_8()
50 self.assertEqual(self.db.database_schema['version'], 7)
51 else:
52 # we are being called directly
53 self.downgrade_only = True
54 self.testUpgrade_7_to_8()
55 self.assertEqual(self.db.database_schema['version'], 7)
56 del(self.downgrade_only)
57 elif self.db.database_schema['version'] != 7:
45 self.skipTest("This test only runs for database version 7") 58 self.skipTest("This test only runs for database version 7")
46 59
47 self.db.database_schema['version'] = 6 60 self.db.database_schema['version'] = 6
48 61
49 # dropping _fts 62 # dropping _fts
69 self.db.sql("select * from __fts") 82 self.db.sql("select * from __fts")
70 83
71 # we should be at the current db version 84 # we should be at the current db version
72 self.assertEqual(self.db.database_schema['version'], 85 self.assertEqual(self.db.database_schema['version'],
73 self.db.current_db_version) 86 self.db.current_db_version)
87
88 def testUpgrade_7_to_8(self):
89 # load the database
90 self.db.issue.create(title="flebble frooz")
91 self.db.commit()
92
93 if self.db.database_schema['version'] != 8:
94 self.skipTest("This test only runs for database version 8")
95
96 # set up separate session/otk db's.
97 self.db.Otk = OneTimeKeys(self.db)
98 self.db.Session = Sessions(self.db)
99
100 handle={}
101 handle['otk'] = self.db.Otk
102 handle['session'] = self.db.Session
103
104 # verify they don't truncate long ints.
105 test_double = 1658718284.7616878
106 for tablename in ['otk', 'session']:
107 Bdb = handle[tablename]
108 Bdb.sql(
109 'insert into %(name)ss(%(name)s_key, %(name)s_time, %(name)s_value) '
110 'values("foo", %(double)s, "value");'%{'name': tablename,
111 'double': test_double}
112 )
113
114 Bdb.cursor.execute('select %(name)s_time from %(name)ss '
115 'where %(name)s_key = "foo"'%{'name': tablename})
116
117 self.assertAlmostEqual(Bdb.cursor.fetchone()[0],
118 test_double, -1)
119
120 # cleanup or else the inserts after the upgrade will not
121 # work.
122 Bdb.sql("delete from %(name)ss where %(name)s_key='foo'"%{
123 'name': tablename} )
124
125 self.db.database_schema['version'] = 7
126
127 if hasattr(self,"downgrade_only"):
128 return
129
130 # test upgrade altering row
131 self.db.post_init()
132
133 # verify they keep all signifcant digits before the decimal point
134 for tablename in ['otk', 'session']:
135 Bdb = handle[tablename]
136 Bdb.sql(
137 'insert into %(name)ss(%(name)s_key, %(name)s_time, %(name)s_value) '
138 'values("foo", %(double)s, "value");'%{'name': tablename,
139 'double': test_double}
140 )
141
142 Bdb.cursor.execute('select %(name)s_time from %(name)ss '
143 'where %(name)s_key = "foo"'%{'name': tablename})
144
145 self.assertAlmostEqual(Bdb.cursor.fetchone()[0],
146 test_double, -1)
147
148 self.assertEqual(self.db.database_schema['version'], 8)
149
74 150
75 class sqliteROTest(sqliteOpener, ROTest, unittest.TestCase): 151 class sqliteROTest(sqliteOpener, ROTest, unittest.TestCase):
76 pass 152 pass
77 153
78 154

Roundup Issue Tracker: http://roundup-tracker.org/