xref: /aosp_15_r20/system/security/keystore2/src/database/versioning.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2021, The Android Open Source Project
2*e1997b9aSAndroid Build Coastguard Worker //
3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e1997b9aSAndroid Build Coastguard Worker //
7*e1997b9aSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*e1997b9aSAndroid Build Coastguard Worker //
9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License.
14*e1997b9aSAndroid Build Coastguard Worker 
15*e1997b9aSAndroid Build Coastguard Worker use anyhow::{anyhow, Context, Result};
16*e1997b9aSAndroid Build Coastguard Worker use rusqlite::{params, OptionalExtension, Transaction};
17*e1997b9aSAndroid Build Coastguard Worker 
create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32>18*e1997b9aSAndroid Build Coastguard Worker fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32> {
19*e1997b9aSAndroid Build Coastguard Worker     tx.execute(
20*e1997b9aSAndroid Build Coastguard Worker         "CREATE TABLE IF NOT EXISTS persistent.version (
21*e1997b9aSAndroid Build Coastguard Worker                 id INTEGER PRIMARY KEY,
22*e1997b9aSAndroid Build Coastguard Worker                 version INTEGER);",
23*e1997b9aSAndroid Build Coastguard Worker         [],
24*e1997b9aSAndroid Build Coastguard Worker     )
25*e1997b9aSAndroid Build Coastguard Worker     .context("In create_or_get_version: Failed to create version table.")?;
26*e1997b9aSAndroid Build Coastguard Worker 
27*e1997b9aSAndroid Build Coastguard Worker     let version = tx
28*e1997b9aSAndroid Build Coastguard Worker         .query_row("SELECT version FROM persistent.version WHERE id = 0;", [], |row| row.get(0))
29*e1997b9aSAndroid Build Coastguard Worker         .optional()
30*e1997b9aSAndroid Build Coastguard Worker         .context("In create_or_get_version: Failed to read version.")?;
31*e1997b9aSAndroid Build Coastguard Worker 
32*e1997b9aSAndroid Build Coastguard Worker     let version = if let Some(version) = version {
33*e1997b9aSAndroid Build Coastguard Worker         version
34*e1997b9aSAndroid Build Coastguard Worker     } else {
35*e1997b9aSAndroid Build Coastguard Worker         // If no version table existed it could mean one of two things:
36*e1997b9aSAndroid Build Coastguard Worker         // 1) This database is completely new. In this case the version has to be set
37*e1997b9aSAndroid Build Coastguard Worker         //    to the current version and the current version which also needs to be
38*e1997b9aSAndroid Build Coastguard Worker         //    returned.
39*e1997b9aSAndroid Build Coastguard Worker         // 2) The database predates db versioning. In this case the version needs to be
40*e1997b9aSAndroid Build Coastguard Worker         //    set to 0, and 0 needs to be returned.
41*e1997b9aSAndroid Build Coastguard Worker         let version = if tx
42*e1997b9aSAndroid Build Coastguard Worker             .query_row(
43*e1997b9aSAndroid Build Coastguard Worker                 "SELECT name FROM persistent.sqlite_master
44*e1997b9aSAndroid Build Coastguard Worker                  WHERE type = 'table' AND name = 'keyentry';",
45*e1997b9aSAndroid Build Coastguard Worker                 [],
46*e1997b9aSAndroid Build Coastguard Worker                 |_| Ok(()),
47*e1997b9aSAndroid Build Coastguard Worker             )
48*e1997b9aSAndroid Build Coastguard Worker             .optional()
49*e1997b9aSAndroid Build Coastguard Worker             .context("In create_or_get_version: Failed to check for keyentry table.")?
50*e1997b9aSAndroid Build Coastguard Worker             .is_none()
51*e1997b9aSAndroid Build Coastguard Worker         {
52*e1997b9aSAndroid Build Coastguard Worker             current_version
53*e1997b9aSAndroid Build Coastguard Worker         } else {
54*e1997b9aSAndroid Build Coastguard Worker             0
55*e1997b9aSAndroid Build Coastguard Worker         };
56*e1997b9aSAndroid Build Coastguard Worker 
57*e1997b9aSAndroid Build Coastguard Worker         tx.execute("INSERT INTO persistent.version (id, version) VALUES(0, ?);", params![version])
58*e1997b9aSAndroid Build Coastguard Worker             .context("In create_or_get_version: Failed to insert initial version.")?;
59*e1997b9aSAndroid Build Coastguard Worker         version
60*e1997b9aSAndroid Build Coastguard Worker     };
61*e1997b9aSAndroid Build Coastguard Worker     Ok(version)
62*e1997b9aSAndroid Build Coastguard Worker }
63*e1997b9aSAndroid Build Coastguard Worker 
update_version(tx: &Transaction, new_version: u32) -> Result<()>64*e1997b9aSAndroid Build Coastguard Worker pub(crate) fn update_version(tx: &Transaction, new_version: u32) -> Result<()> {
65*e1997b9aSAndroid Build Coastguard Worker     let updated = tx
66*e1997b9aSAndroid Build Coastguard Worker         .execute("UPDATE persistent.version SET version = ? WHERE id = 0;", params![new_version])
67*e1997b9aSAndroid Build Coastguard Worker         .context("In update_version: Failed to update row.")?;
68*e1997b9aSAndroid Build Coastguard Worker     if updated == 1 {
69*e1997b9aSAndroid Build Coastguard Worker         Ok(())
70*e1997b9aSAndroid Build Coastguard Worker     } else {
71*e1997b9aSAndroid Build Coastguard Worker         Err(anyhow!("In update_version: No rows were updated."))
72*e1997b9aSAndroid Build Coastguard Worker     }
73*e1997b9aSAndroid Build Coastguard Worker }
74*e1997b9aSAndroid Build Coastguard Worker 
upgrade_database<F>(tx: &Transaction, current_version: u32, upgraders: &[F]) -> Result<()> where F: Fn(&Transaction) -> Result<u32> + 'static,75*e1997b9aSAndroid Build Coastguard Worker pub fn upgrade_database<F>(tx: &Transaction, current_version: u32, upgraders: &[F]) -> Result<()>
76*e1997b9aSAndroid Build Coastguard Worker where
77*e1997b9aSAndroid Build Coastguard Worker     F: Fn(&Transaction) -> Result<u32> + 'static,
78*e1997b9aSAndroid Build Coastguard Worker {
79*e1997b9aSAndroid Build Coastguard Worker     if upgraders.len() < current_version as usize {
80*e1997b9aSAndroid Build Coastguard Worker         return Err(anyhow!("In upgrade_database: Insufficient upgraders provided."));
81*e1997b9aSAndroid Build Coastguard Worker     }
82*e1997b9aSAndroid Build Coastguard Worker     let mut db_version = create_or_get_version(tx, current_version)
83*e1997b9aSAndroid Build Coastguard Worker         .context("In upgrade_database: Failed to get database version.")?;
84*e1997b9aSAndroid Build Coastguard Worker     while db_version < current_version {
85*e1997b9aSAndroid Build Coastguard Worker         log::info!("Current DB version={db_version}, perform upgrade");
86*e1997b9aSAndroid Build Coastguard Worker         db_version = upgraders[db_version as usize](tx).with_context(|| {
87*e1997b9aSAndroid Build Coastguard Worker             format!("In upgrade_database: Trying to upgrade from db version {}.", db_version)
88*e1997b9aSAndroid Build Coastguard Worker         })?;
89*e1997b9aSAndroid Build Coastguard Worker         log::info!("DB upgrade successful, current DB version now={db_version}");
90*e1997b9aSAndroid Build Coastguard Worker     }
91*e1997b9aSAndroid Build Coastguard Worker     update_version(tx, db_version).context("In upgrade_database.")
92*e1997b9aSAndroid Build Coastguard Worker }
93*e1997b9aSAndroid Build Coastguard Worker 
94*e1997b9aSAndroid Build Coastguard Worker #[cfg(test)]
95*e1997b9aSAndroid Build Coastguard Worker mod test {
96*e1997b9aSAndroid Build Coastguard Worker     use super::*;
97*e1997b9aSAndroid Build Coastguard Worker     use rusqlite::{Connection, TransactionBehavior};
98*e1997b9aSAndroid Build Coastguard Worker 
99*e1997b9aSAndroid Build Coastguard Worker     #[test]
upgrade_database_test()100*e1997b9aSAndroid Build Coastguard Worker     fn upgrade_database_test() {
101*e1997b9aSAndroid Build Coastguard Worker         let mut conn = Connection::open_in_memory().unwrap();
102*e1997b9aSAndroid Build Coastguard Worker         conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", []).unwrap();
103*e1997b9aSAndroid Build Coastguard Worker 
104*e1997b9aSAndroid Build Coastguard Worker         let upgraders: Vec<_> = (0..30_u32)
105*e1997b9aSAndroid Build Coastguard Worker             .map(move |i| {
106*e1997b9aSAndroid Build Coastguard Worker                 move |tx: &Transaction| {
107*e1997b9aSAndroid Build Coastguard Worker                     tx.execute(
108*e1997b9aSAndroid Build Coastguard Worker                         "INSERT INTO persistent.test (test_field) VALUES(?);",
109*e1997b9aSAndroid Build Coastguard Worker                         params![i + 1],
110*e1997b9aSAndroid Build Coastguard Worker                     )
111*e1997b9aSAndroid Build Coastguard Worker                     .with_context(|| format!("In upgrade_from_{}_to_{}.", i, i + 1))?;
112*e1997b9aSAndroid Build Coastguard Worker                     Ok(i + 1)
113*e1997b9aSAndroid Build Coastguard Worker                 }
114*e1997b9aSAndroid Build Coastguard Worker             })
115*e1997b9aSAndroid Build Coastguard Worker             .collect();
116*e1997b9aSAndroid Build Coastguard Worker 
117*e1997b9aSAndroid Build Coastguard Worker         for legacy in &[false, true] {
118*e1997b9aSAndroid Build Coastguard Worker             if *legacy {
119*e1997b9aSAndroid Build Coastguard Worker                 conn.execute(
120*e1997b9aSAndroid Build Coastguard Worker                     "CREATE TABLE IF NOT EXISTS persistent.keyentry (
121*e1997b9aSAndroid Build Coastguard Worker                         id INTEGER UNIQUE,
122*e1997b9aSAndroid Build Coastguard Worker                         key_type INTEGER,
123*e1997b9aSAndroid Build Coastguard Worker                         domain INTEGER,
124*e1997b9aSAndroid Build Coastguard Worker                         namespace INTEGER,
125*e1997b9aSAndroid Build Coastguard Worker                         alias BLOB,
126*e1997b9aSAndroid Build Coastguard Worker                         state INTEGER,
127*e1997b9aSAndroid Build Coastguard Worker                         km_uuid BLOB);",
128*e1997b9aSAndroid Build Coastguard Worker                     [],
129*e1997b9aSAndroid Build Coastguard Worker                 )
130*e1997b9aSAndroid Build Coastguard Worker                 .unwrap();
131*e1997b9aSAndroid Build Coastguard Worker             }
132*e1997b9aSAndroid Build Coastguard Worker             for from in 1..29 {
133*e1997b9aSAndroid Build Coastguard Worker                 for to in from..30 {
134*e1997b9aSAndroid Build Coastguard Worker                     conn.execute("DROP TABLE IF EXISTS persistent.version;", []).unwrap();
135*e1997b9aSAndroid Build Coastguard Worker                     conn.execute("DROP TABLE IF EXISTS persistent.test;", []).unwrap();
136*e1997b9aSAndroid Build Coastguard Worker                     conn.execute(
137*e1997b9aSAndroid Build Coastguard Worker                         "CREATE TABLE IF NOT EXISTS persistent.test (
138*e1997b9aSAndroid Build Coastguard Worker                             id INTEGER PRIMARY KEY,
139*e1997b9aSAndroid Build Coastguard Worker                             test_field INTEGER);",
140*e1997b9aSAndroid Build Coastguard Worker                         [],
141*e1997b9aSAndroid Build Coastguard Worker                     )
142*e1997b9aSAndroid Build Coastguard Worker                     .unwrap();
143*e1997b9aSAndroid Build Coastguard Worker 
144*e1997b9aSAndroid Build Coastguard Worker                     {
145*e1997b9aSAndroid Build Coastguard Worker                         let tx =
146*e1997b9aSAndroid Build Coastguard Worker                             conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
147*e1997b9aSAndroid Build Coastguard Worker                         create_or_get_version(&tx, from).unwrap();
148*e1997b9aSAndroid Build Coastguard Worker                         tx.commit().unwrap();
149*e1997b9aSAndroid Build Coastguard Worker                     }
150*e1997b9aSAndroid Build Coastguard Worker                     {
151*e1997b9aSAndroid Build Coastguard Worker                         let tx =
152*e1997b9aSAndroid Build Coastguard Worker                             conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
153*e1997b9aSAndroid Build Coastguard Worker                         upgrade_database(&tx, to, &upgraders).unwrap();
154*e1997b9aSAndroid Build Coastguard Worker                         tx.commit().unwrap();
155*e1997b9aSAndroid Build Coastguard Worker                     }
156*e1997b9aSAndroid Build Coastguard Worker 
157*e1997b9aSAndroid Build Coastguard Worker                     // In the legacy database case all upgraders starting from 0 have to run. So
158*e1997b9aSAndroid Build Coastguard Worker                     // after the upgrade step, the expectations need to be adjusted.
159*e1997b9aSAndroid Build Coastguard Worker                     let from = if *legacy { 0 } else { from };
160*e1997b9aSAndroid Build Coastguard Worker 
161*e1997b9aSAndroid Build Coastguard Worker                     // There must be exactly to - from rows.
162*e1997b9aSAndroid Build Coastguard Worker                     assert_eq!(
163*e1997b9aSAndroid Build Coastguard Worker                         to - from,
164*e1997b9aSAndroid Build Coastguard Worker                         conn.query_row(
165*e1997b9aSAndroid Build Coastguard Worker                             "SELECT COUNT(test_field) FROM persistent.test;",
166*e1997b9aSAndroid Build Coastguard Worker                             [],
167*e1997b9aSAndroid Build Coastguard Worker                             |row| row.get(0)
168*e1997b9aSAndroid Build Coastguard Worker                         )
169*e1997b9aSAndroid Build Coastguard Worker                         .unwrap()
170*e1997b9aSAndroid Build Coastguard Worker                     );
171*e1997b9aSAndroid Build Coastguard Worker                     // Each row must have the correct relation between id and test_field. If this
172*e1997b9aSAndroid Build Coastguard Worker                     // is not the case, the upgraders were not executed in the correct order.
173*e1997b9aSAndroid Build Coastguard Worker                     assert_eq!(
174*e1997b9aSAndroid Build Coastguard Worker                         to - from,
175*e1997b9aSAndroid Build Coastguard Worker                         conn.query_row(
176*e1997b9aSAndroid Build Coastguard Worker                             "SELECT COUNT(test_field) FROM persistent.test
177*e1997b9aSAndroid Build Coastguard Worker                              WHERE id = test_field - ?;",
178*e1997b9aSAndroid Build Coastguard Worker                             params![from],
179*e1997b9aSAndroid Build Coastguard Worker                             |row| row.get(0)
180*e1997b9aSAndroid Build Coastguard Worker                         )
181*e1997b9aSAndroid Build Coastguard Worker                         .unwrap()
182*e1997b9aSAndroid Build Coastguard Worker                     );
183*e1997b9aSAndroid Build Coastguard Worker                 }
184*e1997b9aSAndroid Build Coastguard Worker             }
185*e1997b9aSAndroid Build Coastguard Worker         }
186*e1997b9aSAndroid Build Coastguard Worker     }
187*e1997b9aSAndroid Build Coastguard Worker 
188*e1997b9aSAndroid Build Coastguard Worker     #[test]
create_or_get_version_new_database()189*e1997b9aSAndroid Build Coastguard Worker     fn create_or_get_version_new_database() {
190*e1997b9aSAndroid Build Coastguard Worker         let mut conn = Connection::open_in_memory().unwrap();
191*e1997b9aSAndroid Build Coastguard Worker         conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", []).unwrap();
192*e1997b9aSAndroid Build Coastguard Worker         {
193*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
194*e1997b9aSAndroid Build Coastguard Worker             let version = create_or_get_version(&tx, 3).unwrap();
195*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
196*e1997b9aSAndroid Build Coastguard Worker             assert_eq!(version, 3);
197*e1997b9aSAndroid Build Coastguard Worker         }
198*e1997b9aSAndroid Build Coastguard Worker 
199*e1997b9aSAndroid Build Coastguard Worker         // Was the version table created as expected?
200*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
201*e1997b9aSAndroid Build Coastguard Worker             Ok("version".to_owned()),
202*e1997b9aSAndroid Build Coastguard Worker             conn.query_row(
203*e1997b9aSAndroid Build Coastguard Worker                 "SELECT name FROM persistent.sqlite_master
204*e1997b9aSAndroid Build Coastguard Worker                  WHERE type = 'table' AND name = 'version';",
205*e1997b9aSAndroid Build Coastguard Worker                 [],
206*e1997b9aSAndroid Build Coastguard Worker                 |row| row.get(0),
207*e1997b9aSAndroid Build Coastguard Worker             )
208*e1997b9aSAndroid Build Coastguard Worker         );
209*e1997b9aSAndroid Build Coastguard Worker 
210*e1997b9aSAndroid Build Coastguard Worker         // There is exactly one row in the version table.
211*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
212*e1997b9aSAndroid Build Coastguard Worker             Ok(1),
213*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
214*e1997b9aSAndroid Build Coastguard Worker         );
215*e1997b9aSAndroid Build Coastguard Worker 
216*e1997b9aSAndroid Build Coastguard Worker         // The version must be set to 3
217*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
218*e1997b9aSAndroid Build Coastguard Worker             Ok(3),
219*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
220*e1997b9aSAndroid Build Coastguard Worker                 .get(0))
221*e1997b9aSAndroid Build Coastguard Worker         );
222*e1997b9aSAndroid Build Coastguard Worker 
223*e1997b9aSAndroid Build Coastguard Worker         // Will subsequent calls to create_or_get_version still return the same version even
224*e1997b9aSAndroid Build Coastguard Worker         // if the current version changes.
225*e1997b9aSAndroid Build Coastguard Worker         {
226*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
227*e1997b9aSAndroid Build Coastguard Worker             let version = create_or_get_version(&tx, 5).unwrap();
228*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
229*e1997b9aSAndroid Build Coastguard Worker             assert_eq!(version, 3);
230*e1997b9aSAndroid Build Coastguard Worker         }
231*e1997b9aSAndroid Build Coastguard Worker 
232*e1997b9aSAndroid Build Coastguard Worker         // There is still exactly one row in the version table.
233*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
234*e1997b9aSAndroid Build Coastguard Worker             Ok(1),
235*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
236*e1997b9aSAndroid Build Coastguard Worker         );
237*e1997b9aSAndroid Build Coastguard Worker 
238*e1997b9aSAndroid Build Coastguard Worker         // Bump the version.
239*e1997b9aSAndroid Build Coastguard Worker         {
240*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
241*e1997b9aSAndroid Build Coastguard Worker             update_version(&tx, 5).unwrap();
242*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
243*e1997b9aSAndroid Build Coastguard Worker         }
244*e1997b9aSAndroid Build Coastguard Worker 
245*e1997b9aSAndroid Build Coastguard Worker         // Now the version should have changed.
246*e1997b9aSAndroid Build Coastguard Worker         {
247*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
248*e1997b9aSAndroid Build Coastguard Worker             let version = create_or_get_version(&tx, 7).unwrap();
249*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
250*e1997b9aSAndroid Build Coastguard Worker             assert_eq!(version, 5);
251*e1997b9aSAndroid Build Coastguard Worker         }
252*e1997b9aSAndroid Build Coastguard Worker 
253*e1997b9aSAndroid Build Coastguard Worker         // There is still exactly one row in the version table.
254*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
255*e1997b9aSAndroid Build Coastguard Worker             Ok(1),
256*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
257*e1997b9aSAndroid Build Coastguard Worker         );
258*e1997b9aSAndroid Build Coastguard Worker 
259*e1997b9aSAndroid Build Coastguard Worker         // The version must be set to 5
260*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
261*e1997b9aSAndroid Build Coastguard Worker             Ok(5),
262*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
263*e1997b9aSAndroid Build Coastguard Worker                 .get(0))
264*e1997b9aSAndroid Build Coastguard Worker         );
265*e1997b9aSAndroid Build Coastguard Worker     }
266*e1997b9aSAndroid Build Coastguard Worker 
267*e1997b9aSAndroid Build Coastguard Worker     #[test]
create_or_get_version_legacy_database()268*e1997b9aSAndroid Build Coastguard Worker     fn create_or_get_version_legacy_database() {
269*e1997b9aSAndroid Build Coastguard Worker         let mut conn = Connection::open_in_memory().unwrap();
270*e1997b9aSAndroid Build Coastguard Worker         conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", []).unwrap();
271*e1997b9aSAndroid Build Coastguard Worker         // A legacy (version 0) database is detected if the keyentry table exists but no
272*e1997b9aSAndroid Build Coastguard Worker         // version table.
273*e1997b9aSAndroid Build Coastguard Worker         conn.execute(
274*e1997b9aSAndroid Build Coastguard Worker             "CREATE TABLE IF NOT EXISTS persistent.keyentry (
275*e1997b9aSAndroid Build Coastguard Worker              id INTEGER UNIQUE,
276*e1997b9aSAndroid Build Coastguard Worker              key_type INTEGER,
277*e1997b9aSAndroid Build Coastguard Worker              domain INTEGER,
278*e1997b9aSAndroid Build Coastguard Worker              namespace INTEGER,
279*e1997b9aSAndroid Build Coastguard Worker              alias BLOB,
280*e1997b9aSAndroid Build Coastguard Worker              state INTEGER,
281*e1997b9aSAndroid Build Coastguard Worker              km_uuid BLOB);",
282*e1997b9aSAndroid Build Coastguard Worker             [],
283*e1997b9aSAndroid Build Coastguard Worker         )
284*e1997b9aSAndroid Build Coastguard Worker         .unwrap();
285*e1997b9aSAndroid Build Coastguard Worker 
286*e1997b9aSAndroid Build Coastguard Worker         {
287*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
288*e1997b9aSAndroid Build Coastguard Worker             let version = create_or_get_version(&tx, 3).unwrap();
289*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
290*e1997b9aSAndroid Build Coastguard Worker             // In the legacy case, version 0 must be returned.
291*e1997b9aSAndroid Build Coastguard Worker             assert_eq!(version, 0);
292*e1997b9aSAndroid Build Coastguard Worker         }
293*e1997b9aSAndroid Build Coastguard Worker 
294*e1997b9aSAndroid Build Coastguard Worker         // Was the version table created as expected?
295*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
296*e1997b9aSAndroid Build Coastguard Worker             Ok("version".to_owned()),
297*e1997b9aSAndroid Build Coastguard Worker             conn.query_row(
298*e1997b9aSAndroid Build Coastguard Worker                 "SELECT name FROM persistent.sqlite_master
299*e1997b9aSAndroid Build Coastguard Worker                  WHERE type = 'table' AND name = 'version';",
300*e1997b9aSAndroid Build Coastguard Worker                 [],
301*e1997b9aSAndroid Build Coastguard Worker                 |row| row.get(0),
302*e1997b9aSAndroid Build Coastguard Worker             )
303*e1997b9aSAndroid Build Coastguard Worker         );
304*e1997b9aSAndroid Build Coastguard Worker 
305*e1997b9aSAndroid Build Coastguard Worker         // There is exactly one row in the version table.
306*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
307*e1997b9aSAndroid Build Coastguard Worker             Ok(1),
308*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
309*e1997b9aSAndroid Build Coastguard Worker         );
310*e1997b9aSAndroid Build Coastguard Worker 
311*e1997b9aSAndroid Build Coastguard Worker         // The version must be set to 0
312*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
313*e1997b9aSAndroid Build Coastguard Worker             Ok(0),
314*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
315*e1997b9aSAndroid Build Coastguard Worker                 .get(0))
316*e1997b9aSAndroid Build Coastguard Worker         );
317*e1997b9aSAndroid Build Coastguard Worker 
318*e1997b9aSAndroid Build Coastguard Worker         // Will subsequent calls to create_or_get_version still return the same version even
319*e1997b9aSAndroid Build Coastguard Worker         // if the current version changes.
320*e1997b9aSAndroid Build Coastguard Worker         {
321*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
322*e1997b9aSAndroid Build Coastguard Worker             let version = create_or_get_version(&tx, 5).unwrap();
323*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
324*e1997b9aSAndroid Build Coastguard Worker             assert_eq!(version, 0);
325*e1997b9aSAndroid Build Coastguard Worker         }
326*e1997b9aSAndroid Build Coastguard Worker 
327*e1997b9aSAndroid Build Coastguard Worker         // There is still exactly one row in the version table.
328*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
329*e1997b9aSAndroid Build Coastguard Worker             Ok(1),
330*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
331*e1997b9aSAndroid Build Coastguard Worker         );
332*e1997b9aSAndroid Build Coastguard Worker 
333*e1997b9aSAndroid Build Coastguard Worker         // Bump the version.
334*e1997b9aSAndroid Build Coastguard Worker         {
335*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
336*e1997b9aSAndroid Build Coastguard Worker             update_version(&tx, 5).unwrap();
337*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
338*e1997b9aSAndroid Build Coastguard Worker         }
339*e1997b9aSAndroid Build Coastguard Worker 
340*e1997b9aSAndroid Build Coastguard Worker         // Now the version should have changed.
341*e1997b9aSAndroid Build Coastguard Worker         {
342*e1997b9aSAndroid Build Coastguard Worker             let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
343*e1997b9aSAndroid Build Coastguard Worker             let version = create_or_get_version(&tx, 7).unwrap();
344*e1997b9aSAndroid Build Coastguard Worker             tx.commit().unwrap();
345*e1997b9aSAndroid Build Coastguard Worker             assert_eq!(version, 5);
346*e1997b9aSAndroid Build Coastguard Worker         }
347*e1997b9aSAndroid Build Coastguard Worker 
348*e1997b9aSAndroid Build Coastguard Worker         // There is still exactly one row in the version table.
349*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
350*e1997b9aSAndroid Build Coastguard Worker             Ok(1),
351*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
352*e1997b9aSAndroid Build Coastguard Worker         );
353*e1997b9aSAndroid Build Coastguard Worker 
354*e1997b9aSAndroid Build Coastguard Worker         // The version must be set to 5
355*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
356*e1997b9aSAndroid Build Coastguard Worker             Ok(5),
357*e1997b9aSAndroid Build Coastguard Worker             conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
358*e1997b9aSAndroid Build Coastguard Worker                 .get(0))
359*e1997b9aSAndroid Build Coastguard Worker         );
360*e1997b9aSAndroid Build Coastguard Worker     }
361*e1997b9aSAndroid Build Coastguard Worker }
362