Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add automatic platform version detection #487

Merged
merged 15 commits into from Feb 16, 2014

Conversation

deeky666
Copy link
Member

@deeky666 deeky666 commented Jan 2, 2014

As more and more vendor version specific platforms evolve, it is time to make an approach of autodetecting and using the correct platform on connection. This is done by introducing two new interfaces Doctrine\DBAL\VersionAwarePlatformDriver and Doctrine\DBAL\Driver\ServerInfoAwareConnection which are implemented by Doctrine's driver and driver connection classes. They are introduced to keep BC for custom driver / driver connection classes.
VersionAwarePlatformDriver::createDatabasePlatformForVersion($version) is responsible for evaluating a given vendor specific version number and instantiating the correct platform for it. The current implementations normalize the given version string and use version_compare() for evaluation.
ServerInfoAwareConnection::getServerVersion() is responsible for returning the normalized version string of the database server currently connected to. ServerInfoAwareConnection::requiresQueryForServerVersion() defines whether receiving that information requires an additional database query or not which is important for Doctrine\DBAL\Connection as described in the following.
Doctrine\DBAL\Connection now takes an additional (optional) connection parameter serverVersion which the user can pass to skip platform autodetection and define the desired platform version straight away. This is also required for drivers that cannot return the database server version without an additional query (performance reasons). Platform version detection is now done in the following order of precedence:

  1. Evaluate platform connection parameter and return an instance of that class if given.
  2. Evaluate if the underlying driver is capable of creating platform instances by version. If not, return the default platform instance that would be returned by the current implementation.
  3. Evaluate if serverVersion connection parameter is given and return the appropriate platform instance for that version.
  4. Evaluate if the underlying driver connection can return the server version and can return it without the need of an additional query -> return the appropriate platform instance for that version.
  5. Otherwise return the default platform instance that would be returned by the current implementation.

As a positive side effect while implementing the new interfaces, the driver classes were refactored by extracting abstract base driver classes for each vendor. This removes a lot of duplicated code in the driver classes that are related to the same database vendor. Please also note, that DrizzlePDOMySql now directly inherits from Doctrine\DBAL\Driver\PDOMySql\Driver to come around additional code duplication.

The only BC break introduced by this PR should be the visibility change of Doctrine\DBAL\Connection::$_platform from protected to private (as wished by @beberlei).
The only drawback of this implementation is that Doctrine\DBAL\Connection::getDatabasePlatform() now needs to do the real connect (if uninitialized) under some circumstances when it needs to autodetect the server version from the driver connection. Therefore I had to change the initialization of temporary connections in the functional test suite a little bit, so that calling Doctrine\DBAL\Connection::getDatabasePlatform() does not raise an error if the supplied temporary database does not exist. I think this is acceptable as it is not necessary to connect to a specific database anyways when only needing to drop and create the real test database.

As soon as the doctrine team agrees on this approach, I will add some more test and documentation for this. Until then this is a WIP PR.

@doctrinebot
Copy link

Hello,

thank you for creating this pull request. I have automatically opened an issue
on our Jira Bug Tracker for you. See the issue link:

http://www.doctrine-project.org/jira/browse/DBAL-757

We use Jira to track the state of pull requests and the versions they got
included in.

@deeky666
Copy link
Member Author

deeky666 commented Jan 2, 2014

@doctrine/team-doctrine2 review please :)

@@ -1521,7 +1588,7 @@ public function ping()
}

try {
$this->query($this->_platform->getDummySelectSQL());
$this->query($this->platform->getDummySelectSQL());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shoudln't this use the getter to ensure it has been created ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof connect() ensures that it is set which called at the beginning of the method.

@deeky666
Copy link
Member Author

deeky666 commented Feb 8, 2014

@beberlei rebased with master and removed version awareness for DB2, Oracle and SQLite. Otherwise what else (except for tests) is missing here? Do you think the implementation is ready to go? Then I would start adding tests.

@beberlei
Copy link
Member

beberlei commented Feb 9, 2014

@deeky666 Start with the tests I guess, then we can release another Beta after merging this.

@deeky666
Copy link
Member Author

@doctrine/team-doctrine2 finished this beast. Please review. Hope you like it. Cheers!

@@ -365,6 +360,10 @@ public function connect()
$this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions);
$this->_isConnected = true;

if (null === $this->platform) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just call $this->getDatabasePlatform()

@deeky666
Copy link
Member Author

travis hates me -.- why don't I get this error?

public function getServerVersion()
{
if ( ! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', oci_server_version($this->dbh), $version)) {
return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null is not a valid return value - this should throw an exception.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm good point. I would have to add it to the contract then. But that seems rather stupid because I would have to throw some kind of LogicException as it is not a driver/database problem but rather an "unknown version string". You know what I mean? I cannot 100% tell what each version string looks like that the driver returns because it heavily depends on the database server. For Oracle I did the null return here because Oracle seems to like returning something like "Oracle Enterprise blabla Edition III.2 with a lot of extra features for commercial use ---version---. Thank you for paying a lot of money using this sh*t." as a "version" string. What would you suggest putting in the contract? Please also note BC when thinking of throwing an exception here.

beberlei added a commit that referenced this pull request Feb 16, 2014
Add automatic platform version detection
@beberlei beberlei merged commit b362492 into doctrine:master Feb 16, 2014
stof added a commit to doctrine/DoctrineBundle that referenced this pull request Sep 5, 2014
…drianolek)

This PR was squashed before being merged into the 1.3.x-dev branch (closes #329).

Discussion
----------

Added support for server_version connection parameter

Doctrine DBAL 2.5 will support platform version autodetection, additionally it allows specifying serverVersion connection parameter:

> which the user can pass to skip platform autodetection and define the desired platform version straight away. This is also required for drivers that cannot return the database server version without an additional query (performance reasons).
> doctrine/dbal#487

In symfony it can be useful eg. when warming up cache during build, so you don't need the db connection, but dbal 2.5 will try to autodetect server version anyway eg. during entity class metadata loading, then it will throw an exception because of unavailable connection. server_version parameter can be used to skip the autodetection.

Commits
-------

523b59a Added support for server_version connection parameter
@klaussilveira
Copy link

This introduces a heavy BC for Symfony users. When using the clear:cache command with the warmup enabled, Doctrine's ProxyCacheWarmer is called and tries to connect to the database. If there is no database configured properly, or running, everything crashes. Including the composer install. All our Jenkins builds started to fail after upgrading to DBAL 2.5.0. Jenkins tries to run composer install before running our test suite and crashes hard.

In order to make it work, i had to manually add the following option to the Doctrine Bridge configuration: server_version: "5.6"

👎

@Ocramius
Copy link
Member

Ocramius commented Dec 5, 2014

In order to make it work, i had to manually add the following option to the Doctrine Bridge configuration: server_version: "5.6"

You can survive that :-)

Further discussion on this bug should happen in http://www.doctrine-project.org/jira/browse/DBAL-1057.

@gerryvdm
Copy link

In the same boat as @klaussilveira, this gave us a hell of a headache on this Friday afternoon :)

Anyway, wrote a blogpost on it:

http://www.king-foo.be/2014/12/connection-errors-after-upgrading-to-doctrine-dbal-2-5/

morozov added a commit to morozov/dbal that referenced this pull request Jan 3, 2020
…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront.

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
morozov added a commit to morozov/dbal that referenced this pull request Jan 3, 2020
…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront.

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
morozov added a commit to morozov/dbal that referenced this pull request Jan 3, 2020
…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
morozov added a commit to morozov/dbal that referenced this pull request Jan 3, 2020
…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
morozov added a commit to morozov/dbal that referenced this pull request Jan 3, 2020
…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
morozov added a commit to morozov/dbal that referenced this pull request Jan 3, 2020
…implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”

In addition to the above, the only driver that doesn't support the platform version detection via the underlying driver API is `sqsql` which is barely supported.
morozov added a commit to morozov/dbal that referenced this pull request Jun 27, 2020
…n implementation detail

Testing the implementations of this method requires partial mocking of the implementing class which makes it impossible to make them `final` (doctrine#3590).

Additionally, this API breaks the encapsulation of the driver layer: instead of exposing the fact of whether the connection will perform a query to detect the server version, the driver should just instantiate a platform corresponding to a connection.

The rationale behind introducing this method (doctrine#487) is really questionable:

> This is also required for drivers that cannot return the database server version without an additional query (performance reasons).

1. There's no evidence that an underlying driver that exposes the server version via its API doesn't make a request of any kind to the server.
2. For an application that works with any realistic database, a query like `SELECT VERSION()` wouldn't be a performance bottleneck.
3. Even if it was, it's always possible to specify the platform version upfront. Otherwise, the current logic of falling back to a default platform may cause undefined behavior of the application (we don't test the compatibility of the lowest level of the DBAL platform with all supported server versions). Remember, “If it doesn’t work, it doesn’t matter how fast it doesn’t work.”
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants