migrateto32.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #!/usr/bin/env php
  2. <?php
  3. echo "SabreDAV migrate script for version 3.2\n";
  4. if ($argc < 2) {
  5. echo <<<HELLO
  6. This script help you migrate from a 3.1 database to 3.2 and later
  7. Changes:
  8. * Created a new calendarinstances table to support calendar sharing.
  9. * Remove a lot of columns from calendars.
  10. Keep in mind that ALTER TABLE commands will be executed. If you have a large
  11. dataset this may mean that this process takes a while.
  12. Make a back-up first. This script has been tested, but the amount of
  13. potential variants are extremely high, so it's impossible to deal with every
  14. possible situation.
  15. In the worst case, you will lose all your data. This is not an overstatement.
  16. Lastly, if you are upgrading from an older version than 3.1, make sure you run
  17. the earlier migration script first. Migration scripts must be ran in order.
  18. Usage:
  19. php {$argv[0]} [pdo-dsn] [username] [password]
  20. For example:
  21. php {$argv[0]} "mysql:host=localhost;dbname=sabredav" root password
  22. php {$argv[0]} sqlite:data/sabredav.db
  23. HELLO;
  24. exit();
  25. }
  26. // There's a bunch of places where the autoloader could be, so we'll try all of
  27. // them.
  28. $paths = [
  29. __DIR__.'/../vendor/autoload.php',
  30. __DIR__.'/../../../autoload.php',
  31. ];
  32. foreach ($paths as $path) {
  33. if (file_exists($path)) {
  34. include $path;
  35. break;
  36. }
  37. }
  38. $dsn = $argv[1];
  39. $user = isset($argv[2]) ? $argv[2] : null;
  40. $pass = isset($argv[3]) ? $argv[3] : null;
  41. $backupPostfix = time();
  42. echo 'Connecting to database: '.$dsn."\n";
  43. $pdo = new PDO($dsn, $user, $pass);
  44. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  45. $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
  46. $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
  47. switch ($driver) {
  48. case 'mysql':
  49. echo "Detected MySQL.\n";
  50. break;
  51. case 'sqlite':
  52. echo "Detected SQLite.\n";
  53. break;
  54. default:
  55. echo 'Error: unsupported driver: '.$driver."\n";
  56. exit(-1);
  57. }
  58. echo "Creating 'calendarinstances'\n";
  59. $addValueType = false;
  60. try {
  61. $result = $pdo->query('SELECT * FROM calendarinstances LIMIT 1');
  62. $result->fetch(\PDO::FETCH_ASSOC);
  63. echo "calendarinstances exists. Assuming this part of the migration has already been done.\n";
  64. } catch (Exception $e) {
  65. echo "calendarinstances does not yet exist. Creating table and migrating data.\n";
  66. switch ($driver) {
  67. case 'mysql':
  68. $pdo->exec(<<<SQL
  69. CREATE TABLE calendarinstances (
  70. id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  71. calendarid INTEGER UNSIGNED NOT NULL,
  72. principaluri VARBINARY(100),
  73. access TINYINT(1) NOT NULL DEFAULT '1' COMMENT '1 = owner, 2 = read, 3 = readwrite',
  74. displayname VARCHAR(100),
  75. uri VARBINARY(200),
  76. description TEXT,
  77. calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
  78. calendarcolor VARBINARY(10),
  79. timezone TEXT,
  80. transparent TINYINT(1) NOT NULL DEFAULT '0',
  81. share_href VARBINARY(100),
  82. share_displayname VARCHAR(100),
  83. share_invitestatus TINYINT(1) NOT NULL DEFAULT '2' COMMENT '1 = noresponse, 2 = accepted, 3 = declined, 4 = invalid',
  84. UNIQUE(principaluri, uri),
  85. UNIQUE(calendarid, principaluri),
  86. UNIQUE(calendarid, share_href)
  87. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  88. SQL
  89. );
  90. $pdo->exec('
  91. INSERT INTO calendarinstances
  92. (
  93. calendarid,
  94. principaluri,
  95. access,
  96. displayname,
  97. uri,
  98. description,
  99. calendarorder,
  100. calendarcolor,
  101. transparent
  102. )
  103. SELECT
  104. id,
  105. principaluri,
  106. 1,
  107. displayname,
  108. uri,
  109. description,
  110. calendarorder,
  111. calendarcolor,
  112. transparent
  113. FROM calendars
  114. ');
  115. break;
  116. case 'sqlite':
  117. $pdo->exec(<<<SQL
  118. CREATE TABLE calendarinstances (
  119. id integer primary key asc NOT NULL,
  120. calendarid integer,
  121. principaluri text,
  122. access integer COMMENT '1 = owner, 2 = read, 3 = readwrite' NOT NULL DEFAULT '1',
  123. displayname text,
  124. uri text NOT NULL,
  125. description text,
  126. calendarorder integer,
  127. calendarcolor text,
  128. timezone text,
  129. transparent bool,
  130. share_href text,
  131. share_displayname text,
  132. share_invitestatus integer DEFAULT '2',
  133. UNIQUE (principaluri, uri),
  134. UNIQUE (calendarid, principaluri),
  135. UNIQUE (calendarid, share_href)
  136. );
  137. SQL
  138. );
  139. $pdo->exec('
  140. INSERT INTO calendarinstances
  141. (
  142. calendarid,
  143. principaluri,
  144. access,
  145. displayname,
  146. uri,
  147. description,
  148. calendarorder,
  149. calendarcolor,
  150. transparent
  151. )
  152. SELECT
  153. id,
  154. principaluri,
  155. 1,
  156. displayname,
  157. uri,
  158. description,
  159. calendarorder,
  160. calendarcolor,
  161. transparent
  162. FROM calendars
  163. ');
  164. break;
  165. }
  166. }
  167. try {
  168. $result = $pdo->query('SELECT * FROM calendars LIMIT 1');
  169. $row = $result->fetch(\PDO::FETCH_ASSOC);
  170. if (!$row) {
  171. echo "Source table is empty.\n";
  172. $migrateCalendars = true;
  173. }
  174. $columnCount = count($row);
  175. if (3 === $columnCount) {
  176. echo "The calendars table has 3 columns already. Assuming this part of the migration was already done.\n";
  177. $migrateCalendars = false;
  178. } else {
  179. echo 'The calendars table has '.$columnCount." columns.\n";
  180. $migrateCalendars = true;
  181. }
  182. } catch (Exception $e) {
  183. echo "calendars table does not exist. This is a major problem. Exiting.\n";
  184. exit(-1);
  185. }
  186. if ($migrateCalendars) {
  187. $calendarBackup = 'calendars_3_1_'.$backupPostfix;
  188. echo "Backing up 'calendars' to '", $calendarBackup, "'\n";
  189. switch ($driver) {
  190. case 'mysql':
  191. $pdo->exec('RENAME TABLE calendars TO '.$calendarBackup);
  192. break;
  193. case 'sqlite':
  194. $pdo->exec('ALTER TABLE calendars RENAME TO '.$calendarBackup);
  195. break;
  196. }
  197. echo "Creating new calendars table.\n";
  198. switch ($driver) {
  199. case 'mysql':
  200. $pdo->exec(<<<SQL
  201. CREATE TABLE calendars (
  202. id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  203. synctoken INTEGER UNSIGNED NOT NULL DEFAULT '1',
  204. components VARBINARY(21)
  205. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  206. SQL
  207. );
  208. break;
  209. case 'sqlite':
  210. $pdo->exec(<<<SQL
  211. CREATE TABLE calendars (
  212. id integer primary key asc NOT NULL,
  213. synctoken integer DEFAULT 1 NOT NULL,
  214. components text NOT NULL
  215. );
  216. SQL
  217. );
  218. break;
  219. }
  220. echo "Migrating data from old to new table\n";
  221. $pdo->exec(<<<SQL
  222. INSERT INTO calendars (id, synctoken, components) SELECT id, synctoken, COALESCE(components,"VEVENT,VTODO,VJOURNAL") as components FROM $calendarBackup
  223. SQL
  224. );
  225. }
  226. echo "Upgrade to 3.2 schema completed.\n";