@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add "Edge Logic" support to PolicyAwareQuery

Summary:
Ref T4100. Ref T5595. This allows PolicyAwareQuery to write all the logic for AND, OR, NOT, and NULL (i.e., "not in any projects") queries against any edge type.

It accepts an edge type and a list of constraints (which are basically just operator-value pairs, like `<NOT, PHID-X-Y>`, meaning the results must not have an edge connecting them to `PHID-X-Y`).

This doesn't actually do anything yet; see future diffs.

Test Plan: `arc unit --everything`

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4100, T5595

Differential Revision: https://secure.phabricator.com/D12455

+311 -7
+2
src/__phutil_library_map__.php
··· 2324 2324 'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php', 2325 2325 'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', 2326 2326 'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', 2327 + 'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php', 2327 2328 'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', 2328 2329 'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', 2329 2330 'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', ··· 5702 5703 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 5703 5704 'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule', 5704 5705 'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck', 5706 + 'PhabricatorQueryConstraint' => 'Phobject', 5705 5707 'PhabricatorQueryOrderItem' => 'Phobject', 5706 5708 'PhabricatorQueryOrderTestCase' => 'PhabricatorTestCase', 5707 5709 'PhabricatorQueryOrderVector' => array(
+1 -1
src/applications/conpherence/query/ConpherenceThreadQuery.php
··· 121 121 return $conpherences; 122 122 } 123 123 124 - private function buildGroupClause($conn_r) { 124 + protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 125 125 if ($this->participantPHIDs !== null) { 126 126 return 'GROUP BY conpherence_thread.id'; 127 127 } else {
+1 -1
src/applications/diffusion/query/DiffusionCommitQuery.php
··· 542 542 } 543 543 } 544 544 545 - private function buildGroupClause(AphrontDatabaseConnection $conn_r) { 545 + protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 546 546 $should_group = $this->shouldJoinAudits(); 547 547 548 548 // TODO: Currently, the audit table is missing a unique key, so we may
+1 -1
src/applications/feed/query/PhabricatorFeedQuery.php
··· 82 82 return $this->formatWhereClause($where); 83 83 } 84 84 85 - private function buildGroupClause(AphrontDatabaseConnection $conn_r) { 85 + protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 86 86 if ($this->filterPHIDs) { 87 87 return qsprintf($conn_r, 'GROUP BY ref.chronologicalKey'); 88 88 } else {
+1 -1
src/applications/legalpad/query/LegalpadDocumentQuery.php
··· 157 157 return implode(' ', $joins); 158 158 } 159 159 160 - private function buildGroupClause(AphrontDatabaseConnection $conn_r) { 160 + protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 161 161 if ($this->contributorPHIDs || $this->signerPHIDs) { 162 162 return 'GROUP BY d.id'; 163 163 } else {
+1 -1
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 837 837 return implode(' ', $joins); 838 838 } 839 839 840 - private function buildGroupClause(AphrontDatabaseConnection $conn_r) { 840 + protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 841 841 $joined_multiple_rows = (count($this->projectPHIDs) > 1) || 842 842 (count($this->anyProjectPHIDs) > 1) || 843 843 $this->shouldJoinBlockingTasks() ||
+1 -1
src/applications/people/query/PhabricatorPeopleQuery.php
··· 186 186 return $users; 187 187 } 188 188 189 - private function buildGroupClause(AphrontDatabaseConnection $conn) { 189 + protected function buildGroupClause(AphrontDatabaseConnection $conn) { 190 190 if ($this->nameTokens) { 191 191 return qsprintf( 192 192 $conn,
+1 -1
src/applications/project/query/PhabricatorProjectQuery.php
··· 328 328 return $this->formatWhereClause($where); 329 329 } 330 330 331 - private function buildGroupClause($conn_r) { 331 + protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 332 332 if ($this->memberPHIDs || $this->nameTokens) { 333 333 return 'GROUP BY p.id'; 334 334 } else {
+36
src/infrastructure/query/constraint/PhabricatorQueryConstraint.php
··· 1 + <?php 2 + 3 + final class PhabricatorQueryConstraint extends Phobject { 4 + 5 + const OPERATOR_AND = 'and'; 6 + const OPERATOR_OR = 'or'; 7 + const OPERATOR_NOT = 'not'; 8 + const OPERATOR_NULL = 'null'; 9 + 10 + private $operator; 11 + private $value; 12 + 13 + public function __construct($operator, $value) { 14 + $this->operator = $operator; 15 + $this->value = $value; 16 + } 17 + 18 + public function setOperator($operator) { 19 + $this->operator = $operator; 20 + return $this; 21 + } 22 + 23 + public function getOperator() { 24 + return $this->operator; 25 + } 26 + 27 + public function setValue($value) { 28 + $this->value = $value; 29 + return $this; 30 + } 31 + 32 + public function getValue() { 33 + return $this->value; 34 + } 35 + 36 + }
+266
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 9 9 * @task customfield Integration with CustomField 10 10 * @task paging Paging 11 11 * @task order Result Ordering 12 + * @task edgelogic Working with Edge Logic 12 13 */ 13 14 abstract class PhabricatorCursorPagedPolicyAwareQuery 14 15 extends PhabricatorPolicyAwareQuery { ··· 202 203 $select[] = '*'; 203 204 } 204 205 206 + $select[] = $this->buildEdgeLogicSelectClause($conn); 207 + 205 208 return $select; 206 209 } 207 210 ··· 220 223 */ 221 224 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 222 225 $joins = array(); 226 + $joins[] = $this->buildEdgeLogicJoinClause($conn); 223 227 return $joins; 224 228 } 225 229 ··· 239 243 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 240 244 $where = array(); 241 245 $where[] = $this->buildPagingClause($conn); 246 + $where[] = $this->buildEdgeLogicWhereClause($conn); 242 247 return $where; 243 248 } 244 249 ··· 257 262 */ 258 263 protected function buildHavingClauseParts(AphrontDatabaseConnection $conn) { 259 264 $having = array(); 265 + $having[] = $this->buildEdgeLogicHavingClause($conn); 260 266 return $having; 261 267 } 268 + 269 + 270 + /** 271 + * @task clauses 272 + */ 273 + protected function buildGroupClause(AphrontDatabaseConnection $conn) { 274 + if (!$this->shouldGroupQueryResultRows()) { 275 + return ''; 276 + } 277 + 278 + return qsprintf( 279 + $conn, 280 + 'GROUP BY %Q', 281 + $this->getApplicationSearchObjectPHIDColumn()); 282 + } 283 + 284 + 285 + /** 286 + * @task clauses 287 + */ 288 + protected function shouldGroupQueryResultRows() { 289 + if ($this->shouldGroupEdgeLogicResultRows()) { 290 + return true; 291 + } 292 + 293 + if ($this->getApplicationSearchMayJoinMultipleRows()) { 294 + return true; 295 + } 296 + 297 + return false; 298 + } 299 + 262 300 263 301 264 302 /* -( Paging )------------------------------------------------------------- */ ··· 1215 1253 protected function isCustomFieldOrderKey($key) { 1216 1254 $prefix = 'custom:'; 1217 1255 return !strncmp($key, $prefix, strlen($prefix)); 1256 + } 1257 + 1258 + 1259 + /* -( Edge Logic )--------------------------------------------------------- */ 1260 + 1261 + 1262 + /** 1263 + * @task edgelogic 1264 + */ 1265 + public function withEdgeLogicConstraints($edge_type, array $constraints) { 1266 + assert_instances_of($constraints, 'PhabricatorQueryConstraint'); 1267 + 1268 + $constraints = mgroup($constraints, 'getOperator'); 1269 + foreach ($constraints as $operator => $list) { 1270 + foreach ($list as $item) { 1271 + $value = $item->getValue(); 1272 + $this->edgeLogicConstraints[$edge_type][$operator][$value] = $item; 1273 + } 1274 + } 1275 + 1276 + return $this; 1277 + } 1278 + 1279 + 1280 + /** 1281 + * @task edgelogic 1282 + */ 1283 + public function buildEdgeLogicSelectClause(AphrontDatabaseConnection $conn) { 1284 + $select = array(); 1285 + 1286 + foreach ($this->edgeLogicConstraints as $type => $constraints) { 1287 + foreach ($constraints as $operator => $list) { 1288 + $alias = $this->getEdgeLogicTableAlias($operator, $type); 1289 + switch ($operator) { 1290 + case PhabricatorQueryConstraint::OPERATOR_AND: 1291 + if (count($list) > 1) { 1292 + $select[] = qsprintf( 1293 + $conn, 1294 + 'COUNT(DISTINCT(%T.dst)) %T', 1295 + $alias, 1296 + $this->buildEdgeLogicTableAliasCount($alias)); 1297 + } 1298 + break; 1299 + default: 1300 + break; 1301 + } 1302 + } 1303 + } 1304 + 1305 + return $select; 1306 + } 1307 + 1308 + 1309 + /** 1310 + * @task edgelogic 1311 + */ 1312 + public function buildEdgeLogicJoinClause(AphrontDatabaseConnection $conn) { 1313 + $edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE; 1314 + $phid_column = $this->getApplicationSearchObjectPHIDColumn(); 1315 + 1316 + $joins = array(); 1317 + foreach ($this->edgeLogicConstraints as $type => $constraints) { 1318 + foreach ($constraints as $operator => $list) { 1319 + $alias = $this->getEdgeLogicTableAlias($operator, $type); 1320 + switch ($operator) { 1321 + case PhabricatorQueryConstraint::OPERATOR_NOT: 1322 + $joins[] = qsprintf( 1323 + $conn, 1324 + 'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d 1325 + AND %T.dst IN (%Ls)', 1326 + $edge_table, 1327 + $alias, 1328 + $phid_column, 1329 + $alias, 1330 + $alias, 1331 + $type, 1332 + $alias, 1333 + mpull($list, 'getValue')); 1334 + break; 1335 + case PhabricatorQueryConstraint::OPERATOR_AND: 1336 + case PhabricatorQueryConstraint::OPERATOR_OR: 1337 + $joins[] = qsprintf( 1338 + $conn, 1339 + 'JOIN %T %T ON %Q = %T.src AND %T.type = %d 1340 + AND %T.dst IN (%Ls)', 1341 + $edge_table, 1342 + $alias, 1343 + $phid_column, 1344 + $alias, 1345 + $alias, 1346 + $type, 1347 + $alias, 1348 + mpull($list, 'getValue')); 1349 + break; 1350 + case PhabricatorQueryConstraint::OPERATOR_NULL: 1351 + $joins[] = qsprintf( 1352 + $conn, 1353 + 'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d', 1354 + $edge_table, 1355 + $alias, 1356 + $phid_column, 1357 + $alias, 1358 + $alias, 1359 + $type); 1360 + break; 1361 + } 1362 + } 1363 + } 1364 + 1365 + return $joins; 1366 + } 1367 + 1368 + 1369 + /** 1370 + * @task edgelogic 1371 + */ 1372 + public function buildEdgeLogicWhereClause(AphrontDatabaseConnection $conn) { 1373 + $where = array(); 1374 + 1375 + foreach ($this->edgeLogicConstraints as $type => $constraints) { 1376 + 1377 + $full = array(); 1378 + $null = array(); 1379 + 1380 + foreach ($constraints as $operator => $list) { 1381 + $alias = $this->getEdgeLogicTableAlias($operator, $type); 1382 + switch ($operator) { 1383 + case PhabricatorQueryConstraint::OPERATOR_NOT: 1384 + $full[] = qsprintf( 1385 + $conn, 1386 + '%T.dst IS NULL', 1387 + $alias); 1388 + break; 1389 + case PhabricatorQueryConstraint::OPERATOR_AND: 1390 + case PhabricatorQueryConstraint::OPERATOR_OR: 1391 + break; 1392 + case PhabricatorQueryConstraint::OPERATOR_NULL: 1393 + $null[] = qsprintf( 1394 + $conn, 1395 + '%T.dst IS NULL', 1396 + $alias); 1397 + break; 1398 + } 1399 + } 1400 + 1401 + if ($full && $null) { 1402 + $full = $this->formatWhereSubclause($full); 1403 + $null = $this->formatWhereSubclause($null); 1404 + $where[] = qsprintf($conn, '(%Q OR %Q)', $full, $null); 1405 + } else if ($full) { 1406 + foreach ($full as $condition) { 1407 + $where[] = $condition; 1408 + } 1409 + } else if ($null) { 1410 + foreach ($null as $condition) { 1411 + $where[] = $condition; 1412 + } 1413 + } 1414 + } 1415 + 1416 + return $where; 1417 + } 1418 + 1419 + 1420 + /** 1421 + * @task edgelogic 1422 + */ 1423 + public function buildEdgeLogicHavingClause(AphrontDatabaseConnection $conn) { 1424 + $having = array(); 1425 + 1426 + foreach ($this->edgeLogicConstraints as $type => $constraints) { 1427 + foreach ($constraints as $operator => $list) { 1428 + $alias = $this->getEdgeLogicTableAlias($operator, $type); 1429 + switch ($operator) { 1430 + case PhabricatorQueryConstraint::OPERATOR_AND: 1431 + if (count($list) > 1) { 1432 + $having[] = qsprintf( 1433 + $conn, 1434 + '%T = %d', 1435 + $this->buildEdgeLogicTableAliasCount($alias), 1436 + count($list)); 1437 + } 1438 + break; 1439 + } 1440 + } 1441 + } 1442 + 1443 + return $having; 1444 + } 1445 + 1446 + 1447 + /** 1448 + * @task edgelogic 1449 + */ 1450 + public function shouldGroupEdgeLogicResultRows() { 1451 + foreach ($this->edgeLogicConstraints as $type => $constraints) { 1452 + foreach ($constraints as $operator => $list) { 1453 + switch ($operator) { 1454 + case PhabricatorQueryConstraint::OPERATOR_NOT: 1455 + case PhabricatorQueryConstraint::OPERATOR_AND: 1456 + case PhabricatorQueryConstraint::OPERATOR_OR: 1457 + if (count($list) > 1) { 1458 + return true; 1459 + } 1460 + break; 1461 + case PhabricatorQueryConstraint::OPERATOR_NULL: 1462 + return true; 1463 + } 1464 + } 1465 + } 1466 + 1467 + return false; 1468 + } 1469 + 1470 + 1471 + /** 1472 + * @task edgelogic 1473 + */ 1474 + private function getEdgeLogicTableAlias($operator, $type) { 1475 + return 'edgelogic_'.$operator.'_'.$type; 1476 + } 1477 + 1478 + 1479 + /** 1480 + * @task edgelogic 1481 + */ 1482 + private function buildEdgeLogicTableAliasCount($alias) { 1483 + return $alias.'_count'; 1218 1484 } 1219 1485 1220 1486