diff --git a/ChangeLog b/ChangeLog index c74e3d15e0b7..289bda7d4810 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,8 @@ phpMyAdmin - ChangeLog 5.1.1 (not yet released) - issue #13325 Fixed created procedure shows up in triggers and events and vice-versa - issue Fixed adding an event shows an empty row +- issue #16706 Fixed a PHP error when visualizing a nullable geometry column +- issue Fixed a PHP type error when exporting triggers to ODF 5.1.0 (2021-02-24) - issue #15350 Change Media (MIME) type references to Media type diff --git a/libraries/classes/Gis/GisVisualization.php b/libraries/classes/Gis/GisVisualization.php index 29c829d48a5a..0fc4f816e4af 100644 --- a/libraries/classes/Gis/GisVisualization.php +++ b/libraries/classes/Gis/GisVisualization.php @@ -28,6 +28,7 @@ use function mb_substr; use function ob_get_clean; use function ob_start; +use function is_string; /** * Handles visualization of GIS data @@ -623,6 +624,9 @@ private function scaleDataSet(array $data) foreach ($data as $row) { // Figure out the data type $ref_data = $row[$this->settings['spatialColumn']]; + if (! is_string($ref_data)) { + continue; + } $type_pos = mb_strpos($ref_data, '('); if ($type_pos === false) { continue; diff --git a/libraries/classes/Plugins/Export/ExportOdt.php b/libraries/classes/Plugins/Export/ExportOdt.php index d7b151a7b4fa..aa87978b1cf5 100644 --- a/libraries/classes/Plugins/Export/ExportOdt.php +++ b/libraries/classes/Plugins/Export/ExportOdt.php @@ -725,7 +725,7 @@ public function exportStructure( ); break; case 'triggers': - $triggers = $dbi->getTriggers($db, $table, $aliases); + $triggers = $dbi->getTriggers($db, $table); if ($triggers) { $GLOBALS['odt_buffer'] .= ' 50500, + 'spatialColumn' => 'abc', + 'isMariaDB' => false, + ]); + $this->callFunction( + $gis, + GisVisualization::class, + 'handleOptions', + [] + ); + $dataSet = $this->callFunction( + $gis, + GisVisualization::class, + 'scaleDataSet', + [ + [ + ['abc' => null],// The column is nullable + ['abc' => 2],// Some impossible test case + ], + ] + ); + $this->assertSame( + [ + 'scale' => 1, + 'x' => -15.0, + 'y' => -210.0, + 'minX' => 0.0, + 'maxX' => 0.0, + 'minY' => 0.0, + 'maxY' => 0.0, + 'height' => 450, + ], + $dataSet + ); + $dataSet = $this->callFunction( + $gis, + GisVisualization::class, + 'scaleDataSet', + [ + [ + ['abc' => null],// The column is nullable + ['abc' => 2],// Some impossible test case + ['abc' => 'MULTILINESTRING((36 140,47 233,62 75),(36 100,17 233,178 93))'], + ['abc' => 'POINT(100 250)'], + ['abc' => 'MULTIPOINT(125 50,156 250,178 143,175 80)'], + ], + ] + ); + $this->assertSame( + [ + 'scale' => 2.1, + 'x' => -38.21428571428572, + 'y' => 42.85714285714286, + 'minX' => 17.0, + 'maxX' => 178.0, + 'minY' => 50.0 , + 'maxY' => 250.0, + 'height' => 450, + + ], + $dataSet + ); + } + /** * Modify the query for an old version */ diff --git a/test/classes/UtilTest.php b/test/classes/UtilTest.php index ab725ba2287c..0909533a3ee8 100644 --- a/test/classes/UtilTest.php +++ b/test/classes/UtilTest.php @@ -2349,4 +2349,211 @@ public function testHandleDisableFKCheckCleanup(bool $fkChecksValue, string $set $GLOBALS['dbi'] = $oldDbi; } + + public function testCurrentUserHasPrivilegeSkipGrantTables(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['', ''])); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertTrue(Util::currentUserHasPrivilege('EVENT')); + $GLOBALS['dbi'] = $oldDbi; + } + + public function testCurrentUserHasUserPrivilege(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['groot_%', '%'])); + $dbi->expects($this->once()) + ->method('fetchValue') + ->with( + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + ) + ->will($this->returnValue('EVENT')); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertTrue(Util::currentUserHasPrivilege('EVENT')); + $GLOBALS['dbi'] = $oldDbi; + } + + public function testCurrentUserHasNotUserPrivilege(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['groot_%', '%'])); + $dbi->expects($this->once()) + ->method('fetchValue') + ->with( + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + ) + ->will($this->returnValue(false)); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertFalse(Util::currentUserHasPrivilege('EVENT')); + $GLOBALS['dbi'] = $oldDbi; + } + + public function testCurrentUserHasNotUserPrivilegeButDbPrivilege(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->setMethods(['getCurrentUserAndHost', 'fetchValue']) + ->disableOriginalConstructor() + ->getMock(); + + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['groot_%', '%'])); + $dbi->expects($this->exactly(2)) + ->method('fetchValue') + ->withConsecutive( + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'", + ], + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + . " AND 'my_data_base' LIKE `TABLE_SCHEMA`", + ] + ) + ->willReturnOnConsecutiveCalls( + false, + 'EVENT' + ); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertTrue(Util::currentUserHasPrivilege('EVENT', 'my_data_base')); + $GLOBALS['dbi'] = $oldDbi; + } + + public function testCurrentUserHasNotUserPrivilegeAndNotDbPrivilege(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->setMethods(['getCurrentUserAndHost', 'fetchValue']) + ->disableOriginalConstructor() + ->getMock(); + + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['groot_%', '%'])); + $dbi->expects($this->exactly(2)) + ->method('fetchValue') + ->withConsecutive( + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'", + ], + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + . " AND 'my_data_base' LIKE `TABLE_SCHEMA`", + ] + ) + ->willReturnOnConsecutiveCalls( + false, + false + ); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertFalse(Util::currentUserHasPrivilege('EVENT', 'my_data_base')); + $GLOBALS['dbi'] = $oldDbi; + } + + public function testCurrentUserHasNotUserPrivilegeAndNotDbPrivilegeButTablePrivilege(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->setMethods(['getCurrentUserAndHost', 'fetchValue']) + ->disableOriginalConstructor() + ->getMock(); + + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['groot_%', '%'])); + $dbi->expects($this->exactly(3)) + ->method('fetchValue') + ->withConsecutive( + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'", + ], + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + . " AND 'my_data_base' LIKE `TABLE_SCHEMA`", + ], + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`TABLE_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + . " AND 'my_data_base' LIKE `TABLE_SCHEMA` AND TABLE_NAME='my\_data\_table'", + ] + ) + ->willReturnOnConsecutiveCalls( + false, + false, + 'EVENT' + ); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertTrue(Util::currentUserHasPrivilege('EVENT', 'my_data_base', 'my_data_table')); + $GLOBALS['dbi'] = $oldDbi; + } + + public function testCurrentUserHasNotUserPrivilegeAndNotDbPrivilegeAndNotTablePrivilege(): void + { + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->setMethods(['getCurrentUserAndHost', 'fetchValue']) + ->disableOriginalConstructor() + ->getMock(); + + $dbi->expects($this->once()) + ->method('getCurrentUserAndHost') + ->will($this->returnValue(['groot_%', '%'])); + $dbi->expects($this->exactly(3)) + ->method('fetchValue') + ->withConsecutive( + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'", + ], + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + . " AND 'my_data_base' LIKE `TABLE_SCHEMA`", + ], + [ + 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`TABLE_PRIVILEGES`' + . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'" + . " AND 'my_data_base' LIKE `TABLE_SCHEMA` AND TABLE_NAME='my\_data\_table'", + ] + ) + ->willReturnOnConsecutiveCalls( + false, + false, + false + ); + + $oldDbi = $GLOBALS['dbi']; + $GLOBALS['dbi'] = $dbi; + $this->assertFalse(Util::currentUserHasPrivilege('EVENT', 'my_data_base', 'my_data_table')); + $GLOBALS['dbi'] = $oldDbi; + } }