Skip to content

Commit 4f6bac9

Browse files
Schema upgrade script.
Summary: Created a script the runs all the necessary patches for db schema. Stores information in the db about the latest patch that was applied. Test Plan: Created two test files '024.test.sql' and '023.test.sql' in this order. '023' creates a database and '024' creates a table in this db. First ran './upgrade_schema.php 23' and made sure that patches were applied in order. Then ran './upgrade_schema.php' to make sure db was up-to-date. Checked manually from the db that the database and table exists. Reviewed By: epriestley Reviewers: epriestley CC: jungejason, epriestley, tuomaspelkonen Differential Revision: 115
1 parent 540c2ec commit 4f6bac9

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

scripts/sql/upgrade_schema.php

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/*
5+
* Copyright 2011 Facebook, Inc.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
$root = dirname(dirname(dirname(__FILE__)));
21+
require_once $root.'/scripts/__init_script__.php';
22+
require_once $root.'/scripts/__init_env__.php';
23+
24+
const TABLE_NAME = 'schema_version';
25+
26+
if (isset($argv[1]) && !is_numeric($argv[1])) {
27+
print
28+
"USAGE: ./update_schema.php [first_patch_version]\n\n".
29+
"run './update_schema.php 12' to apply all patches starting from ".
30+
"version 12.\n".
31+
"run './update_schema.php' to apply all patches that are new since\n".
32+
"the last time this script was run\n\n";
33+
exit(0);
34+
}
35+
36+
// Use always the version from the commandline if it is defined
37+
$next_version = isset($argv[1]) ? (int)$argv[1] : null;
38+
39+
// Dummy class needed for creating our database
40+
class DummyUser extends PhabricatorLiskDAO {
41+
public function getApplicationName() {
42+
return 'user';
43+
}
44+
}
45+
46+
// Class needed for setting up the actual SQL connection
47+
class PhabricatorSchemaVersion extends PhabricatorLiskDAO {
48+
public function getApplicationName() {
49+
return 'meta_data';
50+
}
51+
}
52+
53+
// Connect to 'phabricator_user' db first to create our db
54+
$conn = id(new DummyUser())->establishConnection('w');
55+
$create_sql = <<<END
56+
CREATE DATABASE IF NOT EXISTS `phabricator_meta_data`;
57+
END;
58+
queryfx($conn, $create_sql);
59+
60+
// 'phabricator_meta_data' database exists, let's connect to it now
61+
$conn = id(new PhabricatorSchemaVersion())->establishConnection('w');
62+
$create_sql = <<<END
63+
CREATE TABLE IF NOT EXISTS phabricator_meta_data.`schema_version` (
64+
`version` INTEGER not null
65+
);
66+
END;
67+
queryfx($conn, $create_sql);
68+
69+
// Get the version only if commandline argument wasn't given
70+
if ($next_version === null) {
71+
$version = queryfx_one(
72+
$conn,
73+
'SELECT * FROM %T',
74+
TABLE_NAME);
75+
76+
if (!$version) {
77+
print "*** No version information in the database ***\n";
78+
print "*** Give the first patch version which to ***\n";
79+
print "*** apply as the command line argument ***\n";
80+
exit(-1);
81+
}
82+
83+
$next_version = $version['version'] + 1;
84+
}
85+
86+
// Find the patch files
87+
$patches_dir = $root.'/resources/sql/patches/';
88+
$finder = id(new FileFinder($patches_dir))
89+
->withSuffix('sql');
90+
$results = $finder->find();
91+
92+
$patches = array();
93+
foreach ($results as $r) {
94+
$matches = array();
95+
if (preg_match('/(\d+)\..*\.sql$/', $r, $matches)) {
96+
$patches[] = array('version' => (int)$matches[1],
97+
'file' => $r);
98+
} else {
99+
print
100+
"*** WARNING : File {$r} does not follow the normal naming ".
101+
"convention. ***\n";
102+
}
103+
}
104+
105+
// Files are in some 'random' order returned by the operating system
106+
// We need to apply them in proper order
107+
$patches = isort($patches, 'version');
108+
109+
$patch_applied = false;
110+
foreach ($patches as $patch) {
111+
if ($patch['version'] < $next_version) {
112+
continue;
113+
}
114+
115+
print "Applying patch {$patch['file']}\n";
116+
117+
$path = Filesystem::resolvePath($patches_dir.$patch['file']);
118+
119+
$user = PhabricatorEnv::getEnvConfig('mysql.user');
120+
$pass = PhabricatorEnv::getEnvConfig('mysql.pass');
121+
$host = PhabricatorEnv::getEnvConfig('mysql.host');
122+
123+
list($stdout, $stderr) = execx(
124+
"mysql --user=%s --password=%s --host=%s < %s",
125+
$user, $pass, $host, $path);
126+
127+
if ($stderr) {
128+
print $stderr;
129+
exit(-1);
130+
}
131+
132+
// Patch was successful, update the db with the latest applied patch version
133+
// 'DELETE' and 'INSERT' instead of update, because the table might be empty
134+
queryfx($conn, 'DELETE FROM %T', TABLE_NAME);
135+
queryfx($conn, 'INSERT INTO %T values (%d)', TABLE_NAME, $patch['version']);
136+
137+
$patch_applied = true;
138+
}
139+
140+
if (!$patch_applied) {
141+
print "Your database is already up-to-date\n";
142+
}

0 commit comments

Comments
 (0)