Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/query/DiffusionCommitQuery.php
12242 views
1
<?php
2
3
final class DiffusionCommitQuery
4
extends PhabricatorCursorPagedPolicyAwareQuery {
5
6
private $ids;
7
private $phids;
8
private $authorPHIDs;
9
private $defaultRepository;
10
private $identifiers;
11
private $repositoryIDs;
12
private $repositoryPHIDs;
13
private $identifierMap;
14
private $responsiblePHIDs;
15
private $statuses;
16
private $packagePHIDs;
17
private $unreachable;
18
private $permanent;
19
20
private $needAuditRequests;
21
private $needAuditAuthority;
22
private $auditIDs;
23
private $auditorPHIDs;
24
private $epochMin;
25
private $epochMax;
26
private $importing;
27
private $ancestorsOf;
28
29
private $needCommitData;
30
private $needDrafts;
31
private $needIdentities;
32
33
private $mustFilterRefs = false;
34
private $refRepository;
35
36
public function withIDs(array $ids) {
37
$this->ids = $ids;
38
return $this;
39
}
40
41
public function withPHIDs(array $phids) {
42
$this->phids = $phids;
43
return $this;
44
}
45
46
public function withAuthorPHIDs(array $phids) {
47
$this->authorPHIDs = $phids;
48
return $this;
49
}
50
51
/**
52
* Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234",
53
* or "a9caf12". When an identifier matches multiple commits, they will all
54
* be returned; callers should be prepared to deal with more results than
55
* they queried for.
56
*/
57
public function withIdentifiers(array $identifiers) {
58
// Some workflows (like blame lookups) can pass in large numbers of
59
// duplicate identifiers. We only care about unique identifiers, so
60
// get rid of duplicates immediately.
61
$identifiers = array_fuse($identifiers);
62
63
$this->identifiers = $identifiers;
64
return $this;
65
}
66
67
/**
68
* Look up commits in a specific repository. This is a shorthand for calling
69
* @{method:withDefaultRepository} and @{method:withRepositoryIDs}.
70
*/
71
public function withRepository(PhabricatorRepository $repository) {
72
$this->withDefaultRepository($repository);
73
$this->withRepositoryIDs(array($repository->getID()));
74
return $this;
75
}
76
77
/**
78
* Look up commits in a specific repository. Prefer
79
* @{method:withRepositoryIDs}; the underlying table is keyed by ID such
80
* that this method requires a separate initial query to map PHID to ID.
81
*/
82
public function withRepositoryPHIDs(array $phids) {
83
$this->repositoryPHIDs = $phids;
84
return $this;
85
}
86
87
/**
88
* If a default repository is provided, ambiguous commit identifiers will
89
* be assumed to belong to the default repository.
90
*
91
* For example, "r123" appearing in a commit message in repository X is
92
* likely to be unambiguously "rX123". Normally the reference would be
93
* considered ambiguous, but if you provide a default repository it will
94
* be correctly resolved.
95
*/
96
public function withDefaultRepository(PhabricatorRepository $repository) {
97
$this->defaultRepository = $repository;
98
return $this;
99
}
100
101
public function withRepositoryIDs(array $repository_ids) {
102
$this->repositoryIDs = array_unique($repository_ids);
103
return $this;
104
}
105
106
public function needCommitData($need) {
107
$this->needCommitData = $need;
108
return $this;
109
}
110
111
public function needDrafts($need) {
112
$this->needDrafts = $need;
113
return $this;
114
}
115
116
public function needIdentities($need) {
117
$this->needIdentities = $need;
118
return $this;
119
}
120
121
public function needAuditRequests($need) {
122
$this->needAuditRequests = $need;
123
return $this;
124
}
125
126
public function needAuditAuthority(array $users) {
127
assert_instances_of($users, 'PhabricatorUser');
128
$this->needAuditAuthority = $users;
129
return $this;
130
}
131
132
public function withAuditIDs(array $ids) {
133
$this->auditIDs = $ids;
134
return $this;
135
}
136
137
public function withAuditorPHIDs(array $auditor_phids) {
138
$this->auditorPHIDs = $auditor_phids;
139
return $this;
140
}
141
142
public function withResponsiblePHIDs(array $responsible_phids) {
143
$this->responsiblePHIDs = $responsible_phids;
144
return $this;
145
}
146
147
public function withPackagePHIDs(array $package_phids) {
148
$this->packagePHIDs = $package_phids;
149
return $this;
150
}
151
152
public function withUnreachable($unreachable) {
153
$this->unreachable = $unreachable;
154
return $this;
155
}
156
157
public function withPermanent($permanent) {
158
$this->permanent = $permanent;
159
return $this;
160
}
161
162
public function withStatuses(array $statuses) {
163
$this->statuses = $statuses;
164
return $this;
165
}
166
167
public function withEpochRange($min, $max) {
168
$this->epochMin = $min;
169
$this->epochMax = $max;
170
return $this;
171
}
172
173
public function withImporting($importing) {
174
$this->importing = $importing;
175
return $this;
176
}
177
178
public function withAncestorsOf(array $refs) {
179
$this->ancestorsOf = $refs;
180
return $this;
181
}
182
183
public function getIdentifierMap() {
184
if ($this->identifierMap === null) {
185
throw new Exception(
186
pht(
187
'You must %s the query before accessing the identifier map.',
188
'execute()'));
189
}
190
return $this->identifierMap;
191
}
192
193
protected function getPrimaryTableAlias() {
194
return 'commit';
195
}
196
197
protected function willExecute() {
198
if ($this->identifierMap === null) {
199
$this->identifierMap = array();
200
}
201
}
202
203
public function newResultObject() {
204
return new PhabricatorRepositoryCommit();
205
}
206
207
protected function loadPage() {
208
$table = $this->newResultObject();
209
$conn = $table->establishConnection('r');
210
211
$empty_exception = null;
212
$subqueries = array();
213
if ($this->responsiblePHIDs) {
214
$base_authors = $this->authorPHIDs;
215
$base_auditors = $this->auditorPHIDs;
216
217
$responsible_phids = $this->responsiblePHIDs;
218
if ($base_authors) {
219
$all_authors = array_merge($base_authors, $responsible_phids);
220
} else {
221
$all_authors = $responsible_phids;
222
}
223
224
if ($base_auditors) {
225
$all_auditors = array_merge($base_auditors, $responsible_phids);
226
} else {
227
$all_auditors = $responsible_phids;
228
}
229
230
$this->authorPHIDs = $all_authors;
231
$this->auditorPHIDs = $base_auditors;
232
try {
233
$subqueries[] = $this->buildStandardPageQuery(
234
$conn,
235
$table->getTableName());
236
} catch (PhabricatorEmptyQueryException $ex) {
237
$empty_exception = $ex;
238
}
239
240
$this->authorPHIDs = $base_authors;
241
$this->auditorPHIDs = $all_auditors;
242
try {
243
$subqueries[] = $this->buildStandardPageQuery(
244
$conn,
245
$table->getTableName());
246
} catch (PhabricatorEmptyQueryException $ex) {
247
$empty_exception = $ex;
248
}
249
} else {
250
$subqueries[] = $this->buildStandardPageQuery(
251
$conn,
252
$table->getTableName());
253
}
254
255
if (!$subqueries) {
256
throw $empty_exception;
257
}
258
259
if (count($subqueries) > 1) {
260
$unions = null;
261
foreach ($subqueries as $subquery) {
262
if (!$unions) {
263
$unions = qsprintf(
264
$conn,
265
'(%Q)',
266
$subquery);
267
continue;
268
}
269
270
$unions = qsprintf(
271
$conn,
272
'%Q UNION DISTINCT (%Q)',
273
$unions,
274
$subquery);
275
}
276
277
$query = qsprintf(
278
$conn,
279
'%Q %Q %Q',
280
$unions,
281
$this->buildOrderClause($conn, true),
282
$this->buildLimitClause($conn));
283
} else {
284
$query = head($subqueries);
285
}
286
287
$rows = queryfx_all($conn, '%Q', $query);
288
$rows = $this->didLoadRawRows($rows);
289
290
return $table->loadAllFromArray($rows);
291
}
292
293
protected function willFilterPage(array $commits) {
294
$repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID');
295
$repos = id(new PhabricatorRepositoryQuery())
296
->setViewer($this->getViewer())
297
->withIDs($repository_ids)
298
->execute();
299
300
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
301
$result = array();
302
303
foreach ($commits as $key => $commit) {
304
$repo = idx($repos, $commit->getRepositoryID());
305
if ($repo) {
306
$commit->attachRepository($repo);
307
} else {
308
$this->didRejectResult($commit);
309
unset($commits[$key]);
310
continue;
311
}
312
313
// Build the identifierMap
314
if ($this->identifiers !== null) {
315
$ids = $this->identifiers;
316
$prefixes = array(
317
'r'.$commit->getRepository()->getCallsign(),
318
'r'.$commit->getRepository()->getCallsign().':',
319
'R'.$commit->getRepository()->getID().':',
320
'', // No prefix is valid too and will only match the commitIdentifier
321
);
322
$suffix = $commit->getCommitIdentifier();
323
324
if ($commit->getRepository()->isSVN()) {
325
foreach ($prefixes as $prefix) {
326
if (isset($ids[$prefix.$suffix])) {
327
$result[$prefix.$suffix][] = $commit;
328
}
329
}
330
} else {
331
// This awkward construction is so we can link the commits up in O(N)
332
// time instead of O(N^2).
333
for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) {
334
$part = substr($suffix, 0, $ii);
335
foreach ($prefixes as $prefix) {
336
if (isset($ids[$prefix.$part])) {
337
$result[$prefix.$part][] = $commit;
338
}
339
}
340
}
341
}
342
}
343
}
344
345
if ($result) {
346
foreach ($result as $identifier => $matching_commits) {
347
if (count($matching_commits) == 1) {
348
$result[$identifier] = head($matching_commits);
349
} else {
350
// This reference is ambiguous (it matches more than one commit) so
351
// don't link it.
352
unset($result[$identifier]);
353
}
354
}
355
$this->identifierMap += $result;
356
}
357
358
return $commits;
359
}
360
361
protected function didFilterPage(array $commits) {
362
$viewer = $this->getViewer();
363
364
if ($this->mustFilterRefs) {
365
// If this flag is set, the query has an "Ancestors Of" constraint and
366
// at least one of the constraining refs had too many ancestors for us
367
// to apply the constraint with a big "commitIdentifier IN (%Ls)" clause.
368
// We're going to filter each page and hope we get a full result set
369
// before the query overheats.
370
371
$ancestor_list = mpull($commits, 'getCommitIdentifier');
372
$ancestor_list = array_values($ancestor_list);
373
374
foreach ($this->ancestorsOf as $ref) {
375
try {
376
$ancestor_list = DiffusionQuery::callConduitWithDiffusionRequest(
377
$viewer,
378
DiffusionRequest::newFromDictionary(
379
array(
380
'repository' => $this->refRepository,
381
'user' => $viewer,
382
)),
383
'diffusion.internal.ancestors',
384
array(
385
'ref' => $ref,
386
'commits' => $ancestor_list,
387
));
388
} catch (ConduitClientException $ex) {
389
throw new PhabricatorSearchConstraintException(
390
$ex->getMessage());
391
}
392
393
if (!$ancestor_list) {
394
break;
395
}
396
}
397
398
$ancestor_list = array_fuse($ancestor_list);
399
foreach ($commits as $key => $commit) {
400
$identifier = $commit->getCommitIdentifier();
401
if (!isset($ancestor_list[$identifier])) {
402
$this->didRejectResult($commit);
403
unset($commits[$key]);
404
}
405
}
406
407
if (!$commits) {
408
return $commits;
409
}
410
}
411
412
if ($this->needCommitData) {
413
$data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
414
'commitID in (%Ld)',
415
mpull($commits, 'getID'));
416
$data = mpull($data, null, 'getCommitID');
417
foreach ($commits as $commit) {
418
$commit_data = idx($data, $commit->getID());
419
if (!$commit_data) {
420
$commit_data = new PhabricatorRepositoryCommitData();
421
}
422
$commit->attachCommitData($commit_data);
423
}
424
}
425
426
if ($this->needAuditRequests) {
427
$requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere(
428
'commitPHID IN (%Ls)',
429
mpull($commits, 'getPHID'));
430
431
$requests = mgroup($requests, 'getCommitPHID');
432
foreach ($commits as $commit) {
433
$audit_requests = idx($requests, $commit->getPHID(), array());
434
$commit->attachAudits($audit_requests);
435
foreach ($audit_requests as $audit_request) {
436
$audit_request->attachCommit($commit);
437
}
438
}
439
}
440
441
if ($this->needIdentities) {
442
$identity_phids = array_merge(
443
mpull($commits, 'getAuthorIdentityPHID'),
444
mpull($commits, 'getCommitterIdentityPHID'));
445
446
$data = id(new PhabricatorRepositoryIdentityQuery())
447
->withPHIDs($identity_phids)
448
->setViewer($this->getViewer())
449
->execute();
450
$data = mpull($data, null, 'getPHID');
451
452
foreach ($commits as $commit) {
453
$author_identity = idx($data, $commit->getAuthorIdentityPHID());
454
$committer_identity = idx($data, $commit->getCommitterIdentityPHID());
455
$commit->attachIdentities($author_identity, $committer_identity);
456
}
457
}
458
459
if ($this->needDrafts) {
460
PhabricatorDraftEngine::attachDrafts(
461
$viewer,
462
$commits);
463
}
464
465
if ($this->needAuditAuthority) {
466
$authority_users = $this->needAuditAuthority;
467
468
// NOTE: This isn't very efficient since we're running two queries per
469
// user, but there's currently no way to figure out authority for
470
// multiple users in one query. Today, we only ever request authority for
471
// a single user and single commit, so this has no practical impact.
472
473
// NOTE: We're querying with the viewership of query viewer, not the
474
// actual users. If the viewer can't see a project or package, they
475
// won't be able to see who has authority on it. This is safer than
476
// showing them true authority, and should never matter today, but it
477
// also doesn't seem like a significant disclosure and might be
478
// reasonable to adjust later if it causes something weird or confusing
479
// to happen.
480
481
$authority_map = array();
482
foreach ($authority_users as $authority_user) {
483
$authority_phid = $authority_user->getPHID();
484
if (!$authority_phid) {
485
continue;
486
}
487
488
$result_phids = array();
489
490
// Users have authority over themselves.
491
$result_phids[] = $authority_phid;
492
493
// Users have authority over packages they own.
494
$owned_packages = id(new PhabricatorOwnersPackageQuery())
495
->setViewer($viewer)
496
->withAuthorityPHIDs(array($authority_phid))
497
->execute();
498
foreach ($owned_packages as $package) {
499
$result_phids[] = $package->getPHID();
500
}
501
502
// Users have authority over projects they're members of.
503
$projects = id(new PhabricatorProjectQuery())
504
->setViewer($viewer)
505
->withMemberPHIDs(array($authority_phid))
506
->execute();
507
foreach ($projects as $project) {
508
$result_phids[] = $project->getPHID();
509
}
510
511
$result_phids = array_fuse($result_phids);
512
513
foreach ($commits as $commit) {
514
$attach_phids = $result_phids;
515
516
// NOTE: When modifying your own commits, you act only on behalf of
517
// yourself, not your packages or projects. The idea here is that you
518
// can't accept your own commits. In the future, this might change or
519
// depend on configuration.
520
$author_phid = $commit->getAuthorPHID();
521
if ($author_phid == $authority_phid) {
522
$attach_phids = array($author_phid);
523
$attach_phids = array_fuse($attach_phids);
524
}
525
526
$commit->attachAuditAuthority($authority_user, $attach_phids);
527
}
528
}
529
}
530
531
return $commits;
532
}
533
534
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
535
$where = parent::buildWhereClauseParts($conn);
536
537
if ($this->repositoryPHIDs !== null) {
538
$map_repositories = id(new PhabricatorRepositoryQuery())
539
->setViewer($this->getViewer())
540
->withPHIDs($this->repositoryPHIDs)
541
->execute();
542
543
if (!$map_repositories) {
544
throw new PhabricatorEmptyQueryException();
545
}
546
$repository_ids = mpull($map_repositories, 'getID');
547
if ($this->repositoryIDs !== null) {
548
$repository_ids = array_merge($repository_ids, $this->repositoryIDs);
549
}
550
$this->withRepositoryIDs($repository_ids);
551
}
552
553
if ($this->ancestorsOf !== null) {
554
if (count($this->repositoryIDs) !== 1) {
555
throw new PhabricatorSearchConstraintException(
556
pht(
557
'To search for commits which are ancestors of particular refs, '.
558
'you must constrain the search to exactly one repository.'));
559
}
560
561
$repository_id = head($this->repositoryIDs);
562
$history_limit = $this->getRawResultLimit() * 32;
563
$viewer = $this->getViewer();
564
565
$repository = id(new PhabricatorRepositoryQuery())
566
->setViewer($viewer)
567
->withIDs(array($repository_id))
568
->executeOne();
569
570
if (!$repository) {
571
throw new PhabricatorEmptyQueryException();
572
}
573
574
if ($repository->isSVN()) {
575
throw new PhabricatorSearchConstraintException(
576
pht(
577
'Subversion does not support searching for ancestors of '.
578
'a particular ref. This operation is not meaningful in '.
579
'Subversion.'));
580
}
581
582
if ($repository->isHg()) {
583
throw new PhabricatorSearchConstraintException(
584
pht(
585
'Mercurial does not currently support searching for ancestors of '.
586
'a particular ref.'));
587
}
588
589
$can_constrain = true;
590
$history_identifiers = array();
591
foreach ($this->ancestorsOf as $key => $ref) {
592
try {
593
$raw_history = DiffusionQuery::callConduitWithDiffusionRequest(
594
$viewer,
595
DiffusionRequest::newFromDictionary(
596
array(
597
'repository' => $repository,
598
'user' => $viewer,
599
)),
600
'diffusion.historyquery',
601
array(
602
'commit' => $ref,
603
'limit' => $history_limit,
604
));
605
} catch (ConduitClientException $ex) {
606
throw new PhabricatorSearchConstraintException(
607
$ex->getMessage());
608
}
609
610
$ref_identifiers = array();
611
foreach ($raw_history['pathChanges'] as $change) {
612
$ref_identifiers[] = $change['commitIdentifier'];
613
}
614
615
// If this ref had fewer total commits than the limit, we're safe to
616
// apply the constraint as a large `IN (...)` query for a list of
617
// commit identifiers. This is efficient.
618
if ($history_limit) {
619
if (count($ref_identifiers) >= $history_limit) {
620
$can_constrain = false;
621
break;
622
}
623
}
624
625
$history_identifiers += array_fuse($ref_identifiers);
626
}
627
628
// If all refs had a small number of ancestors, we can just put the
629
// constraint into the query here and we're done. Otherwise, we need
630
// to filter each page after it comes out of the MySQL layer.
631
if ($can_constrain) {
632
$where[] = qsprintf(
633
$conn,
634
'commit.commitIdentifier IN (%Ls)',
635
$history_identifiers);
636
} else {
637
$this->mustFilterRefs = true;
638
$this->refRepository = $repository;
639
}
640
}
641
642
if ($this->ids !== null) {
643
$where[] = qsprintf(
644
$conn,
645
'commit.id IN (%Ld)',
646
$this->ids);
647
}
648
649
if ($this->phids !== null) {
650
$where[] = qsprintf(
651
$conn,
652
'commit.phid IN (%Ls)',
653
$this->phids);
654
}
655
656
if ($this->repositoryIDs !== null) {
657
$where[] = qsprintf(
658
$conn,
659
'commit.repositoryID IN (%Ld)',
660
$this->repositoryIDs);
661
}
662
663
if ($this->authorPHIDs !== null) {
664
$author_phids = $this->authorPHIDs;
665
if ($author_phids) {
666
$author_phids = $this->selectPossibleAuthors($author_phids);
667
if (!$author_phids) {
668
throw new PhabricatorEmptyQueryException(
669
pht('Author PHIDs contain no possible authors.'));
670
}
671
}
672
673
$where[] = qsprintf(
674
$conn,
675
'commit.authorPHID IN (%Ls)',
676
$author_phids);
677
}
678
679
if ($this->epochMin !== null) {
680
$where[] = qsprintf(
681
$conn,
682
'commit.epoch >= %d',
683
$this->epochMin);
684
}
685
686
if ($this->epochMax !== null) {
687
$where[] = qsprintf(
688
$conn,
689
'commit.epoch <= %d',
690
$this->epochMax);
691
}
692
693
if ($this->importing !== null) {
694
if ($this->importing) {
695
$where[] = qsprintf(
696
$conn,
697
'(commit.importStatus & %d) != %d',
698
PhabricatorRepositoryCommit::IMPORTED_ALL,
699
PhabricatorRepositoryCommit::IMPORTED_ALL);
700
} else {
701
$where[] = qsprintf(
702
$conn,
703
'(commit.importStatus & %d) = %d',
704
PhabricatorRepositoryCommit::IMPORTED_ALL,
705
PhabricatorRepositoryCommit::IMPORTED_ALL);
706
}
707
}
708
709
if ($this->identifiers !== null) {
710
$min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH;
711
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
712
713
$refs = array();
714
$bare = array();
715
foreach ($this->identifiers as $identifier) {
716
$matches = null;
717
preg_match('/^(?:[rR]([A-Z]+:?|[0-9]+:))?(.*)$/',
718
$identifier, $matches);
719
$repo = nonempty(rtrim($matches[1], ':'), null);
720
$commit_identifier = nonempty($matches[2], null);
721
722
if ($repo === null) {
723
if ($this->defaultRepository) {
724
$repo = $this->defaultRepository->getPHID();
725
}
726
}
727
728
if ($repo === null) {
729
if (strlen($commit_identifier) < $min_unqualified) {
730
continue;
731
}
732
$bare[] = $commit_identifier;
733
} else {
734
$refs[] = array(
735
'repository' => $repo,
736
'identifier' => $commit_identifier,
737
);
738
}
739
}
740
741
$sql = array();
742
743
foreach ($bare as $identifier) {
744
$sql[] = qsprintf(
745
$conn,
746
'(commit.commitIdentifier LIKE %> AND '.
747
'LENGTH(commit.commitIdentifier) = 40)',
748
$identifier);
749
}
750
751
if ($refs) {
752
$repositories = ipull($refs, 'repository');
753
754
$repos = id(new PhabricatorRepositoryQuery())
755
->setViewer($this->getViewer())
756
->withIdentifiers($repositories);
757
$repos->execute();
758
759
$repos = $repos->getIdentifierMap();
760
foreach ($refs as $key => $ref) {
761
$repo = idx($repos, $ref['repository']);
762
if (!$repo) {
763
continue;
764
}
765
766
if ($repo->isSVN()) {
767
if (!ctype_digit((string)$ref['identifier'])) {
768
continue;
769
}
770
$sql[] = qsprintf(
771
$conn,
772
'(commit.repositoryID = %d AND commit.commitIdentifier = %s)',
773
$repo->getID(),
774
// NOTE: Because the 'commitIdentifier' column is a string, MySQL
775
// ignores the index if we hand it an integer. Hand it a string.
776
// See T3377.
777
(int)$ref['identifier']);
778
} else {
779
if (strlen($ref['identifier']) < $min_qualified) {
780
continue;
781
}
782
783
$identifier = $ref['identifier'];
784
if (strlen($identifier) == 40) {
785
// MySQL seems to do slightly better with this version if the
786
// clause, so issue it if we have a full commit hash.
787
$sql[] = qsprintf(
788
$conn,
789
'(commit.repositoryID = %d
790
AND commit.commitIdentifier = %s)',
791
$repo->getID(),
792
$identifier);
793
} else {
794
$sql[] = qsprintf(
795
$conn,
796
'(commit.repositoryID = %d
797
AND commit.commitIdentifier LIKE %>)',
798
$repo->getID(),
799
$identifier);
800
}
801
}
802
}
803
}
804
805
if (!$sql) {
806
// If we discarded all possible identifiers (e.g., they all referenced
807
// bogus repositories or were all too short), make sure the query finds
808
// nothing.
809
throw new PhabricatorEmptyQueryException(
810
pht('No commit identifiers.'));
811
}
812
813
$where[] = qsprintf($conn, '%LO', $sql);
814
}
815
816
if ($this->auditIDs !== null) {
817
$where[] = qsprintf(
818
$conn,
819
'auditor.id IN (%Ld)',
820
$this->auditIDs);
821
}
822
823
if ($this->auditorPHIDs !== null) {
824
$where[] = qsprintf(
825
$conn,
826
'auditor.auditorPHID IN (%Ls)',
827
$this->auditorPHIDs);
828
}
829
830
if ($this->statuses !== null) {
831
$statuses = DiffusionCommitAuditStatus::newModernKeys(
832
$this->statuses);
833
834
$where[] = qsprintf(
835
$conn,
836
'commit.auditStatus IN (%Ls)',
837
$statuses);
838
}
839
840
if ($this->packagePHIDs !== null) {
841
$where[] = qsprintf(
842
$conn,
843
'package.dst IN (%Ls)',
844
$this->packagePHIDs);
845
}
846
847
if ($this->unreachable !== null) {
848
if ($this->unreachable) {
849
$where[] = qsprintf(
850
$conn,
851
'(commit.importStatus & %d) = %d',
852
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
853
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
854
} else {
855
$where[] = qsprintf(
856
$conn,
857
'(commit.importStatus & %d) = 0',
858
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
859
}
860
}
861
862
if ($this->permanent !== null) {
863
if ($this->permanent) {
864
$where[] = qsprintf(
865
$conn,
866
'(commit.importStatus & %d) = %d',
867
PhabricatorRepositoryCommit::IMPORTED_PERMANENT,
868
PhabricatorRepositoryCommit::IMPORTED_PERMANENT);
869
} else {
870
$where[] = qsprintf(
871
$conn,
872
'(commit.importStatus & %d) = 0',
873
PhabricatorRepositoryCommit::IMPORTED_PERMANENT);
874
}
875
}
876
877
return $where;
878
}
879
880
protected function didFilterResults(array $filtered) {
881
if ($this->identifierMap) {
882
foreach ($this->identifierMap as $name => $commit) {
883
if (isset($filtered[$commit->getPHID()])) {
884
unset($this->identifierMap[$name]);
885
}
886
}
887
}
888
}
889
890
private function shouldJoinAuditor() {
891
return ($this->auditIDs || $this->auditorPHIDs);
892
}
893
894
private function shouldJoinOwners() {
895
return (bool)$this->packagePHIDs;
896
}
897
898
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
899
$join = parent::buildJoinClauseParts($conn);
900
$audit_request = new PhabricatorRepositoryAuditRequest();
901
902
if ($this->shouldJoinAuditor()) {
903
$join[] = qsprintf(
904
$conn,
905
'JOIN %T auditor ON commit.phid = auditor.commitPHID',
906
$audit_request->getTableName());
907
}
908
909
if ($this->shouldJoinOwners()) {
910
$join[] = qsprintf(
911
$conn,
912
'JOIN %T package ON commit.phid = package.src
913
AND package.type = %s',
914
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
915
DiffusionCommitHasPackageEdgeType::EDGECONST);
916
}
917
918
return $join;
919
}
920
921
protected function shouldGroupQueryResultRows() {
922
if ($this->shouldJoinAuditor()) {
923
return true;
924
}
925
926
if ($this->shouldJoinOwners()) {
927
return true;
928
}
929
930
return parent::shouldGroupQueryResultRows();
931
}
932
933
public function getQueryApplicationClass() {
934
return 'PhabricatorDiffusionApplication';
935
}
936
937
public function getOrderableColumns() {
938
return parent::getOrderableColumns() + array(
939
'epoch' => array(
940
'table' => $this->getPrimaryTableAlias(),
941
'column' => 'epoch',
942
'type' => 'int',
943
'reverse' => false,
944
),
945
);
946
}
947
948
protected function newPagingMapFromPartialObject($object) {
949
return array(
950
'id' => (int)$object->getID(),
951
'epoch' => (int)$object->getEpoch(),
952
);
953
}
954
955
public function getBuiltinOrders() {
956
$parent = parent::getBuiltinOrders();
957
958
// Rename the default ID-based orders.
959
$parent['importnew'] = array(
960
'name' => pht('Import Date (Newest First)'),
961
) + $parent['newest'];
962
963
$parent['importold'] = array(
964
'name' => pht('Import Date (Oldest First)'),
965
) + $parent['oldest'];
966
967
return array(
968
'newest' => array(
969
'vector' => array('epoch', 'id'),
970
'name' => pht('Commit Date (Newest First)'),
971
),
972
'oldest' => array(
973
'vector' => array('-epoch', '-id'),
974
'name' => pht('Commit Date (Oldest First)'),
975
),
976
) + $parent;
977
}
978
979
private function selectPossibleAuthors(array $phids) {
980
// See PHI1057. Select PHIDs which might possibly be commit authors from
981
// a larger list of PHIDs. This primarily filters out packages and projects
982
// from "Responsible Users: ..." queries. Our goal in performing this
983
// filtering is to improve the performance of the final query.
984
985
foreach ($phids as $key => $phid) {
986
if (phid_get_type($phid) !== PhabricatorPeopleUserPHIDType::TYPECONST) {
987
unset($phids[$key]);
988
}
989
}
990
991
return $phids;
992
}
993
994
995
}
996
997