Skip to content

Bug: Database\BaseUtils::optimizeTable return values and improper use of getResultArray #4059

@sneakyimp

Description

@sneakyimp

Describe the bug
It is not entirely clear what the return value of BaseUtils::optimizeTable is supposed to be, but from its usage in tests/system/Database/Live/DBUtilsTest, it appears to be cast as a boolean value. This function has a few problems.

The first is that it checks the $query value returned by $this->db->query() against false to determine success. Unless I'm missing something, that $query value will never be false because the function that handles the query, BaseConnection::query() will still instantiate and return some Database\ResultInterface with resultID set to the false value returned by the db-specific query function. I wonder if we should ever be instantiating a Result object with resultID of false?

The second problem is that this optimizeTable function immediately calls getResultArray on the ResultInterface object returned, despite the fact that the query being run isn't really the sort of query that should return a result set. While it is true that a MySQLi OPTIMIZE TABLE query does return a fetchable result array on a successful operation, the SQLSRV ALTER INDEX... query that gets run by this optimizeTable function is clearly marked among the isWriteType queries, which means the requested SQL server statement returned will not have a scrollable cursor, and any attempt to iterate it is probably ill-advised. Attempting to call sqlsrv_num_rows on such a resultID will return false, signifying an error condition.

The third problem is that optimizeTable returns current($query), where $query is the (in most cases empty) array returned from getResultArray(). Note that current([]) is false, which is probably not the desired return value for the optimizeTable function. If you run optimizeTable using SQLSRV in production mode, the return value of getResultArray() is in fact [] so that return value of optimizeTable is false. I'm guessing this is not the desired outcome.

I think we should modify the handling of the optimizeTable queries to delegate more responsibility to the various db-specific Util classes rather than trying to have so much code in common. The various databases provide pretty different responses and it seems wiser to me to handle the db-specific code in the relevant class. I also think we should have optimizeTable specifically return a boolean value of true or false and nothing else and clarify the javadoc comments.

Finally, I'm not certain but it looked like the original developer was expecting Database\BaseConnection::query() to return false if the query returns false. It does not. Instead, it instantiates a Result object in line 662https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/Database/BaseConnection.php#L662. IMHO, this function returning BaseResult|Query|false is unnecessarily complicated.

CodeIgniter 4 version
This issue is present in the CI4 develop branch in the very latest code as of this writing.

Affected module(s)
system\Database\BaseUtils
system\Database*\Utils
system\Database\BaseConnection
tests\system\Database\Live\DbUtilsTest

Expected behavior, and steps to reproduce if appropriate
I think the function Database\BaseUtil::optimizeTable should return true if the table optimization succeeded and false otherwise.

To reproduce the problem, just download the latest development branch and edit your .env file to specify:

CI_ENVIRONMENT = production

database.tests.DBDriver = SQLSRV

Be sure to set the appropriate credentials to connect to your SQL Server database.

Put this code in a controller and run it:

	function bar() {
		$db = \Config\Database::connect("tests");

		$util = (new Database())->loadUtils($db);
		
		$d = $util->optimizeTable('db_job');
		var_dump($d);
	}

Despite the successful optimization query being run, the output should be:

bool(false)

You'll also get false if you run it with SQLite3. I've not tested Postgres

Context

  • OS: Ubuntu 18.04
  • Web server: CLI actually, but PHP-FPM and apache as well
  • PHP version: PHP 7.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugVerified issues on the current code behavior or pull requests that will fix themdatabaseIssues or pull requests that affect the database layer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions