@@ -64,115 +64,259 @@ impl Sqlite {
64
64
}
65
65
}
66
66
67
- static MIGRATIONS : & [ & str ] = & [
68
- "" ,
69
- r#"
70
- create table benchmark(
71
- name text primary key,
72
- -- Whether this benchmark supports stable
73
- stabilized bool not null
74
- );
75
- create table artifact(
76
- id integer primary key not null,
77
- name text not null unique,
78
- date integer,
79
- type text not null
80
- );
81
- create table collection(
82
- id integer primary key not null
83
- );
84
- create table error_series(
85
- id integer primary key not null,
86
- crate text not null unique references benchmark(name) on delete cascade on update cascade
87
- );
88
- create table error(
89
- series integer not null references error_series(id) on delete cascade on update cascade,
90
- aid integer not null references artifact(id) on delete cascade on update cascade,
91
- error text,
92
- PRIMARY KEY(series, aid)
93
- );
94
- create table pstat_series(
95
- id integer primary key not null,
96
- crate text not null references benchmark(name) on delete cascade on update cascade,
97
- profile text not null,
98
- cache text not null,
99
- statistic text not null,
100
- UNIQUE(crate, profile, cache, statistic)
101
- );
102
- create table pstat(
103
- series integer references pstat_series(id) on delete cascade on update cascade,
104
- aid integer references artifact(id) on delete cascade on update cascade,
105
- cid integer references collection(id) on delete cascade on update cascade,
106
- value double not null,
107
- PRIMARY KEY(series, aid, cid)
108
- );
109
- create table self_profile_query_series(
110
- id integer primary key not null,
111
- crate text not null references benchmark(name) on delete cascade on update cascade,
112
- profile text not null,
113
- cache text not null,
114
- query text not null,
115
- UNIQUE(crate, profile, cache, query)
116
- );
117
- create table self_profile_query(
118
- series integer references self_profile_query_series(id) on delete cascade on update cascade,
119
- aid integer references artifact(id) on delete cascade on update cascade,
120
- cid integer references collection(id) on delete cascade on update cascade,
121
- self_time integer,
122
- blocked_time integer,
123
- incremental_load_time integer,
124
- number_of_cache_hits integer,
125
- invocation_count integer,
126
- PRIMARY KEY(series, aid, cid)
127
- );
128
- create table pull_request_builds(
129
- bors_sha text unique,
130
- pr integer not null,
131
- parent_sha text,
132
- complete boolean,
133
- requested timestamp without time zone
134
- );
135
- "# ,
136
- r#"
137
- create table artifact_collection_duration(
138
- aid integer primary key not null references artifact(id) on delete cascade on update cascade,
139
- date_recorded timestamp without time zone not null,
140
- duration integer not null
141
- );
142
- "# ,
143
- r#"
144
- create table collector_progress(
145
- aid integer not null references artifact(id) on delete cascade on update cascade,
146
- step text not null,
147
- start integer,
148
- end integer,
149
- UNIQUE(aid, step)
150
- );
151
- "# ,
152
- r#"alter table collection add column perf_commit text;"# ,
153
- r#"alter table pull_request_builds add column include text;"# ,
154
- r#"alter table pull_request_builds add column exclude text;"# ,
155
- r#"alter table pull_request_builds add column runs integer;"# ,
156
- r#"
157
- create table rustc_compilation(
158
- aid integer references artifact(id) on delete cascade on update cascade,
159
- cid integer references collection(id) on delete cascade on update cascade,
160
- crate text not null,
161
- duration integer not null,
162
- PRIMARY KEY(aid, cid, crate)
163
- );
164
- "# ,
165
- r#"alter table pull_request_builds rename to pull_request_build"# ,
166
- r#"
167
- create table raw_self_profile(
168
- aid integer references artifact(id) on delete cascade on update cascade,
169
- cid integer references collection(id) on delete cascade on update cascade,
170
- crate text not null references benchmark(name) on delete cascade on update cascade,
171
- profile text not null,
172
- cache text not null,
173
- PRIMARY KEY(aid, cid, crate, profile, cache)
174
- );
175
- "# ,
67
+ struct Migration {
68
+ /// One or more SQL statements, each terminated by a semicolon.
69
+ sql : & ' static str ,
70
+
71
+ /// If false, indicates that foreign key checking should be delayed until after execution of
72
+ /// the migration SQL, and foreign key `ON UPDATE` and `ON DELETE` actions disabled completely.
73
+ foreign_key_constraints_enabled : bool ,
74
+ }
75
+
76
+ impl Migration {
77
+ /// Returns a `Migration` with foreign key constraints enabled during execution.
78
+ const fn new ( sql : & ' static str ) -> Migration {
79
+ Migration {
80
+ sql,
81
+ foreign_key_constraints_enabled : true ,
82
+ }
83
+ }
84
+
85
+ /// Returns a `Migration` with foreign key checking delayed until after execution, and foreign
86
+ /// key `ON UPDATE` and `ON DELETE` actions disabled completely.
87
+ ///
88
+ /// SQLite has limited `ALTER TABLE` capabilities, so some schema alterations require the
89
+ /// approach of replacing a table with a new one having the desired schema. Because there might
90
+ /// be other tables with foreign key constraints on the table, these constraints need to be
91
+ /// disabled during execution of such migration SQL, and reenabled after. Otherwise, dropping
92
+ /// the old table may trigger `ON DELETE` actions in the referencing tables. See [SQLite
93
+ /// documentation](https://www.sqlite.org/lang_altertable.html) for more information.
94
+ const fn without_foreign_key_constraints ( sql : & ' static str ) -> Migration {
95
+ Migration {
96
+ sql,
97
+ foreign_key_constraints_enabled : false ,
98
+ }
99
+ }
100
+
101
+ fn execute ( & self , conn : & mut rusqlite:: Connection , migration_id : i32 ) {
102
+ if self . foreign_key_constraints_enabled {
103
+ let tx = conn. transaction ( ) . unwrap ( ) ;
104
+ tx. execute_batch ( & self . sql ) . unwrap ( ) ;
105
+ tx. pragma_update ( None , "user_version" , & migration_id)
106
+ . unwrap ( ) ;
107
+ tx. commit ( ) . unwrap ( ) ;
108
+ return ;
109
+ }
110
+
111
+ // The following steps are reproduced from https://www.sqlite.org/lang_altertable.html,
112
+ // from the section titled, "Making Other Kinds Of Table Schema Changes".
113
+
114
+ // 1. If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF.
115
+ conn. pragma_update ( None , "foreign_keys" , & "OFF" ) . unwrap ( ) ;
116
+
117
+ // 2. Start a transaction.
118
+ let tx = conn. transaction ( ) . unwrap ( ) ;
119
+
120
+ // The migration SQL is responsible for steps 3 through 9.
121
+
122
+ // 3. Remember the format of all indexes, triggers, and views associated with table X.
123
+ // This information will be needed in step 8 below. One way to do this is to run a
124
+ // query like the following: SELECT type, sql FROM sqlite_schema WHERE tbl_name='X'.
125
+ //
126
+ // 4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised
127
+ // format of table X. Make sure that the name "new_X" does not collide with any
128
+ // existing table name, of course.
129
+ //
130
+ // 5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT
131
+ // ... FROM X.
132
+ //
133
+ // 6. Drop the old table X: DROP TABLE X.
134
+ //
135
+ // 7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X.
136
+ //
137
+ // 8. Use CREATE INDEX, CREATE TRIGGER, and CREATE VIEW to reconstruct indexes, triggers,
138
+ // and views associated with table X. Perhaps use the old format of the triggers,
139
+ // indexes, and views saved from step 3 above as a guide, making changes as appropriate
140
+ // for the alteration.
141
+ //
142
+ // 9. If any views refer to table X in a way that is affected by the schema change, then
143
+ // drop those views using DROP VIEW and recreate them with whatever changes are
144
+ // necessary to accommodate the schema change using CREATE VIEW.
145
+ tx. execute_batch ( & self . sql ) . unwrap ( ) ;
146
+
147
+ // 10. If foreign key constraints were originally enabled then run PRAGMA foreign_key_check
148
+ // to verify that the schema change did not break any foreign key constraints.
149
+ tx. pragma_query ( None , "foreign_key_check" , |row| {
150
+ let table: String = row. get_unwrap ( 0 ) ;
151
+ let row_id: Option < i64 > = row. get_unwrap ( 1 ) ;
152
+ let foreign_table: String = row. get_unwrap ( 2 ) ;
153
+ let fk_idx: i64 = row. get_unwrap ( 3 ) ;
154
+
155
+ tx. query_row :: < ( ) , _ , _ > (
156
+ "select * from pragma_foreign_key_list(?) where id = ?" ,
157
+ params ! [ & table, & fk_idx] ,
158
+ |row| {
159
+ let col: String = row. get_unwrap ( 3 ) ;
160
+ let foreign_col: String = row. get_unwrap ( 4 ) ;
161
+ panic ! (
162
+ "Foreign key violation encountered during migration\n \
163
+ table: {},\n \
164
+ column: {},\n \
165
+ row_id: {:?},\n \
166
+ foreign table: {},\n \
167
+ foreign column: {}\n \
168
+ migration ID: {}\n ",
169
+ table, col, row_id, foreign_table, foreign_col, migration_id,
170
+ ) ;
171
+ } ,
172
+ )
173
+ . unwrap ( ) ;
174
+ Ok ( ( ) )
175
+ } )
176
+ . unwrap ( ) ;
177
+
178
+ tx. pragma_update ( None , "user_version" , & migration_id)
179
+ . unwrap ( ) ;
180
+
181
+ // 11. Commit the transaction started in step 2.
182
+ tx. commit ( ) . unwrap ( ) ;
183
+
184
+ // 12. If foreign keys constraints were originally enabled, reenable them now.
185
+ conn. pragma_update ( None , "foreign_keys" , & "ON" ) . unwrap ( ) ;
186
+ }
187
+ }
188
+
189
+ static MIGRATIONS : & [ Migration ] = & [
190
+ Migration :: new ( "" ) ,
191
+ Migration :: new (
192
+ r#"
193
+ create table benchmark(
194
+ name text primary key,
195
+ -- Whether this benchmark supports stable
196
+ stabilized bool not null
197
+ );
198
+ create table artifact(
199
+ id integer primary key not null,
200
+ name text not null unique,
201
+ date integer,
202
+ type text not null
203
+ );
204
+ create table collection(
205
+ id integer primary key not null
206
+ );
207
+ create table error_series(
208
+ id integer primary key not null,
209
+ crate text not null unique references benchmark(name) on delete cascade on update cascade
210
+ );
211
+ create table error(
212
+ series integer not null references error_series(id) on delete cascade on update cascade,
213
+ aid integer not null references artifact(id) on delete cascade on update cascade,
214
+ error text,
215
+ PRIMARY KEY(series, aid)
216
+ );
217
+ create table pstat_series(
218
+ id integer primary key not null,
219
+ crate text not null references benchmark(name) on delete cascade on update cascade,
220
+ profile text not null,
221
+ cache text not null,
222
+ statistic text not null,
223
+ UNIQUE(crate, profile, cache, statistic)
224
+ );
225
+ create table pstat(
226
+ series integer references pstat_series(id) on delete cascade on update cascade,
227
+ aid integer references artifact(id) on delete cascade on update cascade,
228
+ cid integer references collection(id) on delete cascade on update cascade,
229
+ value double not null,
230
+ PRIMARY KEY(series, aid, cid)
231
+ );
232
+ create table self_profile_query_series(
233
+ id integer primary key not null,
234
+ crate text not null references benchmark(name) on delete cascade on update cascade,
235
+ profile text not null,
236
+ cache text not null,
237
+ query text not null,
238
+ UNIQUE(crate, profile, cache, query)
239
+ );
240
+ create table self_profile_query(
241
+ series integer references self_profile_query_series(id) on delete cascade on update cascade,
242
+ aid integer references artifact(id) on delete cascade on update cascade,
243
+ cid integer references collection(id) on delete cascade on update cascade,
244
+ self_time integer,
245
+ blocked_time integer,
246
+ incremental_load_time integer,
247
+ number_of_cache_hits integer,
248
+ invocation_count integer,
249
+ PRIMARY KEY(series, aid, cid)
250
+ );
251
+ create table pull_request_builds(
252
+ bors_sha text unique,
253
+ pr integer not null,
254
+ parent_sha text,
255
+ complete boolean,
256
+ requested timestamp without time zone
257
+ );
258
+ "# ,
259
+ ) ,
260
+ Migration :: new (
261
+ r#"
262
+ create table artifact_collection_duration(
263
+ aid integer primary key not null references artifact(id) on delete cascade on update cascade,
264
+ date_recorded timestamp without time zone not null,
265
+ duration integer not null
266
+ );
267
+ "# ,
268
+ ) ,
269
+ Migration :: new (
270
+ r#"
271
+ create table collector_progress(
272
+ aid integer not null references artifact(id) on delete cascade on update cascade,
273
+ step text not null,
274
+ start integer,
275
+ end integer,
276
+ UNIQUE(aid, step)
277
+ );
278
+ "# ,
279
+ ) ,
280
+ Migration :: new ( "alter table collection add column perf_commit text" ) ,
281
+ Migration :: new ( "alter table pull_request_builds add column include text" ) ,
282
+ Migration :: new ( "alter table pull_request_builds add column exclude text" ) ,
283
+ Migration :: new ( "alter table pull_request_builds add column runs integer" ) ,
284
+ Migration :: new (
285
+ r#"
286
+ create table rustc_compilation(
287
+ aid integer references artifact(id) on delete cascade on update cascade,
288
+ cid integer references collection(id) on delete cascade on update cascade,
289
+ crate text not null,
290
+ duration integer not null,
291
+ PRIMARY KEY(aid, cid, crate)
292
+ );
293
+ "# ,
294
+ ) ,
295
+ Migration :: new ( "alter table pull_request_builds rename to pull_request_build" ) ,
296
+ Migration :: new (
297
+ r#"
298
+ create table raw_self_profile(
299
+ aid integer references artifact(id) on delete cascade on update cascade,
300
+ cid integer references collection(id) on delete cascade on update cascade,
301
+ crate text not null references benchmark(name) on delete cascade on update cascade,
302
+ profile text not null,
303
+ cache text not null,
304
+ PRIMARY KEY(aid, cid, crate, profile, cache)
305
+ );
306
+ "# ,
307
+ ) ,
308
+ // Add not null constraint to benchmark name.
309
+ Migration :: without_foreign_key_constraints (
310
+ r#"
311
+ create table benchmark_new(
312
+ name text primary key not null,
313
+ stabilized bool not null
314
+ );
315
+ insert into benchmark_new select * from benchmark where name is not null;
316
+ drop table benchmark;
317
+ alter table benchmark_new rename to benchmark;
318
+ "# ,
319
+ ) ,
176
320
] ;
177
321
178
322
#[ async_trait:: async_trait]
@@ -193,12 +337,7 @@ impl ConnectionManager for Sqlite {
193
337
)
194
338
. unwrap ( ) ;
195
339
for mid in ( version as usize + 1 ) ..MIGRATIONS . len ( ) {
196
- let sql = MIGRATIONS [ mid] ;
197
- let tx = conn. transaction ( ) . unwrap ( ) ;
198
- tx. execute_batch ( & sql) . unwrap ( ) ;
199
- tx. pragma_update ( None , "user_version" , & ( mid as i32 ) )
200
- . unwrap ( ) ;
201
- tx. commit ( ) . unwrap ( ) ;
340
+ MIGRATIONS [ mid] . execute ( & mut conn, mid as i32 ) ;
202
341
}
203
342
} ) ;
204
343
0 commit comments