1-- Copyright 2013 Google Inc.
2-- All rights reserved.
3--
4-- Redistribution and use in source and binary forms, with or without
5-- modification, are permitted provided that the following conditions are
6-- met:
7--
8-- * Redistributions of source code must retain the above copyright
9--   notice, this list of conditions and the following disclaimer.
10-- * Redistributions in binary form must reproduce the above copyright
11--   notice, this list of conditions and the following disclaimer in the
12--   documentation and/or other materials provided with the distribution.
13-- * Neither the name of Google Inc. nor the names of its contributors
14--   may be used to endorse or promote products derived from this software
15--   without specific prior written permission.
16--
17-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29-- \file store/v1-to-v2.sql
30-- Migration of a database with version 1 of the schema to version 2.
31--
32-- Version 2 appeared in revision 9a73561a1e3975bba4cbfd19aee6b2365a39519e
33-- and its changes were:
34--
35-- * Changed the primary key of the metadata table to be the
36--   schema_version, not the timestamp.  Because timestamps only have
37--   second resolution, the old schema made testing of schema migrations
38--   difficult.
39--
40-- * Introduced the metadatas table, which holds the metadata of all test
41--   programs and test cases in an abstract manner regardless of their
42--   interface.
43--
44-- * Added the metadata_id field to the test_programs and test_cases
45--   tables, referencing the new metadatas table.
46--
47-- * Changed the precision of the timeout metadata field to be in seconds
48--   rather than in microseconds.  There is no data loss, and the code that
49--   writes the metadata is simplified.
50--
51-- * Removed the atf_* and plain_* tables.
52--
53-- * Added missing indexes to improve the performance of reports.
54--
55-- * Added missing column affinities to the absolute_path and relative_path
56--   columns of the test_programs table.
57
58
59-- TODO(jmmv): Implement addition of missing affinities.
60
61
62--
63-- Change primary key of the metadata table.
64--
65
66
67CREATE TABLE new_metadata (
68    schema_version INTEGER PRIMARY KEY CHECK (schema_version >= 1),
69    timestamp TIMESTAMP NOT NULL CHECK (timestamp >= 0)
70);
71
72INSERT INTO new_metadata (schema_version, timestamp)
73    SELECT schema_version, timestamp FROM metadata;
74
75DROP TABLE metadata;
76ALTER TABLE new_metadata RENAME TO metadata;
77
78
79--
80-- Add the new tables, columns and indexes.
81--
82
83
84CREATE TABLE metadatas (
85    metadata_id INTEGER NOT NULL,
86    property_name TEXT NOT NULL,
87    property_value TEXT,
88
89    PRIMARY KEY (metadata_id, property_name)
90);
91
92
93-- Upgrade the test_programs table by adding missing column affinities and
94-- the new metadata_id column.
95CREATE TABLE new_test_programs (
96    test_program_id INTEGER PRIMARY KEY AUTOINCREMENT,
97    action_id INTEGER REFERENCES actions,
98
99    absolute_path TEXT NOT NULL,
100    root TEXT NOT NULL,
101    relative_path TEXT NOT NULL,
102    test_suite_name TEXT NOT NULL,
103    metadata_id INTEGER,
104    interface TEXT NOT NULL
105);
106PRAGMA foreign_keys = OFF;
107INSERT INTO new_test_programs (test_program_id, action_id, absolute_path,
108                               root, relative_path, test_suite_name,
109                               interface)
110    SELECT test_program_id, action_id, absolute_path, root, relative_path,
111        test_suite_name, interface FROM test_programs;
112DROP TABLE test_programs;
113ALTER TABLE new_test_programs RENAME TO test_programs;
114PRAGMA foreign_keys = ON;
115
116
117ALTER TABLE test_cases ADD COLUMN metadata_id INTEGER;
118
119
120CREATE INDEX index_metadatas_by_id
121    ON metadatas (metadata_id);
122CREATE INDEX index_test_programs_by_action_id
123    ON test_programs (action_id);
124CREATE INDEX index_test_cases_by_test_programs_id
125    ON test_cases (test_program_id);
126
127
128--
129-- Data migration
130--
131-- This is, by far, the trickiest part of the migration.
132-- TODO(jmmv): Describe the trickiness in here.
133--
134
135
136-- Auxiliary table to construct the final contents of the metadatas table.
137--
138-- We construct the contents by writing a row for every metadata property of
139-- every test program and test case.  Entries corresponding to a test program
140-- will have the test_program_id field set to not NULL and entries corresponding
141-- to test cases will have the test_case_id set to not NULL.
142--
143-- The tricky part, however, is to create the individual identifiers for every
144-- metadata entry.  We do this by picking the minimum ROWID of a particular set
145-- of properties that map to a single test_program_id or test_case_id.
146CREATE TABLE tmp_metadatas (
147    test_program_id INTEGER DEFAULT NULL,
148    test_case_id INTEGER DEFAULT NULL,
149    interface TEXT NOT NULL,
150    property_name TEXT NOT NULL,
151    property_value TEXT NOT NULL,
152
153    UNIQUE (test_program_id, test_case_id, property_name)
154);
155CREATE INDEX index_tmp_metadatas_by_test_case_id
156    ON tmp_metadatas (test_case_id);
157CREATE INDEX index_tmp_metadatas_by_test_program_id
158    ON tmp_metadatas (test_program_id);
159
160
161-- Populate default metadata values for all test programs and test cases.
162--
163-- We do this first to ensure that all test programs and test cases have
164-- explicit values for their metadata.  Because we want to keep historical data
165-- for the tests, we must record these values unconditionally instead of relying
166-- on the built-in values in the code.
167--
168-- Once this is done, we override any values explicity set by the tests.
169CREATE TABLE tmp_default_metadata (
170    default_name TEXT PRIMARY KEY,
171    default_value TEXT NOT NULL
172);
173INSERT INTO tmp_default_metadata VALUES ('allowed_architectures', '');
174INSERT INTO tmp_default_metadata VALUES ('allowed_platforms', '');
175INSERT INTO tmp_default_metadata VALUES ('description', '');
176INSERT INTO tmp_default_metadata VALUES ('has_cleanup', 'false');
177INSERT INTO tmp_default_metadata VALUES ('required_configs', '');
178INSERT INTO tmp_default_metadata VALUES ('required_files', '');
179INSERT INTO tmp_default_metadata VALUES ('required_memory', '0');
180INSERT INTO tmp_default_metadata VALUES ('required_programs', '');
181INSERT INTO tmp_default_metadata VALUES ('required_user', '');
182INSERT INTO tmp_default_metadata VALUES ('timeout', '300');
183INSERT INTO tmp_metadatas
184    SELECT test_program_id, NULL, interface, default_name, default_value
185        FROM test_programs JOIN tmp_default_metadata;
186INSERT INTO tmp_metadatas
187    SELECT NULL, test_case_id, interface, default_name, default_value
188        FROM test_programs JOIN test_cases
189        ON test_cases.test_program_id = test_programs.test_program_id
190        JOIN tmp_default_metadata;
191DROP TABLE tmp_default_metadata;
192
193
194-- Populate metadata overrides from plain test programs.
195UPDATE tmp_metadatas
196    SET property_value = (
197        SELECT CAST(timeout / 1000000 AS TEXT) FROM plain_test_programs AS aux
198            WHERE aux.test_program_id = tmp_metadatas.test_program_id)
199    WHERE test_program_id IS NOT NULL AND property_name = 'timeout'
200        AND interface = 'plain';
201UPDATE tmp_metadatas
202    SET property_value = (
203        SELECT DISTINCT CAST(timeout / 1000000 AS TEXT)
204        FROM test_cases AS aux JOIN plain_test_programs
205            ON aux.test_program_id == plain_test_programs.test_program_id
206        WHERE aux.test_case_id = tmp_metadatas.test_case_id)
207    WHERE test_case_id IS NOT NULL AND property_name = 'timeout'
208        AND interface = 'plain';
209
210
211-- Populate metadata overrides from ATF test cases.
212UPDATE atf_test_cases SET description = '' WHERE description IS NULL;
213UPDATE atf_test_cases SET required_user = '' WHERE required_user IS NULL;
214
215UPDATE tmp_metadatas
216    SET property_value = (
217        SELECT description FROM atf_test_cases AS aux
218            WHERE aux.test_case_id = tmp_metadatas.test_case_id)
219    WHERE test_case_id IS NOT NULL AND property_name = 'description'
220        AND interface = 'atf';
221UPDATE tmp_metadatas
222    SET property_value = (
223        SELECT has_cleanup FROM atf_test_cases AS aux
224            WHERE aux.test_case_id = tmp_metadatas.test_case_id)
225    WHERE test_case_id IS NOT NULL AND property_name = 'has_cleanup'
226        AND interface = 'atf';
227UPDATE tmp_metadatas
228    SET property_value = (
229        SELECT CAST(timeout / 1000000 AS TEXT) FROM atf_test_cases AS aux
230            WHERE aux.test_case_id = tmp_metadatas.test_case_id)
231    WHERE test_case_id IS NOT NULL AND property_name = 'timeout'
232        AND interface = 'atf';
233UPDATE tmp_metadatas
234    SET property_value = (
235        SELECT CAST(required_memory AS TEXT) FROM atf_test_cases AS aux
236            WHERE aux.test_case_id = tmp_metadatas.test_case_id)
237    WHERE test_case_id IS NOT NULL AND property_name = 'required_memory'
238        AND interface = 'atf';
239UPDATE tmp_metadatas
240    SET property_value = (
241        SELECT required_user FROM atf_test_cases AS aux
242            WHERE aux.test_case_id = tmp_metadatas.test_case_id)
243    WHERE test_case_id IS NOT NULL AND property_name = 'required_user'
244        AND interface = 'atf';
245UPDATE tmp_metadatas
246    SET property_value = (
247        SELECT GROUP_CONCAT(aux.property_value, ' ')
248            FROM atf_test_cases_multivalues AS aux
249            WHERE aux.test_case_id = tmp_metadatas.test_case_id AND
250                aux.property_name = 'require.arch')
251    WHERE test_case_id IS NOT NULL AND property_name = 'allowed_architectures'
252        AND interface = 'atf'
253        AND EXISTS(SELECT 1 FROM atf_test_cases_multivalues AS aux
254                   WHERE aux.test_case_id = tmp_metadatas.test_case_id
255                   AND property_name = 'require.arch');
256UPDATE tmp_metadatas
257    SET property_value = (
258        SELECT GROUP_CONCAT(aux.property_value, ' ')
259            FROM atf_test_cases_multivalues AS aux
260            WHERE aux.test_case_id = tmp_metadatas.test_case_id AND
261                aux.property_name = 'require.machine')
262    WHERE test_case_id IS NOT NULL AND property_name = 'allowed_platforms'
263        AND interface = 'atf'
264        AND EXISTS(SELECT 1 FROM atf_test_cases_multivalues AS aux
265                   WHERE aux.test_case_id = tmp_metadatas.test_case_id
266                   AND property_name = 'require.machine');
267UPDATE tmp_metadatas
268    SET property_value = (
269        SELECT GROUP_CONCAT(aux.property_value, ' ')
270            FROM atf_test_cases_multivalues AS aux
271            WHERE aux.test_case_id = tmp_metadatas.test_case_id AND
272                aux.property_name = 'require.config')
273    WHERE test_case_id IS NOT NULL AND property_name = 'required_configs'
274        AND interface = 'atf'
275        AND EXISTS(SELECT 1 FROM atf_test_cases_multivalues AS aux
276                   WHERE aux.test_case_id = tmp_metadatas.test_case_id
277                   AND property_name = 'require.config');
278UPDATE tmp_metadatas
279    SET property_value = (
280        SELECT GROUP_CONCAT(aux.property_value, ' ')
281            FROM atf_test_cases_multivalues AS aux
282            WHERE aux.test_case_id = tmp_metadatas.test_case_id AND
283                aux.property_name = 'require.files')
284    WHERE test_case_id IS NOT NULL AND property_name = 'required_files'
285        AND interface = 'atf'
286        AND EXISTS(SELECT 1 FROM atf_test_cases_multivalues AS aux
287                   WHERE aux.test_case_id = tmp_metadatas.test_case_id
288                   AND property_name = 'require.files');
289UPDATE tmp_metadatas
290    SET property_value = (
291        SELECT GROUP_CONCAT(aux.property_value, ' ')
292            FROM atf_test_cases_multivalues AS aux
293            WHERE aux.test_case_id = tmp_metadatas.test_case_id AND
294                aux.property_name = 'require.progs')
295    WHERE test_case_id IS NOT NULL AND property_name = 'required_programs'
296        AND interface = 'atf'
297        AND EXISTS(SELECT 1 FROM atf_test_cases_multivalues AS aux
298                   WHERE aux.test_case_id = tmp_metadatas.test_case_id
299                   AND property_name = 'require.progs');
300
301
302-- Fill metadata_id pointers in the test_programs and test_cases tables.
303UPDATE test_programs
304    SET metadata_id = (
305        SELECT MIN(ROWID) FROM tmp_metadatas
306            WHERE tmp_metadatas.test_program_id = test_programs.test_program_id
307    );
308UPDATE test_cases
309    SET metadata_id = (
310        SELECT MIN(ROWID) FROM tmp_metadatas
311            WHERE tmp_metadatas.test_case_id = test_cases.test_case_id
312    );
313
314
315-- Populate the metadatas table based on tmp_metadatas.
316INSERT INTO metadatas (metadata_id, property_name, property_value)
317    SELECT (
318        SELECT MIN(ROWID) FROM tmp_metadatas AS s
319        WHERE s.test_program_id = tmp_metadatas.test_program_id
320    ), property_name, property_value
321    FROM tmp_metadatas WHERE test_program_id IS NOT NULL;
322INSERT INTO metadatas (metadata_id, property_name, property_value)
323    SELECT (
324        SELECT MIN(ROWID) FROM tmp_metadatas AS s
325        WHERE s.test_case_id = tmp_metadatas.test_case_id
326    ), property_name, property_value
327    FROM tmp_metadatas WHERE test_case_id IS NOT NULL;
328
329
330-- Drop temporary entities used during the migration.
331DROP INDEX index_tmp_metadatas_by_test_program_id;
332DROP INDEX index_tmp_metadatas_by_test_case_id;
333DROP TABLE tmp_metadatas;
334
335
336--
337-- Drop obsolete tables.
338--
339
340
341DROP TABLE atf_test_cases;
342DROP TABLE atf_test_cases_multivalues;
343DROP TABLE plain_test_programs;
344
345
346--
347-- Update the metadata version.
348--
349
350
351INSERT INTO metadata (timestamp, schema_version)
352    VALUES (strftime('%s', 'now'), 2);
353