class NamedPlaceholderConverterTest

Tests \Drupal\mysqli\Driver\Database\mysqli\NamedPlaceholderConverter.

Attributes

#[CoversClass(NamedPlaceholderConverter::class)] #[Group('Database')]

Hierarchy

Expanded class hierarchy of NamedPlaceholderConverterTest

File

core/modules/mysqli/tests/src/Unit/NamedPlaceholderConverterTest.php, line 16

Namespace

Drupal\Tests\mysqli\Unit
View source
class NamedPlaceholderConverterTest extends UnitTestCase {
  
  /**
   * Tests ::parse().
   *
   * @legacy-covers ::parse
   * @legacy-covers ::getConvertedSQL
   * @legacy-covers ::getConvertedParameters
   */
  public function testParse(string $sql, array $parameters, string $expectedSql, array $expectedParameters) : void {
    $converter = new NamedPlaceholderConverter();
    $converter->parse($sql, $parameters);
    $this->assertSame($expectedSql, $converter->getConvertedSQL());
    $this->assertSame($expectedParameters, $converter->getConvertedParameters());
  }
  
  /**
   * Data for testParse.
   */
  public static function statementsWithParametersProvider() : iterable {
    (yield [
      'SELECT ?',
      [
        'foo',
      ],
      'SELECT ?',
      [
        'foo',
      ],
    ]);
    (yield [
      'SELECT * FROM Foo WHERE bar IN (?, ?, ?)',
      [
        'baz',
        'qux',
        'fred',
      ],
      'SELECT * FROM Foo WHERE bar IN (?, ?, ?)',
      [
        'baz',
        'qux',
        'fred',
      ],
    ]);
    (yield [
      'SELECT ? FROM ?',
      [
        'baz',
        'qux',
      ],
      'SELECT ? FROM ?',
      [
        'baz',
        'qux',
      ],
    ]);
    (yield [
      'SELECT "?" FROM foo WHERE bar = ?',
      [
        'baz',
      ],
      'SELECT "?" FROM foo WHERE bar = ?',
      [
        'baz',
      ],
    ]);
    (yield [
      "SELECT '?' FROM foo WHERE bar = ?",
      [
        'baz',
      ],
      "SELECT '?' FROM foo WHERE bar = ?",
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT `?` FROM foo WHERE bar = ?',
      [
        'baz',
      ],
      'SELECT `?` FROM foo WHERE bar = ?',
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT [?] FROM foo WHERE bar = ?',
      [
        'baz',
      ],
      'SELECT [?] FROM foo WHERE bar = ?',
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[?])',
      [
        'baz',
      ],
      'SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[?])',
      [
        'baz',
      ],
    ]);
    (yield [
      "SELECT 'foo-bar?' FROM foo WHERE bar = ?",
      [
        'baz',
      ],
      "SELECT 'foo-bar?' FROM foo WHERE bar = ?",
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT "foo-bar?" FROM foo WHERE bar = ?',
      [
        'baz',
      ],
      'SELECT "foo-bar?" FROM foo WHERE bar = ?',
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT `foo-bar?` FROM foo WHERE bar = ?',
      [
        'baz',
      ],
      'SELECT `foo-bar?` FROM foo WHERE bar = ?',
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT [foo-bar?] FROM foo WHERE bar = ?',
      [
        'baz',
      ],
      'SELECT [foo-bar?] FROM foo WHERE bar = ?',
      [
        'baz',
      ],
    ]);
    (yield [
      'SELECT :foo FROM :bar',
      [
        ':foo' => 'baz',
        ':bar' => 'qux',
      ],
      'SELECT ? FROM ?',
      [
        'baz',
        'qux',
      ],
    ]);
    (yield [
      'SELECT * FROM Foo WHERE bar IN (:name1, :name2)',
      [
        ':name1' => 'baz',
        ':name2' => 'qux',
      ],
      'SELECT * FROM Foo WHERE bar IN (?, ?)',
      [
        'baz',
        'qux',
      ],
    ]);
    (yield [
      'SELECT ":foo" FROM Foo WHERE bar IN (:name1, :name2)',
      [
        ':name1' => 'baz',
        ':name2' => 'qux',
      ],
      'SELECT ":foo" FROM Foo WHERE bar IN (?, ?)',
      [
        'baz',
        'qux',
      ],
    ]);
    (yield [
      "SELECT ':foo' FROM Foo WHERE bar IN (:name1, :name2)",
      [
        ':name1' => 'baz',
        ':name2' => 'qux',
      ],
      "SELECT ':foo' FROM Foo WHERE bar IN (?, ?)",
      [
        'baz',
        'qux',
      ],
    ]);
    (yield [
      'SELECT :foo_id',
      [
        ':foo_id' => 'bar',
      ],
      'SELECT ?',
      [
        'bar',
      ],
    ]);
    (yield [
      'SELECT @rank := 1 AS rank, :foo AS foo FROM :bar',
      [
        ':foo' => 'baz',
        ':bar' => 'qux',
      ],
      'SELECT @rank := 1 AS rank, ? AS foo FROM ?',
      [
        'baz',
        'qux',
      ],
    ]);
    (yield [
      'SELECT * FROM Foo WHERE bar > :start_date AND baz > :start_date',
      [
        ':start_date' => 'qux',
      ],
      'SELECT * FROM Foo WHERE bar > ? AND baz > ?',
      [
        'qux',
        'qux',
      ],
    ]);
    (yield [
      'SELECT foo::date as date FROM Foo WHERE bar > :start_date AND baz > :start_date',
      [
        ':start_date' => 'qux',
      ],
      'SELECT foo::date as date FROM Foo WHERE bar > ? AND baz > ?',
      [
        'qux',
        'qux',
      ],
    ]);
    (yield [
      'SELECT `d.ns:col_name` FROM my_table d WHERE `d.date` >= :param1',
      [
        ':param1' => 'qux',
      ],
      'SELECT `d.ns:col_name` FROM my_table d WHERE `d.date` >= ?',
      [
        'qux',
      ],
    ]);
    (yield [
      'SELECT [d.ns:col_name] FROM my_table d WHERE [d.date] >= :param1',
      [
        ':param1' => 'qux',
      ],
      'SELECT [d.ns:col_name] FROM my_table d WHERE [d.date] >= ?',
      [
        'qux',
      ],
    ]);
    (yield [
      'SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[:foo])',
      [
        ':foo' => 'qux',
      ],
      'SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[?])',
      [
        'qux',
      ],
    ]);
    (yield [
      'SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, array[:foo])',
      [
        ':foo' => 'qux',
      ],
      'SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, array[?])',
      [
        'qux',
      ],
    ]);
    (yield [
      "SELECT table.column1, ARRAY['3'] FROM schema.table table WHERE table.f1 = :foo AND ARRAY['3']",
      [
        ':foo' => 'qux',
      ],
      "SELECT table.column1, ARRAY['3'] FROM schema.table table WHERE table.f1 = ? AND ARRAY['3']",
      [
        'qux',
      ],
    ]);
    (yield [
      "SELECT table.column1, ARRAY['3']::integer[] FROM schema.table table WHERE table.f1 = :foo AND ARRAY['3']::integer[]",
      [
        ':foo' => 'qux',
      ],
      "SELECT table.column1, ARRAY['3']::integer[] FROM schema.table table WHERE table.f1 = ? AND ARRAY['3']::integer[]",
      [
        'qux',
      ],
    ]);
    (yield [
      "SELECT table.column1, ARRAY[:foo] FROM schema.table table WHERE table.f1 = :bar AND ARRAY['3']",
      [
        ':foo' => 'qux',
        ':bar' => 'git',
      ],
      "SELECT table.column1, ARRAY[?] FROM schema.table table WHERE table.f1 = ? AND ARRAY['3']",
      [
        'qux',
        'git',
      ],
    ]);
    (yield [
      'SELECT table.column1, ARRAY[:foo]::integer[] FROM schema.table table' . " WHERE table.f1 = :bar AND ARRAY['3']::integer[]",
      [
        ':foo' => 'qux',
        ':bar' => 'git',
      ],
      'SELECT table.column1, ARRAY[?]::integer[] FROM schema.table table' . " WHERE table.f1 = ? AND ARRAY['3']::integer[]",
      [
        'qux',
        'git',
      ],
    ]);
    (yield 'Parameter array with placeholder keys missing starting colon' => [
      'SELECT table.column1, ARRAY[:foo]::integer[] FROM schema.table table' . " WHERE table.f1 = :bar AND ARRAY['3']::integer[]",
      [
        'foo' => 'qux',
        'bar' => 'git',
      ],
      'SELECT table.column1, ARRAY[?]::integer[] FROM schema.table table' . " WHERE table.f1 = ? AND ARRAY['3']::integer[]",
      [
        'qux',
        'git',
      ],
    ]);
    (yield 'Quotes inside literals escaped by doubling' => [
      <<<'SQL'
SELECT * FROM foo
WHERE bar = ':not_a_param1 ''":not_a_param2"'''
   OR bar=:a_param1
   OR bar=:a_param2||':not_a_param3'
   OR bar=':not_a_param4 '':not_a_param5'' :not_a_param6'
   OR bar=''
   OR bar=:a_param3
SQL
,
      [
        ':a_param1' => 'qux',
        ':a_param2' => 'git',
        ':a_param3' => 'foo',
      ],
      <<<'SQL'
SELECT * FROM foo
WHERE bar = ':not_a_param1 ''":not_a_param2"'''
   OR bar=?
   OR bar=?||':not_a_param3'
   OR bar=':not_a_param4 '':not_a_param5'' :not_a_param6'
   OR bar=''
   OR bar=?
SQL
,
      [
        'qux',
        'git',
        'foo',
      ],
    ]);
    (yield [
      'SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE \'\\\\\') AND (data.description LIKE :condition_1 ESCAPE \'\\\\\') ORDER BY id ASC',
      [
        ':condition_0' => 'qux',
        ':condition_1' => 'git',
      ],
      'SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE ? ESCAPE \'\\\\\') AND (data.description LIKE ? ESCAPE \'\\\\\') ORDER BY id ASC',
      [
        'qux',
        'git',
      ],
    ]);
    (yield [
      'SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE "\\\\") AND (data.description LIKE :condition_1 ESCAPE "\\\\") ORDER BY id ASC',
      [
        ':condition_0' => 'qux',
        ':condition_1' => 'git',
      ],
      'SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE ? ESCAPE "\\\\") AND (data.description LIKE ? ESCAPE "\\\\") ORDER BY id ASC',
      [
        'qux',
        'git',
      ],
    ]);
    (yield 'Combined single and double quotes' => [
      <<<'SQL'
SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id
  FROM test_data data
 WHERE (data.description LIKE :condition_0 ESCAPE "\\")
   AND (data.description LIKE :condition_1 ESCAPE '\\') ORDER BY id ASC
SQL
,
      [
        ':condition_0' => 'qux',
        ':condition_1' => 'git',
      ],
      <<<'SQL'
SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id
  FROM test_data data
 WHERE (data.description LIKE ? ESCAPE "\\")
   AND (data.description LIKE ? ESCAPE '\\') ORDER BY id ASC
SQL
,
      [
        'qux',
        'git',
      ],
    ]);
    (yield [
      'SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE `\\\\`) AND (data.description LIKE :condition_1 ESCAPE `\\\\`) ORDER BY id ASC',
      [
        ':condition_0' => 'qux',
        ':condition_1' => 'git',
      ],
      'SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE ? ESCAPE `\\\\`) AND (data.description LIKE ? ESCAPE `\\\\`) ORDER BY id ASC',
      [
        'qux',
        'git',
      ],
    ]);
    (yield 'Combined single quotes and backticks' => [
      <<<'SQL'
SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id
  FROM test_data data
 WHERE (data.description LIKE :condition_0 ESCAPE '\\')
   AND (data.description LIKE :condition_1 ESCAPE `\\`) ORDER BY id ASC
SQL
,
      [
        ':condition_0' => 'qux',
        ':condition_1' => 'git',
      ],
      <<<'SQL'
SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id
  FROM test_data data
 WHERE (data.description LIKE ? ESCAPE '\\')
   AND (data.description LIKE ? ESCAPE `\\`) ORDER BY id ASC
SQL
,
      [
        'qux',
        'git',
      ],
    ]);
    (yield '? placeholders inside comments' => [
      <<<'SQL'
/*
 * test placeholder ?
 */
SELECT dummy as "dummy?"
  FROM DUAL
 WHERE '?' = '?'
-- AND dummy <> ?
   AND dummy = ?
SQL
,
      [
        'baz',
      ],
      <<<'SQL'
/*
 * test placeholder ?
 */
SELECT dummy as "dummy?"
  FROM DUAL
 WHERE '?' = '?'
-- AND dummy <> ?
   AND dummy = ?
SQL
,
      [
        'baz',
      ],
    ]);
    (yield 'Named placeholders inside comments' => [
      <<<'SQL'
/*
 * test :placeholder
 */
SELECT dummy as "dummy?"
  FROM DUAL
 WHERE '?' = '?'
-- AND dummy <> :dummy
   AND dummy = :key
SQL
,
      [
        ':key' => 'baz',
      ],
      <<<'SQL'
/*
 * test :placeholder
 */
SELECT dummy as "dummy?"
  FROM DUAL
 WHERE '?' = '?'
-- AND dummy <> :dummy
   AND dummy = ?
SQL
,
      [
        'baz',
      ],
    ]);
    (yield 'Escaped question' => [
      <<<'SQL'
SELECT '{"a":null}'::jsonb ?? :key
SQL
,
      [
        ':key' => 'qux',
      ],
      <<<'SQL'
SELECT '{"a":null}'::jsonb ?? ?
SQL
,
      [
        'qux',
      ],
    ]);
  }
  
  /**
   * Tests reusing the parser object.
   *
   * @legacy-covers ::parse
   * @legacy-covers ::getConvertedSQL
   * @legacy-covers ::getConvertedParameters
   */
  public function testParseReuseObject() : void {
    $converter = new NamedPlaceholderConverter();
    $converter->parse('SELECT ?', [
      'foo',
    ]);
    $this->assertSame('SELECT ?', $converter->getConvertedSQL());
    $this->assertSame([
      'foo',
    ], $converter->getConvertedParameters());
    $this->expectException(\RuntimeException::class);
    $this->expectExceptionMessage('Missing Positional Parameter 0');
    $converter->parse('SELECT ?', []);
  }

}

Members

Title Sort descending Modifiers Object type Summary Overrides
ExpectDeprecationTrait::expectDeprecation public function Adds an expected deprecation.
ExpectDeprecationTrait::setUpErrorHandler public function Sets up the test error handler.
ExpectDeprecationTrait::tearDownErrorHandler public function Tears down the test error handler.
NamedPlaceholderConverterTest::statementsWithParametersProvider public static function Data for testParse.
NamedPlaceholderConverterTest::testParse public function Tests ::parse().
NamedPlaceholderConverterTest::testParseReuseObject public function Tests reusing the parser object.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
UnitTestCase::$root protected property The app root.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.
UnitTestCase::setUp protected function 375
UnitTestCase::setupMockIterator protected function Set up a traversable class mock to return specific items when iterated.

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