FOSSology  4.6.0
Open Source License Compliance by Open Source Software
UploadTreeProxy.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2015 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
8 namespace Fossology\Lib\Proxy;
9 
17 
19 {
20  const OPT_SKIP_THESE = 'skipThese';
21  const OPT_ITEM_FILTER = 'ut.filter';
22  const OPT_GROUP_ID = 'groupId';
23  const OPT_REALPARENT = 'realParent';
24  const OPT_RANGE = 'lft,rgt';
25  const OPT_EXT = 'ext';
26  const OPT_HEAD = 'head';
27  const OPT_AGENT_SET = 'agentArray';
28  const OPT_SCAN_REF = 'scanRef';
29  const OPT_CONCLUDE_REF = 'conRef';
30  const OPT_SKIP_ALREADY_CLEARED = 'alreadyCleared';
31 
33  private $uploadTreeTableName;
35  private $uploadId;
37  private $params = array();
38 
44  public function __construct($uploadId, $options, $uploadTreeTableName, $uploadTreeViewName=null)
45  {
46  $this->uploadId = $uploadId;
47  $this->uploadTreeTableName = $uploadTreeTableName;
48  $dbViewName = $uploadTreeViewName ?: 'UploadTreeView'.(isset($this->dbViewName) ?: '');
49  $dbViewQuery = $this->createUploadTreeViewQuery($options, $uploadTreeTableName);
50  parent::__construct($dbViewQuery, $dbViewName);
51  }
52 
56  public function getUploadTreeTableName()
57  {
58  return $this->uploadTreeTableName;
59  }
60 
66  public function createUploadTreeViewQuery($options, $uploadTreeTableName)
67  {
68  if (empty($options)) {
69  return self::getDefaultUploadTreeView($this->uploadId, $uploadTreeTableName);
70  }
71 
72  $filter = '';
73  $this->dbViewName = '';
74  /* @var $uploadDao UploadDao */
75  $uploadDao = $GLOBALS['container']->get('dao.upload');
76  $applyGlobal = $uploadDao->getGlobalDecisionSettingsFromInfo($this->uploadId);
77  if ($applyGlobal == 1) {
78  $applyGlobal = true;
79  } else {
80  $applyGlobal = false;
81  }
82 
83  if (array_key_exists(self::OPT_REALPARENT, $options)) {
84  $filter .= " AND ut.ufile_mode & (1<<28) = 0 AND ut.realparent=".$this->addParamAndGetExpr('realParent',$options[self::OPT_REALPARENT]);
85  $this->dbViewName .= "_".self::OPT_REALPARENT;
86  } elseif (array_key_exists(self::OPT_RANGE,$options)) {
87  $itemBounds = $options[self::OPT_RANGE];
88  $filter .= " AND ut.ufile_mode & (3<<28) = 0 AND (ut.lft BETWEEN ".$this->addParamAndGetExpr('lft',$itemBounds->getLeft()).
89  " AND ".$this->addParamAndGetExpr('rgt',$itemBounds->getRight()).")";
90  $this->dbViewName .= "_".self::OPT_RANGE;
91  }
92 
93  if (array_key_exists(self::OPT_EXT, $options)) {
94  $filter .= " AND ufile_name ILIKE ".$this->addParamAndGetExpr('patternExt','%.'.$options[self::OPT_EXT]);
95  $this->dbViewName .= "_".self::OPT_EXT;
96  }
97 
98  if (array_key_exists(self::OPT_HEAD, $options)) {
99  $filter .= " AND ufile_name ILIKE ".$this->addParamAndGetExpr('patternHead',$options[self::OPT_HEAD].'%');
100  $this->dbViewName .= "_".self::OPT_HEAD;
101  }
102 
103  if (array_key_exists(self::OPT_SCAN_REF,$options)) {
104  $filter .= $this->addScanFilter($options);
105  }
106 
107  if (array_key_exists(self::OPT_CONCLUDE_REF, $options) && array_key_exists(self::OPT_GROUP_ID, $options)) {
108  $filter .= $this->addConFilter($options);
109  }
110 
111  if (array_key_exists(self::OPT_SKIP_ALREADY_CLEARED, $options) && array_key_exists(self::OPT_GROUP_ID, $options)
112  && array_key_exists(self::OPT_AGENT_SET, $options)) {
113  $agentIdSet = '{' . implode(',', array_values($options[self::OPT_AGENT_SET])) . '}';
114  $agentFilter = " AND agent_fk=ANY(".$this->addParamAndGetExpr('agentIdSet', $agentIdSet).")";
115  $this->dbViewName .= "_".self::OPT_SKIP_ALREADY_CLEARED;
116  $groupAlias = $this->addParamAndGetExpr('groupId', $options[self::OPT_GROUP_ID]);
117  if (array_key_exists(self::OPT_RANGE, $options)) {
118  $filter .= ' AND '.self::getQueryCondition(self::OPT_SKIP_ALREADY_CLEARED, $options, $groupAlias, $agentFilter, $applyGlobal);
119  } elseif (array_key_exists(self::OPT_SKIP_ALREADY_CLEARED, $options) && array_key_exists(self::OPT_GROUP_ID, $options)
120  && array_key_exists(self::OPT_AGENT_SET, $options) && array_key_exists(self::OPT_REALPARENT, $options)) {
121  $childFilter = self::getQueryCondition(self::OPT_SKIP_ALREADY_CLEARED, $options, $groupAlias, $agentFilter, $applyGlobal);
122  $filter .= ' AND EXISTS(SELECT * FROM '.$this->uploadTreeTableName.' utc WHERE utc.upload_fk='.$this->uploadId
123  . ' AND (utc.lft BETWEEN ut.lft AND ut.rgt) AND utc.ufile_mode&(3<<28)=0 AND '
124  .preg_replace('/([a-z])ut\./', '\1utc.', $childFilter).')';
125  }
126  }
127 
128  if (array_key_exists(self::OPT_ITEM_FILTER, $options)) {
129  $filter .= ' '.$options[self::OPT_ITEM_FILTER];
130  $this->dbViewName .= "_".md5($options[self::OPT_ITEM_FILTER]);
131  }
132  $options[self::OPT_ITEM_FILTER] = $filter;
133  return self::getUploadTreeView($this->uploadId, $options, $uploadTreeTableName, $applyGlobal);
134  }
135 
136  private function addConFilter($options)
137  {
138  $filter = '';
139  if (array_key_exists(self::OPT_REALPARENT, $options)) {
140  $filter .=" AND EXISTS(SELECT * FROM ".$this->uploadTreeTableName." usub"
141  . " WHERE (usub.lft BETWEEN ut.lft AND ut.rgt) AND upload_fk=".$this->uploadId
142  . " AND ".$this->subqueryConcludeRefMatches('usub', $options) . ")";
143  $this->dbViewName .= "_".self::OPT_CONCLUDE_REF;
144  } elseif (array_key_exists(self::OPT_RANGE, $options)) {
145  $filter.= " AND ".$this->subqueryConcludeRefMatches('ut', $options);
146  $this->dbViewName .= "_".self::OPT_CONCLUDE_REF;
147  }
148  return $filter;
149  }
150 
151  private function addScanFilter($options)
152  {
153  $this->dbViewName .= "_".self::OPT_SCAN_REF;
154  if (array_key_exists(self::OPT_AGENT_SET, $options)) {
155  $this->dbViewName .= "_".self::OPT_AGENT_SET;
156  }
157  if (array_key_exists(self::OPT_REALPARENT, $options)) {
158  return " AND EXISTS(SELECT * FROM ".$this->uploadTreeTableName." usub, "
159  . $this->subqueryLicenseFileMatchWhere($options)
160  . " usub.pfile_fk=license_file.pfile_fk"
161  . " AND (usub.lft BETWEEN ut.lft AND ut.rgt) AND upload_fk=".$this->uploadId.")";
162  }
163  if (array_key_exists(self::OPT_RANGE, $options)) {
164  return " AND EXISTS(SELECT * FROM " . $this->subqueryLicenseFileMatchWhere($options)
165  . " ut.pfile_fk=license_file.pfile_fk)";
166  }
167  }
168 
169  private function subqueryLicenseFileMatchWhere($options)
170  {
171  $filter = " license_file LEFT JOIN license_map ON license_file.rf_fk=license_map.rf_fk"
172  . " AND usage=".LicenseMap::CONCLUSION." WHERE";
173  if (array_key_exists(self::OPT_AGENT_SET, $options)) {
174  $agentIdSet = '{' . implode(',', array_values($options[self::OPT_AGENT_SET])) . '}';
175  $filter .= " agent_fk=ANY(".$this->addParamAndGetExpr('agentIdSet', $agentIdSet).") AND";
176  }
177  $rfId = $this->addParamAndGetExpr('scanRef', $options[self::OPT_SCAN_REF]);
178  return $filter . " (license_file.rf_fk=$rfId OR rf_parent=$rfId) AND ";
179  }
180 
181  private function subqueryConcludeRefMatches($itemTable,$options)
182  {
183  $globalSql = "";
184  $orderByGlobal = "";
185  /* @var $uploadDao UploadDao */
186  $uploadDao = $GLOBALS['container']->get('dao.upload');
187  $applyGlobal = $uploadDao->getGlobalDecisionSettingsFromInfo($this->uploadId);
188  if ($applyGlobal == 1) {
189  $applyGlobal = true;
190  } else {
191  $applyGlobal = false;
192  }
193  if ($applyGlobal) {
194  $globalSql = "OR (cd.scope=" . DecisionScopes::REPO .
195  " AND cd.pfile_fk=$itemTable.pfile_fk)";
196  $orderByGlobal = "CASE cd.scope WHEN " . DecisionScopes::REPO .
197  " THEN 1 ELSE 0 END,";
198  }
199  return "NOT(SELECT (removed OR cd.decision_type=".DecisionTypes::IRRELEVANT.") excluded"
200  . " FROM clearing_decision cd, clearing_decision_event cde, clearing_event ce"
201  . " WHERE ((cd.group_fk=".$this->addParamAndGetExpr('groupId', $options[self::OPT_GROUP_ID])
202  . " AND cd.uploadtree_fk=$itemTable.uploadtree_pk)"
203  . $globalSql
204  . ") AND clearing_decision_pk=clearing_decision_fk"
205  . " AND clearing_event_fk=clearing_event_pk"
206  . " AND rf_fk=".$this->addParamAndGetExpr('conId',$options[self::OPT_CONCLUDE_REF])
207  . " AND cd.decision_type!=".DecisionTypes::WIP
208  . " ORDER BY $orderByGlobal cd.date_added DESC LIMIT 1)";
209  }
210 
217  private static function getDefaultUploadTreeView($uploadId, $uploadTreeTableName, $additionalCondition='')
218  {
219  $condition = "";
220  if ('uploadtree' === $uploadTreeTableName || 'uploadtree_a' == $uploadTreeTableName) {
221  $condition = " WHERE ut.upload_fk=$uploadId $additionalCondition";
222  } elseif ($additionalCondition) {
223  $condition = " WHERE 1=1 $additionalCondition";
224  }
225  $uploadTreeView = "SELECT * FROM $uploadTreeTableName ut $condition";
226  return $uploadTreeView;
227  }
228 
236  private static function getUploadTreeView($uploadId, $options, $uploadTreeTableName, $applyGlobal = false)
237  {
238  $additionalCondition = array_key_exists(self::OPT_ITEM_FILTER, $options) ? $options[self::OPT_ITEM_FILTER] : '';
239  $skipThese = array_key_exists(self::OPT_SKIP_THESE,$options) ? $options[self::OPT_SKIP_THESE] : 'none';
240  $groupId = array_key_exists(self::OPT_GROUP_ID, $options) ? $options[self::OPT_GROUP_ID] : null;
241  $agentFilter = self::getAgentFilter($options, $uploadId);
242 
243  switch ($skipThese) {
244  case "noLicense":
245  case "nolicensenocopyright":
246  case self::OPT_SKIP_ALREADY_CLEARED:
247  case "noCopyright":
248  case "noIpra":
249  case "noEcc":
250  case "noKeyword":
251 
252  $queryCondition = self::getQueryCondition($skipThese, $options, $groupId, $agentFilter, $applyGlobal)." ".$additionalCondition;
253  if ('uploadtree' === $uploadTreeTableName || 'uploadtree_a' == $uploadTreeTableName) {
254  $queryCondition = "ut.upload_fk=$uploadId AND ($queryCondition)";
255  }
256  $uploadTreeView = "SELECT * FROM $uploadTreeTableName ut WHERE $queryCondition";
257  break;
258 
259  case "none":
260  default:
261  $uploadTreeView = self::getDefaultUploadTreeView($uploadId, $uploadTreeTableName, $additionalCondition);
262  }
263 
264  return $uploadTreeView;
265  }
266 
267  private static function getAgentFilter($options,$uploadId=0)
268  {
269  if (!array_key_exists(self::OPT_SKIP_THESE, $options)) {
270  return '';
271  }
272  $skipThese = $options[self::OPT_SKIP_THESE];
273  if ($skipThese != "noLicense" && $skipThese != "nolicensenocopyright" && $skipThese != self::OPT_SKIP_ALREADY_CLEARED) {
274  return '';
275  }
276 
277  if (array_key_exists(self::OPT_AGENT_SET, $options)) {
278  $agentIds = 'array[' . implode(',',$options[self::OPT_AGENT_SET]) . ']';
279  $agentFilter = " AND lf.agent_fk = ANY($agentIds)";
280  } else {
281  $scanJobProxy = new ScanJobProxy($GLOBALS['container']->get('dao.agent'),$uploadId);
282  $scanJobProxy->createAgentStatus(array_keys(AgentRef::AGENT_LIST));
283  $latestAgentIds = $scanJobProxy->getLatestSuccessfulAgentIds();
284  $agentFilter = $latestAgentIds ? " AND lf.agent_fk = ANY(array[".implode(',',$latestAgentIds)."])" : "AND 0=1";
285  }
286  return $agentFilter;
287  }
288 
297  private static function getQueryCondition($skipThese, $options, $groupId = null, $agentFilter='', $applyGlobal = false)
298  {
299  global $container;
301  $licenseDao = $container->get('dao.license');
302  $licensesToRemove = [];
303  foreach (['No_license_found', 'Void'] as $licenseName) {
304  $license = $licenseDao->getLicenseByShortName($licenseName);
305  if ($license) {
306  $licensesToRemove[] = "lf.rf_fk != " . $license->getId();
307  }
308  }
309  $licensesToRemove = implode(' AND ', $licensesToRemove);
310  if (!empty($licensesToRemove)) {
311  $licensesToRemove = "($licensesToRemove) AND ";
312  }
313  if ($applyGlobal) {
314  $globalSql = "(
315  ut.uploadtree_pk = cd.uploadtree_fk AND cd.group_fk = $groupId
316  AND cd.scope = ".DecisionScopes::ITEM."
317  ) OR (
318  cd.pfile_fk = ut.pfile_fk AND cd.scope=" . DecisionScopes::REPO . "
319  )";
320  } else {
321  $globalSql = "ut.uploadtree_pk = cd.uploadtree_fk AND cd.group_fk = $groupId";
322  }
323  $conditionQueryHasLicense = "(EXISTS (SELECT 1 FROM license_file lf " .
324  "WHERE ($licensesToRemove" .
325  "lf.pfile_fk = ut.pfile_fk $agentFilter))" .
326  "OR EXISTS (SELECT 1 FROM clearing_decision AS cd " .
327  "WHERE cd.group_fk = $groupId AND ut.uploadtree_pk = cd.uploadtree_fk))";
328 
329  switch ($skipThese) {
330  case "noLicense":
331  return $conditionQueryHasLicense;
332  case "nolicensenocopyright":
333  return "(" . $conditionQueryHasLicense .
334  " OR EXISTS (SELECT 1 FROM copyright cp WHERE cp.pfile_fk=ut.pfile_fk)" .
335  " OR EXISTS (SELECT 1 FROM copyright_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk))";
336  case self::OPT_SKIP_ALREADY_CLEARED:
337  $decisionQuery = "
338 SELECT cd.decision_type
339 FROM clearing_decision cd
340 WHERE $globalSql
341 ORDER BY cd.clearing_decision_pk DESC LIMIT 1";
342  return " $conditionQueryHasLicense
343  AND NOT EXISTS (SELECT 1 FROM ($decisionQuery) AS latest_decision WHERE latest_decision.decision_type IN (".DecisionTypes::IRRELEVANT.",".DecisionTypes::IDENTIFIED.",".DecisionTypes::DO_NOT_USE.",".DecisionTypes::NON_FUNCTIONAL."))";
344  case "noCopyright":
345  return "(EXISTS (SELECT 1 FROM copyright cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null)" .
346  " OR EXISTS (SELECT 1 FROM copyright_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk))";
347  case "noIpra":
348  return "EXISTS (SELECT 1 FROM ipra cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )".
349  " OR EXISTS (SELECT 1 FROM ipra_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk)";
350  case "noEcc":
351  return "EXISTS (SELECT 1 FROM ecc cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )".
352  " OR EXISTS (SELECT 1 FROM ecc_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk)";
353  case "noKeyword":
354  return "EXISTS (SELECT 1 FROM keyword cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )";
355  }
356  }
357 
364  public function countMaskedNonArtifactChildren($parent)
365  {
366  $dbManager = $GLOBALS['container']->get('db.manager');
367  $params = $this->params;
368  if (array_key_exists('uploadId', $params)) {
369  $uploadExpr = '$'.(1+array_search('uploadId', array_keys($params)));
370  } else {
371  $params[] = $this->uploadId;
372  $uploadExpr = '$'.count($params);
373  }
374  $params[] = $parent;
375  $parentExpr = '$'.count($params);
376 
377  $sql = "SELECT count(*) cnt, u.uploadtree_pk, u.ufile_mode FROM ".$this->uploadTreeTableName." u, "
378  . $this->getDbViewName() ." v where u.upload_fk=$uploadExpr"
379  . " AND v.lft BETWEEN u.lft and u.rgt and u.parent=$parentExpr GROUP BY u.uploadtree_pk, u.ufile_mode";
380  $stmt = __METHOD__.'.'.$this->getDbViewName();
381  if (!$this->materialized) {
382  $sql = $this->asCTE().' '.$sql;
383  $stmt .= '.cte';
384  }
385  $dbManager->prepare($stmt,$sql);
386  $res = $dbManager->execute($stmt,$params);
387  $children = array();
388  $artifactContainers = array();
389  while ($row=$dbManager->fetchArray($res)) {
390  $children[$row['uploadtree_pk']] = $row['cnt'];
391  if (($row['ufile_mode'] & (3<<28)) == (3<<28)) {
392  $artifactContainers[] = $row['uploadtree_pk'];
393  }
394  }
395  $dbManager->freeResult($res);
396  foreach ($artifactContainers as $ac) {
397  foreach ($this->countMaskedNonArtifactChildren($ac) as $utid => $cnt) {
398  $children[$utid] = $cnt;
399  }
400  }
401  return $children;
402  }
403 
408  public function getNonArtifactDescendants(ItemTreeBounds $itemTreeBounds)
409  {
410  $uploadExpr = '$'.(count($this->params)+1);
411  $lftExpr = '$'.(count($this->params)+2);
412  $rgtExpr = '$'.(count($this->params)+3);
413  $dbManager = $GLOBALS['container']->get('db.manager');
414  $sql = "SELECT u.uploadtree_pk FROM ".$this->getDbViewName()." u "
415  . "WHERE u.upload_fk=$uploadExpr AND (u.lft BETWEEN $lftExpr AND $rgtExpr) AND u.ufile_mode & (3<<28) = 0";
416  $stmt = __METHOD__.'.'.$this->getDbViewName();
417  if (!$this->materialized) {
418  $sql = $this->asCTE().' '.$sql;
419  $stmt .= '.cte';
420  }
421  $dbManager->prepare($stmt,$sql);
422  $params = array_merge($this->params,
423  array($itemTreeBounds->getUploadId(),$itemTreeBounds->getLeft(),$itemTreeBounds->getRight()));
424  $res = $dbManager->execute($stmt,$params);
425  $descendants = array();
426  while ($row = $dbManager->fetchArray($res)) {
427  $descendants[$row['uploadtree_pk']] = 1;
428  }
429  $dbManager->freeResult($res);
430  return $descendants;
431  }
432 
436  public function count()
437  {
438  global $container;
439  $dbManager = $container->get('db.manager');
440  if ($this->materialized) {
441  $sql = "SELECT count(*) FROM $this->dbViewName";
442  } else {
443  $sql = "SELECT count(*) FROM ($this->dbViewQuery) $this->dbViewName";
444  }
445  $summary = $dbManager->getSingleRow($sql,$this->params,$this->dbViewName);
446  return $summary['count'];
447  }
448 
449  private function addParamAndGetExpr($key,$value)
450  {
451  if (array_key_exists($key, $this->params)) {
452  return '$' . (1 + array_search($key, array_keys($this->params)));
453  }
454 
455  $this->params[] = $value;
456  return '$'.count($this->params);
457  }
458 
459  public function getParams()
460  {
461  return $this->params;
462  }
463 }
Wrapper class for license map.
Definition: LicenseMap.php:19
asCTE()
Common Table Expressions.
Definition: DbViewProxy.php:69
__construct($uploadId, $options, $uploadTreeTableName, $uploadTreeViewName=null)
countMaskedNonArtifactChildren($parent)
count elements childrenwise (or grandchildrenwise if child is artifact)
static getUploadTreeView($uploadId, $options, $uploadTreeTableName, $applyGlobal=false)
createUploadTreeViewQuery($options, $uploadTreeTableName)
static getDefaultUploadTreeView($uploadId, $uploadTreeTableName, $additionalCondition='')
getNonArtifactDescendants(ItemTreeBounds $itemTreeBounds)