-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Add automatic platform version detection #487
Conversation
Hello, thank you for creating this pull request. I have automatically opened an issue http://www.doctrine-project.org/jira/browse/DBAL-757 We use Jira to track the state of pull requests and the versions they got |
@doctrine/team-doctrine2 review please :) |
@@ -1521,7 +1588,7 @@ public function ping() | |||
} | |||
|
|||
try { | |||
$this->query($this->_platform->getDummySelectSQL()); | |||
$this->query($this->platform->getDummySelectSQL()); |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
@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. |
@deeky666 Start with the tests I guess, then we can release another Beta after merging this. |
@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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just call $this->getDatabasePlatform()
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Add automatic platform version detection
…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
This introduces a heavy BC for Symfony users. When using the In order to make it work, i had to manually add the following option to the Doctrine Bridge configuration: 👎 |
You can survive that :-) Further discussion on this bug should happen in http://www.doctrine-project.org/jira/browse/DBAL-1057. |
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/ |
…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.
…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.
…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.
…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.
…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.
…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.
…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.”
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
andDoctrine\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 useversion_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 forDoctrine\DBAL\Connection
as described in the following.Doctrine\DBAL\Connection
now takes an additional (optional) connection parameterserverVersion
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:platform
connection parameter and return an instance of that class if given.serverVersion
connection parameter is given and return the appropriate platform instance for that version.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 fromDoctrine\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 callingDoctrine\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.