Unverified Commit 01143c9c authored by Sergei Morozov's avatar Sergei Morozov

Merge branch '3.0.x'

parents 9c91d750 133ce49c
......@@ -3,7 +3,7 @@ build:
analysis:
environment:
php:
version: 7.2
version: 7.3
cache:
disabled: false
directories:
......
This diff is collapsed.
......@@ -30,14 +30,6 @@ The statement implementations no longer detect whether `$params` is a zero- or o
The `ServerInfoAwareConnection::requiresQueryForServerVersion()` method has been removed as an implementation detail which is the same for almost all supported drivers.
## BC BREAK: PingableConnection and ServerInfoAwareConnection interfaces now extends Connection
All implementations of the `PingableConnection` and `ServerInfoAwareConnection` interfaces have to implement the methods defined in the `Connection` interface as well.
## BC BREAK: VersionAwarePlatformDriver interface now extends Driver
All implementations of the `VersionAwarePlatformDriver` interface have to implement the methods defined in the `Driver` interface as well.
## BC BREAK: Removed support for PostgreSQL 9.3 and older
DBAL now requires PostgeSQL 9.4 or newer, support for unmaintained versions has been dropped.
......@@ -355,6 +347,14 @@ The Drizzle project is abandoned and is therefore not supported by Doctrine DBAL
# Upgrade to 3.0
## BC BREAK: PingableConnection and ServerInfoAwareConnection interfaces now extend Connection
All implementations of the `PingableConnection` and `ServerInfoAwareConnection` interfaces have to implement the methods defined in the `Connection` interface as well.
## BC BREAK: VersionAwarePlatformDriver interface now extends Driver
All implementations of the `VersionAwarePlatformDriver` interface have to implement the methods defined in the `Driver` interface as well.
## BC BREAK: Removed Doctrine\DBAL\Version
The `Doctrine\DBAL\Version` class is no longer available: please refrain from checking the DBAL version at runtime.
......
......@@ -32,7 +32,7 @@
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
],
"require": {
"php": "^7.2",
"php": "^7.3",
"doctrine/cache": "^1.0",
"doctrine/event-manager": "^1.0",
"ocramius/package-versions": "^1.4"
......@@ -42,7 +42,7 @@
"jetbrains/phpstorm-stubs": "^2019.1",
"phpstan/phpstan": "^0.11.6",
"phpstan/phpstan-phpunit": "^0.11",
"phpunit/phpunit": "^8.4.1",
"phpunit/phpunit": "^9.0",
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0"
},
"suggest": {
......@@ -51,7 +51,7 @@
"bin": ["bin/doctrine-dbal"],
"config": {
"platform": {
"php": "7.2.0"
"php": "7.3.0"
},
"sort-packages": true
},
......
This diff is collapsed.
......@@ -91,18 +91,7 @@ final class DriverManager
*
* $params must contain at least one of the following.
*
* Either 'driver' with one of the following values:
*
* pdo_mysql
* pdo_sqlite
* pdo_pgsql
* pdo_oci (unstable)
* pdo_sqlsrv
* mysqli
* sqlanywhere
* sqlsrv
* ibm_db2 (unstable)
*
* Either 'driver' with one of the array keys of {@link $_driverMap},
* OR 'driverClass' that contains the full class name (with namespace) of the
* driver class to instantiate.
*
......
......@@ -14,7 +14,7 @@ final class DebugStack implements SQLLogger
/**
* Executed SQL queries.
*
* @var mixed[][]
* @var array<int, array<string, mixed>>
*/
public $queries = [];
......
......@@ -281,7 +281,7 @@ SQL
[$schema, $table] = explode('.', $table);
$schema = $this->quoteStringLiteral($schema);
} else {
$schema = "ANY(string_to_array((select replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))";
$schema = 'ANY(current_schemas(false))';
}
$table = new Identifier($table);
......
......@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Query;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Query\Exception\NonUniqueAlias;
use Doctrine\DBAL\Query\Exception\UnknownAlias;
......@@ -247,7 +247,7 @@ class QueryBuilder
* Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
* for insert, update and delete statements.
*
* @return Statement|int
* @return ResultStatement|int
*/
public function execute()
{
......@@ -1326,9 +1326,12 @@ class QueryBuilder
throw NonUniqueAlias::new($join->alias, array_keys($knownAliases));
}
$sql .= ' ' . $join->type
. ' JOIN ' . $join->table . ' ' . $join->alias
. ' ON ' . ((string) $join->condition);
$sql .= ' ' . $join->type . ' JOIN ' . $join->table . ' ' . $join->alias;
if ($join->condition !== null) {
$sql .= ' ON ' . $join->condition;
}
$knownAliases[$join->alias] = true;
}
......
......@@ -7,7 +7,9 @@ namespace Doctrine\DBAL;
use Doctrine\DBAL\Exception\MissingArrayParameter;
use Doctrine\DBAL\Exception\MissingArrayParameterType;
use function array_fill;
use function array_fill_keys;
use function array_key_exists;
use function array_keys;
use function array_merge;
use function array_slice;
use function array_values;
......@@ -113,6 +115,10 @@ class SQLParserUtils
$bindIndex = -1;
if ($isPositional) {
// make sure that $types has the same keys as $params
// to allow omitting parameters with unspecified types
$types += array_fill_keys(array_keys($params), null);
ksort($params);
ksort($types);
}
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Types\Exception;
use Doctrine\DBAL\Types\ConversionException;
use Throwable;
use function get_class;
use function gettype;
use function implode;
......@@ -24,29 +25,27 @@ final class InvalidType extends ConversionException implements TypesException
* @todo split into two methods
* @todo sanitize value
*/
public static function new($value, string $toType, array $possibleTypes) : self
public static function new($value, string $toType, array $possibleTypes, ?Throwable $previous = null) : self
{
$actualType = is_object($value) ? get_class($value) : gettype($value);
if (is_scalar($value)) {
return new self(
sprintf(
'Could not convert PHP value "%s" of type "%s" to type "%s". Expected one of the following types: %s.',
$value,
$actualType,
$toType,
implode(', ', $possibleTypes)
)
$message = sprintf(
'Could not convert PHP value "%s" of type "%s" to type "%s". Expected one of the following types: %s.',
$value,
$actualType,
$toType,
implode(', ', $possibleTypes)
);
}
return new self(
sprintf(
} else {
$message = sprintf(
'Could not convert PHP value of type "%s" to type "%s". Expected one of the following types: %s.',
$actualType,
$toType,
implode(', ', $possibleTypes)
)
);
);
}
return new self($message, 0, $previous);
}
}
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Types\Exception;
use Doctrine\DBAL\Types\ConversionException;
use Throwable;
use function get_class;
use function gettype;
use function is_object;
......@@ -15,7 +16,7 @@ final class SerializationFailed extends ConversionException implements TypesExce
/**
* @param mixed $value
*/
public static function new($value, string $format, string $error) : self
public static function new($value, string $format, string $error, ?Throwable $previous = null) : self
{
$actualType = is_object($value) ? get_class($value) : gettype($value);
......@@ -25,7 +26,9 @@ final class SerializationFailed extends ConversionException implements TypesExce
$actualType,
$format,
$error
)
),
0,
$previous
);
}
}
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Types\Exception;
use Doctrine\DBAL\Types\ConversionException;
use Throwable;
use function is_string;
use function sprintf;
use function strlen;
......@@ -18,24 +19,22 @@ final class ValueNotConvertible extends ConversionException implements TypesExce
/**
* @param mixed $value
*/
public static function new($value, string $toType, ?string $message = null) : self
public static function new($value, string $toType, ?string $message = null, ?Throwable $previous = null) : self
{
if ($message !== null) {
return new self(
sprintf(
'Could not convert database value to "%s" as an error was triggered by the unserialization: %s',
$toType,
$message
)
$message = sprintf(
'Could not convert database value to "%s" as an error was triggered by the unserialization: %s',
$toType,
$message
);
}
return new self(
sprintf(
} else {
$message = sprintf(
'Could not convert database value "%s" to Doctrine Type "%s".',
is_string($value) && strlen($value) > 32 ? substr($value, 0, 20) . '...' : $value,
$toType
)
);
);
}
return new self($message, 0, $previous);
}
}
......@@ -10,9 +10,6 @@ parameters:
# extension not available
- '~^(Used )?(Function|Constant) sasql_\S+ not found\.\z~i'
# changing these would be a BC break, to be done in next major
- '~^Method Doctrine\\DBAL\\Query\\QueryBuilder::execute\(\) should return Doctrine\\DBAL\\Driver\\Statement\|int but returns Doctrine\\DBAL\\Driver\\ResultStatement\.\z~'
# https://bugs.php.net/bug.php?id=78126
- '~^Call to an undefined method PDO::sqliteCreateFunction\(\)\.\z~'
......
......@@ -7,7 +7,6 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Statement;
use Doctrine\Tests\DbalFunctionalTestCase;
use PHPUnit\Framework\Error\Notice;
use function assert;
use function extension_loaded;
......@@ -36,7 +35,7 @@ class DB2StatementTest extends DbalFunctionalTestCase
// unwrap the statement to prevent the wrapper from handling the PHPUnit-originated exception
$wrappedStmt = $stmt->getWrappedStatement();
$this->expectException(Notice::class);
$this->expectNotice();
$wrappedStmt->execute([[]]);
}
}
......@@ -142,7 +142,11 @@ class PortabilityTest extends DbalFunctionalTestCase
*/
public function assertFetchResultRow(array $row) : void
{
self::assertContains($row['test_int'], [1, 2], 'Primary key test_int should either be 1 or 2.');
self::assertThat($row['test_int'], self::logicalOr(
self::equalTo(1),
self::equalTo(2)
));
self::assertArrayHasKey('test_string', $row, 'Case should be lowered.');
self::assertEquals(3, strlen($row['test_string']), 'test_string should be rtrimed to length of three for CHAR(32) column.');
self::assertNull($row['test_null']);
......
......@@ -354,6 +354,18 @@ class PostgreSqlSchemaManagerTest extends SchemaManagerFunctionalTestCase
self::assertNull($comparator->diffTable($offlineTable, $onlineTable));
}
public function testListTableDetailsWhenCurrentSchemaNameQuoted() : void
{
$this->connection->exec('CREATE SCHEMA "001_test"');
$this->connection->exec('SET search_path TO "001_test"');
try {
$this->testListQuotedTable();
} finally {
$this->connection->close();
}
}
public function testListTablesExcludesViews() : void
{
$this->createTestTable('list_tables_excludes_views');
......
......@@ -94,11 +94,22 @@ class QueryBuilderTest extends DbalTestCase
$qb->select('u.*', 'p.*')
->from('users', 'u')
->Join('u', 'phones', 'p', $expr->eq('p.user_id', 'u.id'));
->join('u', 'phones', 'p', $expr->eq('p.user_id', 'u.id'));
self::assertEquals('SELECT u.*, p.* FROM users u INNER JOIN phones p ON p.user_id = u.id', (string) $qb);
}
public function testSelectWithJoinNoCondition() : void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.*', 'p.*')
->from('users', 'u')
->join('u', 'phones', 'p');
self::assertEquals('SELECT u.*, p.* FROM users u INNER JOIN phones p', (string) $qb);
}
public function testSelectWithInnerJoin() : void
{
$qb = new QueryBuilder($this->conn);
......
......@@ -7,9 +7,10 @@ namespace Doctrine\Tests\DBAL\Types;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Exception\InvalidFormat;
use Doctrine\DBAL\Types\Exception\InvalidType;
use Exception;
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
use PHPUnit\Framework\TestCase;
use stdClass;
use Throwable;
use function get_class;
use function gettype;
use function is_object;
......@@ -18,6 +19,16 @@ use function tmpfile;
class ConversionExceptionTest extends TestCase
{
public function testConversionFailedPreviousException() : void
{
$previous = $this->createMock(Throwable::class);
$exception = ValueNotConvertible::new('foo', 'foo', null, $previous);
self::assertInstanceOf(ConversionException::class, $exception);
self::assertSame($previous, $exception->getPrevious());
}
/**
* @param mixed $scalarValue
*
......@@ -58,9 +69,19 @@ class ConversionExceptionTest extends TestCase
);
}
public function testConversionFailedInvalidTypePreviousException() : void
{
$previous = $this->createMock(Throwable::class);
$exception = InvalidType::new('foo', 'foo', ['bar', 'baz'], $previous);
self::assertInstanceOf(ConversionException::class, $exception);
self::assertSame($previous, $exception->getPrevious());
}
public function testConversionFailedFormatPreservesPreviousException() : void
{
$previous = new Exception();
$previous = $this->createMock(Throwable::class);
$exception = InvalidFormat::new('foo', 'bar', 'baz', $previous);
......
DROP USER IF EXISTS 'travis'@'%';
CREATE USER 'travis'@'%';
CREATE SCHEMA doctrine_tests;
CREATE SCHEMA test_create_database;
CREATE SCHEMA test_drop_database;
GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%';
GRANT ALL PRIVILEGES ON test_create_database.* to travis@'%';
GRANT ALL PRIVILEGES ON test_drop_database.* to travis@'%';
#!/usr/bin/env bash
set -ex
echo "Starting RDBMS…">&2
if [[ "$IMAGE" == "mysql:8.0" ]]
then
CMD_OPTIONS="--default-authentication-plugin=mysql_native_password"
else
CMD_OPTIONS=""
fi
docker run \
--health-cmd='mysqladmin ping --silent' \
--detach \
--env MYSQL_ALLOW_EMPTY_PASSWORD=yes \
--env MYSQL_DATABASE=doctrine_tests \
--publish 33306:3306 \
--name rdbms \
"$IMAGE" $CMD_OPTIONS
while true; do
healthStatus=$(docker inspect --format "{{json .State.Health.Status }}" rdbms)
case $healthStatus in
'"starting"')
echo "Waiting for RDBMS to become ready…">&2
sleep 1
;;
'"healthy"')
echo "Container is healthy">&2
break
;;
'"unhealthy"')
echo "Container is unhealthy">&2
exit 1
;;
*)
echo "Unexpected health status $healthStatus">&2
;;
esac
done
#!/usr/bin/env bash
set -ex
echo "Starting MySQL 5.7..."
sudo docker run \
-d \
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes \
-e MYSQL_DATABASE=doctrine_tests \
-p 33306:3306 \
--name mysql57 \
mysql:5.7
sudo docker exec -i mysql57 bash <<< 'until echo \\q | mysql doctrine_tests > /dev/null 2>&1 ; do sleep 1; done'
#!/usr/bin/env bash
set -ex
echo "Starting MySQL 8.0..."
sudo docker pull mysql:8.0
sudo docker run \
-d \
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes \
-e MYSQL_DATABASE=doctrine_tests \
-p 33306:3306 \
--name mysql80 \
mysql:8.0 \
--default-authentication-plugin=mysql_native_password
sudo docker exec -i mysql80 bash <<< 'until echo \\q | mysql doctrine_tests > /dev/null 2>&1 ; do sleep 1; done'
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="mysqli"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="travis" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="tmpdb_type" value="mysqli"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="travis" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="3306"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../Doctrine/Tests/DBAL</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_mysql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="travis" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="tmpdb_type" value="pdo_mysql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="travis" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="3306"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../Doctrine/Tests/DBAL</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_mysql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="travis" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="tmpdb_type" value="pdo_mysql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="travis" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="3306"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../Doctrine/Tests/DBAL</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="mysqli"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="travis" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="tmpdb_type" value="mysqli"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="travis" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="3306"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../Doctrine/Tests/DBAL</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../lib/Doctrine</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment