@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.

Support some QueryConstraint operations against generic ApplicationSearch query logic

Summary: Ref T13090. Currently, it isn't possible to query custom fields for complex constraints. Lay the groundwork to implement some of the easy ones (none(), any()) for Datasource/PHID fields.

Test Plan: Hard-coded some constraints and queried with them; see next change for more substantial testing.

Maniphest Tasks: T13090

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

+126 -32
+1
src/infrastructure/query/constraint/PhabricatorQueryConstraint.php
··· 9 9 const OPERATOR_ANCESTOR = 'ancestor'; 10 10 const OPERATOR_EMPTY = 'empty'; 11 11 const OPERATOR_ONLY = 'only'; 12 + const OPERATOR_ANY = 'any'; 12 13 13 14 private $operator; 14 15 private $value;
+125 -32
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 342 342 $where[] = $this->buildSpacesWhereClause($conn); 343 343 $where[] = $this->buildNgramsWhereClause($conn); 344 344 $where[] = $this->buildFerretWhereClause($conn); 345 + $where[] = $this->buildApplicationSearchWhereClause($conn); 345 346 return $where; 346 347 } 347 348 ··· 1158 1159 PhabricatorCustomFieldIndexStorage $index, 1159 1160 $value) { 1160 1161 1162 + $values = (array)$value; 1163 + 1164 + $data_values = array(); 1165 + $constraint_values = array(); 1166 + foreach ($values as $value) { 1167 + if ($value instanceof PhabricatorQueryConstraint) { 1168 + $constraint_values[] = $value; 1169 + } else { 1170 + $data_values[] = $value; 1171 + } 1172 + } 1173 + 1174 + $alias = 'appsearch_'.count($this->applicationSearchConstraints); 1175 + 1161 1176 $this->applicationSearchConstraints[] = array( 1162 1177 'type' => $index->getIndexValueType(), 1163 1178 'cond' => '=', 1164 1179 'table' => $index->getTableName(), 1165 1180 'index' => $index->getIndexKey(), 1166 - 'value' => $value, 1181 + 'alias' => $alias, 1182 + 'value' => $values, 1183 + 'data' => $data_values, 1184 + 'constraints' => $constraint_values, 1167 1185 ); 1168 1186 1169 1187 return $this; ··· 1203 1221 'int')); 1204 1222 } 1205 1223 1224 + $alias = 'appsearch_'.count($this->applicationSearchConstraints); 1225 + 1206 1226 $this->applicationSearchConstraints[] = array( 1207 1227 'type' => $index->getIndexValueType(), 1208 1228 'cond' => 'range', 1209 1229 'table' => $index->getTableName(), 1210 1230 'index' => $index->getIndexKey(), 1231 + 'alias' => $alias, 1211 1232 'value' => array($min, $max), 1212 1233 ); 1213 1234 ··· 1256 1277 switch ($type) { 1257 1278 case 'string': 1258 1279 case 'int': 1259 - if (count((array)$value) > 1) { 1280 + if (count($value) > 1) { 1260 1281 return true; 1261 1282 } 1262 1283 break; ··· 1309 1330 * @task appsearch 1310 1331 */ 1311 1332 protected function buildApplicationSearchJoinClause( 1312 - AphrontDatabaseConnection $conn_r) { 1333 + AphrontDatabaseConnection $conn) { 1313 1334 1314 1335 $joins = array(); 1315 1336 foreach ($this->applicationSearchConstraints as $key => $constraint) { 1316 1337 $table = $constraint['table']; 1317 - $alias = 'appsearch_'.$key; 1338 + $alias = $constraint['alias']; 1318 1339 $index = $constraint['index']; 1319 1340 $cond = $constraint['cond']; 1320 1341 $phid_column = $this->getApplicationSearchObjectPHIDColumn(); 1321 1342 switch ($cond) { 1322 1343 case '=': 1323 - $type = $constraint['type']; 1324 - switch ($type) { 1325 - case 'string': 1326 - $constraint_clause = qsprintf( 1327 - $conn_r, 1328 - '%T.indexValue IN (%Ls)', 1329 - $alias, 1330 - (array)$constraint['value']); 1331 - break; 1332 - case 'int': 1333 - $constraint_clause = qsprintf( 1334 - $conn_r, 1335 - '%T.indexValue IN (%Ld)', 1336 - $alias, 1337 - (array)$constraint['value']); 1344 + // Figure out whether we need to do a LEFT JOIN or not. We need to 1345 + // LEFT JOIN if we're going to select "IS NULL" rows. 1346 + $join_type = 'JOIN'; 1347 + foreach ($constraint['constraints'] as $query_constraint) { 1348 + $op = $query_constraint->getOperator(); 1349 + if ($op === PhabricatorQueryConstraint::OPERATOR_NULL) { 1350 + $join_type = 'LEFT JOIN'; 1338 1351 break; 1339 - default: 1340 - throw new Exception(pht('Unknown index type "%s"!', $type)); 1352 + } 1341 1353 } 1342 1354 1343 1355 $joins[] = qsprintf( 1344 - $conn_r, 1345 - 'JOIN %T %T ON %T.objectPHID = %Q 1346 - AND %T.indexKey = %s 1347 - AND (%Q)', 1356 + $conn, 1357 + '%Q %T %T ON %T.objectPHID = %Q 1358 + AND %T.indexKey = %s', 1359 + $join_type, 1348 1360 $table, 1349 1361 $alias, 1350 1362 $alias, 1351 1363 $phid_column, 1352 1364 $alias, 1353 - $index, 1354 - $constraint_clause); 1365 + $index); 1355 1366 break; 1356 1367 case 'range': 1357 1368 list($min, $max) = $constraint['value']; ··· 1362 1373 1363 1374 if ($min === null) { 1364 1375 $constraint_clause = qsprintf( 1365 - $conn_r, 1376 + $conn, 1366 1377 '%T.indexValue <= %d', 1367 1378 $alias, 1368 1379 $max); 1369 1380 } else if ($max === null) { 1370 1381 $constraint_clause = qsprintf( 1371 - $conn_r, 1382 + $conn, 1372 1383 '%T.indexValue >= %d', 1373 1384 $alias, 1374 1385 $min); 1375 1386 } else { 1376 1387 $constraint_clause = qsprintf( 1377 - $conn_r, 1388 + $conn, 1378 1389 '%T.indexValue BETWEEN %d AND %d', 1379 1390 $alias, 1380 1391 $min, ··· 1382 1393 } 1383 1394 1384 1395 $joins[] = qsprintf( 1385 - $conn_r, 1396 + $conn, 1386 1397 'JOIN %T %T ON %T.objectPHID = %Q 1387 1398 AND %T.indexKey = %s 1388 1399 AND (%Q)', ··· 1414 1425 $key = $spec['customfield.index.key']; 1415 1426 1416 1427 $joins[] = qsprintf( 1417 - $conn_r, 1428 + $conn, 1418 1429 'LEFT JOIN %T %T ON %T.objectPHID = %Q 1419 1430 AND %T.indexKey = %s', 1420 1431 $table, ··· 1426 1437 } 1427 1438 1428 1439 return implode(' ', $joins); 1440 + } 1441 + 1442 + /** 1443 + * Construct a WHERE clause appropriate for applying ApplicationSearch 1444 + * constraints. 1445 + * 1446 + * @param AphrontDatabaseConnection Connection executing the query. 1447 + * @return list<string> Where clause parts. 1448 + * @task appsearch 1449 + */ 1450 + protected function buildApplicationSearchWhereClause( 1451 + AphrontDatabaseConnection $conn) { 1452 + 1453 + $where = array(); 1454 + 1455 + foreach ($this->applicationSearchConstraints as $key => $constraint) { 1456 + $alias = $constraint['alias']; 1457 + $cond = $constraint['cond']; 1458 + $type = $constraint['type']; 1459 + 1460 + $data_values = $constraint['data']; 1461 + $constraint_values = $constraint['constraints']; 1462 + 1463 + $constraint_parts = array(); 1464 + switch ($cond) { 1465 + case '=': 1466 + if ($data_values) { 1467 + switch ($type) { 1468 + case 'string': 1469 + $constraint_parts[] = qsprintf( 1470 + $conn, 1471 + '%T.indexValue IN (%Ls)', 1472 + $alias, 1473 + $data_values); 1474 + break; 1475 + case 'int': 1476 + $constraint_parts[] = qsprintf( 1477 + $conn, 1478 + '%T.indexValue IN (%Ld)', 1479 + $alias, 1480 + $data_values); 1481 + break; 1482 + default: 1483 + throw new Exception(pht('Unknown index type "%s"!', $type)); 1484 + } 1485 + } 1486 + 1487 + if ($constraint_values) { 1488 + foreach ($constraint_values as $value) { 1489 + $op = $value->getOperator(); 1490 + switch ($op) { 1491 + case PhabricatorQueryConstraint::OPERATOR_NULL: 1492 + $constraint_parts[] = qsprintf( 1493 + $conn, 1494 + '%T.indexValue IS NULL', 1495 + $alias); 1496 + break; 1497 + case PhabricatorQueryConstraint::OPERATOR_ANY: 1498 + $constraint_parts[] = qsprintf( 1499 + $conn, 1500 + '%T.indexValue IS NOT NULL', 1501 + $alias); 1502 + break; 1503 + default: 1504 + throw new Exception( 1505 + pht( 1506 + 'No support for applying operator "%s" against '. 1507 + 'index of type "%s".', 1508 + $op, 1509 + $type)); 1510 + } 1511 + } 1512 + } 1513 + 1514 + if ($constraint_parts) { 1515 + $where[] = '('.implode(') OR (', $constraint_parts).')'; 1516 + } 1517 + break; 1518 + } 1519 + } 1520 + 1521 + return $where; 1429 1522 } 1430 1523 1431 1524