Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
system_integrity_foreign_keys.php
1 <?php
26 error_reporting(E_ALL);
27 if ((php_sapi_name() != 'cli')) {
28  trigger_error("You can only run this script from the command line\n", E_USER_ERROR);
29 }
30 
31 $SYSTEM_ROOT = (isset($_SERVER['argv'][1])) ? $_SERVER['argv'][1] : '';
32 if (empty($SYSTEM_ROOT)) {
33  echo "ERROR: You need to supply the path to the System Root as the first argument\n";
34  exit();
35 }
36 
37 if (!is_dir($SYSTEM_ROOT) || !is_readable($SYSTEM_ROOT.'/core/include/init.inc')) {
38  echo "ERROR: Path provided doesn't point to a Matrix installation's System Root. Please provide correct path and try again.\n";
39  exit();
40 }
41 
42 $DELETING_ASSET_TYPE = (isset($_SERVER['argv'][2])) ? $_SERVER['argv'][2] : '';
43 require_once $SYSTEM_ROOT.'/core/include/init.inc';
44 
45 $root_user =& $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('root_user');
46 
47 // log in as root
48 if (!$GLOBALS['SQ_SYSTEM']->setCurrentUser($root_user)) {
49  echo "ERROR: Failed loggin in as root user\n";
50  exit();
51 }
52 
53 define('SQ_FOREIGN_KEY_INTEGRITY_CHECK_OUTPUT_WIDTH', 72);
54 
56 {
57 
58 
65  var $fks = Array(
66  'assetid' => Array(
67  'table' => 'sq_ast',
68  'field' => 'assetid',
69  'match' => Array(
70  'assetid',
71  'created_userid',
72  'updated_userid',
73  'published_userid',
74  'status_changed_userid',
75  'majorid',
76  'minorid',
77  'schemaid',
78  'userid',
79  'roleid',
80  ),
81  ),
82  'treeid' => Array(
83  'table' => 'sq_ast_lnk_tree',
84  'field' => 'treeid',
85  'match' => Array(
86  'treeid',
87  ),
88  ),
89  'attrid' => Array(
90  'table' => 'sq_ast_attr',
91  'field' => 'attrid',
92  'match' => Array(
93  'attrid',
94  'owning_attrid',
95  ),
96  ),
97  'linkid' => Array(
98  'table' => 'sq_ast_lnk',
99  'field' => 'linkid',
100  'match' => Array(
101  'linkid',
102  ),
103 
104  ),
105  'urlid' => Array(
106  'table' => 'sq_ast_url',
107  'field' => 'urlid',
108  'match' => Array(
109  'urlid',
110  'root_urlid',
111  ),
112  ),
113  'type_code' => Array(
114  'table' => 'sq_ast_typ',
115  'match' => Array(
116  'type_code',
117  'inhd_type_code',
118  ),
119  ),
120  );
121 
122 
129  var $tables = Array();
130 
131 
139  {
140  if (!$this->load()) {
141  // todo: trigger_error
142  }
143 
144  }//end Foreign_Key_Integrity_Check()
145 
146 
153  function tables()
154  {
155  return $this->tables;
156 
157  }//end tables()
158 
159 
168  function columns($table)
169  {
170  // if we don't know about this table, die
171  if (!in_array($table, $this->tables())) {
172  return Array();
173  }
174 
175  $db =& $GLOBALS['SQ_SYSTEM']->db;
176  $dbtype = $this->_getDbType();
177  switch ($dbtype) {
178  case 'oci':
179  $sql = "SELECT column_name FROM user_tab_cols WHERE table_name = '".strtoupper($table)."'";
180  break;
181  default:
182  $sql = "select column_name from information_schema.columns WHERE table_name='".$table."'";
183  break;
184  }//end switch
185  $results = MatrixDAL::executeSqlAssoc($sql);
186  $columns = Array();
187  foreach ($results as $column) {
188  $column_name = array_get_index($column, 'column_name', '');
189  if (!empty($column_name)) {
190  $columns[] = strtolower($column_name);
191  }//end if
192  }//end foreach
193 
194  return $columns;
195 
196  }//end columns()
197 
198 
207  function fks($table)
208  {
209  $fks = Array();
210  $columns = $this->columns($table);
211 
212  foreach ($this->fks as $fk => $fk_info) {
213  // get all the names that this foreign key goes by
214  $fk_columns = $fk_info['match'];
215 
216  // these are the defined fks we found in the specified table
217  $match_fks = array_intersect($fk_columns, $columns);
218  if (empty($match_fks)) continue;
219 
220  // build the information into expected format
221  foreach ($match_fks as $fk) {
222  $fks[$fk] = $fk_info;
223  }
224  }
225  return $fks;
226 
227  }//end fks()
228 
229 
236  function load()
237  {
238  $db =& $GLOBALS['SQ_SYSTEM']->db;
239  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db');
240  $dbtype = $this->_getDbType();
241 
242  // load our tables from the database
243  switch ($dbtype) {
244  case 'oci':
245  $sql = "SELECT object_name AS table_name FROM user_objects WHERE object_type = 'TABLE'";
246  break;
247  default:
248  $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'";
249  break;
250  }//end switch
251  $results = MatrixDAL::executeSqlAssoc($sql);
252  $tables = Array();
253  foreach ($results as $table) {
254  $table_name = array_get_index($table, 'table_name', '');
255  if (!empty($table_name)) {
256  $tables[] = strtolower($table_name);
257  }//end if
258  }//end foreach
259 
260  $this->tables = $tables;
261  if (empty($this->tables)) {
262  return FALSE;
263  }
264  return TRUE;
265 
266  }//end load()
267 
268 
275  function checkDatabase()
276  {
277  // we're going to check each table in the database
278  $tables = $this->tables();
279 
280  foreach ($tables as $table) {
281  if (substr($table, 0, 6) != 'sq_ast') {
282  continue;
283  }
284  $this->checkTable($table);
285  }
286 
287  }//end checkDatabase()
288 
289 
298  function checkTable($table)
299  {
300  $db =& $GLOBALS['SQ_SYSTEM']->db;
301  $dbtype = $this->_getDbType();
302 
303  // we're going to check these keys
304  $fks = $this->fks($table);
305  if (empty($fks)) {
306  return TRUE;
307  }
308  $this->printTable($table);
309 
310  foreach ($fks as $fk_field => $fk_info) {
311  // query db for invalid foreign keys
312  switch($fk_field) {
313  case 'treeid':
314  case 'type_code':
315  case 'inhd_type_code':
316  switch ($dbtype) {
317  case 'oci':
318  $where_cond = $fk_field." > ''";
319  break;
320  default:
321  $where_cond = $fk_field."::text > ''";
322  }//end switch
323  break;
324  default:
325  switch ($dbtype) {
326  case 'oci':
327  $where_cond = 'CAST ('.$fk_field." as int) > 0";
328  break;
329  default:
330  $where_cond = $fk_field."::int > 0";
331  }//end switch
332  }//end switch
333  $sub_sql = 'SELECT '.$fk_field.' FROM '.$fk_info['table'];
334  $sql = 'SELECT '.$fk_field.' FROM '.$table.' WHERE '.$where_cond.' AND '.$fk_field.' NOT IN ('.$sub_sql.')';
335  $assetids = MatrixDAL::executeSqlAssoc($sql, 0);
336 
337  $broken_count = count($assetids);
338  if (!is_array($assetids)) {
339  $broken_count = 0;
340  }
341 
342  $this->printColumn($fk_info['table'], $fk_field, $broken_count);
343 
344  if (is_array($assetids) && !empty($assetids)) {
345  $this->printResult($assetids);
346  }
347  }//end foreach
348  echo "\n";
349 
350  }//end checkTable()
351 
352 
361  function printTable($table)
362  {
363  echo "> checking table $table...\n\n";
364 
365  }//end printTable()
366 
367 
378  function printColumn($table, $column, $count)
379  {
380  $column_text = "$table.$column";
381  $count_text = "$count found";
382 
383  echo $column_text;
384  echo str_repeat(' ', (SQ_FOREIGN_KEY_INTEGRITY_CHECK_OUTPUT_WIDTH - strlen($column_text) - strlen($count_text)));
385  echo $count_text."\n";
386 
387  }//end printColumn()
388 
389 
398  function printResult($assetids)
399  {
400  echo "\t".'\''.implode('\', \'', $assetids).'\''."\n";
401 
402  }//end printResult
403 
404 
411  private function _getDbType()
412  {
413  $dbtype = MatrixDAL::GetDbType();
414 
415  if ($dbtype instanceof PDO) {
416  $dbtype = $dbtype->getAttribute(PDO::ATTR_DRIVER_NAME);
417  }
418  return strtolower($dbtype);
419 
420  }//end _getDbType()
421 
422 
423 }//end class
424 
425 
426 $foreign_key_checker = new Foreign_Key_Integrity_Check();
427 $foreign_key_checker->checkDatabase();
428 
429 ?>