StatementTest.php

Same filename in other branches
  1. 9 core/tests/Drupal/KernelTests/Core/Database/StatementTest.php
  2. 10 core/tests/Drupal/KernelTests/Core/Database/StatementTest.php

Namespace

Drupal\KernelTests\Core\Database

File

core/tests/Drupal/KernelTests/Core/Database/StatementTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\KernelTests\Core\Database;

use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\StatementInterface;

/**
 * Tests the Statement classes.
 *
 * @group Database
 */
class StatementTest extends DatabaseTestBase {
    
    /**
     * Tests that a prepared statement object can be reused for multiple inserts.
     */
    public function testRepeatedInsertStatementReuse() : void {
        $num_records_before = $this->connection
            ->select('test')
            ->countQuery()
            ->execute()
            ->fetchField();
        $sql = "INSERT INTO {test} ([name], [age]) VALUES (:name, :age)";
        $args = [
            ':name' => 'Larry',
            ':age' => '30',
        ];
        $options = [
            'allow_square_brackets' => FALSE,
        ];
        $stmt = $this->connection
            ->prepareStatement($sql, $options);
        $this->assertInstanceOf(StatementInterface::class, $stmt);
        $this->assertTrue($stmt->execute($args, $options));
        // We should be able to specify values in any order if named.
        $args = [
            ':age' => '31',
            ':name' => 'Curly',
        ];
        $this->assertTrue($stmt->execute($args, $options));
        $num_records_after = $this->connection
            ->select('test')
            ->countQuery()
            ->execute()
            ->fetchField();
        $this->assertEquals($num_records_before + 2, $num_records_after);
        $this->assertSame('30', $this->connection
            ->query('SELECT [age] FROM {test} WHERE [name] = :name', [
            ':name' => 'Larry',
        ])
            ->fetchField());
        $this->assertSame('31', $this->connection
            ->query('SELECT [age] FROM {test} WHERE [name] = :name', [
            ':name' => 'Curly',
        ])
            ->fetchField());
    }
    
    /**
     * Tests statement fetching after a full traversal.
     */
    public function testIteratedStatementFetch() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        foreach ($statement as $row) {
            $this->assertNotNull($row);
        }
        $this->assertSame([], $statement->fetchAll());
        $this->assertSame([], $statement->fetchAllKeyed());
        $this->assertSame([], $statement->fetchAllAssoc('age'));
        $this->assertSame([], $statement->fetchCol());
        $this->assertFalse($statement->fetch());
        $this->assertFalse($statement->fetchObject());
        $this->assertFalse($statement->fetchAssoc());
        $this->assertFalse($statement->fetchField());
    }
    
    /**
     * Tests statement fetchAll after a partial traversal.
     */
    public function testPartiallyIteratedStatementFetchAll() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        for ($i = 0; $i < 2; $i++) {
            $statement->fetch();
        }
        $expected = [
            0 => (object) [
                "id" => "3",
                "name" => "Ringo",
                "age" => "28",
                "job" => "Drummer",
            ],
            1 => (object) [
                "id" => "4",
                "name" => "Paul",
                "age" => "26",
                "job" => "Songwriter",
            ],
        ];
        $this->assertEquals($expected, $statement->fetchAll());
        $this->assertSame([], $statement->fetchAll());
    }
    
    /**
     * Tests statement fetchAllKeyed after a partial traversal.
     */
    public function testPartiallyIteratedStatementFetchAllKeyed() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        for ($i = 0; $i < 2; $i++) {
            $statement->fetch();
        }
        $expected = [
            "3" => "Ringo",
            "4" => "Paul",
        ];
        $this->assertSame($expected, $statement->fetchAllKeyed());
        $this->assertSame([], $statement->fetchAllKeyed());
    }
    
    /**
     * Tests statement fetchAllAssoc after a partial traversal.
     */
    public function testPartiallyIteratedStatementFetchAllAssoc() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        for ($i = 0; $i < 2; $i++) {
            $statement->fetch();
        }
        $expected = [
            "28" => (object) [
                "id" => "3",
                "name" => "Ringo",
                "age" => "28",
                "job" => "Drummer",
            ],
            "26" => (object) [
                "id" => "4",
                "name" => "Paul",
                "age" => "26",
                "job" => "Songwriter",
            ],
        ];
        $this->assertEquals($expected, $statement->fetchAllAssoc('age'));
        $this->assertSame([], $statement->fetchAllAssoc('age'));
    }
    
    /**
     * Tests statement fetchCol after a partial traversal.
     */
    public function testPartiallyIteratedStatementFetchCol() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        for ($i = 0; $i < 2; $i++) {
            $statement->fetch();
        }
        $expected = [
            "3",
            "4",
        ];
        $this->assertSame($expected, $statement->fetchCol());
        $this->assertSame([], $statement->fetchCol());
    }
    
    /**
     * Tests statement rewinding.
     */
    public function testStatementRewind() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        foreach ($statement as $row) {
            $this->assertNotNull($row);
        }
        // Trying to iterate through the same statement again should fail.
        $this->expectException(DatabaseExceptionWrapper::class);
        $this->expectExceptionMessage('Attempted rewinding a StatementInterface object when fetching has already started. Refactor your code to avoid rewinding statement objects.');
        foreach ($statement as $row) {
            $this->assertNotNull($row);
        }
    }
    
    /**
     * Tests empty statement rewinding.
     */
    public function testEmptyStatementRewind() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test} WHERE 1 = 0');
        $count = 0;
        foreach ($statement as $row) {
            $count++;
        }
        foreach ($statement as $row) {
            $count++;
        }
        $this->assertEquals(0, $count);
    }
    
    /**
     * Tests counting a statement twice.
     *
     * We need to use iterator_count() and not assertCount() since the latter
     * would rewind the statement twice anyway.
     */
    public function testStatementCountTwice() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        $rowCount = iterator_count($statement);
        $this->assertSame(4, $rowCount);
        $this->expectException(DatabaseExceptionWrapper::class);
        $this->expectExceptionMessage('Attempted rewinding a StatementInterface object when fetching has already started. Refactor your code to avoid rewinding statement objects.');
        $rowCount = iterator_count($statement);
    }
    
    /**
     * Tests statement with no results counting twice.
     *
     * We need to use iterator_count() and not assertCount() since the latter
     * would rewind the statement twice anyway.
     */
    public function testEmptyStatementCountTwice() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test} WHERE 1 = 0');
        $rowCount = iterator_count($statement);
        $this->assertSame(0, $rowCount);
        $rowCount = iterator_count($statement);
        $this->assertSame(0, $rowCount);
    }
    
    /**
     * Tests a follow-on iteration on a statement using foreach.
     */
    public function testStatementForeachTwice() : void {
        $statement = $this->connection
            ->query('SELECT * FROM {test}');
        $count = 0;
        foreach ($statement as $row) {
            $count++;
            $this->assertNotNull($row);
            if ($count > 2) {
                break;
            }
        }
        $this->assertSame(3, $count);
        // Restart iterating through the same statement. The foreach loop will try
        // rewinding the statement which should fail, and the counter should not be
        // increased.
        $this->expectException(DatabaseExceptionWrapper::class);
        $this->expectExceptionMessage('Attempted rewinding a StatementInterface object when fetching has already started. Refactor your code to avoid rewinding statement objects.');
        foreach ($statement as $row) {
            // No-op.
        }
    }

}

Classes

Title Deprecated Summary
StatementTest Tests the Statement classes.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.