Unverified Commit 01c22b7d authored by Marco Pivetta's avatar Marco Pivetta Committed by GitHub

Merge pull request #3712 from doctrine/develop

Merge all the work done so far for the 3.0.0 release into master
parents 0d953e58 acf1cc08
......@@ -21,7 +21,7 @@ before_commands:
tools:
external_code_coverage:
timeout: 3600
runs: 30 # 25x Travis (jobs with COVERAGE=yes) + 3x AppVeyor (jobs with coverage=yes) + 2x ContinuousPHP
runs: 29 # 24x Travis (jobs with COVERAGE=yes) + 3x AppVeyor (jobs with coverage=yes) + 2x ContinuousPHP
filter:
excluded_paths:
......
......@@ -144,11 +144,6 @@ jobs:
- docker
before_script:
- bash ./tests/travis/install-mysql-8.0.sh
- stage: Test
php: 7.3
env: DB=mariadb MARIADB_VERSION=10.0 COVERAGE=yes
addons:
mariadb: 10.0
- stage: Test
php: 7.3
env: DB=mariadb MARIADB_VERSION=10.1 COVERAGE=yes
......@@ -164,11 +159,6 @@ jobs:
env: DB=mariadb MARIADB_VERSION=10.3 COVERAGE=yes
addons:
mariadb: 10.3
- stage: Test
php: 7.3
env: DB=mariadb.mysqli MARIADB_VERSION=10.0 COVERAGE=yes
addons:
mariadb: 10.0
- stage: Test
php: 7.3
env: DB=mariadb.mysqli MARIADB_VERSION=10.1 COVERAGE=yes
......@@ -184,20 +174,6 @@ jobs:
env: DB=mariadb.mysqli MARIADB_VERSION=10.3 COVERAGE=yes
addons:
mariadb: 10.3
- stage: Test
php: 7.3
env: DB=pgsql POSTGRESQL_VERSION=9.2 COVERAGE=yes
services:
- postgresql
addons:
postgresql: "9.2"
- stage: Test
php: 7.3
env: DB=pgsql POSTGRESQL_VERSION=9.3 COVERAGE=yes
services:
- postgresql
addons:
postgresql: "9.3"
- stage: Test
php: 7.3
env: DB=pgsql POSTGRESQL_VERSION=9.4 COVERAGE=yes
......@@ -226,7 +202,7 @@ jobs:
services:
- postgresql
addons:
postgresql: "9.6"
postgresql: "10.0"
before_script:
- bash ./tests/travis/install-postgres-10.sh
- stage: Test
......
This diff is collapsed.
<?php
declare(strict_types=1);
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
use Symfony\Component\Console\Helper\HelperSet;
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "120172ae44052999ec9a50b1121414cc",
"content-hash": "b8d34165a2e5fbd1c74f7a5125e7d9f1",
"packages": [
{
"name": "doctrine/cache",
......@@ -153,6 +153,56 @@
"eventmanager"
],
"time": "2018-06-11T11:59:03+00:00"
},
{
"name": "ocramius/package-versions",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/Ocramius/PackageVersions.git",
"reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
"reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0.0",
"php": "^7.1.0"
},
"require-dev": {
"composer/composer": "^1.6.3",
"doctrine/coding-standard": "^5.0.1",
"ext-zip": "*",
"infection/infection": "^0.7.1",
"phpunit/phpunit": "^7.0.0"
},
"type": "composer-plugin",
"extra": {
"class": "PackageVersions\\Installer",
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"PackageVersions\\": "src/PackageVersions"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"time": "2019-02-21T12:16:21+00:00"
}
],
"packages-dev": [
......@@ -1095,56 +1145,6 @@
],
"time": "2019-08-12T20:17:41+00:00"
},
{
"name": "ocramius/package-versions",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/Ocramius/PackageVersions.git",
"reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
"reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0.0",
"php": "^7.1.0"
},
"require-dev": {
"composer/composer": "^1.6.3",
"doctrine/coding-standard": "^5.0.1",
"ext-zip": "*",
"infection/infection": "^0.7.1",
"phpunit/phpunit": "^7.0.0"
},
"type": "composer-plugin",
"extra": {
"class": "PackageVersions\\Installer",
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"PackageVersions\\": "src/PackageVersions"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"time": "2019-02-21T12:16:21+00:00"
},
{
"name": "phar-io/manifest",
"version": "1.0.3",
......@@ -1582,6 +1582,63 @@
"description": "PHPStan - PHP Static Analysis Tool",
"time": "2019-08-18T20:51:53+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
"version": "0.11.2",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "fbf2ad56c3b13189d29655e226c9b1da47c2fad9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/fbf2ad56c3b13189d29655e226c9b1da47c2fad9",
"reference": "fbf2ad56c3b13189d29655e226c9b1da47c2fad9",
"shasum": ""
},
"require": {
"nikic/php-parser": "^4.0",
"php": "~7.1",
"phpstan/phpdoc-parser": "^0.3",
"phpstan/phpstan": "^0.11.4"
},
"conflict": {
"phpunit/phpunit": "<7.0"
},
"require-dev": {
"consistence/coding-standard": "^3.0.1",
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
"jakub-onderka/php-parallel-lint": "^1.0",
"phing/phing": "^2.16.0",
"phpstan/phpstan-strict-rules": "^0.11",
"phpunit/phpunit": "^7.0",
"satooshi/php-coveralls": "^1.0",
"slevomat/coding-standard": "^4.5.2"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
"dev-master": "0.11-dev"
},
"phpstan": {
"includes": [
"extension.neon",
"rules.neon"
]
}
},
"autoload": {
"psr-4": {
"PHPStan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPUnit extensions and rules for PHPStan",
"time": "2019-05-17T17:50:16+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "7.0.8",
......@@ -3129,8 +3186,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^7.2",
"ext-pdo": "*"
"php": "^7.2"
},
"platform-dev": []
}
# Azure Federations
Implementing Federations inside a new Doctrine Sharding Extension. Some extensions to the DBAL and ORM core have to be done to get this working.
1. DBAL (Database Abstraction Layer)
* Add support for Database Schema Operations
* CREATE FEDERATION
* CREATE TABLE ... FEDERATED ON
* Add support to create a multi-tenant schema from any given schema
* Add API to pick a shard based on distribution key and atomic value
* Add API to ask about federations, federation members and so on.
* Add Sharding Abstraction
* If a shard is picked via distribution key and atomic value fire queries against this only
* Or query the global database.
2. ORM (Object-Relational Mapper)
* Federation Key has to be part of the clustered index of the table
* Test with a pure Multi-Tenant App with Filtering = ON (TaskList)
* Test with sharded app (Weather)
## Implementation Details
SQL Azure requires one and exactly one clustered index. It makes no difference if the primary key
or any other key is the clustered index. Sharding requires an external ID generation (no auto-increment)
such as GUIDs. GUIDs have negative properties with regard to clustered index performance, so that
typically you would add a "created" timestamp for example that holds the clustered index instead
of making the GUID a clustered index.
## Example API:
@@@ php
<?php
use Doctrine\DBAL\DriverManager;
$dbParams = array(
'dbname' => 'tcp:dbname.database.windows.net',
'sharding' => array(
'federationName' => 'Orders_Federation',
'distributionKey' => 'CustID',
'distributionType' => 'integer',
'filteringEnabled' => false,
),
// ...
);
$conn = DriverManager::getConnection($dbParams);
$shardManager = $conn->getShardManager();
// Example 1: query against root database
$sql = "SELECT * FROM Products";
$rows = $conn->executeQuery($sql);
// Example 2: query against the selected shard with CustomerId = 100
$aCustomerID = 100;
$shardManager->selectShard($aCustomerID); // Using Default federationName and distributionKey
// Query: "USE FEDERATION Orders_Federation (CustID = $aCustomerID) WITH RESET, FILTERING OFF;"
$sql = "SELECT * FROM Customers";
$rows = $conn->executeQuery($sql);
// Example 3: Reset API to root database again
$shardManager->selectGlobal();
## ID Generation
With sharding all the ids have to be generated for global uniqueness. There are three strategies for this.
1. Use GUIDs as described here http://blogs.msdn.com/b/cbiyikoglu/archive/2011/06/20/id-generation-in-federations-identity-sequences-and-guids-uniqueidentifier.aspx
2. Having a central table that is accessed with a second connection to generate sequential ids
3. Using natural keys from the domain.
The second approach has the benefit of having numerical primary keys, however also a central failure location. The third strategy can seldom be used, because the domains don't allow this. Identity columns cannot be used at all.
@@@ php
<?php
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Id\TableHiLoIdGenerator;
$dbParams = array(
'dbname' => 'dbname.database.windows.net',
// ...
);
$conn = DriverManager::getConnection($dbParams);
$idGenerator = new TableHiLoIdGenerator($conn, 'id_table_name', $multiplicator = 1);
// only once, create this table
$idGenerator->createTable();
$nextId = $idGenerator->generateId('for_table_name');
$nextOtherId = $idGenerator->generateId('for_other_table');
The connection for the table generator has to be a different one than the one used for the main app to avoid transaction clashes.
# Doctrine Shards
Doctrine Extension to support horizontal sharding in the Doctrine ORM.
## Idea
Implement sharding inside Doctrine at a level that is as unobtrusive to the developer as possible.
Problems to tackle:
1. Where to send INSERT statements?
2. How to generate primary keys?
3. How to pick shards for update, delete statements?
4. How to pick shards for select operations?
5. How to merge select queries that span multiple shards?
6. How to handle/prevent multi-shard queries that cannot be merged (GROUP BY)?
7. How to handle non-sharded data? (static metadata tables for example)
8. How to handle multiple connections?
9. Implementation on the DBAL or ORM level?
## Roadmap
Version 1: DBAL 2.3 (Multi-Tenant Apps)
1. ID Generation support (in DBAL + ORM done)
2. Multi-Tenant Support: Either pick a global metadata database or exactly one shard.
3. Fan-out queries over all shards (or a subset) by result appending
Version 2: ORM related (complex):
4. ID resolving (Pick shard for a new ID)
5. Query resolving (Pick shards a query should send to)
6. Shard resolving (Pick shards an ID could be on)
7. Transactions
8. Read Only objects
## Technical Requirements for Database Schemas
Sharded tables require the sharding-distribution key as one of their columns. This will affect your code compared to a normalized db-schema. If you have a Blog <-> BlogPost <-> PostComments entity setup sharded by `blog_id` then even the PostComment table needs this column, even if an "unsharded", normalized DB-Schema does not need this information.
## Implementation Details
Assumptions:
* For querying you either want to query ALL or just exactly one shard.
* IDs for ALL sharded tables have to be unique across all shards.
* Non-sharded data is replicated between all shards. They redundantly keep the information available. This is necessary so join queries on shards to reference data work.
* If you retrieve an object A from a shard, then all references and collections of this object reside on the same shard.
* The database schema on all shards is the same (or compatible)
### SQL Azure Federations
SQL Azure is a special case, points 1, 2, 3, 4, 7 and 8 are partly handled on the database level. This makes it a perfect test-implementation for just the subset of features in points 5-6. However there needs to be a way to configure SchemaTool to generate the correct Schema on SQL Azure.
* SELECT Operations: The most simple assumption is to always query all shards unless the user specifies otherwise explicitly.
* Queries can be merged in PHP code, this obviously does not work for DISTINCT, GROUP BY and ORDER BY queries.
### Generic Sharding
More features are necessary to implement sharding on the PHP level, independent from database support:
1. Configuration of multiple connections, one connection = one shard.
2. Primary Key Generation mechanisms (UUID, central table, sequence emulation)
## Primary Use-Cases
1. Multi-Tenant Applications
These are easier to support as you have some value to determine the shard id for the whole request very early on.
Here also queries can always be limited to a single shard.
2. Scale-Out by some attribute (Round-Robin?)
This strategy requires access to multiple shards in a single request based on the data accessed.
......@@ -73,12 +73,8 @@ full driver name::
pdo-mysql://localhost:4486/foo?charset=UTF-8
If you wanted to use the ``drizzle_pdo__mysql`` driver instead::
drizzle-pdo-mysql://localhost:4486/foo?charset=UTF-8
In the last two examples above, mind the dashes instead of the
underscores in the URL schemes.
In the example above, mind the dashes instead of the
underscores in the URL scheme.
For connecting to an SQLite database, the authority portion of the
URL is obviously irrelevant and thus can be omitted. The path part
......@@ -128,8 +124,6 @@ interfaces to use. It can be configured in one of three ways:
- ``pdo_mysql``: A MySQL driver that uses the pdo_mysql PDO
extension.
- ``drizzle_pdo_mysql``: A Drizzle driver that uses pdo_mysql PDO
extension.
- ``mysqli``: A MySQL driver that uses the mysqli extension.
- ``pdo_sqlite``: An SQLite driver that uses the pdo_sqlite PDO
extension.
......@@ -197,23 +191,6 @@ pdo_mysql
- ``charset`` (string): The charset used when connecting to the
database.
drizzle_pdo_mysql
^^^^^^^^^^^^^^^^^
**Requires** drizzle plugin ``mysql_protocol`` or ``mysql_unix_socket_protocol`` to be enabled.
On Ubuntu this can be done by editing ``/etc/drizzle/conf.d/mysql-protocol.cnf``
or ``/etc/drizzle/conf.d/mysql-unix-socket-protocol.cnf`` and restarting the drizzled daemon.
- ``user`` (string): Username to use when connecting to the
database. Only needed if authentication is configured for drizzled.
- ``password`` (string): Password to use when connecting to the
database. Only needed if authentication is configured for drizzled.
- ``host`` (string): Hostname of the database to connect to.
- ``port`` (integer): Port of the database to connect to.
- ``dbname`` (string): Name of the database/schema to connect to.
- ``unix_socket`` (string): Name of the socket used to connect to
the database.
mysqli
^^^^^^
......@@ -252,20 +229,20 @@ pdo_pgsql
- ``sslmode`` (string): Determines whether or with what priority
a SSL TCP/IP connection will be negotiated with the server.
See the list of available modes:
`http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLMODE`
`https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNECT-SSLMODE`
- ``sslrootcert`` (string): specifies the name of a file containing
SSL certificate authority (CA) certificate(s). If the file exists,
the server's certificate will be verified to be signed by one of these
authorities.
See http://www.postgresql.org/docs/9.0/static/libpq-connect.html#LIBPQ-CONNECT-SSLROOTCERT
See https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNECT-SSLROOTCERT
- ``sslcert`` (string): specifies the file name of the client SSL certificate.
See `https://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLCERT`
See `https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNECT-SSLCERT`
- ``sslkey`` (string): specifies the location for the secret key used for the
client certificate.
See `https://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLKEY`
See `https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNECT-SSLKEY`
- ``sslcrl`` (string): specifies the file name of the SSL certificate
revocation list (CRL).
See `https://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLCRL`
See `https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNECT-SSLCRL`
- ``application_name`` (string): Name of the application that is
connecting to database. Optional. It will be displayed at ``pg_stat_activity``.
......@@ -333,14 +310,9 @@ sqlanywhere
Depending on the used underlying platform version, you can specify
any other connection parameter that is supported by the particular
platform version via the ``driverOptions`` option.
You can find a list of supported connection parameters for each
platform version here:
- `SQL Anywhere 10.0.1 <http://dcx.sybase.com/index.html#1001/en/dbdaen10/da-conmean.html>`_
- `SQL Anywhere 11.0.0 <http://dcx.sybase.com/index.html#1100/en/dbadmin_en11/conmean.html>`_
- `SQL Anywhere 11.0.1 <http://dcx.sybase.com/index.html#1101/en/dbadmin_en11/conmean.html>`_
- `SQL Anywhere 12.0.0 <http://dcx.sybase.com/index.html#1200/en/dbadmin/da-conparm.html>`_
- `SQL Anywhere 12.0.1 <http://dcx.sybase.com/index.html#1201/en/dbadmin/da-conparm.html>`_
You can find a list of supported connection parameters for the
currently supported platform here:
- `SAP Sybase SQL Anywhere 16.0 <http://dcx.sybase.com/index.html#sa160/en/dbadmin/da-conparm.html>`_
Automatic platform version detection
......
......@@ -21,7 +21,6 @@ The following database vendors are currently supported:
- PostgreSQL
- SAP Sybase SQL Anywhere
- SQLite
- Drizzle
The Doctrine 2 database layer can be used independently of the
object-relational mapper. In order to use the DBAL all you need is
......
......@@ -50,18 +50,14 @@ Oracle
Microsoft SQL Server
^^^^^^^^^^^^^^^^^^^^
- ``SQLServerPlatform`` for version 2000 and above.
- ``SQLServer2005Platform`` for version 2005 and above.
- ``SQLServer2008Platform`` for version 2008 and above.
- ``SQLServerPlatform`` for version 2008 and above.
- ``SQLServer2012Platform`` for version 2012 and above.
PostgreSQL
^^^^^^^^^^
- ``PostgreSqlPlatform`` for all versions.
- ``PostgreSQL91Platform`` for version 9.1 and above.
- ``PostgreSQL92Platform`` for version 9.2 and above.
- ``PostgreSQL94Platform`` for version 9.4 and above.
- ``PostgreSqlPlatform`` for version 9.4 and above.
- ``PostgreSQL100Platform`` for version 10.0 and above.
SAP Sybase SQL Anywhere
^^^^^^^^^^^^^^^^^^^^^^^
......@@ -76,11 +72,6 @@ SQLite
- ``SqlitePlatform`` for all versions.
Drizzle
^^^^^^
- ``DrizzlePlatform`` for all versions.
It is highly encouraged to use the platform class that matches your
database vendor and version best. Otherwise it is not guaranteed
that the compatibility in terms of SQL dialect and feature support
......
......@@ -116,10 +116,10 @@ The following options are not completely portable but are supported by most of t
vendors:
- **unsigned** (boolean): Whether a ``smallint``, ``integer`` or ``bigint`` Doctrine
type column should allow unsigned values only. Supported by MySQL, SQL Anywhere
and Drizzle. Defaults to ``false``.
type column should allow unsigned values only. Supported by MySQL and SQL Anywhere.
Defaults to ``false``.
- **comment** (integer|string): The column comment. Supported by MySQL, PostgreSQL,
Oracle, SQL Server, SQL Anywhere and Drizzle. Defaults to ``null``.
Oracle, SQL Server and SQL Anywhere. Defaults to ``null``.
Vendor specific options
^^^^^^^^^^^^^^^^^^^^^^^
......@@ -134,8 +134,8 @@ The following options are completely vendor specific and absolutely not portable
supported by some vendors but not portable:
- **charset** (string): The character set to use for the column. Currently only supported
on MySQL and Drizzle.
on MySQL.
- **collation** (string): The collation to use for the column. Supported by MySQL, PostgreSQL,
Sqlite, SQL Server and Drizzle.
Sqlite and SQL Server.
- **check** (string): The check constraint clause to add to the column.
Defaults to ``null``.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -13,8 +13,6 @@
reference/schema-representation
reference/events
reference/security
reference/sharding
reference/sharding_azure_tutorial
reference/supporting-other-databases
reference/portability
reference/caching
......
# Sharding with SQLAzure Example
This example demonstrates Sharding with SQL Azure Federations.
## Requirements
1. Windows Azure Account
2. SQL Azure Database
3. Composer for dependencies
## Install
composer install
Change "examples/sharding/bootstrap.php" to contain Database connection.
## Order to execute Scripts
1. create_schema.php
2. view_federation_members.php
3. insert_data.php
4. split_federation.php
5. insert_data_after_split.php
6. query_filtering_off.php
7. query_filtering_on.php
<?php
// bootstrap.php
use Doctrine\DBAL\DriverManager;
use Doctrine\Shards\DBAL\SQLAzure\SQLAzureShardManager;
require_once "vendor/autoload.php";
$config = array(
'dbname' => 'SalesDB',
'host' => 'tcp:dbname.windows.net',
'user' => 'user@dbname',
'password' => 'XXX',
'sharding' => array(
'federationName' => 'Orders_Federation',
'distributionKey' => 'CustId',
'distributionType' => 'integer',
)
);
if ($config['host'] == "tcp:dbname.windows.net") {
die("You have to change the configuration to your Azure account.\n");
}
$conn = DriverManager::getConnection($config);
$shardManager = new SQLAzureShardManager($conn);
{
"require": {
"doctrine/dbal": "*",
"doctrine/shards": "0.3"
}
}
<?php
// create_schema.php
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Shards\DBAL\SQLAzure\SQLAzureSchemaSynchronizer;
require_once 'bootstrap.php';
$schema = new Schema();
$products = $schema->createTable('Products');
$products->addColumn('ProductID', 'integer');
$products->addColumn('SupplierID', 'integer');
$products->addColumn('ProductName', 'string');
$products->addColumn('Price', 'decimal', array('scale' => 2, 'precision' => 12));
$products->setPrimaryKey(array('ProductID'));
$products->addOption('azure.federated', true);
$customers = $schema->createTable('Customers');
$customers->addColumn('CustomerID', 'integer');
$customers->addColumn('CompanyName', 'string');
$customers->addColumn('FirstName', 'string');
$customers->addColumn('LastName', 'string');
$customers->setPrimaryKey(array('CustomerID'));
$customers->addOption('azure.federated', true);
$customers->addOption('azure.federatedOnColumnName', 'CustomerID');
$orders = $schema->createTable('Orders');
$orders->addColumn('CustomerID', 'integer');
$orders->addColumn('OrderID', 'integer');
$orders->addColumn('OrderDate', 'datetime');
$orders->setPrimaryKey(array('CustomerID', 'OrderID'));
$orders->addOption('azure.federated', true);
$orders->addOption('azure.federatedOnColumnName', 'CustomerID');
$orderItems = $schema->createTable('OrderItems');
$orderItems->addColumn('CustomerID', 'integer');
$orderItems->addColumn('OrderID', 'integer');
$orderItems->addColumn('ProductID', 'integer');
$orderItems->addColumn('Quantity', 'integer');
$orderItems->setPrimaryKey(array('CustomerID', 'OrderID', 'ProductID'));
$orderItems->addOption('azure.federated', true);
$orderItems->addOption('azure.federatedOnColumnName', 'CustomerID');
// Create the Schema + Federation:
$synchronizer = new SQLAzureSchemaSynchronizer($conn, $shardManager);
// Or just look at the SQL:
echo implode("\n", $synchronizer->getCreateSchema($schema));
$synchronizer->createSchema($schema);
<?php
// insert_data.php
require_once "bootstrap.php";
$shardManager->selectShard(0);
$conn->insert("Products", array(
"ProductID" => 386,
"SupplierID" => 1001,
"ProductName" => 'Titanium Extension Bracket Left Hand',
"Price" => 5.25,
));
$conn->insert("Products", array(
"ProductID" => 387,
"SupplierID" => 1001,
"ProductName" => 'Titanium Extension Bracket Right Hand',
"Price" => 5.25,
));
$conn->insert("Products", array(
"ProductID" => 388,
"SupplierID" => 1001,
"ProductName" => 'Fusion Generator Module 5 kV',
"Price" => 10.50,
));
$conn->insert("Products", array(
"ProductID" => 389,
"SupplierID" => 1001,
"ProductName" => 'Bypass Filter 400 MHz Low Pass',
"Price" => 10.50,
));
$conn->insert("Customers", array(
'CustomerID' => 10,
'CompanyName' => 'Van Nuys',
'FirstName' => 'Catherine',
'LastName' => 'Abel',
));
$conn->insert("Customers", array(
'CustomerID' => 20,
'CompanyName' => 'Abercrombie',
'FirstName' => 'Kim',
'LastName' => 'Branch',
));
$conn->insert("Customers", array(
'CustomerID' => 30,
'CompanyName' => 'Contoso',
'FirstName' => 'Frances',
'LastName' => 'Adams',
));
$conn->insert("Customers", array(
'CustomerID' => 40,
'CompanyName' => 'A. Datum Corporation',
'FirstName' => 'Mark',
'LastName' => 'Harrington',
));
$conn->insert("Customers", array(
'CustomerID' => 50,
'CompanyName' => 'Adventure Works',
'FirstName' => 'Keith',
'LastName' => 'Harris',
));
$conn->insert("Customers", array(
'CustomerID' => 60,
'CompanyName' => 'Alpine Ski House',
'FirstName' => 'Wilson',
'LastName' => 'Pais',
));
$conn->insert("Customers", array(
'CustomerID' => 70,
'CompanyName' => 'Baldwin Museum of Science',
'FirstName' => 'Roger',
'LastName' => 'Harui',
));
$conn->insert("Customers", array(
'CustomerID' => 80,
'CompanyName' => 'Blue Yonder Airlines',
'FirstName' => 'Pilar',
'LastName' => 'Pinilla',
));
$conn->insert("Customers", array(
'CustomerID' => 90,
'CompanyName' => 'City Power & Light',
'FirstName' => 'Kari',
'LastName' => 'Hensien',
));
$conn->insert("Customers", array(
'CustomerID' => 100,
'CompanyName' => 'Coho Winery',
'FirstName' => 'Peter',
'LastName' => 'Brehm',
));
$conn->executeUpdate("
DECLARE @orderId INT
DECLARE @customerId INT
SET @orderId = 10
SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Hensien' and FirstName = 'Kari'
INSERT INTO Orders (CustomerId, OrderId, OrderDate)
VALUES (@customerId, @orderId, GetDate())
INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
VALUES (@customerId, @orderId, 388, 4)
SET @orderId = 20
SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Harui' and FirstName = 'Roger'
INSERT INTO Orders (CustomerId, OrderId, OrderDate)
VALUES (@customerId, @orderId, GetDate())
INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
VALUES (@customerId, @orderId, 389, 2)
SET @orderId = 30
SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Brehm' and FirstName = 'Peter'
INSERT INTO Orders (CustomerId, OrderId, OrderDate)
VALUES (@customerId, @orderId, GetDate())
INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
VALUES (@customerId, @orderId, 387, 3)
SET @orderId = 40
SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Pais' and FirstName = 'Wilson'
INSERT INTO Orders (CustomerId, OrderId, OrderDate)
VALUES (@customerId, @orderId, GetDate())
INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
VALUES (@customerId, @orderId, 388, 1)");
<?php
// insert_data_aftersplit.php
require_once 'bootstrap.php';
$newCustomerId = 55;
$shardManager->selectShard($newCustomerId);
$conn->insert("Customers", array(
"CustomerID" => $newCustomerId,
"CompanyName" => "Microsoft",
"FirstName" => "Brian",
"LastName" => "Swan",
));
$conn->insert("Orders", array(
"CustomerID" => 55,
"OrderID" => 37,
"OrderDate" => date('Y-m-d H:i:s'),
));
$conn->insert("OrderItems", array(
"CustomerID" => 55,
"OrderID" => 37,
"ProductID" => 387,
"Quantity" => 1,
));
<?php
// query_filtering_off.php
require_once "bootstrap.php";
$shardManager->selectShard(0);
$data = $conn->fetchAll('SELECT * FROM Customers');
print_r($data);
<?php
// query_filtering_on.php
require_once "bootstrap.php";
$shardManager->setFilteringEnabled(true);
$shardManager->selectShard(55);
$data = $conn->fetchAll('SELECT * FROM Customers');
print_r($data);
<?php
// split_federation.php
require_once 'bootstrap.php';
$shardManager->splitFederation(60);
<?php
// view_federation_members.php
require_once "bootstrap.php";
$shards = $shardManager->getShards();
foreach ($shards as $shard) {
print_r($shard);
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Cache;
use ArrayIterator;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Exception\InvalidColumnIndex;
use Doctrine\DBAL\FetchMode;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use function array_key_exists;
use function array_merge;
use function array_values;
use function count;
use function reset;
use function sprintf;
class ArrayStatement implements IteratorAggregate, ResultStatement
{
......@@ -43,15 +47,15 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function closeCursor()
public function closeCursor() : void
{
unset($this->data);
$this->data = null;
}
/**
* {@inheritdoc}
*/
public function columnCount()
public function columnCount() : int
{
return $this->columnCount;
}
......@@ -59,15 +63,25 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
public function rowCount() : int
{
if ($arg2 !== null || $arg3 !== null) {
throw new InvalidArgumentException('Caching layer does not support 2nd/3rd argument to setFetchMode()');
if ($this->data === null) {
return 0;
}
$this->defaultFetchMode = $fetchMode;
return count($this->data);
}
return true;
/**
* {@inheritdoc}
*/
public function setFetchMode(int $fetchMode, ...$args) : void
{
if (count($args) > 0) {
throw new InvalidArgumentException('Caching layer does not support 2nd/3rd argument to setFetchMode().');
}
$this->defaultFetchMode = $fetchMode;
}
/**
......@@ -83,7 +97,7 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
public function fetch(?int $fetchMode = null, ...$args)
{
if (! isset($this->data[$this->num])) {
return false;
......@@ -108,16 +122,18 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
return reset($row);
}
throw new InvalidArgumentException('Invalid fetch-style given for fetching result.');
throw new InvalidArgumentException(
sprintf('Invalid fetch mode given for fetching result, %d given.', $fetchMode)
);
}
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
public function fetchAll(?int $fetchMode = null, ...$args) : array
{
$rows = [];
while ($row = $this->fetch($fetchMode)) {
while ($row = $this->fetch($fetchMode, ...$args)) {
$rows[] = $row;
}
......@@ -127,11 +143,18 @@ class ArrayStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn(int $columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
if ($row === false) {
return false;
}
if (! array_key_exists($columnIndex, $row)) {
throw InvalidColumnIndex::new($columnIndex, count($row));
}
return $row[$columnIndex];
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Cache;
use Doctrine\DBAL\DBALException;
class CacheException extends DBALException
{
/**
* @return \Doctrine\DBAL\Cache\CacheException
*/
public static function noCacheKey()
{
return new self('No cache key was set.');
}
/**
* @return \Doctrine\DBAL\Cache\CacheException
*/
public static function noResultDriverConfigured()
{
return new self('Trying to cache a query but no result driver is configured.');
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Cache\Exception;
use Doctrine\DBAL\Cache\CacheException;
final class NoCacheKey extends CacheException
{
public static function new() : self
{
return new self('No cache key was set.');
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Cache\Exception;
use Doctrine\DBAL\Cache\CacheException;
final class NoResultDriverConfigured extends CacheException
{
public static function new() : self
{
return new self('Trying to cache a query but no result driver is configured.');
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Cache\Exception\NoCacheKey;
use function hash;
use function serialize;
use function sha1;
......@@ -23,42 +26,30 @@ class QueryCacheProfile
/** @var string|null */
private $cacheKey;
/**
* @param int $lifetime
* @param string|null $cacheKey
*/
public function __construct($lifetime = 0, $cacheKey = null, ?Cache $resultCache = null)
public function __construct(int $lifetime = 0, ?string $cacheKey = null, ?Cache $resultCache = null)
{
$this->lifetime = $lifetime;
$this->cacheKey = $cacheKey;
$this->resultCacheDriver = $resultCache;
}
/**
* @return Cache|null
*/
public function getResultCacheDriver()
public function getResultCacheDriver() : ?Cache
{
return $this->resultCacheDriver;
}
/**
* @return int
*/
public function getLifetime()
public function getLifetime() : int
{
return $this->lifetime;
}
/**
* @return string
*
* @throws CacheException
*/
public function getCacheKey()
public function getCacheKey() : string
{
if ($this->cacheKey === null) {
throw CacheException::noCacheKey();
throw NoCacheKey::new();
}
return $this->cacheKey;
......@@ -67,14 +58,13 @@ class QueryCacheProfile
/**
* Generates the real cache key from query, params, types and connection parameters.
*
* @param string $query
* @param mixed[] $params
* @param int[]|string[] $types
* @param mixed[] $connectionParams
*
* @return string[]
*/
public function generateCacheKeys($query, $params, $types, array $connectionParams = [])
public function generateCacheKeys(string $query, array $params, array $types, array $connectionParams = []) : array
{
$realCacheKey = 'query=' . $query .
'&params=' . serialize($params) .
......@@ -91,30 +81,17 @@ class QueryCacheProfile
return [$cacheKey, $realCacheKey];
}
/**
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function setResultCacheDriver(Cache $cache)
public function setResultCacheDriver(Cache $cache) : self
{
return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache);
}
/**
* @param string|null $cacheKey
*
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function setCacheKey($cacheKey)
public function setCacheKey(?string $cacheKey) : self
{
return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver);
}
/**
* @param int $lifetime
*
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function setLifetime($lifetime)
public function setLifetime(int $lifetime) : self
{
return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver);
}
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Cache;
use ArrayIterator;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Exception\InvalidColumnIndex;
use Doctrine\DBAL\FetchMode;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use function array_key_exists;
use function array_merge;
use function array_values;
use function assert;
use function count;
use function reset;
/**
......@@ -58,12 +60,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/** @var int */
private $defaultFetchMode = FetchMode::MIXED;
/**
* @param string $cacheKey
* @param string $realKey
* @param int $lifetime
*/
public function __construct(ResultStatement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime)
public function __construct(ResultStatement $stmt, Cache $resultCache, string $cacheKey, string $realKey, int $lifetime)
{
$this->statement = $stmt;
$this->resultCache = $resultCache;
......@@ -75,11 +72,12 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function closeCursor()
public function closeCursor() : void
{
$this->statement->closeCursor();
if (! $this->emptied || $this->data === null) {
return true;
return;
}
$data = $this->resultCache->fetch($this->cacheKey);
......@@ -90,14 +88,12 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
$this->resultCache->save($this->cacheKey, $data, $this->lifetime);
unset($this->data);
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
public function columnCount() : int
{
return $this->statement->columnCount();
}
......@@ -105,11 +101,9 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
public function setFetchMode(int $fetchMode, ...$args) : void
{
$this->defaultFetchMode = $fetchMode;
return true;
}
/**
......@@ -125,7 +119,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
public function fetch(?int $fetchMode = null, ...$args)
{
if ($this->data === null) {
$this->data = [];
......@@ -165,9 +159,9 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
public function fetchAll(?int $fetchMode = null, ...$args) : array
{
$data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
$data = $this->statement->fetchAll($fetchMode, ...$args);
if ($fetchMode === FetchMode::COLUMN) {
foreach ($data as $key => $value) {
......@@ -184,12 +178,19 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
public function fetchColumn(int $columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
if ($row === false) {
return false;
}
if (! array_key_exists($columnIndex, $row)) {
throw InvalidColumnIndex::new($columnIndex, count($row));
}
return $row[$columnIndex];
}
/**
......@@ -203,10 +204,8 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
*
* @return int The number of rows.
*/
public function rowCount()
public function rowCount() : int
{
assert($this->statement instanceof Statement);
return $this->statement->rowCount();
}
}
<?php
namespace Doctrine\DBAL;
declare(strict_types=1);
use PDO;
namespace Doctrine\DBAL;
/**
* Contains portable column case conversions.
......@@ -14,14 +14,14 @@ final class ColumnCase
*
* @see \PDO::CASE_UPPER
*/
public const UPPER = PDO::CASE_UPPER;
public const UPPER = 1;
/**
* Convert column names to lower case.
*
* @see \PDO::CASE_LOWER
*/
public const LOWER = PDO::CASE_LOWER;
public const LOWER = 2;
/**
* This class cannot be instantiated.
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Logging\NullLogger;
use Doctrine\DBAL\Logging\SQLLogger;
use Doctrine\DBAL\Schema\AbstractAsset;
use function preg_match;
......@@ -24,41 +27,33 @@ class Configuration
protected $_attributes = [];
/**
* Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled.
*
* @return void
* Sets the SQL logger to use.
*/
public function setSQLLogger(?SQLLogger $logger = null)
public function setSQLLogger(?SQLLogger $logger) : void
{
$this->_attributes['sqlLogger'] = $logger;
}
/**
* Gets the SQL logger that is used.
*
* @return SQLLogger|null
*/
public function getSQLLogger()
public function getSQLLogger() : SQLLogger
{
return $this->_attributes['sqlLogger'] ?? null;
return $this->_attributes['sqlLogger'] ?? $this->_attributes['sqlLogger'] = new NullLogger();
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return Cache|null
*/
public function getResultCacheImpl()
public function getResultCacheImpl() : ?Cache
{
return $this->_attributes['resultCacheImpl'] ?? null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @return void
*/
public function setResultCacheImpl(Cache $cacheImpl)
public function setResultCacheImpl(Cache $cacheImpl) : void
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
......@@ -71,12 +66,8 @@ class Configuration
* {AbstractSchemaManager#createSchema()}.
*
* @deprecated Use Configuration::setSchemaAssetsFilter() instead
*
* @param string $filterExpression
*
* @return void
*/
public function setFilterSchemaAssetsExpression($filterExpression)
public function setFilterSchemaAssetsExpression(?string $filterExpression) : void
{
$this->_attributes['filterSchemaAssetsExpression'] = $filterExpression;
if ($filterExpression) {
......@@ -86,29 +77,14 @@ class Configuration
}
}
/**
* Returns filter schema assets expression.
*
* @deprecated Use Configuration::getSchemaAssetsFilter() instead
*
* @return string|null
*/
public function getFilterSchemaAssetsExpression()
{
return $this->_attributes['filterSchemaAssetsExpression'] ?? null;
}
/**
* @param string $filterExpression
*/
private function buildSchemaAssetsFilterFromExpression($filterExpression) : callable
private function buildSchemaAssetsFilterFromExpression(string $filterExpression) : callable
{
return static function ($assetName) use ($filterExpression) {
return static function ($assetName) use ($filterExpression) : bool {
if ($assetName instanceof AbstractAsset) {
$assetName = $assetName->getName();
}
return preg_match($filterExpression, $assetName);
return preg_match($filterExpression, $assetName) > 0;
};
}
......@@ -137,13 +113,13 @@ class Configuration
* transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
* the method commit or the method rollback. By default, new connections are in auto-commit mode.
*
* @see getAutoCommit
* @see getAutoCommit
*
* @param bool $autoCommit True to enable auto-commit mode; false to disable it.
*/
public function setAutoCommit($autoCommit)
public function setAutoCommit(bool $autoCommit) : void
{
$this->_attributes['autoCommit'] = (bool) $autoCommit;
$this->_attributes['autoCommit'] = $autoCommit;
}
/**
......@@ -153,7 +129,7 @@ class Configuration
*
* @return bool True if auto-commit mode is enabled by default for connections, false otherwise.
*/
public function getAutoCommit()
public function getAutoCommit() : bool
{
return $this->_attributes['autoCommit'] ?? true;
}
......
This diff is collapsed.
<?php
declare(strict_types=1);
namespace Doctrine\DBAL;
class ConnectionException extends DBALException
{
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function commitFailedRollbackOnly()
{
return new self('Transaction commit failed because the transaction has been marked for rollback only.');
}
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function noActiveTransaction()
{
return new self('There is no active transaction.');
}
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function savepointsNotSupported()
{
return new self('Savepoints are not supported by this driver.');
}
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function mayNotAlterNestedTransactionWithSavepointsInTransaction()
{
return new self('May not alter the nested transaction with savepoints behavior while a transaction is open.');
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Connections;
use Doctrine\Common\EventManager;
......@@ -7,13 +9,14 @@ use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use InvalidArgumentException;
use function array_rand;
use function assert;
use function count;
use function func_get_args;
/**
* Master-Slave Connection
......@@ -64,14 +67,15 @@ use function func_get_args;
* )
* ));
*
* You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information.
* You can also pass 'driverOptions' and any other documented option to each of this drivers
* to pass additional information.
*/
class MasterSlaveConnection extends Connection
{
/**
* Master and slave connection (one of the randomly picked slaves).
*
* @var DriverConnection[]|null[]
* @var array<string, DriverConnection|null>
*/
protected $connections = ['master' => null, 'slave' => null];
......@@ -86,12 +90,16 @@ class MasterSlaveConnection extends Connection
/**
* Creates Master Slave Connection.
*
* @param mixed[] $params
* @param array<string, mixed> $params
*
* @throws InvalidArgumentException
*/
public function __construct(array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null)
{
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
if (! isset($params['slaves'], $params['master'])) {
throw new InvalidArgumentException('master or slaves configuration missing');
}
......@@ -111,10 +119,8 @@ class MasterSlaveConnection extends Connection
/**
* Checks if the connection is currently towards the master or not.
*
* @return bool
*/
public function isConnectedToMaster()
public function isConnectedToMaster() : bool
{
return $this->_conn !== null && $this->_conn === $this->connections['master'];
}
......@@ -122,7 +128,7 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function connect($connectionName = null)
public function connect($connectionName = null) : void
{
$requestedConnectionChange = ($connectionName !== null);
$connectionName = $connectionName ?: 'slave';
......@@ -135,7 +141,7 @@ class MasterSlaveConnection extends Connection
// change request, then abort right here, because we are already done.
// This prevents writes to the slave in case of "keepSlave" option enabled.
if ($this->_conn !== null && ! $requestedConnectionChange) {
return false;
return;
}
$forceMasterAsSlave = false;
......@@ -152,7 +158,7 @@ class MasterSlaveConnection extends Connection
$this->connections['slave'] = $this->_conn;
}
return false;
return;
}
if ($connectionName === 'master') {
......@@ -166,22 +172,18 @@ class MasterSlaveConnection extends Connection
$this->connections['slave'] = $this->_conn = $this->connectTo($connectionName);
}
if ($this->_eventManager->hasListeners(Events::postConnect)) {
$eventArgs = new ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
if (! $this->_eventManager->hasListeners(Events::postConnect)) {
return;
}
return true;
$eventArgs = new ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
}
/**
* Connects to a specific connection.
*
* @param string $connectionName
*
* @return DriverConnection
*/
protected function connectTo($connectionName)
protected function connectTo(string $connectionName) : DriverConnection
{
$params = $this->getParams();
......@@ -189,19 +191,18 @@ class MasterSlaveConnection extends Connection
$connectionParams = $this->chooseConnectionConfiguration($connectionName, $params);
$user = $connectionParams['user'] ?? null;
$password = $connectionParams['password'] ?? null;
$user = $connectionParams['user'] ?? '';
$password = $connectionParams['password'] ?? '';
return $this->_driver->connect($connectionParams, $user, $password, $driverOptions);
}
/**
* @param string $connectionName
* @param mixed[] $params
* @param array<string, mixed> $params
*
* @return mixed
* @return array<string, mixed>
*/
protected function chooseConnectionConfiguration($connectionName, $params)
protected function chooseConnectionConfiguration(string $connectionName, array $params) : array
{
if ($connectionName === 'master') {
return $params['master'];
......@@ -219,7 +220,7 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function executeUpdate($query, array $params = [], array $types = [])
public function executeUpdate(string $query, array $params = [], array $types = []) : int
{
$this->connect('master');
......@@ -229,47 +230,47 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function beginTransaction()
public function beginTransaction() : void
{
$this->connect('master');
return parent::beginTransaction();
parent::beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
public function commit() : void
{
$this->connect('master');
return parent::commit();
parent::commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
public function rollBack() : void
{
$this->connect('master');
return parent::rollBack();
parent::rollBack();
}
/**
* {@inheritDoc}
*/
public function delete($tableName, array $identifier, array $types = [])
public function delete(string $table, array $identifier, array $types = []) : int
{
$this->connect('master');
return parent::delete($tableName, $identifier, $types);
return parent::delete($table, $identifier, $types);
}
/**
* {@inheritDoc}
*/
public function close()
public function close() : void
{
unset($this->connections['master'], $this->connections['slave']);
......@@ -282,27 +283,27 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function update($tableName, array $data, array $identifier, array $types = [])
public function update(string $table, array $data, array $identifier, array $types = []) : int
{
$this->connect('master');
return parent::update($tableName, $data, $identifier, $types);
return parent::update($table, $data, $identifier, $types);
}
/**
* {@inheritDoc}
*/
public function insert($tableName, array $data, array $types = [])
public function insert(string $table, array $data, array $types = []) : int
{
$this->connect('master');
return parent::insert($tableName, $data, $types);
return parent::insert($table, $data, $types);
}
/**
* {@inheritDoc}
*/
public function exec($statement)
public function exec(string $statement) : int
{
$this->connect('master');
......@@ -312,7 +313,7 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function createSavepoint($savepoint)
public function createSavepoint(string $savepoint) : void
{
$this->connect('master');
......@@ -322,7 +323,7 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function releaseSavepoint($savepoint)
public function releaseSavepoint(string $savepoint) : void
{
$this->connect('master');
......@@ -332,7 +333,7 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function rollbackSavepoint($savepoint)
public function rollbackSavepoint(string $savepoint) : void
{
$this->connect('master');
......@@ -342,25 +343,19 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function query()
public function query(string $sql) : ResultStatement
{
$this->connect('master');
assert($this->_conn instanceof DriverConnection);
$args = func_get_args();
$logger = $this->getConfiguration()->getSQLLogger();
if ($logger) {
$logger->startQuery($args[0]);
}
$logger->startQuery($sql);
$statement = $this->_conn->query(...$args);
$statement = $this->_conn->query($sql);
$statement->setFetchMode($this->defaultFetchMode);
if ($logger) {
$logger->stopQuery();
}
$logger->stopQuery();
return $statement;
}
......@@ -368,10 +363,10 @@ class MasterSlaveConnection extends Connection
/**
* {@inheritDoc}
*/
public function prepare($statement)
public function prepare(string $sql) : Statement
{
$this->connect('master');
return parent::prepare($statement);
return parent::prepare($sql);
}
}
This diff is collapsed.
<?php
declare(strict_types=1);
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
......@@ -14,16 +17,19 @@ interface Driver
/**
* Attempts to create a connection with the database.
*
* The usage of NULL to indicate empty username or password is deprecated. Use an empty string instead.
*
* @param mixed[] $params All connection parameters passed by the user.
* @param string|null $username The username to use when connecting.
* @param string|null $password The password to use when connecting.
* @param mixed[] $driverOptions The driver options to use when connecting.
* @param mixed[] $params All connection parameters passed by the user.
* @param string $username The username to use when connecting.
* @param string $password The password to use when connecting.
* @param mixed[] $driverOptions The driver options to use when connecting.
*
* @return \Doctrine\DBAL\Driver\Connection The database connection.
* @return DriverConnection The database connection.
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = []);
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
) : DriverConnection;
/**
* Gets the DatabasePlatform instance that provides all the metadata about
......@@ -31,29 +37,11 @@ interface Driver
*
* @return AbstractPlatform The database platform.
*/
public function getDatabasePlatform();
public function getDatabasePlatform() : AbstractPlatform;
/**
* Gets the SchemaManager that can be used to inspect and change the underlying
* database schema of the platform this driver connects to.
*
* @return AbstractSchemaManager
*/
public function getSchemaManager(Connection $conn);
/**
* Gets the name of the driver.
*
* @deprecated
*
* @return string The name of the driver.
*/
public function getName();
/**
* Gets the name of the database connected to for this driver.
*
* @return string The name of the database.
*/
public function getDatabase(Connection $conn);
public function getSchemaManager(Connection $conn) : AbstractSchemaManager;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\DB2SchemaManager;
/**
......@@ -15,17 +19,7 @@ abstract class AbstractDB2Driver implements Driver
/**
* {@inheritdoc}
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'];
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
public function getDatabasePlatform() : AbstractPlatform
{
return new DB2Platform();
}
......@@ -33,7 +27,7 @@ abstract class AbstractDB2Driver implements Driver
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
public function getSchemaManager(Connection $conn) : AbstractSchemaManager
{
return new DB2SchemaManager($conn);
}
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Exception;
use Throwable;
/**
* Abstract base implementation of the {@link DriverException} interface.
*/
abstract class AbstractDriverException extends Exception implements DriverException
{
/**
* The driver specific error code.
*
* @var int|string|null
*/
private $errorCode;
/**
* The SQLSTATE of the driver.
*
......@@ -24,30 +20,22 @@ abstract class AbstractDriverException extends Exception implements DriverExcept
private $sqlState;
/**
* @param string $message The driver error message.
* @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any.
* @param int|string|null $errorCode The driver specific error code if any.
* @param string $message The driver error message.
* @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any.
* @param int $code The driver specific error code if any.
* @param Throwable|null $previous The previous throwable used for the exception chaining.
*/
public function __construct($message, $sqlState = null, $errorCode = null)
public function __construct(string $message, ?string $sqlState = null, int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message);
parent::__construct($message, $code, $previous);
$this->errorCode = $errorCode;
$this->sqlState = $sqlState;
}
/**
* {@inheritdoc}
*/
public function getErrorCode()
{
return $this->errorCode;
$this->sqlState = $sqlState;
}
/**
* {@inheritdoc}
*/
public function getSQLState()
public function getSQLState() : ?string
{
return $this->sqlState;
}
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Platforms\SQLServer2005Platform;
use Doctrine\DBAL\Platforms\SQLServer2008Platform;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SQLServerSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function preg_match;
......@@ -22,14 +24,14 @@ abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDr
/**
* {@inheritdoc}
*/
public function createDatabasePlatformForVersion($version)
public function createDatabasePlatformForVersion(string $version) : AbstractPlatform
{
if (! preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+)(?:\.(?P<build>\d+))?)?)?/',
$version,
$versionParts
)) {
throw DBALException::invalidPlatformVersionSpecified(
throw InvalidPlatformVersion::new(
$version,
'<major_version>.<minor_version>.<patch_version>.<build_version>'
);
......@@ -44,10 +46,6 @@ abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDr
switch (true) {
case version_compare($version, '11.00.2100', '>='):
return new SQLServer2012Platform();
case version_compare($version, '10.00.1600', '>='):
return new SQLServer2008Platform();
case version_compare($version, '9.00.1399', '>='):
return new SQLServer2005Platform();
default:
return new SQLServerPlatform();
}
......@@ -56,25 +54,15 @@ abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDr
/**
* {@inheritdoc}
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'] ?? $conn->query('SELECT DB_NAME()')->fetchColumn();
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
public function getDatabasePlatform() : AbstractPlatform
{
return new SQLServer2008Platform();
return new SQLServerPlatform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
public function getSchemaManager(Connection $conn) : AbstractSchemaManager
{
return new SQLServerSchemaManager($conn);
}
......
This diff is collapsed.
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Throwable;
......@@ -12,29 +14,10 @@ use Throwable;
*/
interface DriverException extends Throwable
{
/**
* Returns the driver specific error code if available.
*
* Returns null if no driver specific error code is available
* for the error raised by the driver.
*
* @return int|string|null
*/
public function getErrorCode();
/**
* Returns the driver error message.
*
* @return string
*/
public function getMessage();
/**
* Returns the SQLSTATE the driver was in at the time the error occurred.
*
* Returns null if the driver does not provide a SQLSTATE for the error occurred.
*
* @return string|null
*/
public function getSQLState();
public function getSQLState() : ?string;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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