Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
Class.VaultAnalyzer.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Anakeen
4  * @package FDL
5 */
6 
7 namespace Dcp\Vault;
8 
11 
12 require_once 'WHAT/Lib.Common.php';
13 
15 {
16 }
17 
19 {
20  const STMT_DELETE_ID_FILE = 'delete_id_file';
21  const STMT_INSERT_TMP = 'insert_tmp';
22 
23  protected static $savePointSeq = 0;
24  protected $verbose = true;
25 
26  protected $_conn = null;
27 
28  public function __construct()
29  {
30  $this->sqlConnect();
31  }
32  /**
33  * @param boolean $verbose
34  */
35  public function setVerbose($verbose)
36  {
37  $this->verbose = $verbose;
38  }
39  public function summary()
40  {
41  $report = array();
42 
43  $res = $this->sqlQuery('SELECT count(id_file) AS count, sum(size) AS size, pg_size_pretty(sum(size)) AS size_pretty FROM vaultdiskstorage');
44  $t = pg_fetch_array($res, NULL, PGSQL_ASSOC);
45  if ($t === false) {
46  throw new VaultAnalyzerException(pg_last_error($this->_conn));
47  }
48  $report['all'] = $t;
49 
50  $res = $this->sqlQuery('SELECT count(id_file) AS count, sum(size) AS size, pg_size_pretty(sum(size)) AS size_pretty FROM vaultdiskstorage WHERE NOT EXISTS (SELECT 1 FROM docvaultindex WHERE vaultid = id_file)');
51  $t = pg_fetch_array($res, NULL, PGSQL_ASSOC);
52  if ($t === false) {
53  throw new VaultAnalyzerException(pg_last_error($this->_conn));
54  }
55  $report['orphan'] = $t;
56 
57  $res = $this->sqlQuery('SELECT count(id_file) AS count, sum(size) AS size, pg_size_pretty(sum(size)) AS size_pretty FROM vaultdiskstorage WHERE EXISTS (SELECT 1 FROM docvaultindex WHERE vaultid = id_file)');
58  $t = pg_fetch_array($res, NULL, PGSQL_ASSOC);
59  if ($t === false) {
60  throw new VaultAnalyzerException(pg_last_error($this->_conn));
61  }
62  $report['used'] = $t;
63 
64  return $report;
65  }
66 
67  public function analyzePhysicalFiles()
68  {
69  $report = array();
70 
71  $query = <<<'EOF'
72 WITH files AS (
73 SELECT id_file, vdfs.r_path AS vault_root, l_path || '/' || vds.id_file ||
74  CASE WHEN name ~ E'\\.[^.]+$' THEN regexp_replace(name, E'.*(\\.[^.]+)$', E'\\1')
75  ELSE '.nop'
76  END AS filename
77 FROM vaultdiskfsstorage AS vdfs, vaultdiskdirstorage AS vdds, vaultdiskstorage AS vds
78 WHERE
79  vds.id_fs = vdfs.id_fs
80  AND vds.id_dir = vdds.id_dir
81  AND vdds.id_fs = vds.id_fs
82 )
83 SELECT files.*, dvi.* FROM files LEFT OUTER JOIN docvaultindex AS dvi ON vaultid = id_file
84 ORDER BY id_file, docid
85 ;
86 EOF;
87 
88  $res = $this->sqlQuery($query);
89  $count = pg_num_rows($res);
90  if ($count <= - 1) {
91  throw new VaultAnalyzerException("Invalid result count '%s'.", $count);
92  }
93 
94  $report['count'] = $count;
95  $report['iterator'] = new PgFetchArrayIterator($res);
96 
97  return $report;
98  }
99 
100  protected function getFamilies()
101  {
102  $set = array();
103  $s = new \SearchDoc('', -1);
104  $s->setObjectReturn(true);
105  $s->setOrder('id');
106  /**
107  * @var \DocFam[] $iterator
108  */
109  $iterator = $this->searchErrors($s);
110  foreach ($iterator as $fam) {
111  /* Fetch file or image attributes */
112  $attrList = array();
113  foreach ($fam->getAttributes() as $attr) {
114  if (!$attr || ($attr->type != 'file' && $attr->type != 'image')) {
115  continue;
116  }
117  if ($attr->usefor != 'Q') {
118  $attrList[] = $attr->id;
119  }
120  }
121  $set[$fam->id] = array(
122  'id' => $fam->id,
123  'name' => $fam->name,
124  'title' => $fam->title,
125  'file_attributes' => $attrList,
126  'vid' => \Dcp\Core\vidExtractor\vidExtractor::getVidsFromDocFam($fam)
127  );
128  }
129  return $set;
130  }
131 
132  protected function searchErrors(\SearchDoc $searchDoc)
133  {
134  $searchDoc->search();
135  if (($err = $searchDoc->searchError()) !== '') {
136  throw new \Dcp\SearchDoc\Exception($err);
137  }
138  return $searchDoc->getDocumentList();
139  }
140 
141  public function checkDocVaultIndex(&$report)
142  {
143  return $this->_regenerateDocVaultIndex(true, $report);
144  }
145 
147  {
148  return $this->_regenerateDocVaultIndex(false, $report);
149  }
150 
151  protected function _regenerateDocVaultIndex($check = true, &$report)
152  {
153  $mode = ($check ? 'Checking' : 'Re-indexing');
154 
155  $point = $this->newPoint();
156  $this->sqlSavePoint($point);
157 
158  $this->verbose("[+] Locking tables...\n");
159  $this->sqlQuery("LOCK TABLE docvaultindex, doc * IN ACCESS EXCLUSIVE MODE");
160  $this->sqlQuery("CREATE TEMPORARY TABLE tmp_docvaultindex (LIKE docvaultindex) ON COMMIT DROP");
161  $this->sqlQuery("CREATE TEMPORARY TABLE tmp2_docvaultindex (LIKE docvaultindex) ON COMMIT DROP");
162  $this->sqlPrepare(self::STMT_INSERT_TMP, "INSERT INTO tmp_docvaultindex(docid, vaultid) VALUES ($1, $2)");
163  $this->verbose("[+] Done.\n");
164 
165  $this->verbose("[+] Analyzing dead entries in docvaultindex...\n");
166  $this->sqlQuery("DELETE FROM docvaultindex WHERE NOT EXISTS (SELECT 1 FROM doc WHERE id = docid)");
167  $this->verbose("|+] Done.\n");
168 
169  $this->verbose("[+] Analyzing families...\n");
170  $families = $this->getFamilies();
171  $this->verbose("[+] Done.\n");
172 
173  $famIndex = 0;
174  foreach ($families as $famid => & $fam) {
175  $famIndex++;
176  $this->verbose("[+] (%d/%d) %s family '%s'...\n", $famIndex, count($families) , $mode, $fam['name']);
177  foreach ($fam['vid'] as $vid) {
178  $this->sqlExec(self::STMT_INSERT_TMP, array(
179  $famid,
180  $vid
181  ));
182  }
183  $this->verbose("[+] Done.\n");
184 
185  $relname = sprintf("doc%d", $famid);
186  $res = $this->sqlQuery(sprintf("SELECT count(id) FROM ONLY %s", pg_escape_identifier($relname)));
187  $row = pg_fetch_row($res, 0);
188  $count = $row[0];
189  unset($res);
190  $addColumns = '';
191  if (count($fam['file_attributes']) > 0) {
192  $addColumns = array();
193  foreach ($fam['file_attributes'] as $attrid) {
194  $addColumns[] = pg_escape_identifier($attrid);
195  }
196  $addColumns = ", " . join(", ", $addColumns);
197  }
198  if ($count <= 0) {
199  continue;
200  }
201  $this->verbose("[+] %s '%d' documents from family '%s'...\n", $mode, $count, $fam['name']);
202  $pom = null;
203  if ($this->verbose) {
204  $pom = (new ConsoleProgressOMeter())->setMax($count)->setInterval(1000)->start();
205  }
206  $rows = new PgFetchArrayIterator($this->sqlQuery(sprintf("SELECT id, icon %s FROM ONLY %s", $addColumns, pg_escape_identifier($relname))));
207  $rowIndex = 0;
208  foreach ($rows as $row) {
209  $rowIndex++;
210  $vidList = \Dcp\Core\vidExtractor\vidExtractor::getVidsFromRawDoc($row, $fam['file_attributes']);
211  foreach ($vidList as $vid) {
212  $this->sqlExec(self::STMT_INSERT_TMP, array(
213  $row['id'],
214  $vid
215  ));
216  }
217  if ($pom) {
218  $pom->progress($rowIndex);
219  }
220  }
221  if ($pom) {
222  $pom->finish();
223  }
224  $this->verbose("[+] Done.\n");
225  }
226  unset($fam);
227 
228  $this->verbose("\n");
229  /* De-duplicate entries */
230  $this->sqlQuery("INSERT INTO tmp2_docvaultindex (docid, vaultid) SELECT DISTINCT ON (docid, vaultid) docid, vaultid FROM tmp_docvaultindex");
231  /* Copy de-duplicated entries to tmp_docvaultindex */
232  $this->sqlQuery("DROP TABLE tmp_docvaultindex");
233  $this->sqlQuery("ALTER TABLE tmp2_docvaultindex RENAME TO tmp_docvaultindex");
234  /* New */
235  $res = $this->sqlQuery("SELECT * FROM tmp_docvaultindex AS d1 WHERE NOT EXISTS (SELECT 1 FROM docvaultindex AS d2 WHERE d2.docid = d1.docid AND d2.vaultid = d1.vaultid) ORDER BY docid, vaultid");
236  $new = array(
237  'count' => pg_num_rows($res) ,
238  'iterator' => new PgFetchArrayIterator($res)
239  );
240  /* Missing */
241  $res = $this->sqlQuery("SELECT * FROM docvaultindex AS d1 WHERE NOT EXISTS (SELECT 1 FROM tmp_docvaultindex AS d2 WHERE d2.docid = d1.docid AND d2.vaultid = d1.vaultid) ORDER BY docid, vaultid");
242  $missing = array(
243  'count' => pg_num_rows($res) ,
244  'iterator' => new PgFetchArrayIterator($res)
245  );
246 
247  $report = array(
248  'new' => $new,
249  'missing' => $missing
250  );
251 
252  if ($check) {
253  $this->sqlRollbackPoint($point);
254  return ($report['new']['count'] == 0 && $report['missing']['count'] == 0);
255  } else {
256  /* Reset content of docvaultindex with new content from tmp_docvaultindex */
257  $this->verbose("[+] Committing docvaultindex...\n");
258  $this->sqlQuery("DELETE FROM docvaultindex");
259  $this->sqlQuery("INSERT INTO docvaultindex (docid, vaultid) SELECT docid, vaultid FROM tmp_docvaultindex");
260  $this->sqlQuery("DROP TABLE tmp_docvaultindex");
261  $this->sqlCommitPoint($point);
262  $this->verbose("[+] Done.\n");
263  }
264  return true;
265  }
266 
267  protected function verbose($format)
268  {
269  if ($this->verbose) {
270  call_user_func_array("printf", func_get_args());
271  }
272  }
273 
274  public function cleanDocVaultIndex()
275  {
276  $report = array();
277 
278  $res = $this->sqlQuery("DELETE FROM docvaultindex WHERE NOT EXISTS (SELECT 1 FROM doc WHERE id = docid) RETURNING *");
279  $count = pg_num_rows($res);
280 
281  $report['count'] = $count;
282  $report['iterator'] = new PgFetchArrayIterator($res);
283 
284  return $report;
285  }
286 
287  public function deleteIdFile($vid)
288  {
289  $report = array();
290 
291  $res = $this->sqlExec(self::STMT_DELETE_ID_FILE, array(
292  $vid
293  ));
294  $count = pg_num_rows($res);
295 
296  $report['count'] = $count;
297  $report['iterator'] = new PgFetchArrayIterator($res);
298 
299  return $report;
300  }
301 
302  public function analyzeOrphans()
303  {
304  $report = array();
305 
306  $query = <<<'EOF'
307 SELECT
308  id_file, vdfs.r_path AS vault_root, l_path || '/' || vds.id_file ||
309  CASE WHEN name ~ E'\\.[^.]+$' THEN regexp_replace(name, E'.*(\\.[^.]+)$', E'\\1')
310  ELSE '.nop'
311  END AS filename
312  FROM vaultdiskfsstorage AS vdfs, vaultdiskdirstorage AS vdds, vaultdiskstorage AS vds
313  WHERE
314  vds.id_fs = vdfs.id_fs
315  AND vds.id_dir = vdds.id_dir
316  AND vdds.id_fs = vds.id_fs
317  AND NOT EXISTS (SELECT 1 FROM docvaultindex WHERE vaultid = id_file)
318 ;
319 EOF;
320 
321  $res = $this->sqlQuery($query);
322  $count = pg_num_rows($res);
323  if ($count <= - 1) {
324  throw new VaultAnalyzerException("Invalid result count '%s'.", $count);
325  }
326 
327  $report['count'] = $count;
328  $report['iterator'] = new PgFetchArrayIterator($res);
329 
330  return $report;
331  }
332 
333  protected function sqlDisconnect()
334  {
335  pg_close($this->_conn);
336  }
337 
338  protected function sqlConnect()
339  {
340  $this->_conn = getDbId('');
341  $this->sqlPrepare(self::STMT_DELETE_ID_FILE, 'DELETE FROM vaultdiskstorage WHERE id_file = $1 RETURNING *');
342  }
343 
344  protected function sqlSavePoint($point)
345  {
346  $o = new \DbObj();
347  if (($err = $o->savePoint($point)) !== '') {
348  throw new VaultAnalyzerException(sprintf("Error in %s: %s", __METHOD__, $err));
349  }
350  }
351 
352  protected function sqlCommitPoint($point)
353  {
354  $o = new \DbObj();
355  if (($err = $o->commitPoint($point)) !== '') {
356  throw new VaultAnalyzerException(sprintf("Error in %s: %s", __METHOD__, $err));
357  }
358  }
359 
360  protected function sqlRollbackPoint($point)
361  {
362  $o = new \DbObj();
363  if (($err = $o->rollbackPoint($point)) !== '') {
364  throw new VaultAnalyzerException(sprintf("Error in %s: %s", __METHOD__, $err));
365  }
366  }
367 
368  protected function newPoint()
369  {
370  return sprintf("%s:%d", __CLASS__, self::$savePointSeq++);
371  }
372 
373  protected function sqlQuery($query)
374  {
375  $res = pg_query($this->_conn, $query);
376  if ($res === false) {
377  throw new VaultAnalyzerException(pg_last_error($this->_conn));
378  }
379  return $res;
380  }
381 
382  protected function sqlCount($query)
383  {
384  $res = pg_query($this->_conn, $query);
385  if ($res === false) {
386  throw new VaultAnalyzerException(pg_last_error($this->_conn));
387  }
388  $row = pg_fetch_row($res);
389  if ($row === false) {
390  throw new VaultAnalyzerException(pg_last_error($this->_conn));
391  }
392  return $row[0];
393  }
394 
395  protected function sqlExec($stmt, $argv)
396  {
397  $res = pg_execute($this->_conn, $stmt, $argv);
398  if ($res === false) {
399  throw new VaultAnalyzerException(pg_last_error($this->_conn));
400  }
401  return $res;
402  }
403 
404  protected function sqlPrepare($stmt, $query)
405  {
406  $res = pg_prepare($this->_conn, $stmt, $query);
407  if ($res === false) {
408  throw new VaultAnalyzerException(pg_last_error($this->_conn));
409  }
410  return $res;
411  }
412 }
searchErrors(\SearchDoc $searchDoc)
print $fam getTitle() $fam name
Exception class use exceptionCode to identifiy correctly exception.
Definition: exceptions.php:19
if($famId) $s
print docid
Definition: migrEnum.php:33
static getVidsFromRawDoc($raw, $fileAttrIdList=null)
_regenerateDocVaultIndex($check=true, &$report)
$rows
Definition: Api/ods2csv.php:23
if($updateExistingTable) $point
Definition: updateclass.php:88
if(is_numeric($parms['famid'])) $attrList
if($dbaccess=="") $o
if(($docid!==0)&&(!is_numeric($docid))) $query
if($file) if($subject==""&&$file) if($subject=="") $err
← centre documentaire © anakeen