Skip to content

Commit 64242e6

Browse files
committed
Support for views #409
1 parent da352c2 commit 64242e6

File tree

3 files changed

+127
-45
lines changed

3 files changed

+127
-45
lines changed

api.php

Lines changed: 111 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -482,36 +482,34 @@ public function jsonSerialize()
482482
class ReflectedDatabase implements \JsonSerializable
483483
{
484484
private $name;
485-
private $tableNames;
485+
private $tableTypes;
486486

487-
public function __construct(String $name, array $tableNames)
487+
public function __construct(String $name, array $tableTypes)
488488
{
489489
$this->name = $name;
490-
$this->tableNames = [];
491-
foreach ($tableNames as $tableName) {
492-
$this->tableNames[$tableName] = true;
493-
}
490+
$this->tableTypes = $tableTypes;
494491
}
495492

496493
public static function fromReflection(GenericReflection $reflection): ReflectedDatabase
497494
{
498495
$name = $reflection->getDatabaseName();
499-
$tableNames = [];
496+
$tableTypes = [];
500497
foreach ($reflection->getTables() as $table) {
501498
$tableName = $table['TABLE_NAME'];
499+
$tableType = $table['TABLE_TYPE'];
502500
if (in_array($tableName, $reflection->getIgnoredTables())) {
503501
continue;
504502
}
505-
$tableNames[$tableName] = true;
503+
$tableTypes[$tableName] = $tableType;
506504
}
507-
return new ReflectedDatabase($name, array_keys($tableNames));
505+
return new ReflectedDatabase($name, $tableTypes);
508506
}
509507

510508
public static function fromJson( /* object */$json): ReflectedDatabase
511509
{
512510
$name = $json->name;
513-
$tableNames = $json->tables;
514-
return new ReflectedDatabase($name, $tableNames);
511+
$tableTypes = (array) $json->tables;
512+
return new ReflectedDatabase($name, $tableTypes);
515513
}
516514

517515
public function getName(): String
@@ -521,28 +519,33 @@ public function getName(): String
521519

522520
public function hasTable(String $tableName): bool
523521
{
524-
return isset($this->tableNames[$tableName]);
522+
return isset($this->tableTypes[$tableName]);
523+
}
524+
525+
public function getType(String $tableName): String
526+
{
527+
return isset($this->tableTypes[$tableName]) ? $this->tableTypes[$tableName] : '';
525528
}
526529

527530
public function getTableNames(): array
528531
{
529-
return array_keys($this->tableNames);
532+
return array_keys($this->tableTypes);
530533
}
531534

532535
public function removeTable(String $tableName): bool
533536
{
534-
if (!isset($this->tableNames[$tableName])) {
537+
if (!isset($this->tableTypes[$tableName])) {
535538
return false;
536539
}
537-
unset($this->tableNames[$tableName]);
540+
unset($this->tableTypes[$tableName]);
538541
return true;
539542
}
540543

541544
public function serialize()
542545
{
543546
return [
544547
'name' => $this->name,
545-
'tables' => array_keys($this->tableNames),
548+
'tables' => $this->tableTypes,
546549
];
547550
}
548551

@@ -557,13 +560,15 @@ public function jsonSerialize()
557560
class ReflectedTable implements \JsonSerializable
558561
{
559562
private $name;
563+
private $type;
560564
private $columns;
561565
private $pk;
562566
private $fks;
563567

564-
public function __construct(String $name, array $columns)
568+
public function __construct(String $name, String $type, array $columns)
565569
{
566570
$this->name = $name;
571+
$this->type = $type;
567572
$this->columns = [];
568573
foreach ($columns as $column) {
569574
$columnName = $column->getName();
@@ -585,10 +590,10 @@ public function __construct(String $name, array $columns)
585590
}
586591
}
587592

588-
public static function fromReflection(GenericReflection $reflection, String $name): ReflectedTable
593+
public static function fromReflection(GenericReflection $reflection, String $name, String $type): ReflectedTable
589594
{
590595
$columns = [];
591-
foreach ($reflection->getTableColumns($name) as $tableColumn) {
596+
foreach ($reflection->getTableColumns($name, $type) as $tableColumn) {
592597
$column = ReflectedColumn::fromReflection($reflection, $tableColumn);
593598
$columns[$column->getName()] = $column;
594599
}
@@ -604,19 +609,20 @@ public static function fromReflection(GenericReflection $reflection, String $nam
604609
foreach ($fks as $columnName => $table) {
605610
$columns[$columnName]->setFk($table);
606611
}
607-
return new ReflectedTable($name, array_values($columns));
612+
return new ReflectedTable($name, $type, array_values($columns));
608613
}
609614

610615
public static function fromJson( /* object */$json): ReflectedTable
611616
{
612617
$name = $json->name;
618+
$type = $json->type;
613619
$columns = [];
614620
if (isset($json->columns) && is_array($json->columns)) {
615621
foreach ($json->columns as $column) {
616622
$columns[] = ReflectedColumn::fromJson($column);
617623
}
618624
}
619-
return new ReflectedTable($name, $columns);
625+
return new ReflectedTable($name, $type, $columns);
620626
}
621627

622628
public function hasColumn(String $columnName): bool
@@ -639,6 +645,11 @@ public function getName(): String
639645
return $this->name;
640646
}
641647

648+
public function getType(): String
649+
{
650+
return $this->type;
651+
}
652+
642653
public function columnNames(): array
643654
{
644655
return array_keys($this->columns);
@@ -673,6 +684,7 @@ public function serialize()
673684
{
674685
return [
675686
'name' => $this->name,
687+
'type' => $this->type,
676688
'columns' => array_values($this->columns),
677689
];
678690
}
@@ -871,7 +883,8 @@ private function loadTable(String $tableName, bool $useCache): ReflectedTable
871883
if ($data != '') {
872884
$table = ReflectedTable::fromJson(json_decode(gzuncompress($data)));
873885
} else {
874-
$table = ReflectedTable::fromReflection($this->db->reflection(), $tableName);
886+
$tableType = $this->database->getType($tableName);
887+
$table = ReflectedTable::fromReflection($this->db->reflection(), $tableName, $tableType);
875888
$data = gzcompress(json_encode($table, JSON_UNESCAPED_UNICODE));
876889
$this->cache->set("ReflectedTable($tableName)", $data, $this->ttl);
877890
}
@@ -893,6 +906,11 @@ public function hasTable(String $tableName): bool
893906
return $this->database->hasTable($tableName);
894907
}
895908

909+
public function getType(String $tableName): String
910+
{
911+
return $this->database->getType($tableName);
912+
}
913+
896914
public function getTable(String $tableName): ReflectedTable
897915
{
898916
if (!isset($this->tables[$tableName])) {
@@ -1147,11 +1165,14 @@ public function _list(Request $request): Response
11471165
public function read(Request $request): Response
11481166
{
11491167
$table = $request->getPathSegment(2);
1150-
$id = $request->getPathSegment(3);
1151-
$params = $request->getParams();
11521168
if (!$this->service->hasTable($table)) {
11531169
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
11541170
}
1171+
if ($this->service->getType($table) != 'table') {
1172+
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
1173+
}
1174+
$id = $request->getPathSegment(3);
1175+
$params = $request->getParams();
11551176
if (strpos($id, ',') !== false) {
11561177
$ids = explode(',', $id);
11571178
$result = [];
@@ -1171,14 +1192,17 @@ public function read(Request $request): Response
11711192
public function create(Request $request): Response
11721193
{
11731194
$table = $request->getPathSegment(2);
1195+
if (!$this->service->hasTable($table)) {
1196+
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
1197+
}
1198+
if ($this->service->getType($table) != 'table') {
1199+
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
1200+
}
11741201
$record = $request->getBody();
11751202
if ($record === null) {
11761203
return $this->responder->error(ErrorCode::HTTP_MESSAGE_NOT_READABLE, '');
11771204
}
11781205
$params = $request->getParams();
1179-
if (!$this->service->hasTable($table)) {
1180-
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
1181-
}
11821206
if (is_array($record)) {
11831207
$result = array();
11841208
foreach ($record as $r) {
@@ -1193,15 +1217,18 @@ public function create(Request $request): Response
11931217
public function update(Request $request): Response
11941218
{
11951219
$table = $request->getPathSegment(2);
1220+
if (!$this->service->hasTable($table)) {
1221+
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
1222+
}
1223+
if ($this->service->getType($table) != 'table') {
1224+
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
1225+
}
11961226
$id = $request->getPathSegment(3);
1227+
$params = $request->getParams();
11971228
$record = $request->getBody();
11981229
if ($record === null) {
11991230
return $this->responder->error(ErrorCode::HTTP_MESSAGE_NOT_READABLE, '');
12001231
}
1201-
$params = $request->getParams();
1202-
if (!$this->service->hasTable($table)) {
1203-
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
1204-
}
12051232
$ids = explode(',', $id);
12061233
if (is_array($record)) {
12071234
if (count($ids) != count($record)) {
@@ -1223,11 +1250,14 @@ public function update(Request $request): Response
12231250
public function delete(Request $request): Response
12241251
{
12251252
$table = $request->getPathSegment(2);
1226-
$id = $request->getPathSegment(3);
1227-
$params = $request->getParams();
12281253
if (!$this->service->hasTable($table)) {
12291254
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
12301255
}
1256+
if ($this->service->getType($table) != 'table') {
1257+
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
1258+
}
1259+
$id = $request->getPathSegment(3);
1260+
$params = $request->getParams();
12311261
$ids = explode(',', $id);
12321262
if (count($ids) > 1) {
12331263
$result = array();
@@ -1243,15 +1273,18 @@ public function delete(Request $request): Response
12431273
public function increment(Request $request): Response
12441274
{
12451275
$table = $request->getPathSegment(2);
1276+
if (!$this->service->hasTable($table)) {
1277+
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
1278+
}
1279+
if ($this->service->getType($table) != 'table') {
1280+
return $this->responder->error(ErrorCode::OPERATION_NOT_SUPPORTED, __FUNCTION__);
1281+
}
12461282
$id = $request->getPathSegment(3);
12471283
$record = $request->getBody();
12481284
if ($record === null) {
12491285
return $this->responder->error(ErrorCode::HTTP_MESSAGE_NOT_READABLE, '');
12501286
}
12511287
$params = $request->getParams();
1252-
if (!$this->service->hasTable($table)) {
1253-
return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table);
1254-
}
12551288
$ids = explode(',', $id);
12561289
if (is_array($record)) {
12571290
if (count($ids) != count($record)) {
@@ -2406,17 +2439,17 @@ public function getIgnoredTables(): array
24062439
{
24072440
switch ($this->driver) {
24082441
case 'mysql':return [];
2409-
case 'pgsql':return ['spatial_ref_sys'];
2442+
case 'pgsql':return ['spatial_ref_sys', 'raster_columns', 'raster_overviews', 'geography_columns', 'geometry_columns'];
24102443
case 'sqlsrv':return [];
24112444
}
24122445
}
24132446

24142447
private function getTablesSQL(): String
24152448
{
24162449
switch ($this->driver) {
2417-
case 'mysql':return 'SELECT "TABLE_NAME" FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" IN (\'BASE TABLE\') AND "TABLE_SCHEMA" = ? ORDER BY BINARY "TABLE_NAME"';
2418-
case 'pgsql':return 'SELECT c.relname as "TABLE_NAME" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN (\'r\') AND n.nspname <> \'pg_catalog\' AND n.nspname <> \'information_schema\' AND n.nspname !~ \'^pg_toast\' AND pg_catalog.pg_table_is_visible(c.oid) AND \'\' <> ? ORDER BY "TABLE_NAME";';
2419-
case 'sqlsrv':return 'SELECT o.name as "TABLE_NAME" FROM sysobjects o WHERE o.xtype = \'U\' ORDER BY "TABLE_NAME"';
2450+
case 'mysql':return 'SELECT "TABLE_NAME", "TABLE_TYPE" FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" IN (\'BASE TABLE\' , \'VIEW\') AND "TABLE_SCHEMA" = ? ORDER BY BINARY "TABLE_NAME"';
2451+
case 'pgsql':return 'SELECT c.relname as "TABLE_NAME", c.relkind as "TABLE_TYPE" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN (\'r\', \'v\') AND n.nspname <> \'pg_catalog\' AND n.nspname <> \'information_schema\' AND n.nspname !~ \'^pg_toast\' AND pg_catalog.pg_table_is_visible(c.oid) AND \'\' <> ? ORDER BY "TABLE_NAME";';
2452+
case 'sqlsrv':return 'SELECT o.name as "TABLE_NAME", o.xtype as "TABLE_TYPE" FROM sysobjects o WHERE o.xtype IN (\'U\', \'V\') ORDER BY "TABLE_NAME"';
24202453
}
24212454
}
24222455

@@ -2455,13 +2488,36 @@ public function getDatabaseName(): String
24552488
public function getTables(): array
24562489
{
24572490
$sql = $this->getTablesSQL();
2458-
return $this->query($sql, [$this->database]);
2491+
$results = $this->query($sql, [$this->database]);
2492+
foreach ($results as &$result) {
2493+
switch ($this->driver) {
2494+
case 'mysql':
2495+
$map = ['BASE TABLE' => 'table', 'VIEW' => 'view'];
2496+
$result['TABLE_TYPE'] = $map[$result['TABLE_TYPE']];
2497+
break;
2498+
case 'pgsql':
2499+
$map = ['r' => 'table', 'v' => 'view'];
2500+
$result['TABLE_TYPE'] = $map[$result['TABLE_TYPE']];
2501+
break;
2502+
case 'sqlsrv':
2503+
$map = ['U' => 'table', 'V' => 'view'];
2504+
$result['TABLE_TYPE'] = $map[trim($result['TABLE_TYPE'])];
2505+
break;
2506+
}
2507+
}
2508+
return $results;
24592509
}
24602510

2461-
public function getTableColumns(String $tableName): array
2511+
public function getTableColumns(String $tableName, String $type): array
24622512
{
24632513
$sql = $this->getTableColumnsSQL();
2464-
return $this->query($sql, [$tableName, $this->database]);
2514+
$results = $this->query($sql, [$tableName, $this->database]);
2515+
if ($type == 'view') {
2516+
foreach ($results as &$result) {
2517+
$result['IS_NULLABLE'] = false;
2518+
}
2519+
}
2520+
return $results;
24652521
}
24662522

24672523
public function getTablePrimaryKeys(String $tableName): array
@@ -3494,12 +3550,16 @@ private function isOperationOnColumnAllowed(String $operation, String $tableName
34943550
private function setPath(String $tableName) /*: void*/
34953551
{
34963552
$table = $this->reflection->getTable($tableName);
3553+
$type = $table->getType($tableName);
34973554
$pk = $table->getPk();
34983555
$pkName = $pk ? $pk->getName() : '';
34993556
foreach ($this->operations as $operation => $method) {
35003557
if (!$pkName && $operation != 'list') {
35013558
continue;
35023559
}
3560+
if ($type != 'table' && $operation != 'list') {
3561+
continue;
3562+
}
35033563
if (!$this->isOperationOnTableAllowed($operation, $tableName)) {
35043564
continue;
35053565
}
@@ -4041,6 +4101,7 @@ class ErrorCode
40414101
const ACCESS_DENIED = 1012;
40424102
const INPUT_VALIDATION_FAILED = 1013;
40434103
const OPERATION_FORBIDDEN = 1014;
4104+
const OPERATION_NOT_SUPPORTED = 1015;
40444105

40454106
private $values = [
40464107
9999 => ["%s", Response::INTERNAL_SERVER_ERROR],
@@ -4059,6 +4120,7 @@ class ErrorCode
40594120
1012 => ["Access denied for '%s'", Response::FORBIDDEN],
40604121
1013 => ["Input validation failed for '%s'", Response::UNPROCESSABLE_ENTITY],
40614122
1014 => ["Operation forbidden", Response::FORBIDDEN],
4123+
1015 => ["Operation '%s' not supported", Response::METHOD_NOT_ALLOWED],
40624124
];
40634125

40644126
public function __construct(int $code)
@@ -4370,6 +4432,11 @@ public function hasTable(String $table): bool
43704432
return $this->reflection->hasTable($table);
43714433
}
43724434

4435+
public function getType(String $table): String
4436+
{
4437+
return $this->reflection->getType($table);
4438+
}
4439+
43734440
public function create(String $tableName, /* object */ $record, array $params)
43744441
{
43754442
$this->sanitizeRecord($tableName, $record, '');
@@ -5231,10 +5298,10 @@ class Response
52315298
const UNAUTHORIZED = 401;
52325299
const FORBIDDEN = 403;
52335300
const NOT_FOUND = 404;
5301+
const METHOD_NOT_ALLOWED = 405;
52345302
const CONFLICT = 409;
52355303
const UNPROCESSABLE_ENTITY = 422;
52365304
const INTERNAL_SERVER_ERROR = 500;
5237-
52385305
private $status;
52395306
private $headers;
52405307
private $body;

0 commit comments

Comments
 (0)