18f259467SAndreas Gohr<?php 2d6d97f60SAnna Dabrowska 38f259467SAndreas Gohr/** 48f259467SAndreas Gohr * DokuWiki Plugin struct (Action Component) 58f259467SAndreas Gohr * 68f259467SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 78f259467SAndreas Gohr * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 88f259467SAndreas Gohr */ 98f259467SAndreas Gohr 108f259467SAndreas Gohr/** 118f259467SAndreas Gohr * Class action_plugin_struct_migration 128f259467SAndreas Gohr * 138f259467SAndreas Gohr * Handle migrations that need more than just SQL 148f259467SAndreas Gohr */ 15d6d97f60SAnna Dabrowskaclass action_plugin_struct_migration extends DokuWiki_Action_Plugin 16d6d97f60SAnna Dabrowska{ 178f259467SAndreas Gohr /** 188f259467SAndreas Gohr * @inheritDoc 198f259467SAndreas Gohr */ 20d6d97f60SAnna Dabrowska public function register(Doku_Event_Handler $controller) 21d6d97f60SAnna Dabrowska { 22748e747fSAnna Dabrowska $controller->register_hook('PLUGIN_SQLITE_DATABASE_UPGRADE', 'BEFORE', $this, 'handleMigrations'); 238f259467SAndreas Gohr } 248f259467SAndreas Gohr 258f259467SAndreas Gohr /** 268f259467SAndreas Gohr * Call our custom migrations when defined 278f259467SAndreas Gohr * 288f259467SAndreas Gohr * @param Doku_Event $event 298f259467SAndreas Gohr * @param $param 308f259467SAndreas Gohr */ 31748e747fSAnna Dabrowska public function handleMigrations(Doku_Event $event, $param) 32d6d97f60SAnna Dabrowska { 333eafcbabSMichael Große if ($event->data['sqlite']->getAdapter()->getDbname() !== 'struct') { 343eafcbabSMichael Große return; 353eafcbabSMichael Große } 368f259467SAndreas Gohr $to = $event->data['to']; 378f259467SAndreas Gohr 388f259467SAndreas Gohr if (is_callable(array($this, "migration$to"))) { 398f259467SAndreas Gohr $event->preventDefault(); 408f259467SAndreas Gohr $event->result = call_user_func(array($this, "migration$to"), $event->data['sqlite']); 418f259467SAndreas Gohr } 428f259467SAndreas Gohr } 438f259467SAndreas Gohr 448f259467SAndreas Gohr /** 458f259467SAndreas Gohr * Executes Migration 12 468f259467SAndreas Gohr * 478f259467SAndreas Gohr * Add a latest column to all existing multi tables 488f259467SAndreas Gohr * 498f259467SAndreas Gohr * @param helper_plugin_sqlite $sqlite 508f259467SAndreas Gohr * @return bool 518f259467SAndreas Gohr */ 52d6d97f60SAnna Dabrowska protected function migration12(helper_plugin_sqlite $sqlite) 53d6d97f60SAnna Dabrowska { 548f259467SAndreas Gohr /** @noinspection SqlResolve */ 558f259467SAndreas Gohr $sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'multi_%'"; 568f259467SAndreas Gohr $res = $sqlite->query($sql); 578f259467SAndreas Gohr $tables = $sqlite->res2arr($res); 588f259467SAndreas Gohr $sqlite->res_close($res); 598f259467SAndreas Gohr 608f259467SAndreas Gohr foreach ($tables as $row) { 618f259467SAndreas Gohr $sql = 'ALTER TABLE ? ADD COLUMN latest INT DEFAULT 1'; 628f259467SAndreas Gohr $sqlite->query($sql, $row['name']); 638f259467SAndreas Gohr } 648f259467SAndreas Gohr 658f259467SAndreas Gohr return true; 668f259467SAndreas Gohr } 67*76a74c23SAnna Dabrowska 68*76a74c23SAnna Dabrowska /** 69*76a74c23SAnna Dabrowska * Executes Migration 16 70*76a74c23SAnna Dabrowska * 71*76a74c23SAnna Dabrowska * Unifies previous page and lookup schema types 72*76a74c23SAnna Dabrowska * 73*76a74c23SAnna Dabrowska * @param helper_plugin_sqlite $sqlite 74*76a74c23SAnna Dabrowska * @return bool 75*76a74c23SAnna Dabrowska */ 76*76a74c23SAnna Dabrowska protected function migration16(helper_plugin_sqlite $sqlite) 77*76a74c23SAnna Dabrowska { 78*76a74c23SAnna Dabrowska // get tables and their SQL definitions 79*76a74c23SAnna Dabrowska $sql = "SELECT sql, name FROM sqlite_master 80*76a74c23SAnna Dabrowska WHERE type = 'table' 81*76a74c23SAnna Dabrowska AND (name LIKE 'data_%' OR name LIKE 'multi_%')"; 82*76a74c23SAnna Dabrowska $res = $sqlite->query($sql); 83*76a74c23SAnna Dabrowska $tables = $sqlite->res2arr($res); 84*76a74c23SAnna Dabrowska $sqlite->res_close($res); 85*76a74c23SAnna Dabrowska 86*76a74c23SAnna Dabrowska // get latest versions of schemas with islookup property 87*76a74c23SAnna Dabrowska $sql = "SELECT MAX(id) AS id, tbl, islookup FROM schemas 88*76a74c23SAnna Dabrowska GROUP BY tbl 89*76a74c23SAnna Dabrowska "; 90*76a74c23SAnna Dabrowska $res = $sqlite->query($sql); 91*76a74c23SAnna Dabrowska $schemas = $sqlite->res2arr($res); 92*76a74c23SAnna Dabrowska 93*76a74c23SAnna Dabrowska $sqlite->query('BEGIN TRANSACTION'); 94*76a74c23SAnna Dabrowska $ok = true; 95*76a74c23SAnna Dabrowska 96*76a74c23SAnna Dabrowska // Step 1: move original data to temporary tables and create new ones with modified schemas 97*76a74c23SAnna Dabrowska foreach($tables as $table) { 98*76a74c23SAnna Dabrowska $name = $table['name']; 99*76a74c23SAnna Dabrowska $sql = $table['sql']; 100*76a74c23SAnna Dabrowska 101*76a74c23SAnna Dabrowska // move original data to temp_* 102*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query("ALTER TABLE $name RENAME TO temp_$name"); 103*76a74c23SAnna Dabrowska 104*76a74c23SAnna Dabrowska // update pid definitions 105*76a74c23SAnna Dabrowska $sql = preg_replace('/pid (\w* ?NOT NULL|\w* ?PRIMARY KEY)/', 'pid TEXT DEFAULT ""', $sql); 106*76a74c23SAnna Dabrowska 107*76a74c23SAnna Dabrowska // add rid and new primary key to regular tables 108*76a74c23SAnna Dabrowska $cnt = 0; 109*76a74c23SAnna Dabrowska $sql = preg_replace('/(PRIMARY KEY ?\([^\)]+?)(\))/', ' rid INTEGER, $1, rid $2', $sql, -1, $cnt); 110*76a74c23SAnna Dabrowska // add rid and new primary key to lookup tables 111*76a74c23SAnna Dabrowska if (!$cnt) { 112*76a74c23SAnna Dabrowska $sql = str_replace(')', ', rid INTEGER, PRIMARY KEY(pid,rid) )', $sql); 113*76a74c23SAnna Dabrowska } 114*76a74c23SAnna Dabrowska 115*76a74c23SAnna Dabrowska // create the new table 116*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($sql); 117*76a74c23SAnna Dabrowska if (!$ok) return false; 118*76a74c23SAnna Dabrowska } 119*76a74c23SAnna Dabrowska 120*76a74c23SAnna Dabrowska // Step 2: transfer data back from original tables (temp_*) 121*76a74c23SAnna Dabrowska foreach($schemas as $schema) { 122*76a74c23SAnna Dabrowska $name = $schema['tbl']; 123*76a74c23SAnna Dabrowska $sid = $schema['id']; 124*76a74c23SAnna Dabrowska $isLookup = $schema['islookup']; 125*76a74c23SAnna Dabrowska 126*76a74c23SAnna Dabrowska if (!$isLookup) { 127*76a74c23SAnna Dabrowska $s = sprintf('INSERT INTO data_%s SELECT *, 0 FROM temp_data_%s', $name, $name); 128*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 129*76a74c23SAnna Dabrowska if (!$ok) return false; 130*76a74c23SAnna Dabrowska 131*76a74c23SAnna Dabrowska $s = sprintf('INSERT INTO multi_%s SELECT *, 0 FROM temp_multi_%s', $name, $name); 132*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 133*76a74c23SAnna Dabrowska if (!$ok) return false; 134*76a74c23SAnna Dabrowska } else { 135*76a74c23SAnna Dabrowska // transfer pid to rid 136*76a74c23SAnna Dabrowska $s = sprintf('INSERT INTO data_%s SELECT *, pid FROM temp_data_%s', $name, $name); 137*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 138*76a74c23SAnna Dabrowska if (!$ok) return false; 139*76a74c23SAnna Dabrowska 140*76a74c23SAnna Dabrowska $s = sprintf('INSERT INTO multi_%s SELECT *, pid FROM temp_multi_%s', $name, $name); 141*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 142*76a74c23SAnna Dabrowska if (!$ok) return false; 143*76a74c23SAnna Dabrowska 144*76a74c23SAnna Dabrowska // all lookup data has empty pids at this point 145*76a74c23SAnna Dabrowska $s = "UPDATE data_$name SET pid = ''"; 146*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 147*76a74c23SAnna Dabrowska if (!$ok) return false; 148*76a74c23SAnna Dabrowska 149*76a74c23SAnna Dabrowska $s = "UPDATE multi_$name SET pid = ''"; 150*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 151*76a74c23SAnna Dabrowska if (!$ok) return false; 152*76a74c23SAnna Dabrowska } 153*76a74c23SAnna Dabrowska 154*76a74c23SAnna Dabrowska // introduce composite ids in lookup columns 155*76a74c23SAnna Dabrowska $s = "SELECT C.colref AS COL, T.class AS TYPE 156*76a74c23SAnna Dabrowska FROM schema_cols AS C 157*76a74c23SAnna Dabrowska LEFT OUTER JOIN types AS T 158*76a74c23SAnna Dabrowska ON C.tid = T.id 159*76a74c23SAnna Dabrowska WHERE C.sid = $sid 160*76a74c23SAnna Dabrowska AND TYPE = 'Lookup' 161*76a74c23SAnna Dabrowska "; 162*76a74c23SAnna Dabrowska $res = $sqlite->query($s); 163*76a74c23SAnna Dabrowska $cols = $sqlite->res2arr($res); 164*76a74c23SAnna Dabrowska 165*76a74c23SAnna Dabrowska if ($cols) { 166*76a74c23SAnna Dabrowska foreach ($cols as $col) { 167*76a74c23SAnna Dabrowska $colno = $col['COL']; 168*76a74c23SAnna Dabrowska $s = "UPDATE data_$name SET col$colno = '[". '""' . ",'||col$colno||']' WHERE col$colno != ''"; 169*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 170*76a74c23SAnna Dabrowska if (!$ok) return false; 171*76a74c23SAnna Dabrowska 172*76a74c23SAnna Dabrowska // multi_ 173*76a74c23SAnna Dabrowska $s = "UPDATE multi_$name SET value = '[". '""' . ",'||value||']' WHERE colref=$colno"; 174*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 175*76a74c23SAnna Dabrowska if (!$ok) return false; 176*76a74c23SAnna Dabrowska } 177*76a74c23SAnna Dabrowska } 178*76a74c23SAnna Dabrowska } 179*76a74c23SAnna Dabrowska 180*76a74c23SAnna Dabrowska // Step 3: delete temp_* tables 181*76a74c23SAnna Dabrowska foreach($tables as $table) { 182*76a74c23SAnna Dabrowska $name = $table['name']; 183*76a74c23SAnna Dabrowska $s = "DROP TABLE temp_$name"; 184*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 185*76a74c23SAnna Dabrowska if (!$ok) return false; 186*76a74c23SAnna Dabrowska } 187*76a74c23SAnna Dabrowska 188*76a74c23SAnna Dabrowska // Step 4: remove islookup in schemas table 189*76a74c23SAnna Dabrowska $sql = "SELECT sql FROM sqlite_master 190*76a74c23SAnna Dabrowska WHERE type = 'table' 191*76a74c23SAnna Dabrowska AND name = 'schemas'"; 192*76a74c23SAnna Dabrowska $res = $sqlite->query($sql); 193*76a74c23SAnna Dabrowska $t = $sqlite->res2arr($res); 194*76a74c23SAnna Dabrowska $sql = $t[0]['sql']; 195*76a74c23SAnna Dabrowska $sql = str_replace('islookup INTEGER,', '', $sql); 196*76a74c23SAnna Dabrowska 197*76a74c23SAnna Dabrowska $s = 'ALTER TABLE schemas RENAME TO temp_schemas'; 198*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 199*76a74c23SAnna Dabrowska if (!$ok) return false; 200*76a74c23SAnna Dabrowska 201*76a74c23SAnna Dabrowska // create a new table without islookup 202*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($sql); 203*76a74c23SAnna Dabrowska if (!$ok) return false; 204*76a74c23SAnna Dabrowska 205*76a74c23SAnna Dabrowska $s = 'INSERT INTO schemas SELECT id, tbl, ts, user, comment, config FROM temp_schemas'; 206*76a74c23SAnna Dabrowska $ok = $ok && $sqlite->query($s); 207*76a74c23SAnna Dabrowska 208*76a74c23SAnna Dabrowska if (!$ok) { 209*76a74c23SAnna Dabrowska $sqlite->query('ROLLBACK TRANSACTION'); 210*76a74c23SAnna Dabrowska return false; 211*76a74c23SAnna Dabrowska } 212*76a74c23SAnna Dabrowska $sqlite->query('COMMIT TRANSACTION'); 213*76a74c23SAnna Dabrowska return true; 214*76a74c23SAnna Dabrowska } 2158f259467SAndreas Gohr} 216