push/internal/storage/
schema.rs1use crate::error::warn;
5use rusqlite::Transaction;
6use sql_support::open_database;
7
8const CREATE_TABLE_PUSH_SQL: &str = include_str!("schema.sql");
9
10pub struct PushConnectionInitializer;
11
12impl open_database::ConnectionInitializer for PushConnectionInitializer {
13 const NAME: &'static str = "push db";
14 const END_VERSION: u32 = 3;
15
16 fn init(&self, db: &Transaction<'_>) -> open_database::Result<()> {
26 db.execute_batch(CREATE_TABLE_PUSH_SQL)?;
27 Ok(())
28 }
29
30 fn upgrade_from(&self, db: &Transaction<'_>, version: u32) -> open_database::Result<()> {
31 match version {
32 0 => db.execute_batch(CREATE_TABLE_PUSH_SQL)?,
33 1 => db.execute_batch(CREATE_TABLE_PUSH_SQL)?,
34 2 => {
35 let sql = format!(
38 "
39 -- rename the old table.
40 ALTER TABLE push_record RENAME TO push_record_old;
41 -- create the new table with the new schema.
42 {CREATE_TABLE_PUSH_SQL};
43 -- move the data across.
44 INSERT OR IGNORE INTO push_record ({COMMON_COLS})
45 SELECT {COMMON_COLS} FROM push_record_old WHERE length(scope) > 0;
46 -- drop the old table
47 DROP TABLE push_record_old;",
48 CREATE_TABLE_PUSH_SQL = CREATE_TABLE_PUSH_SQL,
49 COMMON_COLS = COMMON_COLS,
50 );
51 db.execute_batch(&sql)?;
52 }
53 other => {
54 warn!(
55 "Loaded future schema version {} (we only understand version {}). \
56 Optimistically ",
57 other,
58 Self::END_VERSION
59 )
60 }
61 };
62 Ok(())
63 }
64}
65
66pub const COMMON_COLS: &str = "
67 channel_id,
68 endpoint,
69 scope,
70 key,
71 ctime,
72 app_server_key
73";
74
75#[cfg(test)]
76mod test {
77 use crate::internal::storage::db::{PushDb, Storage};
78 use rusqlite::{Connection, OpenFlags};
79 use sql_support::ConnExt;
80
81 const CREATE_V2_SCHEMA: &str = include_str!("test/schema_v2.sql");
82
83 #[test]
84 fn test_migrate_v2_v3() {
85 error_support::init_for_tests();
86 let tempdir = tempfile::tempdir().unwrap();
87 let path = tempdir.path().join("push_v2.sql");
88
89 let conn = Connection::open_with_flags(path.clone(), OpenFlags::default()).unwrap();
90 conn.execute_batch(CREATE_V2_SCHEMA).unwrap();
91
92 conn.execute_batch(
94 r#"
95 INSERT INTO push_record (
96 uaid, channel_id, endpoint, scope, key, ctime, app_server_key, native_id
97 ) VALUES
98 ("id-1", "cid1", "ep-1", "sc-1", x'1234', 1, "ask-1", "nid-1"),
99 -- duplicate scope, which isn't allowed in the new schema
100 ("id-2", "cid2", "ep-2", "sc-1", x'5678', 2, "ask-2", "nid-2"),
101 -- empty scope, which isn't allowed in the new schema
102 ("id-3", "cid3", "ep-3", "", x'0000', 3, "ask-3", "nid-3")
103 ;
104 INSERT into meta_data (
105 key, value
106 ) VALUES
107 ("key-1", "value-1"),
108 ("key-2", "value-2")
109 "#,
110 )
111 .unwrap();
112
113 drop(conn);
115 let db = PushDb::open(path).expect("should open");
116
117 assert_eq!(
119 db.conn_ext_query_one::<u32>("SELECT COUNT(*) FROM push_record")
120 .unwrap(),
121 1
122 );
123 let record = db
124 .get_record("cid1")
125 .expect("should work")
126 .expect("should get a record");
127 assert_eq!(record.channel_id, "cid1");
128 assert_eq!(record.endpoint, "ep-1");
129 assert_eq!(record.scope, "sc-1");
130 assert_eq!(record.key, [0x12, 0x34]);
131 assert_eq!(record.ctime.0, 1);
132 assert_eq!(record.app_server_key.unwrap(), "ask-1");
133
134 assert_eq!(
136 db.db
137 .conn_ext_query_one::<u32>("SELECT COUNT(*) FROM meta_data")
138 .unwrap(),
139 2
140 );
141 assert_eq!(db.get_meta("key-1").unwrap().unwrap(), "value-1");
142 assert_eq!(db.get_meta("key-2").unwrap().unwrap(), "value-2");
143 }
144}