FOSSology  4.4.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 self::OPT_SKIP_ALREADY_CLEARED:
246  case "noCopyright":
247  case "noIpra":
248  case "noEcc":
249  case "noKeyword":
250 
251  $queryCondition = self::getQueryCondition($skipThese, $options, $groupId, $agentFilter, $applyGlobal)." ".$additionalCondition;
252  if ('uploadtree' === $uploadTreeTableName || 'uploadtree_a' == $uploadTreeTableName) {
253  $queryCondition = "ut.upload_fk=$uploadId AND ($queryCondition)";
254  }
255  $uploadTreeView = "SELECT * FROM $uploadTreeTableName ut WHERE $queryCondition";
256  break;
257 
258  case "none":
259  default:
260  $uploadTreeView = self::getDefaultUploadTreeView($uploadId, $uploadTreeTableName, $additionalCondition);
261  }
262 
263  return $uploadTreeView;
264  }
265 
266  private static function getAgentFilter($options,$uploadId=0)
267  {
268  if (!array_key_exists(self::OPT_SKIP_THESE, $options)) {
269  return '';
270  }
271  $skipThese = $options[self::OPT_SKIP_THESE];
272  if ($skipThese != "noLicense" && $skipThese != self::OPT_SKIP_ALREADY_CLEARED) {
273  return '';
274  }
275 
276  if (array_key_exists(self::OPT_AGENT_SET, $options)) {
277  $agentIds = 'array[' . implode(',',$options[self::OPT_AGENT_SET]) . ']';
278  $agentFilter = " AND lf.agent_fk = ANY($agentIds)";
279  } else {
280  $scanJobProxy = new ScanJobProxy($GLOBALS['container']->get('dao.agent'),$uploadId);
281  $scanJobProxy->createAgentStatus(array_keys(AgentRef::AGENT_LIST));
282  $latestAgentIds = $scanJobProxy->getLatestSuccessfulAgentIds();
283  $agentFilter = $latestAgentIds ? " AND lf.agent_fk = ANY(array[".implode(',',$latestAgentIds)."])" : "AND 0=1";
284  }
285  return $agentFilter;
286  }
287 
296  private static function getQueryCondition($skipThese, $options, $groupId = null, $agentFilter='', $applyGlobal = false)
297  {
298  global $container;
300  $licenseDao = $container->get('dao.license');
301  $licensesToRemove = [];
302  foreach (['No_license_found', 'Void'] as $licenseName) {
303  $license = $licenseDao->getLicenseByShortName($licenseName);
304  if ($license) {
305  $licensesToRemove[] = "lf.rf_fk != " . $license->getId();
306  }
307  }
308  $licensesToRemove = implode(' AND ', $licensesToRemove);
309  if (!empty($licensesToRemove)) {
310  $licensesToRemove = "($licensesToRemove) AND ";
311  }
312  if ($applyGlobal) {
313  $globalSql = "(
314  ut.uploadtree_pk = cd.uploadtree_fk AND cd.group_fk = $groupId
315  AND cd.scope = ".DecisionScopes::ITEM."
316  ) OR (
317  cd.pfile_fk = ut.pfile_fk AND cd.scope=" . DecisionScopes::REPO . "
318  )";
319  } else {
320  $globalSql = "ut.uploadtree_pk = cd.uploadtree_fk AND cd.group_fk = $groupId";
321  }
322  $conditionQueryHasLicense = "(EXISTS (SELECT 1 FROM license_file lf " .
323  "WHERE ($licensesToRemove" .
324  "lf.pfile_fk = ut.pfile_fk $agentFilter))" .
325  "OR EXISTS (SELECT 1 FROM clearing_decision AS cd " .
326  "WHERE cd.group_fk = $groupId AND ut.uploadtree_pk = cd.uploadtree_fk))";
327 
328  switch ($skipThese) {
329  case "noLicense":
330  return $conditionQueryHasLicense;
331  case self::OPT_SKIP_ALREADY_CLEARED:
332  $decisionQuery = "
333 SELECT cd.decision_type
334 FROM clearing_decision cd
335 WHERE $globalSql
336 ORDER BY cd.clearing_decision_pk DESC LIMIT 1";
337  return " $conditionQueryHasLicense
338  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."))";
339  case "noCopyright":
340  return "EXISTS (SELECT copyright_pk FROM copyright cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )".
341  " OR EXISTS (SELECT 1 FROM copyright_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk)";
342  case "noIpra":
343  return "EXISTS (SELECT ipra_pk FROM ipra cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )".
344  " OR EXISTS (SELECT 1 FROM ipra_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk)";
345  case "noEcc":
346  return "EXISTS (SELECT ecc_pk FROM ecc cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )".
347  " OR EXISTS (SELECT 1 FROM ecc_decision AS cd WHERE ut.pfile_fk = cd.pfile_fk)";
348  case "noKeyword":
349  return "EXISTS (SELECT keyword_pk FROM keyword cp WHERE cp.pfile_fk=ut.pfile_fk and cp.hash is not null )";
350  }
351  }
352 
359  public function countMaskedNonArtifactChildren($parent)
360  {
361  $dbManager = $GLOBALS['container']->get('db.manager');
362  $params = $this->params;
363  if (array_key_exists('uploadId', $params)) {
364  $uploadExpr = '$'.(1+array_search('uploadId', array_keys($params)));
365  } else {
366  $params[] = $this->uploadId;
367  $uploadExpr = '$'.count($params);
368  }
369  $params[] = $parent;
370  $parentExpr = '$'.count($params);
371 
372  $sql = "SELECT count(*) cnt, u.uploadtree_pk, u.ufile_mode FROM ".$this->uploadTreeTableName." u, "
373  . $this->getDbViewName() ." v where u.upload_fk=$uploadExpr"
374  . " AND v.lft BETWEEN u.lft and u.rgt and u.parent=$parentExpr GROUP BY u.uploadtree_pk, u.ufile_mode";
375  $stmt = __METHOD__.'.'.$this->getDbViewName();
376  if (!$this->materialized) {
377  $sql = $this->asCTE().' '.$sql;
378  $stmt .= '.cte';
379  }
380  $dbManager->prepare($stmt,$sql);
381  $res = $dbManager->execute($stmt,$params);
382  $children = array();
383  $artifactContainers = array();
384  while ($row=$dbManager->fetchArray($res)) {
385  $children[$row['uploadtree_pk']] = $row['cnt'];
386  if (($row['ufile_mode'] & (3<<28)) == (3<<28)) {
387  $artifactContainers[] = $row['uploadtree_pk'];
388  }
389  }
390  $dbManager->freeResult($res);
391  foreach ($artifactContainers as $ac) {
392  foreach ($this->countMaskedNonArtifactChildren($ac) as $utid => $cnt) {
393  $children[$utid] = $cnt;
394  }
395  }
396  return $children;
397  }
398 
403  public function getNonArtifactDescendants(ItemTreeBounds $itemTreeBounds)
404  {
405  $uploadExpr = '$'.(count($this->params)+1);
406  $lftExpr = '$'.(count($this->params)+2);
407  $rgtExpr = '$'.(count($this->params)+3);
408  $dbManager = $GLOBALS['container']->get('db.manager');
409  $sql = "SELECT u.uploadtree_pk FROM ".$this->getDbViewName()." u "
410  . "WHERE u.upload_fk=$uploadExpr AND (u.lft BETWEEN $lftExpr AND $rgtExpr) AND u.ufile_mode & (3<<28) = 0";
411  $stmt = __METHOD__.'.'.$this->getDbViewName();
412  if (!$this->materialized) {
413  $sql = $this->asCTE().' '.$sql;
414  $stmt .= '.cte';
415  }
416  $dbManager->prepare($stmt,$sql);
417  $params = array_merge($this->params,
418  array($itemTreeBounds->getUploadId(),$itemTreeBounds->getLeft(),$itemTreeBounds->getRight()));
419  $res = $dbManager->execute($stmt,$params);
420  $descendants = array();
421  while ($row = $dbManager->fetchArray($res)) {
422  $descendants[$row['uploadtree_pk']] = 1;
423  }
424  $dbManager->freeResult($res);
425  return $descendants;
426  }
427 
431  public function count()
432  {
433  global $container;
434  $dbManager = $container->get('db.manager');
435  if ($this->materialized) {
436  $sql = "SELECT count(*) FROM $this->dbViewName";
437  } else {
438  $sql = "SELECT count(*) FROM ($this->dbViewQuery) $this->dbViewName";
439  }
440  $summary = $dbManager->getSingleRow($sql,$this->params,$this->dbViewName);
441  return $summary['count'];
442  }
443 
444  private function addParamAndGetExpr($key,$value)
445  {
446  if (array_key_exists($key, $this->params)) {
447  return '$' . (1 + array_search($key, array_keys($this->params)));
448  }
449 
450  $this->params[] = $value;
451  return '$'.count($this->params);
452  }
453 
454  public function getParams()
455  {
456  return $this->params;
457  }
458 }
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)