1fb31ca9fSAndreas Gohr<?php 2fb31ca9fSAndreas Gohr 3fb31ca9fSAndreas Gohrnamespace plugin\struct\meta; 4fb31ca9fSAndreas Gohr 51a8d1235SAndreas Gohr/** 61a8d1235SAndreas Gohr * Class Assignments 71a8d1235SAndreas Gohr * 81a8d1235SAndreas Gohr * Manages the assignment of schemas (table names) to pages and namespaces 91a8d1235SAndreas Gohr * 101a8d1235SAndreas Gohr * @package plugin\struct\meta 111a8d1235SAndreas Gohr */ 12fb31ca9fSAndreas Gohrclass Assignments { 13fb31ca9fSAndreas Gohr 14fb31ca9fSAndreas Gohr /** @var \helper_plugin_sqlite|null */ 15fb31ca9fSAndreas Gohr protected $sqlite; 16fb31ca9fSAndreas Gohr 1733d7be6aSAndreas Gohr /** @var array All the assignments patterns */ 1849d38573SAndreas Gohr protected $patterns; 19fb31ca9fSAndreas Gohr 20fb31ca9fSAndreas Gohr /** 21fb31ca9fSAndreas Gohr * Assignments constructor. 22fb31ca9fSAndreas Gohr */ 23fb31ca9fSAndreas Gohr public function __construct() { 24fb31ca9fSAndreas Gohr /** @var \helper_plugin_struct_db $helper */ 25fb31ca9fSAndreas Gohr $helper = plugin_load('helper', 'struct_db'); 26fb31ca9fSAndreas Gohr $this->sqlite = $helper->getDB(); 27fb31ca9fSAndreas Gohr 2833d7be6aSAndreas Gohr if($this->sqlite) $this->loadPatterns(); 29fb31ca9fSAndreas Gohr } 30fb31ca9fSAndreas Gohr 31fb31ca9fSAndreas Gohr /** 3249d38573SAndreas Gohr * Load existing assignment patterns 33fb31ca9fSAndreas Gohr */ 3433d7be6aSAndreas Gohr protected function loadPatterns() { 3549d38573SAndreas Gohr $sql = 'SELECT * FROM schema_assignments_patterns ORDER BY pattern'; 36fb31ca9fSAndreas Gohr $res = $this->sqlite->query($sql); 3749d38573SAndreas Gohr $this->patterns = $this->sqlite->res2arr($res); 38fb31ca9fSAndreas Gohr $this->sqlite->res_close($res); 39fb31ca9fSAndreas Gohr } 40fb31ca9fSAndreas Gohr 41fb31ca9fSAndreas Gohr /** 4249d38573SAndreas Gohr * Add a new assignment pattern to the pattern table 431a8d1235SAndreas Gohr * 4449d38573SAndreas Gohr * @param string $pattern 451a8d1235SAndreas Gohr * @param string $table 461a8d1235SAndreas Gohr * @return bool 471a8d1235SAndreas Gohr */ 4833d7be6aSAndreas Gohr public function addPattern($pattern, $table) { 49ed60c3b3SAndreas Gohr // add the pattern 5049d38573SAndreas Gohr $sql = 'REPLACE INTO schema_assignments_patterns (pattern, tbl) VALUES (?,?)'; 51ed60c3b3SAndreas Gohr $ok = (bool) $this->sqlite->query($sql, array($pattern, $table)); 52ed60c3b3SAndreas Gohr 53ed60c3b3SAndreas Gohr // reload patterns 54ed60c3b3SAndreas Gohr $this->loadPatterns(); 55ed60c3b3SAndreas Gohr 56ed60c3b3SAndreas Gohr // fetch all pages where the schema isn't assigned, yet 57ed60c3b3SAndreas Gohr $sql = 'SELECT pid FROM schema_assignments WHERE tbl != ? OR assigned != 1'; 58ed60c3b3SAndreas Gohr $res = $this->sqlite->query($sql, $table); 59*0e9e058fSAndreas Gohr $pagerows = $this->sqlite->res2arr($res); 60ed60c3b3SAndreas Gohr $this->sqlite->res_close($res); 61ed60c3b3SAndreas Gohr 62ed60c3b3SAndreas Gohr // reevalute the pages and assign when needed 63*0e9e058fSAndreas Gohr foreach($pagerows as $row) { 64*0e9e058fSAndreas Gohr $tables = $this->getPageAssignments($row['pid'], true); 65ed60c3b3SAndreas Gohr if(in_array($table, $tables)) { 66*0e9e058fSAndreas Gohr $this->assignPageSchema($row['pid'], $table); 67ed60c3b3SAndreas Gohr } 68ed60c3b3SAndreas Gohr } 69ed60c3b3SAndreas Gohr 70ed60c3b3SAndreas Gohr return $ok; 711a8d1235SAndreas Gohr } 721a8d1235SAndreas Gohr 731a8d1235SAndreas Gohr /** 7449d38573SAndreas Gohr * Remove an existing assignment pattern from the pattern table 751a8d1235SAndreas Gohr * 7649d38573SAndreas Gohr * @param string $pattern 771a8d1235SAndreas Gohr * @param string $table 781a8d1235SAndreas Gohr * @return bool 791a8d1235SAndreas Gohr */ 8033d7be6aSAndreas Gohr public function removePattern($pattern, $table) { 81ed60c3b3SAndreas Gohr // remove the pattern 8249d38573SAndreas Gohr $sql = 'DELETE FROM schema_assignments_patterns WHERE pattern = ? AND tbl = ?'; 83ed60c3b3SAndreas Gohr $ok = (bool) $this->sqlite->query($sql, array($pattern, $table)); 84ed60c3b3SAndreas Gohr 85ed60c3b3SAndreas Gohr // reload patterns 86ed60c3b3SAndreas Gohr $this->loadPatterns(); 87ed60c3b3SAndreas Gohr 88ed60c3b3SAndreas Gohr // fetch possibly affected pages 89ed60c3b3SAndreas Gohr $sql = 'SELECT pid FROM schema_assignments WHERE tbl = ?'; 90ed60c3b3SAndreas Gohr $res = $this->sqlite->query($sql, $table); 91*0e9e058fSAndreas Gohr $pagerows = $this->sqlite->res2arr($res); 92ed60c3b3SAndreas Gohr $this->sqlite->res_close($res); 93ed60c3b3SAndreas Gohr 94ed60c3b3SAndreas Gohr // reevalute the pages and unassign when needed 95*0e9e058fSAndreas Gohr foreach($pagerows as $row) { 96be94e9d9SAndreas Gohr $tables = $this->getPageAssignments($row['pid'], true); 97ed60c3b3SAndreas Gohr if(!in_array($table, $tables)) { 98be94e9d9SAndreas Gohr $this->deassignPageSchema($row['pid'], $table); 99ed60c3b3SAndreas Gohr } 100ed60c3b3SAndreas Gohr } 101ed60c3b3SAndreas Gohr 102ed60c3b3SAndreas Gohr return $ok; 103ed60c3b3SAndreas Gohr } 104ed60c3b3SAndreas Gohr 105ed60c3b3SAndreas Gohr /** 106*0e9e058fSAndreas Gohr * Clear all patterns - deassigns all pages 107*0e9e058fSAndreas Gohr * 108*0e9e058fSAndreas Gohr * This is mostly useful for testing and not used in the interface currently 109*0e9e058fSAndreas Gohr * 110*0e9e058fSAndreas Gohr * @return bool 111*0e9e058fSAndreas Gohr */ 112*0e9e058fSAndreas Gohr public function clear() { 113*0e9e058fSAndreas Gohr $sql = 'DELETE FROM schema_assignments_patterns'; 114*0e9e058fSAndreas Gohr $ok = (bool) $this->sqlite->query($sql); 115*0e9e058fSAndreas Gohr 116*0e9e058fSAndreas Gohr $sql = 'UPDATE schema_assignments SET assigned = 0'; 117*0e9e058fSAndreas Gohr $ok = $ok && (bool) $this->sqlite->query($sql); 118*0e9e058fSAndreas Gohr 119*0e9e058fSAndreas Gohr // reload patterns 120*0e9e058fSAndreas Gohr $this->loadPatterns(); 121*0e9e058fSAndreas Gohr 122*0e9e058fSAndreas Gohr return $ok; 123*0e9e058fSAndreas Gohr } 124*0e9e058fSAndreas Gohr 125*0e9e058fSAndreas Gohr /** 126ed60c3b3SAndreas Gohr * Add page to assignments 127ed60c3b3SAndreas Gohr * 128ed60c3b3SAndreas Gohr * @param string $page 129ed60c3b3SAndreas Gohr * @param string $table 130ed60c3b3SAndreas Gohr * @return bool 131ed60c3b3SAndreas Gohr */ 132*0e9e058fSAndreas Gohr protected function assignPageSchema($page, $table) { 133ed60c3b3SAndreas Gohr $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 1)'; 134ed60c3b3SAndreas Gohr return (bool) $this->sqlite->query($sql, array($page, $table)); 135ed60c3b3SAndreas Gohr } 136ed60c3b3SAndreas Gohr 137ed60c3b3SAndreas Gohr /** 138ed60c3b3SAndreas Gohr * Remove page from assignments 139ed60c3b3SAndreas Gohr * 140ed60c3b3SAndreas Gohr * @param string $page 141ed60c3b3SAndreas Gohr * @param string $table 142ed60c3b3SAndreas Gohr * @return bool 143ed60c3b3SAndreas Gohr */ 144*0e9e058fSAndreas Gohr protected function deassignPageSchema($page, $table) { 145ed60c3b3SAndreas Gohr $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 0)'; 146ed60c3b3SAndreas Gohr return (bool) $this->sqlite->query($sql, array($page, $table)); 1471a8d1235SAndreas Gohr } 1481a8d1235SAndreas Gohr 1491a8d1235SAndreas Gohr /** 15049d38573SAndreas Gohr * Get the whole pattern table 1511a8d1235SAndreas Gohr * 1521a8d1235SAndreas Gohr * @return array 1531a8d1235SAndreas Gohr */ 15433d7be6aSAndreas Gohr public function getAllPatterns() { 15549d38573SAndreas Gohr return $this->patterns; 1561a8d1235SAndreas Gohr } 1571a8d1235SAndreas Gohr 1581a8d1235SAndreas Gohr /** 159fb31ca9fSAndreas Gohr * Returns a list of table names assigned to the given page 160fb31ca9fSAndreas Gohr * 161fb31ca9fSAndreas Gohr * @param string $page 1629ff81b7fSAndreas Gohr * @param bool $checkpatterns Should the current patterns be re-evaluated? 1639ff81b7fSAndreas Gohr * @return \string[] tables assigned 164fb31ca9fSAndreas Gohr */ 1659ff81b7fSAndreas Gohr public function getPageAssignments($page, $checkpatterns=true) { 166fb31ca9fSAndreas Gohr $tables = array(); 167fb31ca9fSAndreas Gohr $page = cleanID($page); 168fb31ca9fSAndreas Gohr 1699ff81b7fSAndreas Gohr if($checkpatterns) { 1709ff81b7fSAndreas Gohr // evaluate patterns 1719ff81b7fSAndreas Gohr $pns = ':' . getNS($page) . ':'; 17249d38573SAndreas Gohr foreach($this->patterns as $row) { 173ed60c3b3SAndreas Gohr if($this->matchPagePattern($row['pattern'], $page, $pns)) { 174ed60c3b3SAndreas Gohr $tables[] = $row['tbl']; 175fb31ca9fSAndreas Gohr } 176fb31ca9fSAndreas Gohr } 1779ff81b7fSAndreas Gohr } else { 1789ff81b7fSAndreas Gohr // just select 1799ff81b7fSAndreas Gohr $sql = 'SELECT tbl FROM schema_assignments WHERE pid = ? AND assigned = 1'; 1809ff81b7fSAndreas Gohr $res = $this->sqlite->query($sql, array($page)); 1819ff81b7fSAndreas Gohr $list = $this->sqlite->res2arr($res); 1829ff81b7fSAndreas Gohr $this->sqlite->res_close($res); 1839ff81b7fSAndreas Gohr foreach($list as $row) { 1849ff81b7fSAndreas Gohr $tables[] = $row['tbl']; 1859ff81b7fSAndreas Gohr } 1869ff81b7fSAndreas Gohr } 187fb31ca9fSAndreas Gohr 188fb31ca9fSAndreas Gohr return array_unique($tables); 189fb31ca9fSAndreas Gohr } 19056672c36SAndreas Gohr 19156672c36SAndreas Gohr /** 192ed60c3b3SAndreas Gohr * Check if the given pattern matches the given page 193ed60c3b3SAndreas Gohr * 194ed60c3b3SAndreas Gohr * @param string $pattern the pattern to check against 195ed60c3b3SAndreas Gohr * @param string $page the cleaned pageid to check 196ed60c3b3SAndreas Gohr * @param string|null $pns optimization, the colon wrapped namespace of the page, set null for automatic 197ed60c3b3SAndreas Gohr * @return bool 198ed60c3b3SAndreas Gohr */ 199ed60c3b3SAndreas Gohr protected function matchPagePattern($pattern, $page, $pns = null) { 200*0e9e058fSAndreas Gohr if(trim($pattern,':') == '**') return true; // match all 201*0e9e058fSAndreas Gohr 202ed60c3b3SAndreas Gohr if(is_null($pns)) { 203ed60c3b3SAndreas Gohr $pns = ':' . getNS($page) . ':'; 204ed60c3b3SAndreas Gohr } 205ed60c3b3SAndreas Gohr 206ed60c3b3SAndreas Gohr $ans = ':' . cleanID($pattern) . ':'; 207ed60c3b3SAndreas Gohr 208ed60c3b3SAndreas Gohr if(substr($pattern, -2) == '**') { 209ed60c3b3SAndreas Gohr // upper namespaces match 210ed60c3b3SAndreas Gohr if(strpos($pns, $ans) === 0) { 211ed60c3b3SAndreas Gohr return true; 212ed60c3b3SAndreas Gohr } 213ed60c3b3SAndreas Gohr } else if(substr($pattern, -1) == '*') { 214ed60c3b3SAndreas Gohr // namespaces match exact 215ed60c3b3SAndreas Gohr if($ans == $pns) { 216ed60c3b3SAndreas Gohr return true; 217ed60c3b3SAndreas Gohr } 218ed60c3b3SAndreas Gohr } else { 219ed60c3b3SAndreas Gohr // exact match 220ed60c3b3SAndreas Gohr if(cleanID($pattern) == $page) { 221ed60c3b3SAndreas Gohr return true; 222ed60c3b3SAndreas Gohr } 223ed60c3b3SAndreas Gohr } 224ed60c3b3SAndreas Gohr 225ed60c3b3SAndreas Gohr return false; 226ed60c3b3SAndreas Gohr } 227ed60c3b3SAndreas Gohr 228ed60c3b3SAndreas Gohr /** 22956672c36SAndreas Gohr * Returns all tables of schemas that existed and stored data for the page back then 23056672c36SAndreas Gohr * 231*0e9e058fSAndreas Gohr * @deprecated because we're always only interested in the current state of affairs, even when restoring. 23256672c36SAndreas Gohr * 23356672c36SAndreas Gohr * @param string $page 23456672c36SAndreas Gohr * @param string $ts 23556672c36SAndreas Gohr * @return array 23656672c36SAndreas Gohr */ 23756672c36SAndreas Gohr public function getHistoricAssignments($page, $ts) { 23856672c36SAndreas Gohr $sql = "SELECT DISTINCT tbl FROM schemas WHERE ts <= ? ORDER BY ts DESC"; 23956672c36SAndreas Gohr $res = $this->sqlite->query($sql, $ts); 24056672c36SAndreas Gohr $tables = $this->sqlite->res2arr($res); 24156672c36SAndreas Gohr $this->sqlite->res_close($res); 24256672c36SAndreas Gohr 24356672c36SAndreas Gohr $assigned = array(); 24456672c36SAndreas Gohr foreach($tables as $row) { 24556672c36SAndreas Gohr $table = $row['tbl']; 246ed60c3b3SAndreas Gohr /** @noinspection SqlResolve */ 24756672c36SAndreas Gohr $sql = "SELECT pid FROM data_$table WHERE pid = ? AND rev <= ? LIMIT 1"; 24856672c36SAndreas Gohr $res = $this->sqlite->query($sql, $page, $ts); 24956672c36SAndreas Gohr $found = $this->sqlite->res2arr($res); 25056672c36SAndreas Gohr $this->sqlite->res_close($res); 25156672c36SAndreas Gohr 25256672c36SAndreas Gohr if($found) $assigned[] = $table; 25356672c36SAndreas Gohr } 25456672c36SAndreas Gohr 25556672c36SAndreas Gohr return $assigned; 25656672c36SAndreas Gohr } 257fb31ca9fSAndreas Gohr} 258