Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php
12262 views
1
<?php
2
3
final class PhabricatorConfigDatabaseStatusController
4
extends PhabricatorConfigDatabaseController {
5
6
private $database;
7
private $table;
8
private $column;
9
private $key;
10
private $ref;
11
12
public function handleRequest(AphrontRequest $request) {
13
$viewer = $request->getViewer();
14
$this->database = $request->getURIData('database');
15
$this->table = $request->getURIData('table');
16
$this->column = $request->getURIData('column');
17
$this->key = $request->getURIData('key');
18
$this->ref = $request->getURIData('ref');
19
20
$query = new PhabricatorConfigSchemaQuery();
21
22
$actual = $query->loadActualSchemata();
23
$expect = $query->loadExpectedSchemata();
24
$comp = $query->buildComparisonSchemata($expect, $actual);
25
26
if ($this->ref !== null) {
27
$server_actual = idx($actual, $this->ref);
28
if (!$server_actual) {
29
return new Aphront404Response();
30
}
31
32
$server_comparison = $comp[$this->ref];
33
$server_expect = $expect[$this->ref];
34
35
if ($this->column) {
36
return $this->renderColumn(
37
$server_comparison,
38
$server_expect,
39
$server_actual,
40
$this->database,
41
$this->table,
42
$this->column);
43
} else if ($this->key) {
44
return $this->renderKey(
45
$server_comparison,
46
$server_expect,
47
$server_actual,
48
$this->database,
49
$this->table,
50
$this->key);
51
} else if ($this->table) {
52
return $this->renderTable(
53
$server_comparison,
54
$server_expect,
55
$server_actual,
56
$this->database,
57
$this->table);
58
} else if ($this->database) {
59
return $this->renderDatabase(
60
$server_comparison,
61
$server_expect,
62
$server_actual,
63
$this->database);
64
}
65
}
66
67
return $this->renderServers(
68
$comp,
69
$expect,
70
$actual);
71
}
72
73
private function buildResponse($title, $body) {
74
$nav = $this->newNavigation('schemata');
75
76
if (!$title) {
77
$title = pht('Database Status');
78
}
79
80
$ref = $this->ref;
81
$database = $this->database;
82
$table = $this->table;
83
$column = $this->column;
84
$key = $this->key;
85
86
$links = array();
87
$links[] = array(
88
pht('Database Status'),
89
'database/',
90
);
91
92
if ($database) {
93
$links[] = array(
94
$database,
95
"database/{$ref}/{$database}/",
96
);
97
}
98
99
if ($table) {
100
$links[] = array(
101
$table,
102
"database/{$ref}/{$database}/{$table}/",
103
);
104
}
105
106
if ($column) {
107
$links[] = array(
108
$column,
109
"database/{$ref}/{$database}/{$table}/col/{$column}/",
110
);
111
}
112
113
if ($key) {
114
$links[] = array(
115
$key,
116
"database/{$ref}/{$database}/{$table}/key/{$key}/",
117
);
118
}
119
120
$crumbs = $this->newCrumbs();
121
122
$last_key = last_key($links);
123
foreach ($links as $link_key => $link) {
124
list($name, $href) = $link;
125
if ($link_key == $last_key) {
126
$crumbs->addTextCrumb($name);
127
} else {
128
$crumbs->addTextCrumb($name, $this->getApplicationURI($href));
129
}
130
}
131
132
$doc_link = PhabricatorEnv::getDoclink('Managing Storage Adjustments');
133
$button = id(new PHUIButtonView())
134
->setTag('a')
135
->setIcon('fa-book')
136
->setHref($doc_link)
137
->setText(pht('Documentation'));
138
139
$header = $this->buildHeaderView($title, $button);
140
141
$content = id(new PHUITwoColumnView())
142
->setHeader($header)
143
->setFooter($body);
144
145
return $this->newPage()
146
->setTitle($title)
147
->setCrumbs($crumbs)
148
->setNavigation($nav)
149
->appendChild($content);
150
}
151
152
153
private function renderServers(
154
array $comp_servers,
155
array $expect_servers,
156
array $actual_servers) {
157
158
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
159
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
160
161
$rows = array();
162
foreach ($comp_servers as $ref_key => $comp) {
163
$actual = $actual_servers[$ref_key];
164
$expect = $expect_servers[$ref_key];
165
foreach ($comp->getDatabases() as $database_name => $database) {
166
$actual_database = $actual->getDatabase($database_name);
167
if ($actual_database) {
168
$charset = $actual_database->getCharacterSet();
169
$collation = $actual_database->getCollation();
170
} else {
171
$charset = null;
172
$collation = null;
173
}
174
175
$status = $database->getStatus();
176
$issues = $database->getIssues();
177
178
$uri = $this->getURI(
179
array(
180
'ref' => $ref_key,
181
'database' => $database_name,
182
));
183
184
$rows[] = array(
185
$this->renderIcon($status),
186
$ref_key,
187
phutil_tag(
188
'a',
189
array(
190
'href' => $uri,
191
),
192
$database_name),
193
$this->renderAttr($charset, $database->hasIssue($charset_issue)),
194
$this->renderAttr($collation, $database->hasIssue($collation_issue)),
195
);
196
}
197
}
198
199
$table = id(new AphrontTableView($rows))
200
->setHeaders(
201
array(
202
null,
203
pht('Server'),
204
pht('Database'),
205
pht('Charset'),
206
pht('Collation'),
207
))
208
->setColumnClasses(
209
array(
210
null,
211
null,
212
'wide pri',
213
null,
214
null,
215
));
216
217
$title = pht('Database Status');
218
$properties = $this->buildProperties(
219
array(
220
),
221
$comp->getIssues());
222
$properties = $this->buildConfigBoxView(pht('Properties'), $properties);
223
$table = $this->buildConfigBoxView(pht('Database'), $table);
224
225
return $this->buildResponse($title, array($properties, $table));
226
}
227
228
private function renderDatabase(
229
PhabricatorConfigServerSchema $comp,
230
PhabricatorConfigServerSchema $expect,
231
PhabricatorConfigServerSchema $actual,
232
$database_name) {
233
234
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
235
236
$database = $comp->getDatabase($database_name);
237
if (!$database) {
238
return new Aphront404Response();
239
}
240
241
$rows = array();
242
foreach ($database->getTables() as $table_name => $table) {
243
$status = $table->getStatus();
244
245
$uri = $this->getURI(
246
array(
247
'table' => $table_name,
248
));
249
250
$rows[] = array(
251
$this->renderIcon($status),
252
phutil_tag(
253
'a',
254
array(
255
'href' => $uri,
256
),
257
$table_name),
258
$this->renderAttr(
259
$table->getCollation(),
260
$table->hasIssue($collation_issue)),
261
$table->getPersistenceTypeDisplayName(),
262
);
263
}
264
265
$table = id(new AphrontTableView($rows))
266
->setHeaders(
267
array(
268
null,
269
pht('Table'),
270
pht('Collation'),
271
pht('Persistence'),
272
))
273
->setColumnClasses(
274
array(
275
null,
276
'wide pri',
277
null,
278
null,
279
));
280
281
$title = $database_name;
282
283
$actual_database = $actual->getDatabase($database_name);
284
if ($actual_database) {
285
$actual_charset = $actual_database->getCharacterSet();
286
$actual_collation = $actual_database->getCollation();
287
} else {
288
$actual_charset = null;
289
$actual_collation = null;
290
}
291
292
$expect_database = $expect->getDatabase($database_name);
293
if ($expect_database) {
294
$expect_charset = $expect_database->getCharacterSet();
295
$expect_collation = $expect_database->getCollation();
296
} else {
297
$expect_charset = null;
298
$expect_collation = null;
299
}
300
301
$properties = $this->buildProperties(
302
array(
303
array(
304
pht('Server'),
305
$this->ref,
306
),
307
array(
308
pht('Character Set'),
309
$actual_charset,
310
),
311
array(
312
pht('Expected Character Set'),
313
$expect_charset,
314
),
315
array(
316
pht('Collation'),
317
$actual_collation,
318
),
319
array(
320
pht('Expected Collation'),
321
$expect_collation,
322
),
323
),
324
$database->getIssues());
325
326
$properties = $this->buildConfigBoxView(pht('Properties'), $properties);
327
$table = $this->buildConfigBoxView(pht('Database'), $table);
328
329
return $this->buildResponse($title, array($properties, $table));
330
}
331
332
private function renderTable(
333
PhabricatorConfigServerSchema $comp,
334
PhabricatorConfigServerSchema $expect,
335
PhabricatorConfigServerSchema $actual,
336
$database_name,
337
$table_name) {
338
339
$type_issue = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE;
340
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
341
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
342
$nullable_issue = PhabricatorConfigStorageSchema::ISSUE_NULLABLE;
343
$unique_issue = PhabricatorConfigStorageSchema::ISSUE_UNIQUE;
344
$columns_issue = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS;
345
$longkey_issue = PhabricatorConfigStorageSchema::ISSUE_LONGKEY;
346
$auto_issue = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT;
347
348
$database = $comp->getDatabase($database_name);
349
if (!$database) {
350
return new Aphront404Response();
351
}
352
353
$table = $database->getTable($table_name);
354
if (!$table) {
355
return new Aphront404Response();
356
}
357
358
$actual_database = $actual->getDatabase($database_name);
359
$actual_table = null;
360
if ($actual_database) {
361
$actual_table = $actual_database->getTable($table_name);
362
}
363
364
$expect_database = $expect->getDatabase($database_name);
365
$expect_table = null;
366
if ($expect_database) {
367
$expect_table = $expect_database->getTable($table_name);
368
}
369
370
$rows = array();
371
foreach ($table->getColumns() as $column_name => $column) {
372
$expect_column = null;
373
if ($expect_table) {
374
$expect_column = $expect_table->getColumn($column_name);
375
}
376
377
$status = $column->getStatus();
378
379
$data_type = null;
380
if ($expect_column) {
381
$data_type = $expect_column->getDataType();
382
}
383
384
$uri = $this->getURI(
385
array(
386
'column' => $column_name,
387
));
388
389
$rows[] = array(
390
$this->renderIcon($status),
391
phutil_tag(
392
'a',
393
array(
394
'href' => $uri,
395
),
396
$column_name),
397
$data_type,
398
$this->renderAttr(
399
$column->getColumnType(),
400
$column->hasIssue($type_issue)),
401
$this->renderAttr(
402
$this->renderBoolean($column->getNullable()),
403
$column->hasIssue($nullable_issue)),
404
$this->renderAttr(
405
$this->renderBoolean($column->getAutoIncrement()),
406
$column->hasIssue($auto_issue)),
407
$this->renderAttr(
408
$column->getCharacterSet(),
409
$column->hasIssue($charset_issue)),
410
$this->renderAttr(
411
$column->getCollation(),
412
$column->hasIssue($collation_issue)),
413
);
414
}
415
416
$table_view = id(new AphrontTableView($rows))
417
->setHeaders(
418
array(
419
null,
420
pht('Column'),
421
pht('Data Type'),
422
pht('Column Type'),
423
pht('Nullable'),
424
pht('Autoincrement'),
425
pht('Character Set'),
426
pht('Collation'),
427
))
428
->setColumnClasses(
429
array(
430
null,
431
'wide pri',
432
null,
433
null,
434
null,
435
null,
436
null,
437
));
438
439
$key_rows = array();
440
foreach ($table->getKeys() as $key_name => $key) {
441
$expect_key = null;
442
if ($expect_table) {
443
$expect_key = $expect_table->getKey($key_name);
444
}
445
446
$status = $key->getStatus();
447
448
$size = 0;
449
foreach ($key->getColumnNames() as $column_spec) {
450
list($column_name, $prefix) = $key->getKeyColumnAndPrefix($column_spec);
451
$column = $table->getColumn($column_name);
452
if (!$column) {
453
$size = 0;
454
break;
455
}
456
$size += $column->getKeyByteLength($prefix);
457
}
458
459
$size_formatted = null;
460
if ($size) {
461
$size_formatted = $this->renderAttr(
462
$size,
463
$key->hasIssue($longkey_issue));
464
}
465
466
$uri = $this->getURI(
467
array(
468
'key' => $key_name,
469
));
470
471
$key_rows[] = array(
472
$this->renderIcon($status),
473
phutil_tag(
474
'a',
475
array(
476
'href' => $uri,
477
),
478
$key_name),
479
$this->renderAttr(
480
implode(', ', $key->getColumnNames()),
481
$key->hasIssue($columns_issue)),
482
$this->renderAttr(
483
$this->renderBoolean($key->getUnique()),
484
$key->hasIssue($unique_issue)),
485
$size_formatted,
486
);
487
}
488
489
$keys_view = id(new AphrontTableView($key_rows))
490
->setHeaders(
491
array(
492
null,
493
pht('Key'),
494
pht('Columns'),
495
pht('Unique'),
496
pht('Size'),
497
))
498
->setColumnClasses(
499
array(
500
null,
501
'wide pri',
502
null,
503
null,
504
null,
505
));
506
507
$title = pht('%s.%s', $database_name, $table_name);
508
509
if ($actual_table) {
510
$actual_collation = $actual_table->getCollation();
511
} else {
512
$actual_collation = null;
513
}
514
515
if ($expect_table) {
516
$expect_collation = $expect_table->getCollation();
517
} else {
518
$expect_collation = null;
519
}
520
521
$properties = $this->buildProperties(
522
array(
523
array(
524
pht('Server'),
525
$this->ref,
526
),
527
array(
528
pht('Collation'),
529
$actual_collation,
530
),
531
array(
532
pht('Expected Collation'),
533
$expect_collation,
534
),
535
),
536
$table->getIssues());
537
538
$box_header = pht('%s.%s', $database_name, $table_name);
539
540
$properties = $this->buildConfigBoxView(pht('Properties'), $properties);
541
$table = $this->buildConfigBoxView(pht('Database'), $table_view);
542
$keys = $this->buildConfigBoxView(pht('Keys'), $keys_view);
543
544
return $this->buildResponse(
545
$title, array($properties, $table, $keys));
546
}
547
548
private function renderColumn(
549
PhabricatorConfigServerSchema $comp,
550
PhabricatorConfigServerSchema $expect,
551
PhabricatorConfigServerSchema $actual,
552
$database_name,
553
$table_name,
554
$column_name) {
555
556
$database = $comp->getDatabase($database_name);
557
if (!$database) {
558
return new Aphront404Response();
559
}
560
561
$table = $database->getTable($table_name);
562
if (!$table) {
563
return new Aphront404Response();
564
}
565
566
$column = $table->getColumn($column_name);
567
if (!$column) {
568
return new Aphront404Response();
569
}
570
571
$actual_database = $actual->getDatabase($database_name);
572
$actual_table = null;
573
$actual_column = null;
574
if ($actual_database) {
575
$actual_table = $actual_database->getTable($table_name);
576
if ($actual_table) {
577
$actual_column = $actual_table->getColumn($column_name);
578
}
579
}
580
581
$expect_database = $expect->getDatabase($database_name);
582
$expect_table = null;
583
$expect_column = null;
584
if ($expect_database) {
585
$expect_table = $expect_database->getTable($table_name);
586
if ($expect_table) {
587
$expect_column = $expect_table->getColumn($column_name);
588
}
589
}
590
591
if ($actual_column) {
592
$actual_coltype = $actual_column->getColumnType();
593
$actual_charset = $actual_column->getCharacterSet();
594
$actual_collation = $actual_column->getCollation();
595
$actual_nullable = $actual_column->getNullable();
596
$actual_auto = $actual_column->getAutoIncrement();
597
} else {
598
$actual_coltype = null;
599
$actual_charset = null;
600
$actual_collation = null;
601
$actual_nullable = null;
602
$actual_auto = null;
603
}
604
605
if ($expect_column) {
606
$data_type = $expect_column->getDataType();
607
$expect_coltype = $expect_column->getColumnType();
608
$expect_charset = $expect_column->getCharacterSet();
609
$expect_collation = $expect_column->getCollation();
610
$expect_nullable = $expect_column->getNullable();
611
$expect_auto = $expect_column->getAutoIncrement();
612
} else {
613
$data_type = null;
614
$expect_coltype = null;
615
$expect_charset = null;
616
$expect_collation = null;
617
$expect_nullable = null;
618
$expect_auto = null;
619
}
620
621
622
$title = pht(
623
'%s.%s.%s',
624
$database_name,
625
$table_name,
626
$column_name);
627
628
$properties = $this->buildProperties(
629
array(
630
array(
631
pht('Server'),
632
$this->ref,
633
),
634
array(
635
pht('Data Type'),
636
$data_type,
637
),
638
array(
639
pht('Column Type'),
640
$actual_coltype,
641
),
642
array(
643
pht('Expected Column Type'),
644
$expect_coltype,
645
),
646
array(
647
pht('Character Set'),
648
$actual_charset,
649
),
650
array(
651
pht('Expected Character Set'),
652
$expect_charset,
653
),
654
array(
655
pht('Collation'),
656
$actual_collation,
657
),
658
array(
659
pht('Expected Collation'),
660
$expect_collation,
661
),
662
array(
663
pht('Nullable'),
664
$this->renderBoolean($actual_nullable),
665
),
666
array(
667
pht('Expected Nullable'),
668
$this->renderBoolean($expect_nullable),
669
),
670
array(
671
pht('Autoincrement'),
672
$this->renderBoolean($actual_auto),
673
),
674
array(
675
pht('Expected Autoincrement'),
676
$this->renderBoolean($expect_auto),
677
),
678
),
679
$column->getIssues());
680
681
$properties = $this->buildConfigBoxView(pht('Properties'), $properties);
682
683
return $this->buildResponse($title, $properties);
684
}
685
686
private function renderKey(
687
PhabricatorConfigServerSchema $comp,
688
PhabricatorConfigServerSchema $expect,
689
PhabricatorConfigServerSchema $actual,
690
$database_name,
691
$table_name,
692
$key_name) {
693
694
$database = $comp->getDatabase($database_name);
695
if (!$database) {
696
return new Aphront404Response();
697
}
698
699
$table = $database->getTable($table_name);
700
if (!$table) {
701
return new Aphront404Response();
702
}
703
704
$key = $table->getKey($key_name);
705
if (!$key) {
706
return new Aphront404Response();
707
}
708
709
$actual_database = $actual->getDatabase($database_name);
710
$actual_table = null;
711
$actual_key = null;
712
if ($actual_database) {
713
$actual_table = $actual_database->getTable($table_name);
714
if ($actual_table) {
715
$actual_key = $actual_table->getKey($key_name);
716
}
717
}
718
719
$expect_database = $expect->getDatabase($database_name);
720
$expect_table = null;
721
$expect_key = null;
722
if ($expect_database) {
723
$expect_table = $expect_database->getTable($table_name);
724
if ($expect_table) {
725
$expect_key = $expect_table->getKey($key_name);
726
}
727
}
728
729
if ($actual_key) {
730
$actual_columns = $actual_key->getColumnNames();
731
$actual_unique = $actual_key->getUnique();
732
} else {
733
$actual_columns = array();
734
$actual_unique = null;
735
}
736
737
if ($expect_key) {
738
$expect_columns = $expect_key->getColumnNames();
739
$expect_unique = $expect_key->getUnique();
740
} else {
741
$expect_columns = array();
742
$expect_unique = null;
743
}
744
745
$title = pht(
746
'%s.%s (%s)',
747
$database_name,
748
$table_name,
749
$key_name);
750
751
$properties = $this->buildProperties(
752
array(
753
array(
754
pht('Server'),
755
$this->ref,
756
),
757
array(
758
pht('Unique'),
759
$this->renderBoolean($actual_unique),
760
),
761
array(
762
pht('Expected Unique'),
763
$this->renderBoolean($expect_unique),
764
),
765
array(
766
pht('Columns'),
767
implode(', ', $actual_columns),
768
),
769
array(
770
pht('Expected Columns'),
771
implode(', ', $expect_columns),
772
),
773
),
774
$key->getIssues());
775
776
$properties = $this->buildConfigBoxView(pht('Properties'), $properties);
777
778
return $this->buildResponse($title, $properties);
779
}
780
781
private function buildProperties(array $properties, array $issues) {
782
$view = id(new PHUIPropertyListView())
783
->setUser($this->getRequest()->getUser());
784
785
foreach ($properties as $property) {
786
list($key, $value) = $property;
787
$view->addProperty($key, $value);
788
}
789
790
$status_view = new PHUIStatusListView();
791
if (!$issues) {
792
$status_view->addItem(
793
id(new PHUIStatusItemView())
794
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
795
->setTarget(pht('No Schema Issues')));
796
} else {
797
foreach ($issues as $issue) {
798
$note = PhabricatorConfigStorageSchema::getIssueDescription($issue);
799
800
$status = PhabricatorConfigStorageSchema::getIssueStatus($issue);
801
switch ($status) {
802
case PhabricatorConfigStorageSchema::STATUS_WARN:
803
$icon = PHUIStatusItemView::ICON_WARNING;
804
$color = 'yellow';
805
break;
806
case PhabricatorConfigStorageSchema::STATUS_FAIL:
807
default:
808
$icon = PHUIStatusItemView::ICON_REJECT;
809
$color = 'red';
810
break;
811
}
812
813
$item = id(new PHUIStatusItemView())
814
->setTarget(PhabricatorConfigStorageSchema::getIssueName($issue))
815
->setIcon($icon, $color)
816
->setNote($note);
817
818
$status_view->addItem($item);
819
}
820
}
821
$view->addProperty(pht('Schema Status'), $status_view);
822
823
return phutil_tag_div('config-page-property', $view);
824
}
825
826
private function getURI(array $properties) {
827
$defaults = array(
828
'ref' => $this->ref,
829
'database' => $this->database,
830
'table' => $this->table,
831
'column' => $this->column,
832
'key' => $this->key,
833
);
834
835
$properties = $properties + $defaults;
836
$properties = array_select_keys($properties, array_keys($defaults));
837
838
$parts = array();
839
foreach ($properties as $key => $property) {
840
if ($property === null || !strlen($property)) {
841
continue;
842
}
843
844
if ($key == 'column') {
845
$parts[] = 'col';
846
} else if ($key == 'key') {
847
$parts[] = 'key';
848
}
849
850
$parts[] = $property;
851
}
852
853
if ($parts) {
854
$parts = implode('/', $parts).'/';
855
} else {
856
$parts = null;
857
}
858
859
return $this->getApplicationURI('/database/'.$parts);
860
}
861
862
}
863
864