PK T SECURITY.mdnu ٘ Security
========
The Doctrine library is operating very close to your database and as such needs
to handle and make assumptions about SQL injection vulnerabilities.
It is vital that you understand how Doctrine approaches security because
we cannot protect you from SQL injection.
Please read the documentation chapter on Security in Doctrine DBAL to
understand the assumptions we make.
- [Latest security.rst page on Github](https://github.com/doctrine/dbal/blob/4.0.x/docs/en/reference/security.rst)
- [Security Page in rendered documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
PK T p p psalm.xml.distnu ٘
PK T ci/github/ext/install-ibm_db2.shnu [y #!/usr/bin/env bash
set -ex
echo "Installing extension"
(
cd /tmp
wget https://public.dhe.ibm.com/ibmdl/export/pub/software/data/db2/drivers/odbc_cli/linuxx64_odbc_cli.tar.gz
tar xf linuxx64_odbc_cli.tar.gz
pecl download ibm_db2
tar xf ibm_db2-*
rm ibm_db2-*.tgz
cd ibm_db2-*
phpize
./configure --with-IBM_DB2=/tmp/clidriver
make -j "$(nproc)"
sudo make install
)
PK T7Ea ci/github/phpunit/sqlite.xmlnu ٘
../../../tests
../../../src
PK T=3 3 ci/github/phpunit/mysqli.xmlnu ٘
../../../tests
../../../src
PK Ti, ci/github/phpunit/mysqli-tls.xmlnu ٘
../../../tests
../../../src
PK T6 6 ci/github/phpunit/pdo_mysql.xmlnu ٘
../../../tests
../../../src
PK T ci/github/phpunit/oci8.xmlnu ٘
../../../tests
../../../src
PK T_k ci/github/phpunit/pdo_oci.xmlnu ٘
../../../tests
../../../src
PK TF, ci/github/phpunit/ibm_db2.xmlnu ٘
../../../tests
../../../src
PK Tb ci/github/phpunit/pdo_pgsql.xmlnu ٘
../../../tests
../../../src
PK T
ci/github/phpunit/pdo_sqlsrv.xmlnu ٘
../../../tests
../../../src
PK TG ci/github/phpunit/sqlsrv.xmlnu ٘
../../../tests
../../../src
PK T4˯q 2 ci/appveyor/mssql.sql2008r2sp2.sqlsrv.appveyor.xmlnu ٘
../../tests
../../src
PK T'G. - ci/appveyor/mssql.sql2017.sqlsrv.appveyor.xmlnu ٘
../../tests
../../src
PK T)0 1 ci/appveyor/mssql.sql2017.pdo_sqlsrv.appveyor.xmlnu ٘
../../tests
../../src
PK Tjf2 0 ci/appveyor/mssql.sql2012sp1.sqlsrv.appveyor.xmlnu ٘
../../tests
../../src
PK TؤB B ci/travis/install-mariadb.shnu ٘ #!/usr/bin/env bash
set -ex
sudo docker run \
-d \
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes \
-e MYSQL_DATABASE=doctrine_tests \
-p 33306:3306 \
--name mariadb \
mariadb:${MARIADB_VERSION}
sudo docker exec -i mariadb bash <<< 'until echo \\q | mysql doctrine_tests > /dev/null 2>&1 ; do sleep 1; done'
PK TMD D README.mdnu ٘ # Doctrine DBAL
| [4.0-dev][4.0] | [3.3][3.3] |
|:-----------------------------------------------:|:---------------------------------------------------:|
| [![GitHub Actions][GA 4.0 image]][GA 4.0] | [![GitHub Actions][GA 3.3 image]][GA 3.3] |
| [![AppVeyor][AppVeyor 4.0 image]][AppVeyor 4.0] | [![AppVeyor][AppVeyor 3.3 image]][AppVeyor 3.3] |
| [![Code Coverage][Coverage image]][CodeCov 4.0] | [![Code Coverage][Coverage 3.3 image]][CodeCov 3.3] |
| N/A | [![Code Coverage][TypeCov 3.3 image]][TypeCov 3.3] |
Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features for database schema introspection and schema management.
## More resources:
* [Website](http://www.doctrine-project.org/projects/dbal.html)
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/)
* [Issue Tracker](https://github.com/doctrine/dbal/issues)
[Coverage image]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x/graph/badge.svg
[4.0]: https://github.com/doctrine/dbal/tree/4.0.x
[CodeCov 4.0]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x
[AppVeyor 4.0]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.0.x
[AppVeyor 4.0 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.0.x?svg=true
[GA 4.0]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x
[GA 4.0 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg
[Coverage 3.3 image]: https://codecov.io/gh/doctrine/dbal/branch/3.3.x/graph/badge.svg
[3.3]: https://github.com/doctrine/dbal/tree/3.3.x
[CodeCov 3.3]: https://codecov.io/gh/doctrine/dbal/branch/3.3.x
[AppVeyor 3.3]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.3.x
[AppVeyor 3.3 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.3.x?svg=true
[GA 3.3]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.3.x
[GA 3.3 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.3.x
[TypeCov 3.3]: https://shepherd.dev/github/doctrine/dbal
[TypeCov 3.3 image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg
PK Tc9ȱ CONTRIBUTING.mdnu ٘ Doctrine has [general contributing guidelines][contributor workflow], make
sure you follow them.
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
PK Ty(>?
UPGRADE.mdnu ٘ Note about upgrading: Doctrine uses static and runtime mechanisms to raise
awareness about deprecated code.
- Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or
Static Analysis tools (like Psalm, phpstan)
- Use of our low-overhead runtime deprecation API, details:
https://github.com/doctrine/deprecations/
# Upgrade to 3.4
## Deprecated `AbstractPlatform` methods exposing quote characters.
The `AbstractPlatform::getStringLiteralQuoteCharacter()` and `::getIdentifierQuoteCharacter()` methods
have been deprecated. Use `::quoteStringLiteral()` and `::quoteIdentifier()` to quote string literals and identifiers
respectively.
## Deprecated `AbstractSchemaManager::getDatabasePlatform()`
The `AbstractSchemaManager::getDatabasePlatform()` method has been deprecated. Use `Connection::getDatabasePlatform()`
instead.
## Deprecated passing date interval parameters as integer.
Passing date interval parameters to the following `AbstractPlatform` methods as integer has been deprecated:
- the `$seconds` argument in `::getDateAddSecondsExpression()`,
- the `$seconds` parameter in `::getDateSubSecondsExpression()`,
- the `$minutes` parameter in `::getDateAddMinutesExpression()`,
- the `$minutes` parameter in `::getDateSubMinutesExpression()`,
- the `$hours` parameter in `::getDateAddHourExpression()`,
- the `$hours` parameter in `::getDateAddHourExpression()`,
- the `$days` parameter in `::getDateAddDaysExpression()`,
- the `$days` parameter in `::getDateSubDaysExpression()`,
- the `$weeks` parameter in `::getDateAddWeeksExpression()`,
- the `$weeks` parameter in `::getDateSubWeeksExpression()`,
- the `$months` parameter in `::getDateAddMonthExpression()`,
- the `$months` parameter in `::getDateSubMonthExpression()`,
- the `$quarters` parameter in `::getDateAddQuartersExpression()`,
- the `$quarters` parameter in `::getDateSubQuartersExpression()`,
- the `$years` parameter in `::getDateAddYearsExpression()`,
- the `$years` parameter in `::getDateSubYearsExpression()`.
Use the strings representing numeric SQL literals instead (e.g. `'1'` instead of `1`).
## Deprecated transaction nesting without savepoints
Starting a transaction inside another transaction with
`Doctrine\DBAL\Connection::beginTransaction()` without enabling transaction
nesting with savepoints beforehand is deprecated.
Transaction nesting with savepoints can be enabled with
`$connection->setNestTransactionsWithSavepoints(true);`
In case your platform does not support savepoints, you will have to rework your
application logic so as to avoid nested transaction blocks.
## Added runtime deprecations for the default string column length.
In addition to the formal deprecation introduced in DBAL 3.2, the library will now emit a deprecation message at runtime
if the string or binary column length is omitted, but it's required by the target database platform.
## Deprecated `AbstractPlatform::getVarcharTypeDeclarationSQL()`
The `AbstractPlatform::getVarcharTypeDeclarationSQL()` method has been deprecated.
Use `AbstractPlatform::getStringTypeDeclarationSQL()` instead.
## Deprecated `$database` parameter of `AbstractSchemaManager::list*()` methods
Passing `$database` to the following methods has been deprecated:
- `AbstractSchemaManager::listSequences()`,
- `AbstractSchemaManager::listTableColumns()`,
- `AbstractSchemaManager::listTableForeignKeys()`.
Only introspection of the current database will be supported in DBAL 4.0.
## Deprecated `AbstractPlatform` schema introspection methods
The following schema introspection methods have been deprecated:
- `AbstractPlatform::getListTablesSQL()`,
- `AbstractPlatform::getListTableColumnsSQL()`,
- `AbstractPlatform::getListTableIndexesSQL()`,
- `AbstractPlatform::getListTableForeignKeysSQL()`.
The queries used for schema introspection are an internal implementation detail of the DBAL.
## Deprecated `collate` option for MySQL
This undocumented option is deprecated in favor of `collation`.
## Deprecated `AbstractPlatform::getListTableConstraintsSQL()`
This method is unused by the DBAL since 2.0.
## Deprecated `Type::getName()`
This will method is not useful for the DBAL anymore, and will be removed in 4.0.
As a consequence, depending on the name of a type being `json` for `jsonb` to
be used for the Postgres platform is deprecated in favor of extending
`Doctrine\DBAL\Types\JsonType`.
## Deprecated `AbstractPlatform::getColumnComment()` and `AbstractPlatform::getDoctrineTypeComment()`
DBAL no longer needs column comments to ensure proper diffing. Note that both
methods should probably have been marked as internal as these comments were an
implementation detail of the DBAL.
# Upgrade to 3.3
## Deprecated `Type::canRequireSQLConversion()`.
Consumers should call `Type::convertToDatabaseValueSQL()` and `Type::convertToPHPValueSQL()` regardless of the type.
## Deprecated the `doctrine-dbal` binary.
The documentation explains how the console tools can be bootstrapped for standalone usage.
The method `ConsoleRunner::printCliConfigTemplate()` is deprecated because it was only useful in the context of the
`doctrine-dbal` binary.
## Deprecated the `Graphviz` visitor.
This class is not part of the database abstraction provided by the library and will be removed in DBAL 4.
## Deprecated the `--depth` option of `RunSqlCommand`.
This option does not have any effect anymore and will be removed in DBAL 4.
## Deprecated platform "commented type" API
Since `Type::requiresSQLCommentTypeHint()` already allows determining whether a
type should result in SQL columns with a type hint in their comments, the
following methods are deprecated:
- `AbstractPlatform::isCommentedDoctrineType()`
- `AbstractPlatform::initializeCommentedDoctrineTypes()`
- `AbstractPlatform::markDoctrineTypeCommented()`
The protected property `AbstractPlatform::$doctrineTypeComments` is deprecated
as well.
## Deprecated support for IBM DB2 10.5 and older
IBM DB2 10.5 and older won't be supported in DBAL 4. Consider upgrading to IBM DB2 11.1 or later.
## Deprecated support for Oracle 12c (12.2.0.1) and older
Oracle 12c (12.2.0.1) won't be supported in DBAL 4. Consider upgrading to Oracle 18c (12.2.0.2) or later.
## Deprecated support for MariaDB 10.2.6 and older
MariaDB 10.2.6 and older won't be supported in DBAL 4. Consider upgrading to MariaDB 10.2.7 or later.
The following classes have been deprecated:
* `Doctrine\DBAL\Platforms\MariaDb1027Platform`
* `Doctrine\DBAL\Platforms\Keywords\MariaDb102Keywords`
## Deprecated support for MySQL 5.6 and older
MySQL 5.6 and older won't be actively supported in DBAL 4. Consider upgrading to MySQL 5.7 or later.
The following classes have been deprecated:
* `Doctrine\DBAL\Platforms\MySQL57Platform`
* `Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords`
## Deprecated support for Postgres 9
Postgres 9 won't be actively supported in DBAL 4. Consider upgrading to Postgres 10 or later.
The following classes have been deprecated:
* `Doctrine\DBAL\Platforms\PostgreSQL100Platform`
* `Doctrine\DBAL\Platforms\Keywords\PostgreSQL100Keywords`
## Deprecated `Connection::getWrappedConnection()`, `Connection::connect()` made `@internal`.
The wrapper-level `Connection::getWrappedConnection()` method has been deprecated.
Use `Connection::getNativeConnection()` to access the native connection.
The `Connection::connect()` method has been marked internal. It will be marked `protected` in DBAL 4.0.
## Add `Connection::getNativeConnection()`
Driver and middleware connections need to implement a new method `getNativeConnection()` that gives access to the
native database connection. Not doing so is deprecated.
## Deprecate accessors for the native connection in favor of `getNativeConnection()`
The following methods have been deprecated:
* `Doctrine\DBAL\Driver\PDO\Connection::getWrappedConnection()`
* `Doctrine\DBAL\Driver\PDO\SQLSrv\Connection::getWrappedConnection()`
* `Doctrine\DBAL\Driver\Mysqli\Connection::getWrappedResourceHandle()`
Call `getNativeConnection()` to access the underlying PDO or MySQLi connection.
# Upgrade to 3.2
## Minor BC Break: using cache keys with characters reserved by `psr/cache`
We have been working on phasing out `doctrine/cache`, and 3.2.0 allows to use
`psr/cache` instead. To help calling our own internal APIs in a unified way, we
also wrap `doctrine/cache` implementations with a `psr/cache` adapter.
Using cache keys containing characters reserved by `psr/cache` will result in
an exception. The characters are the following: `{}()/\@`.
## Deprecated `SQLLogger` and its implementations.
The `SQLLogger` and its implementations `DebugStack` and `LoggerChain` have been deprecated.
For logging purposes, use `Doctrine\DBAL\Logging\Middleware` instead. No replacement for `DebugStack` is provided.
## Deprecated `SqliteSchemaManager::createDatabase()` and `dropDatabase()` methods.
The `SqliteSchemaManager::createDatabase()` and `dropDatabase()` methods have been deprecated. The SQLite engine
will create the database file automatically. In order to delete the database file, use the filesystem.
## Deprecated `AbstractSchemaManager::dropAndCreate*()` and `::tryMethod()` methods.
The following `AbstractSchemaManager::dropAndCreate*()` methods have been deprecated:
1. `AbstractSchemaManager::dropAndCreateConstraint()`. Use `AbstractSchemaManager::dropIndex()`
and `AbstractSchemaManager::createIndex()`, `AbstractSchemaManager::dropForeignKey()`
and `AbstractSchemaManager::createForeignKey()` or `AbstractSchemaManager::dropUniqueConstraint()`
and `AbstractSchemaManager::createUniqueConstraint()` instead.
2. `AbstractSchemaManager::dropAndCreateIndex()`. Use `AbstractSchemaManager::dropIndex()`
and `AbstractSchemaManager::createIndex()` instead.
3. `AbstractSchemaManager::dropAndCreateForeignKey()`.
Use AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey() instead.
4. `AbstractSchemaManager::dropAndCreateSequence()`. Use `AbstractSchemaManager::dropSequence()`
and `AbstractSchemaManager::createSequence()` instead.
5. `AbstractSchemaManager::dropAndCreateTable()`. Use `AbstractSchemaManager::dropTable()`
and `AbstractSchemaManager::createTable()` instead.
6. `AbstractSchemaManager::dropAndCreateDatabase()`. Use `AbstractSchemaManager::dropDatabase()`
and `AbstractSchemaManager::createDatabase()` instead.
7. `AbstractSchemaManager::dropAndCreateView()`. Use `AbstractSchemaManager::dropView()`
and `AbstractSchemaManager::createView()` instead.
The `AbstractSchemaManager::tryMethod()` method has been also deprecated.
## Deprecated `AbstractSchemaManager::getSchemaSearchPaths()`.
1. The `AbstractSchemaManager::getSchemaSearchPaths()` method has been deprecated.
2. Relying on `AbstractSchemaManager::createSchemaConfig()` populating the schema name for those database
platforms that don't support schemas (currently, all except for PostgreSQL) is deprecated.
3. Relying on `Schema` using "public" as the default name is deprecated.
## Deprecated `AbstractAsset::getFullQualifiedName()`.
The `AbstractAsset::getFullQualifiedName()` method has been deprecated. Use `::getNamespaceName()`
and `::getName()` instead.
## Deprecated schema methods related to explicit foreign key indexes.
The following methods have been deprecated:
- `Schema::hasExplicitForeignKeyIndexes()`,
- `SchemaConfig::hasExplicitForeignKeyIndexes()`,
- `SchemaConfig::setExplicitForeignKeyIndexes()`.
## Deprecated `Schema::getTableNames()`.
The `Schema::getTableNames()` method has been deprecated. In order to obtain schema table names,
use `Schema::getTables()` and call `Table::getName()` on the elements of the returned array.
## Deprecated features of `Schema::getTables()`
Using the returned array keys as table names is deprecated. Retrieve the name from the table
via `Table::getName()` instead. In order to retrieve a table by name, use `Schema::getTable()`.
## Deprecated `AbstractPlatform::canEmulateSchemas()`.
The `AbstractPlatform::canEmulateSchemas()` method and the schema emulation implemented in the SQLite platform
have been deprecated.
## Deprecated `udf*` methods of the `SQLitePlatform` methods.
The following `SQLServerPlatform` methods have been deprecated in favor of their implementations
in the `UserDefinedFunctions` class:
- `udfSqrt()`,
- `udfMod()`,
- `udfLocate()`.
## `SQLServerPlatform` methods marked internal.
The following `SQLServerPlatform` methods have been marked internal:
- `getDefaultConstraintDeclarationSQL()`,
- `getAddExtendedPropertySQL()`,
- `getDropExtendedPropertySQL()`,
- `getUpdateExtendedPropertySQL()`.
## `OraclePlatform` methods marked internal.
The `OraclePlatform::getCreateAutoincrementSql()` and `::getDropAutoincrementSql()` have been marked internal.
## Deprecated `OraclePlatform::assertValidIdentifier()`
The `OraclePlatform::assertValidIdentifier()` method has been deprecated.
## Deprecated features of `Table::getColumns()`
1. Using the returned array keys as column names is deprecated. Retrieve the name from the column
via `Column::getName()` instead. In order to retrieve a column by name, use `Table::getColumn()`.
2. Relying on the columns being sorted based on whether they belong to the primary key or a foreign key is deprecated.
If necessary, maintain the column order explicitly.
## Deprecated not passing the `$fromColumn` argument to the `ColumnDiff` constructor.
Not passing the `$fromColumn` argument to the `ColumnDiff` constructor is deprecated.
## Deprecated `AbstractPlatform::getName()`
Relying on the name of the platform is discouraged. To identify the platform, use its class name.
## Deprecated versioned platform classes that represent the lowest supported version:
1. `PostgreSQL94Platform` and `PostgreSQL94Keywords`. Use `PostgreSQLPlatform` and `PostgreSQLKeywords` instead.
2. `SQLServer2012Platform` and `SQLServer2012Keywords`. Use `SQLServerPlatform` and `SQLServerKeywords` instead.
## Deprecated schema comparison APIs that don't account for the current database connection and the database platform
1. Instantiation of the `Comparator` class outside the DBAL is deprecated. Use `SchemaManager::createComparator()`
to create the comparator specific to the current database connection and the database platform.
2. The `Schema::getMigrateFromSql()` and `::getMigrateToSql()` methods are deprecated. Compare the schemas using the
connection-aware comparator and produce the SQL by passing the resulting diff to the target platform.
## Deprecated driver-level APIs that don't take the server version into account.
The `ServerInfoAwareConnection` and `VersionAwarePlatformDriver` interfaces are deprecated. In the next major version,
all drivers and driver connections will be required to implement the APIs aware of the server version.
## Deprecated `AbstractPlatform::prefersIdentityColumns()`.
Whether to use identity columns should be decided by the application developer. For example, based on the set
of supported database platforms.
## Deprecated `AbstractPlatform::getNowExpression()`.
Relying on dates generated by the database is deprecated. Generate dates within the application.
## Deprecated reference from `ForeignKeyConstraint` to its local (referencing) `Table`.
Reference from `ForeignKeyConstraint` to its local (referencing) `Table` is deprecated as well as the following methods:
- `setLocalTable()`,
- `getLocalTable()`,
- `getLocalTableName()`.
When a foreign key is used as part of the `Table` definition, the table should be used directly. When a foreign key is
used as part of another collection (e.g. `SchemaDiff`), the collection should store the reference to the key's
referencing table separately.
## Deprecated redundant `AbstractPlatform` methods.
The following methods implement simple SQL fragments that don't vary across supported platforms. The SQL fragments
implemented by these methods should be used as is:
- `getSqlCommentStartString()`,
- `getSqlCommentEndString()`,
- `getWildcards()`,
- `getAvgExpression()`,
- `getCountExpression()`,
- `getMaxExpression()`,
- `getMinExpression()`,
- `getSumExpression()`,
- `getMd5Expression()`,
- `getSqrtExpression()`,
- `getRoundExpression()`,
- `getRtrimExpression()`,
- `getLtrimExpression()`,
- `getUpperExpression()`,
- `getLowerExpression()`,
- `getNotExpression()`,
- `getIsNullExpression()`,
- `getIsNotNullExpression()`,
- `getBetweenExpression()`,
- `getAcosExpression()`,
- `getSinExpression()`,
- `getPiExpression()`,
- `getCosExpression()`,
- `getTemporaryTableSQL()`,
- `getUniqueFieldDeclarationSQL()`.
The `getListUsersSQL()` method is not implemented by any of the supported platforms.
The following methods describe the features consistently implemented across all the supported platforms:
- `supportsIndexes()`,
- `supportsAlterTable()`,
- `supportsTransactions()`,
- `supportsPrimaryConstraints()`,
- `supportsViews()`,
- `supportsLimitOffset()`.
All 3rd-party platform implementations must implement the support for these features as well.
The `supportsGettingAffectedRows()` method describes a driver-level feature and does not belong to the Platform API.
## Deprecated `AbstractPlatform` methods that describe the default and the maximum column lengths.
Relying on the default and the maximum column lengths provided by the DBAL is deprecated.
The following `AbstractPlatform` methods and their implementations in specific platforms have been deprecated:
- `getCharMaxLength()`,
- `getVarcharDefaultLength()`,
- `getVarcharMaxLength()`,
- `getBinaryDefaultLength()`,
- `getBinaryMaxLength()`.
If required by the target platform(s), the column length should be specified based on the application logic.
## Deprecated static calls to `Comparator::compareSchemas($fromSchema, $toSchema)`
The usage of `Comparator::compareSchemas($fromSchema, $toSchema)` statically is
deprecated in order to provide a more consistent API.
## Deprecated `Comparator::compare($fromSchema, $toSchema)`
The usage of `Comparator::compare($fromSchema, $toSchema)` is deprecated and
replaced by `Comparator::compareSchemas($fromSchema, $toSchema)` in order to
clarify the purpose of the method.
## Deprecated `Connection::lastInsertId($name)`
The usage of `Connection::lastInsertId()` with a sequence name is deprecated as unsafe in scenarios with multiple
concurrent connections. If a newly inserted row needs to be referenced, it is recommended to generate its identifier
explicitly prior to insertion.
## Introduction of PSR-6 for result caching
Instead of relying on the deprecated `doctrine/cache` library, a PSR-6 cache
can now be used for result caching. The usage of Doctrine Cache is deprecated
in favor of PSR-6. The following methods related to Doctrine Cache have been
replaced with PSR-6 counterparts:
| class | old method | new method |
| ------------------- | ------------------------ | ------------------ |
| `Configuration` | `setResultCacheImpl()` | `setResultCache()` |
| `Configuration` | `getResultCacheImpl()` | `getResultCache()` |
| `QueryCacheProfile` | `setResultCacheDriver()` | `setResultCache()` |
| `QueryCacheProfile` | `getResultCacheDriver()` | `getResultCache()` |
# Upgrade to 3.1
## Deprecated schema- and namespace-related methods
The usage of the following schema- and namespace-related methods is deprecated:
- `AbstractPlatform::getListNamespacesSQL()`,
- `AbstractSchemaManager::listNamespaceNames()`,
- `AbstractSchemaManager::getPortableNamespacesList()`,
- `AbstractSchemaManager::getPortableNamespaceDefinition()`,
- `PostgreSQLSchemaManager::getSchemaNames()`.
Use `AbstractSchemaManager::listSchemaNames()` instead.
## `PostgreSQLSchemaManager` methods marked internal.
`PostgreSQLSchemaManager::getExistingSchemaSearchPaths()` and `::determineExistingSchemaSearchPaths()` have been marked internal.
## `OracleSchemaManager` methods marked internal.
`OracleSchemaManager::dropAutoincrement()` has been marked internal.
## Deprecated `AbstractPlatform::getReservedKeywordsClass()`
Instead of implementing `getReservedKeywordsClass()`, `AbstractPlatform` subclasses should implement
`createReservedKeywordsList()`.
## Deprecated `ReservedWordsCommand::setKeywordListClass()`
The usage of `ReservedWordsCommand::setKeywordListClass()` has been deprecated. To add or replace a keyword list,
use `setKeywordList()` instead.
## Deprecated `$driverOptions` argument of `PDO\Statement::bindParam()` and `PDO\SQLSrv\Statement::bindParam()`
The usage of the `$driverOptions` argument of `PDO\Statement::bindParam()` and `PDO\SQLSrv\Statement::bindParam()` is deprecated.
To define parameter binding type as `ASCII`, `BINARY` or `BLOB`, use the corresponding `ParameterType::*` constant.
## Deprecated `Connection::$_schemaManager` and `Connection::getSchemaManager()`
The usage of `Connection::$_schemaManager` and `Connection::getSchemaManager()` is deprecated.
Use `Connection::createSchemaManager()` instead.
## Deprecated `Connection::$_expr` and `Connection::getExpressionBuilder()`
The usage of `Connection::$_expr` and `Connection::getExpressionBuilder()` is deprecated.
Use `Connection::createExpressionBuilder()` instead.
## Deprecated `QueryBuilder::execute()`
The usage of `QueryBuilder::execute()` is deprecated. Use either `QueryBuilder::executeQuery()` or
`QueryBuilder::executeStatement()`, depending on whether the queryBuilder is a query (SELECT) or a statement (INSERT,
UPDATE, DELETE).
You might also consider the use of the new shortcut methods, such as:
- `fetchAllAssociative()`
- `fetchAllAssociativeIndexed()`
- `fetchAllKeyValue()`
- `fetchAllNumeric()`
- `fetchAssociative()`
- `fetchFirstColumn()`
- `fetchNumeric()`
- `fetchOne()`
# Upgrade to 3.0
## BC BREAK: leading colon in named parameter names not supported
The usage of the colon prefix when binding named parameters is no longer supported.
## BC BREAK `Doctrine\DBAL\Abstraction\Result` removed
The `Doctrine\DBAL\Abstraction\Result` interface is removed. Use the `Doctrine\DBAL\Result` class instead.
## BC BREAK: `Doctrine\DBAL\Types\Type::getDefaultLength()` removed
The `Doctrine\DBAL\Types\Type::getDefaultLength()` method has been removed as it served no purpose.
## BC BREAK: `Doctrine\DBAL\DBALException` class renamed
The `Doctrine\DBAL\DBALException` class has been renamed to `Doctrine\DBAL\Exception`.
## BC BREAK: `Doctrine\DBAL\Schema\Table` constructor new parameter
Deprecated parameter `$idGeneratorType` removed and added a new parameter `$uniqueConstraints`.
Constructor changed like so:
```diff
- __construct($name, array $columns = [], array $indexes = [], array $fkConstraints = [], $idGeneratorType = 0, array $options = [])
+ __construct($name, array $columns = [], array $indexes = [], array $uniqueConstraints = [], array $fkConstraints = [], array $options = [])
```
## BC BREAK: change in the behavior of `SchemaManager::dropDatabase()`
When dropping a database, the DBAL no longer attempts to kill the client sessions that use the database.
It's the responsibility of the operator to make sure that the database is not being used.
## BC BREAK: removed `Synchronizer` package
The `Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer` interface and all its implementations have been removed.
## BC BREAK: removed wrapper `Connection` methods
The following methods of the `Connection` class have been removed:
1. `query()`.
2. `exec()`.
3. `executeUpdate()`.
## BC BREAK: Changes in the wrapper-level API ancestry
The wrapper-level `Connection` and `Statement` classes no longer implement the corresponding driver-level interfaces.
## BC BREAK: Removed `DBALException` factory methods
The following factory methods of the `DBALException` class have been removed:
1. `DBALException::invalidPlatformSpecified()`.
2. `DBALException::invalidPdoInstance()`.
## BC BREAK: PDO-based driver classes are moved under the `PDO` namespace
The following classes have been renamed:
- `PDOMySql\Driver` → `PDO\MySQL\Driver`
- `PDOOracle\Driver` → `PDO\OCI\Driver`
- `PDOPgSql\Driver` → `PDO\PgSQL\Driver`
- `PDOSqlite\Driver` → `PDO\SQLite\Driver`
- `PDOSqlsrv\Driver` → `PDO\SQLSrv\Driver`
- `PDOSqlsrv\Connection` → `PDO\SQLSrv\Connection`
- `PDOSqlsrv\Statement` → `PDO\SQLSrv\Statement`
## BC BREAK: Changes schema manager instantiation.
1. The `$platform` argument of all schema manager constructors is no longer optional.
2. A new `$platform` argument has been added to the `Driver::getSchemaManager()` method.
## BC BREAK: Changes in driver classes
1. All implementations of the `Driver` interface have been made final.
2. The `PDO\Connection` and `PDO\Statement` classes have been made final.
3. The `PDOSqlsrv\Connection` and `PDOSqlsrv\Statement` classes have been made final and no longer extend the corresponding PDO classes.
4. The `SQLSrv\LastInsertId` class has been made final.
## BC BREAK: Changes in wrapper-level exceptions
`DBALException::invalidTableName()` has been replaced with the `InvalidTableName` class.
## BC BREAK: Changes in driver-level exception handling
1. The `convertException()` method has been removed from the `Driver` interface. The logic of exception conversion has been moved to the `ExceptionConverter` interface. The drivers now must implement the `getExceptionConverter()` method.
2. The `driverException()` and `driverExceptionDuringQuery()` factory methods have been removed from the `DBALException` class.
3. Non-driver exceptions (e.g. exceptions of type `Error`) are no longer wrapped in a `DBALException`.
## BC BREAK: More driver-level methods are allowed to throw a `Driver\Exception`.
The following driver-level methods are allowed to throw a `Driver\Exception`:
- `Connection::prepare()`
- `Connection::lastInsertId()`
- `Connection::beginTransaction()`
- `Connection::commit()`
- `Connection::rollBack()`
- `ServerInfoAwareConnection::getServerVersion()`
- `Statement::bindParam()`
- `Statement::bindValue()`
- `Result::rowCount()`
- `Result::columnCount()`
The driver-level implementations of `Connection::query()` and `Connection::exec()` may no longer throw a `DBALException`.
## The `ExceptionConverterDriver` interface is removed
All drivers must implement the `convertException()` method which is now part of the `Driver` interface.
## The `PingableConnection` interface is removed
The functionality of pinging the server is no longer supported. Lost
connections are now automatically reconnected by Doctrine internally.
## BC BREAK: Deprecated driver-level classes and interfaces are removed.
- `AbstractDriverException`
- `DriverException`
- `PDOConnection`
- `PDOException`
- `PDOStatement`
- `IBMDB2\DB2Connection`
- `IBMDB2\DB2Driver`
- `IBMDB2\DB2Exception`
- `IBMDB2\DB2Statement`
- `Mysqli\MysqliConnection`
- `Mysqli\MysqliException`
- `Mysqli\MysqliStatement`
- `OCI8\OCI8Connection`
- `OCI8\OCI8Exception`
- `OCI8\OCI8Statement`
- `SQLSrv\SQLSrvConnection`
- `SQLSrv\SQLSrvException`
- `SQLSrv\SQLSrvStatement`
## BC BREAK: `ServerInfoAwareConnection::requiresQueryForServerVersion()` is removed.
The `ServerInfoAwareConnection::requiresQueryForServerVersion()` method has been removed as an implementation detail which is the same for all supported drivers.
## BC BREAK Changes in driver exceptions
1. The `Doctrine\DBAL\Driver\DriverException::getErrorCode()` method is removed. In order to obtain the driver error code, please use `::getCode()` or `::getSQLState()`.
2. The value returned by `Doctrine\DBAL\Driver\PDOException::getSQLState()` no longer falls back to the driver error code.
## BC BREAK: Changes in `OracleSchemaManager::createDatabase()`
The `$database` argument is no longer nullable or optional.
## BC BREAK: `Doctrine\DBAL\Types\Type::__toString()` removed
Relying on string representation was discouraged and has been removed.
## BC BREAK: Changes in the `Doctrine\DBAL\Schema` API
- Removed unused method `Doctrine\DBAL\Schema\AbstractSchemaManager::_getPortableFunctionsList()`
- Removed unused method `Doctrine\DBAL\Schema\AbstractSchemaManager::_getPortableFunctionDefinition()`
- Removed unused method `Doctrine\DBAL\Schema\OracleSchemaManager::_getPortableFunctionDefinition()`
- Removed unused method `Doctrine\DBAL\Schema\SqliteSchemaManager::_getPortableTableIndexDefinition()`
## BC BREAK: Removed support for DB-generated UUIDs
The support for DB-generated UUIDs was removed as non-portable.
Please generate UUIDs on the application side (e.g. using [ramsey/uuid](https://packagist.org/packages/ramsey/uuid)).
## BC BREAK: Changes in the `Doctrine\DBAL\Connection` API
- The following methods have been removed as leaking internal implementation details: `::getHost()`, `::getPort()`, `::getUsername()`, `::getPassword()`.
## BC BREAK: Changes in the `Doctrine\DBAL\Event` API
- `ConnectionEventArgs::getDriver()`, `::getDatabasePlatform()` and `::getSchemaManager()` methods have been removed. The connection information can be obtained from the connection which is available via `::getConnection()`.
- `SchemaColumnDefinitionEventArgs::getDatabasePlatform()` and `SchemaIndexDefinitionEventArgs::getDatabasePlatform()` have been removed for the same reason as above.
## BC BREAK: Changes in obtaining the currently selected database name
- The `Doctrine\DBAL\Driver::getDatabase()` method has been removed. Please use `Doctrine\DBAL\Connection::getDatabase()` instead.
- `Doctrine\DBAL\Connection::getDatabase()` will always return the name of the database currently connected to, regardless of the configuration parameters and will initialize a database connection if it's not yet established.
- A call to `Doctrine\DBAL\Connection::getDatabase()`, when connected to an SQLite database, will no longer return the database file path.
## BC BREAK: `Doctrine\DBAL\Driver::getName()` removed
The `Doctrine\DBAL\Driver::getName()` has been removed.
## BC BREAK Removed previously deprecated features
* Removed `json_array` type and all associated hacks.
* Removed `Connection::TRANSACTION_*` constants.
* Removed `AbstractPlatform::DATE_INTERVAL_UNIT_*` and `AbstractPlatform::TRIM_*` constants.
* Removed `AbstractPlatform::getSQLResultCasing()`, `::prefersSequences()` and `::supportsForeignKeyOnUpdate()` methods.
* Removed `PostgreSqlPlatform::getDisallowDatabaseConnectionsSQL()` and `::getCloseActiveDatabaseConnectionsSQL()` methods.
* Removed `MysqlSessionInit` listener.
* Removed `MySQLPlatform::getCollationFieldDeclaration()`.
* Removed `AbstractPlatform::getIdentityColumnNullInsertSQL()`.
* Removed `AbstractPlatform::fixSchemaElementName()`.
* Removed `Table::addUnnamedForeignKeyConstraint()` and `Table::addNamedForeignKeyConstraint()`.
* Removed `Table::renameColumn()`.
* Removed `SQLParserUtils::getPlaceholderPositions()`.
* Removed `LoggerChain::addLogger`.
* Removed `AbstractSchemaManager::getFilterSchemaAssetsExpression()`, `Configuration::getFilterSchemaAssetsExpression()`
and `Configuration::getFilterSchemaAssetsExpression()`.
* `SQLParserUtils::*_TOKEN` constants made private.
## BC BREAK changes the `Driver::connect()` signature
The method no longer accepts the `$username`, `$password` and `$driverOptions` arguments. The corresponding values are expected to be passed as the `"user"`, `"password"` and `"driver_options"` keys of the `$params` argument respectively.
## Removed `MasterSlaveConnection`
This class was deprecated in favor of `PrimaryReadReplicaConnection`
## BC BREAK: Changes in the portability layer
1. The platform-specific portability constants (`Portability\Connection::PORTABILITY_{PLATFORM}`) were internal implementation details which are no longer relevant.
2. The `Portability\Connection` class no longer extends the DBAL `Connection`.
3. The `Portability\Class` class has been made final.
## BC BREAK changes in fetching statement results
1. The `Statement` interface no longer extends `ResultStatement`.
2. The `ResultStatement` interface has been renamed to `Result`.
3. Instead of returning `bool`, `Statement::execute()` now returns a `Result` that should be used for fetching the result data and metadata.
4. The functionality previously available via `Statement::closeCursor()` is now available via `Result::free()`. The behavior of fetching data from a freed result is no longer portable. In this case, some drivers will return `false` while others may throw an exception.
Additional related changes:
1. The `ArrayStatement` and `ResultCacheStatement` classes from the `Cache` package have been renamed to `ArrayResult` and `CachingResult` respectively and marked `@internal`.
## BC BREAK `Statement::rowCount()` is moved.
`Statement::rowCount()` has been moved to the `ResultStatement` interface where it belongs by definition.
## Removed `FetchMode` and the corresponding methods
1. The `FetchMode` class and the `setFetchMode()` method of the `Connection` and `Statement` interfaces are removed.
2. The `Statement::fetch()` method is replaced with `fetchNumeric()`, `fetchAssociative()` and `fetchOne()`.
3. The `Statement::fetchAll()` method is replaced with `fetchAllNumeric()`, `fetchAllAssociative()` and `fechColumn()`.
4. The `Statement::fetchColumn()` method is replaced with `fetchOne()`.
5. The `Connection::fetchArray()` and `fetchAssoc()` methods are replaced with `fetchNumeric()` and `fetchAssociative()` respectively.
6. The `StatementIterator` class is removed. The usage of a `Statement` object as `Traversable` is no longer possible. Use `iterateNumeric()`, `iterateAssociative()` and `iterateColumn()` instead.
7. Fetching data in mixed mode (former `FetchMode::MIXED`) is no longer possible.
## BC BREAK: Dropped handling of one-based numeric arrays of parameters in `Statement::execute()`
The statement implementations no longer detect whether `$params` is a zero- or one-based array. A zero-based numeric array is expected.
## BC BREAK `Statement::project()` has been removed
- The `Statement::project()` method has been removed. Use `::executeQuery()` and fetch the data from the statement using one of the `Statement::fetch*()` methods instead.
## BC BREAK `::errorCode()` and `::errorInfo()` removed from `Connection` and `Statement` APIs
The error information is available in `DriverException` thrown in case of an error.
## BC BREAK: Dropped support for `FetchMode::CUSTOM_OBJECT` and `::STANDARD_OBJECT`
Instead of fetching an object, fetch an array and map it to an object of the desired class.
## BC BREAK: Dropped support for the `$columnIndex` argument in `ResultStatement::fetchColumn()`, other `ResultStatement::fetch*()` methods invoked with `FetchMode::COLUMN` and `Connection::fetchColumn()`.
In order to fetch a column with an index other than `0`, use `FetchMode::NUMERIC` and the array element with the corresponding index.
## BC BREAK: Removed `EchoSQLLogger`
`EchoSQLLogger` is no longer available as part of the package.
## BC BREAK: Removed support for SQL Anywhere
The support for the SQL Anywhere database platform and the corresponding driver has been removed.
## BC BREAK: Removed support for PostgreSQL 9.3 and older
DBAL now requires PostgreSQL 9.4 or newer, support for unmaintained versions has been dropped.
If you are using any of the legacy versions, you have to upgrade to a newer PostgreSQL version (9.6+ is recommended).
The following classes have been removed:
* `Doctrine\DBAL\Platforms\PostgreSqlPlatform`
* `Doctrine\DBAL\Platforms\PostgreSQL91Platform`
* `Doctrine\DBAL\Platforms\PostgreSQL92Platform`
* `Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords`
* `Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords`
* `Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords`
## BC BREAK: Removed support for MariaDB 10.0 and older
DBAL now requires MariaDB 10.1 or newer, support for unmaintained versions has been dropped.
If you are using any of the legacy versions, you have to upgrade to a newer MariaDB version (10.1+ is recommended).
## BC BREAK: The `ServerInfoAwareConnection` interface now extends `Connection`
All implementations of the `ServerInfoAwareConnection` interface have to implement the methods defined in the `Connection` interface as well.
## BC BREAK: `VersionAwarePlatformDriver` interface now extends `Driver`
All implementations of the `VersionAwarePlatformDriver` interface have to implement the methods defined in the `Driver` interface as well.
## BC BREAK: Removed `MsSQLKeywords` class
The `Doctrine\DBAL\Platforms\MsSQLKeywords` class has been removed.
Please use `Doctrine\DBAL\Platforms\SQLServerPlatform `instead.
## BC BREAK: Removed PDO DB2 driver
This PDO-based IBM DB2 driver (built on top of `pdo_ibm` extension) has already been unsupported as of 2.5, it has been now removed.
The following class has been removed:
* `Doctrine\DBAL\Driver\PDOIbm\Driver`
## BC BREAK: Removed support for SQL Server 2008 and older
DBAL now requires SQL Server 2012 or newer, support for unmaintained versions has been dropped.
If you are using any of the legacy versions, you have to upgrade to a newer SQL Server version.
The following classes have been removed:
* `Doctrine\DBAL\Platforms\SQLServerPlatform`
* `Doctrine\DBAL\Platforms\SQLServer2005Platform`
* `Doctrine\DBAL\Platforms\SQLServer2008Platform`
* `Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords`
* `Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords`
* `Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords`
The `AbstractSQLServerDriver` class and its subclasses no longer implement the `VersionAwarePlatformDriver` interface.
## BC BREAK: Removed `Doctrine\DBAL\Version`
The `Doctrine\DBAL\Version` class is no longer available: please refrain from checking the DBAL version at runtime.
## BC BREAK User-provided `PDO` instance is no longer supported
In order to share the same `PDO` instances between DBAL and other components, initialize the connection in DBAL and access it using `Connection::getWrappedConnection()->getWrappedConnection()`.
## BC BREAK: the PDO symbols are no longer part of the DBAL API
1. The support of `PDO::PARAM_*`, `PDO::FETCH_*`, `PDO::CASE_*` and `PDO::PARAM_INPUT_OUTPUT` constants in the DBAL API is removed.
2. `\Doctrine\DBAL\Driver\PDOConnection` does not extend `\PDO` anymore. Please use `\Doctrine\DBAL\Driver\PDOConnection::getWrappedConnection()` to access the underlying `PDO` object.
3. `\Doctrine\DBAL\Driver\PDOStatement` does not extend `\PDOStatement` anymore.
Before:
```php
use Doctrine\DBAL\Portability\Connection;
$params = array(
'wrapperClass' => Connection::class,
'fetch_case' => PDO::CASE_LOWER,
);
$stmt->bindValue(1, 1, PDO::PARAM_INT);
$stmt->fetchAll(PDO::FETCH_COLUMN);
```
After:
```php
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Portability\Connection;
$params = array(
'wrapperClass' => Connection::class,
'fetch_case' => ColumnCase::LOWER,
);
$stmt->bindValue(1, 1, ParameterType::INTEGER);
$stmt->fetchAll(FetchMode::COLUMN);
```
## BC BREAK: Removed Drizzle support
The Drizzle project is abandoned and is therefore not supported by Doctrine DBAL anymore.
## BC BREAK: Removed `dbal:import` CLI command
The `dbal:import` CLI command has been removed since it only worked with PDO-based drivers by relying on a non-documented behavior of the extension, and it was impossible to make it work with other drivers.
Please use other database client applications for import, e.g.:
* For MySQL and MariaDB: `mysql [dbname] < data.sql`.
* For PostgreSQL: `psql [dbname] < data.sql`.
* For SQLite: `sqlite3 /path/to/file.db < data.sql`.
## BC BREAK: Changed signature of `ExceptionConverter::convert()`
Before:
```php
public function convert(string $message, Doctrine\DBAL\Driver\Exception $exception): DriverException
```
After:
```php
public function convert(Doctrine\DBAL\Driver\Exception $exception, ?Doctrine\DBAL\Query $query): DriverException
```
## BC Break: The `DriverException` constructor is now internal
The constructor of `Doctrine\DBAL\Exception\DriverException` is now `@internal`.
## BC Break: `Configuration`
- all `Configuration` methods are now typed
- `Configuration::setSchemaAssetsFilter()` now returns `void`
- `Configuration::$_attributes` has been removed; use individual properties in subclasses instead
PK Tǎږ
.gitignorenu ٘ /composer.lock
.phpunit.result.cache
build/
logs/
reports/
dist/
download/
vendor/
/*.phpunit.xml
/phpunit.xml
/.phpcs-cache
/phpstan.neon
/psalm.xml
PK T4`?* ?* 0 src/Connections/PrimaryReadReplicaConnection.phpnu ٘ executeQuery("DELETE FROM table");
*
* Be aware that Connection#executeQuery is a method specifically for READ
* operations only.
*
* Use Connection#executeStatement for any SQL statement that changes/updates
* state in the database (UPDATE, INSERT, DELETE or DDL statements).
*
* This connection is limited to replica operations using the
* Connection#executeQuery operation only, because it wouldn't be compatible
* with the ORM or SchemaManager code otherwise. Both use all the other
* operations in a context where writes could happen to a replica, which makes
* this restricted approach necessary.
*
* You can manually connect to the primary at any time by calling:
*
* $conn->ensureConnectedToPrimary();
*
* Instantiation through the DriverManager looks like:
*
* @psalm-import-type Params from DriverManager
* @example
*
* $conn = DriverManager::getConnection(array(
* 'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection',
* 'driver' => 'pdo_mysql',
* 'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'replica' => array(
* array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''),
* )
* ));
*
* You can also pass 'driverOptions' and any other documented option to each of this drivers
* to pass additional information.
*/
class PrimaryReadReplicaConnection extends Connection
{
/**
* Primary and Replica connection (one of the randomly picked replicas).
*
* @var DriverConnection[]|null[]
*/
protected $connections = ['primary' => null, 'replica' => null];
/**
* You can keep the replica connection and then switch back to it
* during the request if you know what you are doing.
*
* @var bool
*/
protected $keepReplica = false;
/**
* Creates Primary Replica Connection.
*
* @internal The connection can be only instantiated by the driver manager.
*
* @param array $params
* @psalm-param Params $params
* @phpstan-param array $params
*
* @throws Exception
* @throws InvalidArgumentException
*/
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
if (! isset($params['replica'], $params['primary'])) {
throw new InvalidArgumentException('primary or replica configuration missing');
}
if (count($params['replica']) === 0) {
throw new InvalidArgumentException('You have to configure at least one replica.');
}
if (isset($params['driver'])) {
$params['primary']['driver'] = $params['driver'];
foreach ($params['replica'] as $replicaKey => $replica) {
$params['replica'][$replicaKey]['driver'] = $params['driver'];
}
}
$this->keepReplica = (bool) ($params['keepReplica'] ?? false);
parent::__construct($params, $driver, $config, $eventManager);
}
/**
* Checks if the connection is currently towards the primary or not.
*/
public function isConnectedToPrimary(): bool
{
return $this->_conn !== null && $this->_conn === $this->connections['primary'];
}
/**
* @param string|null $connectionName
*
* @return bool
*/
public function connect($connectionName = null)
{
if ($connectionName !== null) {
throw new InvalidArgumentException(
'Passing a connection name as first argument is not supported anymore.'
. ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.'
);
}
return $this->performConnect();
}
protected function performConnect(?string $connectionName = null): bool
{
$requestedConnectionChange = ($connectionName !== null);
$connectionName = $connectionName ?? 'replica';
if ($connectionName !== 'replica' && $connectionName !== 'primary') {
throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.');
}
// If we have a connection open, and this is not an explicit connection
// change request, then abort right here, because we are already done.
// This prevents writes to the replica in case of "keepReplica" option enabled.
if ($this->_conn !== null && ! $requestedConnectionChange) {
return false;
}
$forcePrimaryAsReplica = false;
if ($this->getTransactionNestingLevel() > 0) {
$connectionName = 'primary';
$forcePrimaryAsReplica = true;
}
if (isset($this->connections[$connectionName])) {
$this->_conn = $this->connections[$connectionName];
if ($forcePrimaryAsReplica && ! $this->keepReplica) {
$this->connections['replica'] = $this->_conn;
}
return false;
}
if ($connectionName === 'primary') {
$this->connections['primary'] = $this->_conn = $this->connectTo($connectionName);
// Set replica connection to primary to avoid invalid reads
if (! $this->keepReplica) {
$this->connections['replica'] = $this->connections['primary'];
}
} else {
$this->connections['replica'] = $this->_conn = $this->connectTo($connectionName);
}
if ($this->_eventManager->hasListeners(Events::postConnect)) {
$eventArgs = new ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
}
return true;
}
/**
* Connects to the primary node of the database cluster.
*
* All following statements after this will be executed against the primary node.
*/
public function ensureConnectedToPrimary(): bool
{
return $this->performConnect('primary');
}
/**
* Connects to a replica node of the database cluster.
*
* All following statements after this will be executed against the replica node,
* unless the keepReplica option is set to false and a primary connection
* was already opened.
*/
public function ensureConnectedToReplica(): bool
{
return $this->performConnect('replica');
}
/**
* Connects to a specific connection.
*
* @param string $connectionName
*
* @return DriverConnection
*
* @throws Exception
*/
protected function connectTo($connectionName)
{
$params = $this->getParams();
$connectionParams = $this->chooseConnectionConfiguration($connectionName, $params);
try {
return $this->_driver->connect($connectionParams);
} catch (DriverException $e) {
throw $this->convertException($e);
}
}
/**
* @param string $connectionName
* @param mixed[] $params
*
* @return mixed
*/
protected function chooseConnectionConfiguration($connectionName, $params)
{
if ($connectionName === 'primary') {
return $params['primary'];
}
$config = $params['replica'][array_rand($params['replica'])];
if (! isset($config['charset']) && isset($params['primary']['charset'])) {
$config['charset'] = $params['primary']['charset'];
}
return $config;
}
/**
* {@inheritDoc}
*/
public function executeStatement($sql, array $params = [], array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::executeStatement($sql, $params, $types);
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
$this->ensureConnectedToPrimary();
return parent::beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
{
$this->ensureConnectedToPrimary();
return parent::commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
$this->ensureConnectedToPrimary();
return parent::rollBack();
}
/**
* {@inheritDoc}
*/
public function close()
{
unset($this->connections['primary'], $this->connections['replica']);
parent::close();
$this->_conn = null;
$this->connections = ['primary' => null, 'replica' => null];
}
/**
* {@inheritDoc}
*/
public function createSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::createSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function releaseSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::releaseSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function rollbackSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::rollbackSavepoint($savepoint);
}
public function prepare(string $sql): Statement
{
$this->ensureConnectedToPrimary();
return parent::prepare($sql);
}
}
PK T} src/Statement.phpnu ٘ Statement for the given SQL and Connection.
*
* @internal The statement can be only instantiated by {@see Connection}.
*
* @param Connection $conn The connection for handling statement errors.
* @param Driver\Statement $statement The underlying driver-level statement.
* @param string $sql The SQL of the statement.
*
* @throws Exception
*/
public function __construct(Connection $conn, Driver\Statement $statement, string $sql)
{
$this->conn = $conn;
$this->stmt = $statement;
$this->sql = $sql;
$this->platform = $conn->getDatabasePlatform();
}
/**
* Binds a parameter value to the statement.
*
* The value can optionally be bound with a DBAL mapping type.
* If bound with a DBAL mapping type, the binding type is derived from the mapping
* type and the value undergoes the conversion routines of the mapping type before
* being bound.
*
* @param string|int $param The name or position of the parameter.
* @param mixed $value The value of the parameter.
* @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance.
*
* @return bool TRUE on success, FALSE on failure.
*
* @throws Exception
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
$this->params[$param] = $value;
$this->types[$param] = $type;
$bindingType = ParameterType::STRING;
if ($type !== null) {
if (is_string($type)) {
$type = Type::getType($type);
}
$bindingType = $type;
if ($type instanceof Type) {
$value = $type->convertToDatabaseValue($value, $this->platform);
$bindingType = $type->getBindingType();
}
}
try {
return $this->stmt->bindValue($param, $value, $bindingType);
} catch (Driver\Exception $e) {
throw $this->conn->convertException($e);
}
}
/**
* Binds a parameter to a value by reference.
*
* Binding a parameter by reference does not support DBAL mapping types.
*
* @param string|int $param The name or position of the parameter.
* @param mixed $variable The reference to the variable to bind.
* @param int $type The binding type.
* @param int|null $length Must be specified when using an OUT bind
* so that PHP allocates enough memory to hold the returned value.
*
* @return bool TRUE on success, FALSE on failure.
*
* @throws Exception
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
$this->params[$param] = $variable;
$this->types[$param] = $type;
try {
if (func_num_args() > 3) {
return $this->stmt->bindParam($param, $variable, $type, $length);
}
return $this->stmt->bindParam($param, $variable, $type);
} catch (Driver\Exception $e) {
throw $this->conn->convertException($e);
}
}
/**
* Executes the statement with the currently bound parameters.
*
* @deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead
*
* @param mixed[]|null $params
*
* @throws Exception
*/
public function execute($params = null): Result
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4580',
'Statement::execute() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead'
);
if ($params !== null) {
$this->params = $params;
}
$logger = $this->conn->getConfiguration()->getSQLLogger();
if ($logger !== null) {
$logger->startQuery($this->sql, $this->params, $this->types);
}
try {
return new Result(
$this->stmt->execute($params),
$this->conn
);
} catch (Driver\Exception $ex) {
throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types);
} finally {
if ($logger !== null) {
$logger->stopQuery();
}
}
}
/**
* Executes the statement with the currently bound parameters and return result.
*
* @param mixed[] $params
*
* @throws Exception
*/
public function executeQuery(array $params = []): Result
{
if ($params === []) {
$params = null; // Workaround as long execute() exists and used internally.
}
return $this->execute($params);
}
/**
* Executes the statement with the currently bound parameters and return affected rows.
*
* @param mixed[] $params
*
* @throws Exception
*/
public function executeStatement(array $params = []): int
{
if ($params === []) {
$params = null; // Workaround as long execute() exists and used internally.
}
return $this->execute($params)->rowCount();
}
/**
* Gets the wrapped driver statement.
*
* @return Driver\Statement
*/
public function getWrappedStatement()
{
return $this->stmt;
}
}
PK T̟mF F src/Events.phpnu ٘ getMessage();
} else {
$message = 'An exception occurred in the driver: ' . $driverException->getMessage();
}
parent::__construct($message, $driverException->getCode(), $driverException);
$this->query = $query;
}
/**
* {@inheritDoc}
*/
public function getSQLState()
{
$previous = $this->getPrevious();
assert($previous instanceof TheDriverException);
return $previous->getSQLState();
}
public function getQuery(): ?Query
{
return $this->query;
}
}
PK Tu4 src/Exception/NoKeyValue.phpnu ٘ |array */
private $params = [];
/** @var array|array */
private $types = [];
/**
* @internal This statement can be only instantiated by its connection.
*/
public function __construct(StatementInterface $statement, LoggerInterface $logger, string $sql)
{
parent::__construct($statement);
$this->logger = $logger;
$this->sql = $sql;
}
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
$this->params[$param] = &$variable;
$this->types[$param] = $type;
return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3));
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
$this->params[$param] = $value;
$this->types[$param] = $type;
return parent::bindValue($param, $value, $type);
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
$this->logger->debug('Executing statement: {sql} (parameters: {params}, types: {types})', [
'sql' => $this->sql,
'params' => $params ?? $this->params,
'types' => $this->types,
]);
return parent::execute($params);
}
}
PK TF= src/Logging/DebugStack.phpnu ٘ >
*/
public $queries = [];
/**
* If Debug Stack is enabled (log queries) or not.
*
* @var bool
*/
public $enabled = true;
/** @var float|null */
public $start = null;
/** @var int */
public $currentQuery = 0;
public function __construct()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4967',
'DebugStack is deprecated.'
);
}
/**
* {@inheritdoc}
*/
public function startQuery($sql, ?array $params = null, ?array $types = null)
{
if (! $this->enabled) {
return;
}
$this->start = microtime(true);
$this->queries[++$this->currentQuery] = [
'sql' => $sql,
'params' => $params,
'types' => $types,
'executionMS' => 0,
];
}
/**
* {@inheritdoc}
*/
public function stopQuery()
{
if (! $this->enabled) {
return;
}
$this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start;
}
}
PK T9: : src/Logging/Connection.phpnu ٘ logger = $logger;
}
public function __destruct()
{
$this->logger->info('Disconnecting');
}
public function prepare(string $sql): DriverStatement
{
return new Statement(
parent::prepare($sql),
$this->logger,
$sql
);
}
public function query(string $sql): Result
{
$this->logger->debug('Executing query: {sql}', ['sql' => $sql]);
return parent::query($sql);
}
public function exec(string $sql): int
{
$this->logger->debug('Executing statement: {sql}', ['sql' => $sql]);
return parent::exec($sql);
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
$this->logger->debug('Beginning transaction');
return parent::beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
{
$this->logger->debug('Committing transaction');
return parent::commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
$this->logger->debug('Rolling back transaction');
return parent::rollBack();
}
}
PK T_# # src/Logging/Middleware.phpnu ٘ logger = $logger;
}
public function wrap(DriverInterface $driver): DriverInterface
{
return new Driver($driver, $this->logger);
}
}
PK Tx src/Logging/LoggerChain.phpnu ٘ */
private $loggers;
/**
* @param iterable $loggers
*/
public function __construct(iterable $loggers = [])
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4967',
'LoggerChain is deprecated'
);
$this->loggers = $loggers;
}
/**
* {@inheritdoc}
*/
public function startQuery($sql, ?array $params = null, ?array $types = null)
{
foreach ($this->loggers as $logger) {
$logger->startQuery($sql, $params, $types);
}
}
/**
* {@inheritdoc}
*/
public function stopQuery()
{
foreach ($this->loggers as $logger) {
$logger->stopQuery();
}
}
}
PK T-
src/Logging/Driver.phpnu ٘ logger = $logger;
}
/**
* {@inheritDoc}
*/
public function connect(array $params)
{
$this->logger->info('Connecting with parameters {params}', ['params' => $this->maskPassword($params)]);
return new Connection(
parent::connect($params),
$this->logger
);
}
/**
* @param array $params Connection parameters
*
* @return array
*/
private function maskPassword(array $params): array
{
if (isset($params['password'])) {
$params['password'] = '';
}
if (isset($params['url'])) {
$params['url'] = '';
}
return $params;
}
}
PK TDL! src/Logging/SQLLogger.phpnu ٘ |array|null $params Statement parameters
* @param array|array|null $types Parameter types
*
* @return void
*/
public function startQuery($sql, ?array $params = null, ?array $types = null);
/**
* Marks the last started query as stopped. This can be used for timing of queries.
*
* @return void
*/
public function stopQuery();
}
PK T src/LockMode.phpnu ٘ generatorTableName = $generatorTableName;
}
/**
* {@inheritdoc}
*/
public function acceptSchema(Schema $schema)
{
$table = $schema->createTable($this->generatorTableName);
$table->addColumn('sequence_name', 'string');
$table->addColumn('sequence_value', 'integer', ['default' => 1]);
$table->addColumn('sequence_increment_by', 'integer', ['default' => 1]);
}
/**
* {@inheritdoc}
*/
public function acceptTable(Table $table)
{
}
/**
* {@inheritdoc}
*/
public function acceptColumn(Table $table, Column $column)
{
}
/**
* {@inheritdoc}
*/
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
{
}
/**
* {@inheritdoc}
*/
public function acceptIndex(Table $table, Index $index)
{
}
/**
* {@inheritdoc}
*/
public function acceptSequence(Sequence $sequence)
{
}
}
PK T,Ss s src/Id/TableGenerator.phpnu ٘ getDriver() instanceof Driver\PDO\SQLite\Driver) {
throw new Exception('Cannot use TableGenerator with SQLite.');
}
$this->conn = DriverManager::getConnection(
$conn->getParams(),
$conn->getConfiguration(),
$conn->getEventManager()
);
$this->generatorTableName = $generatorTableName;
}
/**
* Generates the next unused value for the given sequence name.
*
* @param string $sequence
*
* @return int
*
* @throws Exception
*/
public function nextValue($sequence)
{
if (isset($this->sequences[$sequence])) {
$value = $this->sequences[$sequence]['value'];
$this->sequences[$sequence]['value']++;
if ($this->sequences[$sequence]['value'] >= $this->sequences[$sequence]['max']) {
unset($this->sequences[$sequence]);
}
return $value;
}
$this->conn->beginTransaction();
try {
$platform = $this->conn->getDatabasePlatform();
$sql = 'SELECT sequence_value, sequence_increment_by'
. ' FROM ' . $platform->appendLockHint($this->generatorTableName, LockMode::PESSIMISTIC_WRITE)
. ' WHERE sequence_name = ? ' . $platform->getWriteLockSQL();
$row = $this->conn->fetchAssociative($sql, [$sequence]);
if ($row !== false) {
$row = array_change_key_case($row, CASE_LOWER);
$value = $row['sequence_value'];
$value++;
assert(is_int($value));
if ($row['sequence_increment_by'] > 1) {
$this->sequences[$sequence] = [
'value' => $value,
'max' => $row['sequence_value'] + $row['sequence_increment_by'],
];
}
$sql = 'UPDATE ' . $this->generatorTableName . ' ' .
'SET sequence_value = sequence_value + sequence_increment_by ' .
'WHERE sequence_name = ? AND sequence_value = ?';
$rows = $this->conn->executeStatement($sql, [$sequence, $row['sequence_value']]);
if ($rows !== 1) {
throw new Exception('Race-condition detected while updating sequence. Aborting generation');
}
} else {
$this->conn->insert(
$this->generatorTableName,
['sequence_name' => $sequence, 'sequence_value' => 1, 'sequence_increment_by' => 1]
);
$value = 1;
}
$this->conn->commit();
} catch (Throwable $e) {
$this->conn->rollBack();
throw new Exception(
'Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(),
0,
$e
);
}
return $value;
}
}
PK T%a1 1 src/Schema/Schema.phpnu ٘ _schemaConfig = $schemaConfig;
$this->_setName($schemaConfig->getName() ?? 'public');
foreach ($namespaces as $namespace) {
$this->createNamespace($namespace);
}
foreach ($tables as $table) {
$this->_addTable($table);
}
foreach ($sequences as $sequence) {
$this->_addSequence($sequence);
}
}
/**
* @deprecated
*
* @return bool
*/
public function hasExplicitForeignKeyIndexes()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4822',
'Schema::hasExplicitForeignKeyIndexes() is deprecated.'
);
return $this->_schemaConfig->hasExplicitForeignKeyIndexes();
}
/**
* @return void
*
* @throws SchemaException
*/
protected function _addTable(Table $table)
{
$namespaceName = $table->getNamespaceName();
$tableName = $this->normalizeName($table);
if (isset($this->_tables[$tableName])) {
throw SchemaException::tableAlreadyExists($tableName);
}
if (
$namespaceName !== null
&& ! $table->isInDefaultNamespace($this->getName())
&& ! $this->hasNamespace($namespaceName)
) {
$this->createNamespace($namespaceName);
}
$this->_tables[$tableName] = $table;
$table->setSchemaConfig($this->_schemaConfig);
}
/**
* @return void
*
* @throws SchemaException
*/
protected function _addSequence(Sequence $sequence)
{
$namespaceName = $sequence->getNamespaceName();
$seqName = $this->normalizeName($sequence);
if (isset($this->_sequences[$seqName])) {
throw SchemaException::sequenceAlreadyExists($seqName);
}
if (
$namespaceName !== null
&& ! $sequence->isInDefaultNamespace($this->getName())
&& ! $this->hasNamespace($namespaceName)
) {
$this->createNamespace($namespaceName);
}
$this->_sequences[$seqName] = $sequence;
}
/**
* Returns the namespaces of this schema.
*
* @return string[] A list of namespace names.
*/
public function getNamespaces()
{
return $this->namespaces;
}
/**
* Gets all tables of this schema.
*
* @return Table[]
*/
public function getTables()
{
return $this->_tables;
}
/**
* @param string $name
*
* @return Table
*
* @throws SchemaException
*/
public function getTable($name)
{
$name = $this->getFullQualifiedAssetName($name);
if (! isset($this->_tables[$name])) {
throw SchemaException::tableDoesNotExist($name);
}
return $this->_tables[$name];
}
/**
* @param string $name
*/
private function getFullQualifiedAssetName($name): string
{
$name = $this->getUnquotedAssetName($name);
if (strpos($name, '.') === false) {
$name = $this->getName() . '.' . $name;
}
return strtolower($name);
}
private function normalizeName(AbstractAsset $asset): string
{
return $asset->getFullQualifiedName($this->getName());
}
/**
* Returns the unquoted representation of a given asset name.
*
* @param string $assetName Quoted or unquoted representation of an asset name.
*/
private function getUnquotedAssetName($assetName): string
{
if ($this->isIdentifierQuoted($assetName)) {
return $this->trimQuotes($assetName);
}
return $assetName;
}
/**
* Does this schema have a namespace with the given name?
*
* @param string $name
*
* @return bool
*/
public function hasNamespace($name)
{
$name = strtolower($this->getUnquotedAssetName($name));
return isset($this->namespaces[$name]);
}
/**
* Does this schema have a table with the given name?
*
* @param string $name
*
* @return bool
*/
public function hasTable($name)
{
$name = $this->getFullQualifiedAssetName($name);
return isset($this->_tables[$name]);
}
/**
* Gets all table names, prefixed with a schema name, even the default one if present.
*
* @deprecated Use {@see getTables()} and {@see Table::getName()} instead.
*
* @return string[]
*/
public function getTableNames()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4800',
'Schema::getTableNames() is deprecated.'
. ' Use Schema::getTables() and Table::getName() instead.',
__METHOD__
);
return array_keys($this->_tables);
}
/**
* @param string $name
*
* @return bool
*/
public function hasSequence($name)
{
$name = $this->getFullQualifiedAssetName($name);
return isset($this->_sequences[$name]);
}
/**
* @param string $name
*
* @return Sequence
*
* @throws SchemaException
*/
public function getSequence($name)
{
$name = $this->getFullQualifiedAssetName($name);
if (! $this->hasSequence($name)) {
throw SchemaException::sequenceDoesNotExist($name);
}
return $this->_sequences[$name];
}
/**
* @return Sequence[]
*/
public function getSequences()
{
return $this->_sequences;
}
/**
* Creates a new namespace.
*
* @param string $name The name of the namespace to create.
*
* @return Schema This schema instance.
*
* @throws SchemaException
*/
public function createNamespace($name)
{
$unquotedName = strtolower($this->getUnquotedAssetName($name));
if (isset($this->namespaces[$unquotedName])) {
throw SchemaException::namespaceAlreadyExists($unquotedName);
}
$this->namespaces[$unquotedName] = $name;
return $this;
}
/**
* Creates a new table.
*
* @param string $name
*
* @return Table
*
* @throws SchemaException
*/
public function createTable($name)
{
$table = new Table($name);
$this->_addTable($table);
foreach ($this->_schemaConfig->getDefaultTableOptions() as $option => $value) {
$table->addOption($option, $value);
}
return $table;
}
/**
* Renames a table.
*
* @param string $oldName
* @param string $newName
*
* @return Schema
*
* @throws SchemaException
*/
public function renameTable($oldName, $newName)
{
$table = $this->getTable($oldName);
$table->_setName($newName);
$this->dropTable($oldName);
$this->_addTable($table);
return $this;
}
/**
* Drops a table from the schema.
*
* @param string $name
*
* @return Schema
*
* @throws SchemaException
*/
public function dropTable($name)
{
$name = $this->getFullQualifiedAssetName($name);
$this->getTable($name);
unset($this->_tables[$name]);
return $this;
}
/**
* Creates a new sequence.
*
* @param string $name
* @param int $allocationSize
* @param int $initialValue
*
* @return Sequence
*
* @throws SchemaException
*/
public function createSequence($name, $allocationSize = 1, $initialValue = 1)
{
$seq = new Sequence($name, $allocationSize, $initialValue);
$this->_addSequence($seq);
return $seq;
}
/**
* @param string $name
*
* @return Schema
*/
public function dropSequence($name)
{
$name = $this->getFullQualifiedAssetName($name);
unset($this->_sequences[$name]);
return $this;
}
/**
* Returns an array of necessary SQL queries to create the schema on the given platform.
*
* @return string[]
*/
public function toSql(AbstractPlatform $platform)
{
$sqlCollector = new CreateSchemaSqlCollector($platform);
$this->visit($sqlCollector);
return $sqlCollector->getQueries();
}
/**
* Return an array of necessary SQL queries to drop the schema on the given platform.
*
* @return string[]
*/
public function toDropSql(AbstractPlatform $platform)
{
$dropSqlCollector = new DropSchemaSqlCollector($platform);
$this->visit($dropSqlCollector);
return $dropSqlCollector->getQueries();
}
/**
* @deprecated
*
* @return string[]
*
* @throws SchemaException
*/
public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform)
{
$schemaDiff = (new Comparator())->compareSchemas($this, $toSchema);
return $schemaDiff->toSql($platform);
}
/**
* @deprecated
*
* @return string[]
*
* @throws SchemaException
*/
public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform)
{
$schemaDiff = (new Comparator())->compareSchemas($fromSchema, $this);
return $schemaDiff->toSql($platform);
}
/**
* @return void
*/
public function visit(Visitor $visitor)
{
$visitor->acceptSchema($this);
if ($visitor instanceof NamespaceVisitor) {
foreach ($this->namespaces as $namespace) {
$visitor->acceptNamespace($namespace);
}
}
foreach ($this->_tables as $table) {
$table->visit($visitor);
}
foreach ($this->_sequences as $sequence) {
$sequence->visit($visitor);
}
}
/**
* Cloning a Schema triggers a deep clone of all related assets.
*
* @return void
*/
public function __clone()
{
foreach ($this->_tables as $k => $table) {
$this->_tables[$k] = clone $table;
}
foreach ($this->_sequences as $k => $sequence) {
$this->_sequences[$k] = clone $sequence;
}
}
}
PK T<{ { ) src/Schema/Exception/InvalidTableName.phpnu ٘ name = $tableName;
$this->addedColumns = $addedColumns;
$this->changedColumns = $changedColumns;
$this->removedColumns = $removedColumns;
$this->addedIndexes = $addedIndexes;
$this->changedIndexes = $changedIndexes;
$this->removedIndexes = $removedIndexes;
$this->fromTable = $fromTable;
}
/**
* @param AbstractPlatform $platform The platform to use for retrieving this table diff's name.
*
* @return Identifier
*/
public function getName(AbstractPlatform $platform)
{
return new Identifier(
$this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name
);
}
/**
* @return Identifier|false
*/
public function getNewName()
{
if ($this->newName === false) {
return false;
}
return new Identifier($this->newName);
}
}
PK TW@ @ src/Schema/Sequence.phpnu ٘ _setName($name);
$this->setAllocationSize($allocationSize);
$this->setInitialValue($initialValue);
$this->cache = $cache;
}
/**
* @return int
*/
public function getAllocationSize()
{
return $this->allocationSize;
}
/**
* @return int
*/
public function getInitialValue()
{
return $this->initialValue;
}
/**
* @return int|null
*/
public function getCache()
{
return $this->cache;
}
/**
* @param int $allocationSize
*
* @return Sequence
*/
public function setAllocationSize($allocationSize)
{
if ($allocationSize > 0) {
$this->allocationSize = $allocationSize;
} else {
$this->allocationSize = 1;
}
return $this;
}
/**
* @param int $initialValue
*
* @return Sequence
*/
public function setInitialValue($initialValue)
{
if ($initialValue > 0) {
$this->initialValue = $initialValue;
} else {
$this->initialValue = 1;
}
return $this;
}
/**
* @param int $cache
*
* @return Sequence
*/
public function setCache($cache)
{
$this->cache = $cache;
return $this;
}
/**
* Checks if this sequence is an autoincrement sequence for a given table.
*
* This is used inside the comparator to not report sequences as missing,
* when the "from" schema implicitly creates the sequences.
*
* @return bool
*/
public function isAutoIncrementsFor(Table $table)
{
$primaryKey = $table->getPrimaryKey();
if ($primaryKey === null) {
return false;
}
$pkColumns = $primaryKey->getColumns();
if (count($pkColumns) !== 1) {
return false;
}
$column = $table->getColumn($pkColumns[0]);
if (! $column->getAutoincrement()) {
return false;
}
$sequenceName = $this->getShortestName($table->getNamespaceName());
$tableName = $table->getShortestName($table->getNamespaceName());
$tableSequenceName = sprintf('%s_%s_seq', $tableName, $column->getShortestName($table->getNamespaceName()));
return $tableSequenceName === $sequenceName;
}
/**
* @return void
*/
public function visit(Visitor $visitor)
{
$visitor->acceptSequence($this);
}
}
PK TE src/Schema/UniqueConstraint.phpnu ٘ Identifier)
*
* @var Identifier[]
*/
protected $columns = [];
/**
* Platform specific flags.
* array($flagName => true)
*
* @var true[]
*/
protected $flags = [];
/**
* Platform specific options.
*
* @var mixed[]
*/
private $options;
/**
* @param string[] $columns
* @param string[] $flags
* @param mixed[] $options
*/
public function __construct(string $name, array $columns, array $flags = [], array $options = [])
{
$this->_setName($name);
$this->options = $options;
foreach ($columns as $column) {
$this->addColumn($column);
}
foreach ($flags as $flag) {
$this->addFlag($flag);
}
}
/**
* {@inheritdoc}
*/
public function getColumns()
{
return array_keys($this->columns);
}
/**
* {@inheritdoc}
*/
public function getQuotedColumns(AbstractPlatform $platform)
{
$columns = [];
foreach ($this->columns as $column) {
$columns[] = $column->getQuotedName($platform);
}
return $columns;
}
/**
* @return string[]
*/
public function getUnquotedColumns(): array
{
return array_map([$this, 'trimQuotes'], $this->getColumns());
}
/**
* Returns platform specific flags for unique constraint.
*
* @return string[]
*/
public function getFlags(): array
{
return array_keys($this->flags);
}
/**
* Adds flag for a unique constraint that translates to platform specific handling.
*
* @return $this
*
* @example $uniqueConstraint->addFlag('CLUSTERED')
*/
public function addFlag(string $flag): UniqueConstraint
{
$this->flags[strtolower($flag)] = true;
return $this;
}
/**
* Does this unique constraint have a specific flag?
*/
public function hasFlag(string $flag): bool
{
return isset($this->flags[strtolower($flag)]);
}
/**
* Removes a flag.
*/
public function removeFlag(string $flag): void
{
unset($this->flags[strtolower($flag)]);
}
/**
* Does this unique constraint have a specific option?
*/
public function hasOption(string $name): bool
{
return isset($this->options[strtolower($name)]);
}
/**
* @return mixed
*/
public function getOption(string $name)
{
return $this->options[strtolower($name)];
}
/**
* @return mixed[]
*/
public function getOptions(): array
{
return $this->options;
}
/**
* Adds a new column to the unique constraint.
*/
protected function addColumn(string $column): void
{
$this->columns[$column] = new Identifier($column);
}
}
PK Tܽr2 r2 src/Schema/DB2SchemaManager.phpnu ٘
*/
class DB2SchemaManager extends AbstractSchemaManager
{
/**
* {@inheritdoc}
*
* Apparently creator is the schema not the user who created it:
* {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm}
*/
public function listTableNames()
{
$sql = $this->_platform->getListTablesSQL() . ' AND CREATOR = CURRENT_USER';
$tables = $this->_conn->fetchAllAssociative($sql);
return $this->filterAssetNames($this->_getPortableTablesList($tables));
}
/**
* {@inheritDoc}
*/
public function listTables()
{
return $this->doListTables();
}
/**
* {@inheritDoc}
*/
public function listTableDetails($name)
{
return $this->doListTableDetails($name);
}
/**
* {@inheritDoc}
*/
public function listTableColumns($table, $database = null)
{
return $this->doListTableColumns($table, $database);
}
/**
* {@inheritDoc}
*/
public function listTableIndexes($table)
{
return $this->doListTableIndexes($table);
}
/**
* {@inheritDoc}
*/
public function listTableForeignKeys($table, $database = null)
{
return $this->doListTableForeignKeys($table, $database);
}
/**
* {@inheritdoc}
*
* @throws Exception
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
$length = null;
$fixed = null;
$scale = false;
$precision = false;
$default = null;
if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') {
$default = $tableColumn['default'];
if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) {
$default = str_replace("''", "'", $matches[1]);
}
}
$type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']);
if (isset($tableColumn['comment'])) {
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
}
switch (strtolower($tableColumn['typename'])) {
case 'varchar':
if ($tableColumn['codepage'] === 0) {
$type = Types::BINARY;
}
$length = $tableColumn['length'];
$fixed = false;
break;
case 'character':
if ($tableColumn['codepage'] === 0) {
$type = Types::BINARY;
}
$length = $tableColumn['length'];
$fixed = true;
break;
case 'clob':
$length = $tableColumn['length'];
break;
case 'decimal':
case 'double':
case 'real':
$scale = $tableColumn['scale'];
$precision = $tableColumn['length'];
break;
}
$options = [
'length' => $length,
'unsigned' => false,
'fixed' => (bool) $fixed,
'default' => $default,
'autoincrement' => (bool) $tableColumn['autoincrement'],
'notnull' => $tableColumn['nulls'] === 'N',
'scale' => null,
'precision' => null,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
? $tableColumn['comment']
: null,
'platformOptions' => [],
];
if ($scale !== null && $precision !== null) {
$options['scale'] = $scale;
$options['precision'] = $precision;
}
return new Column($tableColumn['colname'], Type::getType($type), $options);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTablesList($tables)
{
$tableNames = [];
foreach ($tables as $tableRow) {
$tableRow = array_change_key_case($tableRow, CASE_LOWER);
$tableNames[] = $tableRow['name'];
}
return $tableNames;
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexes as &$tableIndexRow) {
$tableIndexRow = array_change_key_case($tableIndexRow, CASE_LOWER);
$tableIndexRow['primary'] = (bool) $tableIndexRow['primary'];
}
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
{
return new ForeignKeyConstraint(
$tableForeignKey['local_columns'],
$tableForeignKey['foreign_table'],
$tableForeignKey['foreign_columns'],
$tableForeignKey['name'],
$tableForeignKey['options']
);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$foreignKeys = [];
foreach ($tableForeignKeys as $tableForeignKey) {
$tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER);
if (! isset($foreignKeys[$tableForeignKey['index_name']])) {
$foreignKeys[$tableForeignKey['index_name']] = [
'local_columns' => [$tableForeignKey['local_column']],
'foreign_table' => $tableForeignKey['foreign_table'],
'foreign_columns' => [$tableForeignKey['foreign_column']],
'name' => $tableForeignKey['index_name'],
'options' => [
'onUpdate' => $tableForeignKey['on_update'],
'onDelete' => $tableForeignKey['on_delete'],
],
];
} else {
$foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column'];
$foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column'];
}
}
return parent::_getPortableTableForeignKeysList($foreignKeys);
}
/**
* @param string $def
*
* @return string|null
*/
protected function _getPortableForeignKeyRuleDef($def)
{
if ($def === 'C') {
return 'CASCADE';
}
if ($def === 'N') {
return 'SET NULL';
}
return null;
}
/**
* {@inheritdoc}
*/
protected function _getPortableViewDefinition($view)
{
$view = array_change_key_case($view, CASE_LOWER);
$sql = '';
$pos = strpos($view['text'], ' AS ');
if ($pos !== false) {
$sql = substr($view['text'], $pos + 4);
}
return new View($view['name'], $sql);
}
protected function normalizeName(string $name): string
{
$identifier = new Identifier($name);
return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name);
}
protected function selectDatabaseColumns(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';
if ($tableName === null) {
$sql .= ' C.TABNAME,';
}
$sql .= <<<'SQL'
C.COLNAME,
C.TYPENAME,
C.CODEPAGE,
C.NULLS,
C.LENGTH,
C.SCALE,
C.REMARKS AS COMMENT,
CASE
WHEN C.GENERATED = 'D' THEN 1
ELSE 0
END AS AUTOINCREMENT,
C.DEFAULT
FROM SYSCAT.COLUMNS C
JOIN SYSCAT.TABLES AS T
ON T.TABSCHEMA = C.TABSCHEMA
AND T.TABNAME = C.TABNAME
SQL;
$conditions = ['C.TABSCHEMA = ?', "T.TYPE = 'T'"];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 'C.TABNAME = ?';
$params[] = $tableName;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.TABNAME, C.COLNO';
return $this->_conn->executeQuery($sql, $params);
}
protected function selectDatabaseIndexes(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';
if ($tableName === null) {
$sql .= ' IDX.TABNAME,';
}
$sql .= <<<'SQL'
IDX.INDNAME AS KEY_NAME,
IDXCOL.COLNAME AS COLUMN_NAME,
CASE
WHEN IDX.UNIQUERULE = 'P' THEN 1
ELSE 0
END AS PRIMARY,
CASE
WHEN IDX.UNIQUERULE = 'D' THEN 1
ELSE 0
END AS NON_UNIQUE
FROM SYSCAT.INDEXES AS IDX
JOIN SYSCAT.TABLES AS T
ON IDX.TABSCHEMA = T.TABSCHEMA AND IDX.TABNAME = T.TABNAME
JOIN SYSCAT.INDEXCOLUSE AS IDXCOL
ON IDX.INDSCHEMA = IDXCOL.INDSCHEMA AND IDX.INDNAME = IDXCOL.INDNAME
SQL;
$conditions = ['IDX.TABSCHEMA = ?', "T.TYPE = 'T'"];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 'IDX.TABNAME = ?';
$params[] = $tableName;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IDX.INDNAME, IDXCOL.COLSEQ';
return $this->_conn->executeQuery($sql, $params);
}
protected function selectDatabaseForeignKeys(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';
if ($tableName === null) {
$sql .= ' R.TABNAME,';
}
$sql .= <<<'SQL'
FKCOL.COLNAME AS LOCAL_COLUMN,
R.REFTABNAME AS FOREIGN_TABLE,
PKCOL.COLNAME AS FOREIGN_COLUMN,
R.CONSTNAME AS INDEX_NAME,
CASE
WHEN R.UPDATERULE = 'R' THEN 'RESTRICT'
END AS ON_UPDATE,
CASE
WHEN R.DELETERULE = 'C' THEN 'CASCADE'
WHEN R.DELETERULE = 'N' THEN 'SET NULL'
WHEN R.DELETERULE = 'R' THEN 'RESTRICT'
END AS ON_DELETE
FROM SYSCAT.REFERENCES AS R
JOIN SYSCAT.TABLES AS T
ON T.TABSCHEMA = R.TABSCHEMA
AND T.TABNAME = R.TABNAME
JOIN SYSCAT.KEYCOLUSE AS FKCOL
ON FKCOL.CONSTNAME = R.CONSTNAME
AND FKCOL.TABSCHEMA = R.TABSCHEMA
AND FKCOL.TABNAME = R.TABNAME
JOIN SYSCAT.KEYCOLUSE AS PKCOL
ON PKCOL.CONSTNAME = R.REFKEYNAME
AND PKCOL.TABSCHEMA = R.REFTABSCHEMA
AND PKCOL.TABNAME = R.REFTABNAME
AND PKCOL.COLSEQ = FKCOL.COLSEQ
SQL;
$conditions = ['R.TABSCHEMA = ?', "T.TYPE = 'T'"];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 'R.TABNAME = ?';
$params[] = $tableName;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY R.CONSTNAME, FKCOL.COLSEQ';
return $this->_conn->executeQuery($sql, $params);
}
/**
* {@inheritDoc}
*/
protected function getDatabaseTableOptions(string $databaseName, ?string $tableName = null): array
{
$sql = 'SELECT NAME, REMARKS';
$conditions = [];
$params = [];
if ($tableName !== null) {
$conditions[] = 'NAME = ?';
$params[] = $tableName;
}
$sql .= ' FROM SYSIBM.SYSTABLES';
if ($conditions !== []) {
$sql .= ' WHERE ' . implode(' AND ', $conditions);
}
/** @var array> $metadata */
$metadata = $this->_conn->executeQuery($sql, $params)
->fetchAllAssociativeIndexed();
$tableOptions = [];
foreach ($metadata as $table => $data) {
$data = array_change_key_case($data, CASE_LOWER);
$tableOptions[$table] = ['comment' => $data['remarks']];
}
return $tableOptions;
}
}
PK Th src/Schema/Column.phpnu ٘ _setName($name);
$this->setType($type);
$this->setOptions($options);
}
/**
* @param mixed[] $options
*
* @return Column
*
* @throws SchemaException
*/
public function setOptions(array $options)
{
foreach ($options as $name => $value) {
$method = 'set' . $name;
if (! method_exists($this, $method)) {
throw UnknownColumnOption::new($name);
}
$this->$method($value);
}
return $this;
}
/**
* @return Column
*/
public function setType(Type $type)
{
$this->_type = $type;
return $this;
}
/**
* @param int|null $length
*
* @return Column
*/
public function setLength($length)
{
if ($length !== null) {
$this->_length = (int) $length;
} else {
$this->_length = null;
}
return $this;
}
/**
* @param int $precision
*
* @return Column
*/
public function setPrecision($precision)
{
if (! is_numeric($precision)) {
$precision = 10; // defaults to 10 when no valid precision is given.
}
$this->_precision = (int) $precision;
return $this;
}
/**
* @param int $scale
*
* @return Column
*/
public function setScale($scale)
{
if (! is_numeric($scale)) {
$scale = 0;
}
$this->_scale = (int) $scale;
return $this;
}
/**
* @param bool $unsigned
*
* @return Column
*/
public function setUnsigned($unsigned)
{
$this->_unsigned = (bool) $unsigned;
return $this;
}
/**
* @param bool $fixed
*
* @return Column
*/
public function setFixed($fixed)
{
$this->_fixed = (bool) $fixed;
return $this;
}
/**
* @param bool $notnull
*
* @return Column
*/
public function setNotnull($notnull)
{
$this->_notnull = (bool) $notnull;
return $this;
}
/**
* @param mixed $default
*
* @return Column
*/
public function setDefault($default)
{
$this->_default = $default;
return $this;
}
/**
* @param mixed[] $platformOptions
*
* @return Column
*/
public function setPlatformOptions(array $platformOptions)
{
$this->_platformOptions = $platformOptions;
return $this;
}
/**
* @param string $name
* @param mixed $value
*
* @return Column
*/
public function setPlatformOption($name, $value)
{
$this->_platformOptions[$name] = $value;
return $this;
}
/**
* @param string $value
*
* @return Column
*/
public function setColumnDefinition($value)
{
$this->_columnDefinition = $value;
return $this;
}
/**
* @return Type
*/
public function getType()
{
return $this->_type;
}
/**
* @return int|null
*/
public function getLength()
{
return $this->_length;
}
/**
* @return int
*/
public function getPrecision()
{
return $this->_precision;
}
/**
* @return int
*/
public function getScale()
{
return $this->_scale;
}
/**
* @return bool
*/
public function getUnsigned()
{
return $this->_unsigned;
}
/**
* @return bool
*/
public function getFixed()
{
return $this->_fixed;
}
/**
* @return bool
*/
public function getNotnull()
{
return $this->_notnull;
}
/**
* @return string|null
*/
public function getDefault()
{
return $this->_default;
}
/**
* @return mixed[]
*/
public function getPlatformOptions()
{
return $this->_platformOptions;
}
/**
* @param string $name
*
* @return bool
*/
public function hasPlatformOption($name)
{
return isset($this->_platformOptions[$name]);
}
/**
* @param string $name
*
* @return mixed
*/
public function getPlatformOption($name)
{
return $this->_platformOptions[$name];
}
/**
* @return string|null
*/
public function getColumnDefinition()
{
return $this->_columnDefinition;
}
/**
* @return bool
*/
public function getAutoincrement()
{
return $this->_autoincrement;
}
/**
* @param bool $flag
*
* @return Column
*/
public function setAutoincrement($flag)
{
$this->_autoincrement = $flag;
return $this;
}
/**
* @param string|null $comment
*
* @return Column
*/
public function setComment($comment)
{
$this->_comment = $comment;
return $this;
}
/**
* @return string|null
*/
public function getComment()
{
return $this->_comment;
}
/**
* @param string $name
* @param mixed $value
*
* @return Column
*/
public function setCustomSchemaOption($name, $value)
{
$this->_customSchemaOptions[$name] = $value;
return $this;
}
/**
* @param string $name
*
* @return bool
*/
public function hasCustomSchemaOption($name)
{
return isset($this->_customSchemaOptions[$name]);
}
/**
* @param string $name
*
* @return mixed
*/
public function getCustomSchemaOption($name)
{
return $this->_customSchemaOptions[$name];
}
/**
* @param mixed[] $customSchemaOptions
*
* @return Column
*/
public function setCustomSchemaOptions(array $customSchemaOptions)
{
$this->_customSchemaOptions = $customSchemaOptions;
return $this;
}
/**
* @return mixed[]
*/
public function getCustomSchemaOptions()
{
return $this->_customSchemaOptions;
}
/**
* @return mixed[]
*/
public function toArray()
{
return array_merge([
'name' => $this->_name,
'type' => $this->_type,
'default' => $this->_default,
'notnull' => $this->_notnull,
'length' => $this->_length,
'precision' => $this->_precision,
'scale' => $this->_scale,
'fixed' => $this->_fixed,
'unsigned' => $this->_unsigned,
'autoincrement' => $this->_autoincrement,
'columnDefinition' => $this->_columnDefinition,
'comment' => $this->_comment,
], $this->_platformOptions, $this->_customSchemaOptions);
}
}
PK Ten src/Schema/AbstractAsset.phpnu ٘ Table($tableName)); if you want to rename the table, you have to make sure
*/
abstract class AbstractAsset
{
/** @var string */
protected $_name = '';
/**
* Namespace of the asset. If none isset the default namespace is assumed.
*
* @var string|null
*/
protected $_namespace;
/** @var bool */
protected $_quoted = false;
/**
* Sets the name of this asset.
*
* @param string $name
*
* @return void
*/
protected function _setName($name)
{
if ($this->isIdentifierQuoted($name)) {
$this->_quoted = true;
$name = $this->trimQuotes($name);
}
if (strpos($name, '.') !== false) {
$parts = explode('.', $name);
$this->_namespace = $parts[0];
$name = $parts[1];
}
$this->_name = $name;
}
/**
* Is this asset in the default namespace?
*
* @param string $defaultNamespaceName
*
* @return bool
*/
public function isInDefaultNamespace($defaultNamespaceName)
{
return $this->_namespace === $defaultNamespaceName || $this->_namespace === null;
}
/**
* Gets the namespace name of this asset.
*
* If NULL is returned this means the default namespace is used.
*
* @return string|null
*/
public function getNamespaceName()
{
return $this->_namespace;
}
/**
* The shortest name is stripped of the default namespace. All other
* namespaced elements are returned as full-qualified names.
*
* @param string|null $defaultNamespaceName
*
* @return string
*/
public function getShortestName($defaultNamespaceName)
{
$shortestName = $this->getName();
if ($this->_namespace === $defaultNamespaceName) {
$shortestName = $this->_name;
}
return strtolower($shortestName);
}
/**
* The normalized name is full-qualified and lower-cased. Lower-casing is
* actually wrong, but we have to do it to keep our sanity. If you are
* using database objects that only differentiate in the casing (FOO vs
* Foo) then you will NOT be able to use Doctrine Schema abstraction.
*
* Every non-namespaced element is prefixed with the default namespace
* name which is passed as argument to this method.
*
* @deprecated Use {@see getNamespaceName()} and {@see getName()} instead.
*
* @param string $defaultNamespaceName
*
* @return string
*/
public function getFullQualifiedName($defaultNamespaceName)
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4814',
'AbstractAsset::getFullQualifiedName() is deprecated.'
. ' Use AbstractAsset::getNamespaceName() and ::getName() instead.'
);
$name = $this->getName();
if ($this->_namespace === null) {
$name = $defaultNamespaceName . '.' . $name;
}
return strtolower($name);
}
/**
* Checks if this asset's name is quoted.
*
* @return bool
*/
public function isQuoted()
{
return $this->_quoted;
}
/**
* Checks if this identifier is quoted.
*
* @param string $identifier
*
* @return bool
*/
protected function isIdentifierQuoted($identifier)
{
return isset($identifier[0]) && ($identifier[0] === '`' || $identifier[0] === '"' || $identifier[0] === '[');
}
/**
* Trim quotes from the identifier.
*
* @param string $identifier
*
* @return string
*/
protected function trimQuotes($identifier)
{
return str_replace(['`', '"', '[', ']'], '', $identifier);
}
/**
* Returns the name of this schema asset.
*
* @return string
*/
public function getName()
{
if ($this->_namespace !== null) {
return $this->_namespace . '.' . $this->_name;
}
return $this->_name;
}
/**
* Gets the quoted representation of this asset but only if it was defined with one. Otherwise
* return the plain unquoted value as inserted.
*
* @return string
*/
public function getQuotedName(AbstractPlatform $platform)
{
$keywords = $platform->getReservedKeywordsList();
$parts = explode('.', $this->getName());
foreach ($parts as $k => $v) {
$parts[$k] = $this->_quoted || $keywords->isKeyword($v) ? $platform->quoteIdentifier($v) : $v;
}
return implode('.', $parts);
}
/**
* Generates an identifier from a list of column names obeying a certain string length.
*
* This is especially important for Oracle, since it does not allow identifiers larger than 30 chars,
* however building idents automatically for foreign keys, composite keys or such can easily create
* very long names.
*
* @param string[] $columnNames
* @param string $prefix
* @param int $maxSize
*
* @return string
*/
protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30)
{
$hash = implode('', array_map(static function ($column): string {
return dechex(crc32($column));
}, $columnNames));
return strtoupper(substr($prefix . '_' . $hash, 0, $maxSize));
}
}
PK TR> > ! src/Schema/MySQLSchemaManager.phpnu ٘
*/
class MySQLSchemaManager extends AbstractSchemaManager
{
/**
* @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences
*/
private const MARIADB_ESCAPE_SEQUENCES = [
'\\0' => "\0",
"\\'" => "'",
'\\"' => '"',
'\\b' => "\b",
'\\n' => "\n",
'\\r' => "\r",
'\\t' => "\t",
'\\Z' => "\x1a",
'\\\\' => '\\',
'\\%' => '%',
'\\_' => '_',
// Internally, MariaDB escapes single quotes using the standard syntax
"''" => "'",
];
/**
* {@inheritDoc}
*/
public function listTables()
{
return $this->doListTables();
}
/**
* {@inheritDoc}
*/
public function listTableDetails($name)
{
return $this->doListTableDetails($name);
}
/**
* {@inheritDoc}
*/
public function listTableColumns($table, $database = null)
{
return $this->doListTableColumns($table, $database);
}
/**
* {@inheritDoc}
*/
public function listTableIndexes($table)
{
return $this->doListTableIndexes($table);
}
/**
* {@inheritDoc}
*/
public function listTableForeignKeys($table, $database = null)
{
return $this->doListTableForeignKeys($table, $database);
}
/**
* {@inheritdoc}
*/
protected function _getPortableViewDefinition($view)
{
return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableDefinition($table)
{
return array_shift($table);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexes as $k => $v) {
$v = array_change_key_case($v, CASE_LOWER);
if ($v['key_name'] === 'PRIMARY') {
$v['primary'] = true;
} else {
$v['primary'] = false;
}
if (strpos($v['index_type'], 'FULLTEXT') !== false) {
$v['flags'] = ['FULLTEXT'];
} elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
$v['flags'] = ['SPATIAL'];
}
// Ignore prohibited prefix `length` for spatial index
if (strpos($v['index_type'], 'SPATIAL') === false) {
$v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null;
}
$tableIndexes[$k] = $v;
}
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableDatabaseDefinition($database)
{
return $database['Database'];
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
$dbType = strtolower($tableColumn['type']);
$dbType = strtok($dbType, '(), ');
assert(is_string($dbType));
$length = $tableColumn['length'] ?? strtok('(), ');
$fixed = null;
if (! isset($tableColumn['name'])) {
$tableColumn['name'] = '';
}
$scale = null;
$precision = null;
$type = $this->_platform->getDoctrineTypeMapping($dbType);
// In cases where not connected to a database DESCRIBE $table does not return 'Comment'
if (isset($tableColumn['comment'])) {
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
}
switch ($dbType) {
case 'char':
case 'binary':
$fixed = true;
break;
case 'float':
case 'double':
case 'real':
case 'numeric':
case 'decimal':
if (
preg_match(
'([A-Za-z]+\(([0-9]+),([0-9]+)\))',
$tableColumn['type'],
$match
) === 1
) {
$precision = $match[1];
$scale = $match[2];
$length = null;
}
break;
case 'tinytext':
$length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYTEXT;
break;
case 'text':
$length = AbstractMySQLPlatform::LENGTH_LIMIT_TEXT;
break;
case 'mediumtext':
$length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT;
break;
case 'tinyblob':
$length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYBLOB;
break;
case 'blob':
$length = AbstractMySQLPlatform::LENGTH_LIMIT_BLOB;
break;
case 'mediumblob':
$length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB;
break;
case 'tinyint':
case 'smallint':
case 'mediumint':
case 'int':
case 'integer':
case 'bigint':
case 'year':
$length = null;
break;
}
if ($this->_platform instanceof MariaDb1027Platform) {
$columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
} else {
$columnDefault = $tableColumn['default'];
}
$options = [
'length' => $length !== null ? (int) $length : null,
'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false,
'fixed' => (bool) $fixed,
'default' => $columnDefault,
'notnull' => $tableColumn['null'] !== 'YES',
'scale' => null,
'precision' => null,
'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
? $tableColumn['comment']
: null,
];
if ($scale !== null && $precision !== null) {
$options['scale'] = (int) $scale;
$options['precision'] = (int) $precision;
}
$column = new Column($tableColumn['field'], Type::getType($type), $options);
if (isset($tableColumn['characterset'])) {
$column->setPlatformOption('charset', $tableColumn['characterset']);
}
if (isset($tableColumn['collation'])) {
$column->setPlatformOption('collation', $tableColumn['collation']);
}
return $column;
}
/**
* Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
*
* - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted
* to distinguish them from expressions (see MDEV-10134).
* - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema
* as current_timestamp(), currdate(), currtime()
* - Quoted 'NULL' is not enforced by Maria, it is technically possible to have
* null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053)
* - \' is always stored as '' in information_schema (normalized)
*
* @link https://mariadb.com/kb/en/library/information-schema-columns-table/
* @link https://jira.mariadb.org/browse/MDEV-13132
*
* @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
*/
private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string
{
if ($columnDefault === 'NULL' || $columnDefault === null) {
return null;
}
if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) {
return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES);
}
switch ($columnDefault) {
case 'current_timestamp()':
return $platform->getCurrentTimestampSQL();
case 'curdate()':
return $platform->getCurrentDateSQL();
case 'curtime()':
return $platform->getCurrentTimeSQL();
}
return $columnDefault;
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$list = [];
foreach ($tableForeignKeys as $value) {
$value = array_change_key_case($value, CASE_LOWER);
if (! isset($list[$value['constraint_name']])) {
if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') {
$value['delete_rule'] = null;
}
if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') {
$value['update_rule'] = null;
}
$list[$value['constraint_name']] = [
'name' => $value['constraint_name'],
'local' => [],
'foreign' => [],
'foreignTable' => $value['referenced_table_name'],
'onDelete' => $value['delete_rule'],
'onUpdate' => $value['update_rule'],
];
}
$list[$value['constraint_name']]['local'][] = $value['column_name'];
$list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
}
$result = [];
foreach ($list as $constraint) {
$result[] = new ForeignKeyConstraint(
$constraint['local'],
$constraint['foreignTable'],
$constraint['foreign'],
$constraint['name'],
[
'onDelete' => $constraint['onDelete'],
'onUpdate' => $constraint['onUpdate'],
]
);
}
return $result;
}
public function createComparator(): Comparator
{
return new MySQL\Comparator($this->_platform);
}
protected function selectDatabaseColumns(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';
if ($tableName === null) {
$sql .= ' TABLE_NAME,';
}
$sql .= <<<'SQL'
COLUMN_NAME AS field,
COLUMN_TYPE AS type,
IS_NULLABLE AS `null`,
COLUMN_KEY AS `key`,
COLUMN_DEFAULT AS `default`,
EXTRA,
COLUMN_COMMENT AS comment,
CHARACTER_SET_NAME AS characterset,
COLLATION_NAME AS collation
FROM information_schema.COLUMNS
SQL;
$conditions = ['TABLE_SCHEMA = ?'];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 'TABLE_NAME = ?';
$params[] = $tableName;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY ORDINAL_POSITION';
return $this->_conn->executeQuery($sql, $params);
}
protected function selectDatabaseIndexes(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';
if ($tableName === null) {
$sql .= ' TABLE_NAME,';
}
$sql .= <<<'SQL'
NON_UNIQUE AS Non_Unique,
INDEX_NAME AS Key_name,
COLUMN_NAME AS Column_Name,
SUB_PART AS Sub_Part,
INDEX_TYPE AS Index_Type
FROM information_schema.STATISTICS
SQL;
$conditions = ['TABLE_SCHEMA = ?'];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 'TABLE_NAME = ?';
$params[] = $tableName;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX';
return $this->_conn->executeQuery($sql, $params);
}
protected function selectDatabaseForeignKeys(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT DISTINCT';
if ($tableName === null) {
$sql .= ' k.TABLE_NAME,';
}
$sql .= <<<'SQL'
k.CONSTRAINT_NAME,
k.COLUMN_NAME,
k.REFERENCED_TABLE_NAME,
k.REFERENCED_COLUMN_NAME,
k.ORDINAL_POSITION /*!50116,
c.UPDATE_RULE,
c.DELETE_RULE */
FROM information_schema.key_column_usage k /*!50116
INNER JOIN information_schema.referential_constraints c
ON c.CONSTRAINT_NAME = k.CONSTRAINT_NAME
AND c.TABLE_NAME = k.TABLE_NAME
AND c.CONSTRAINT_SCHEMA = k.TABLE_SCHEMA */
SQL;
$conditions = ['k.TABLE_SCHEMA = ?'];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 'k.TABLE_NAME = ?';
$params[] = $tableName;
}
$conditions[] = 'k.REFERENCED_COLUMN_NAME IS NOT NULL';
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY k.ORDINAL_POSITION';
return $this->_conn->executeQuery($sql, $params);
}
/**
* {@inheritDoc}
*/
protected function getDatabaseTableOptions(string $databaseName, ?string $tableName = null): array
{
$sql = <<<'SQL'
SELECT t.TABLE_NAME,
t.ENGINE,
t.AUTO_INCREMENT,
t.TABLE_COMMENT,
t.CREATE_OPTIONS,
t.TABLE_COLLATION,
ccsa.CHARACTER_SET_NAME
FROM information_schema.TABLES t
INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa
ON ccsa.COLLATION_NAME = t.TABLE_COLLATION
SQL;
$conditions = ['t.TABLE_SCHEMA = ?'];
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 't.TABLE_NAME = ?';
$params[] = $tableName;
}
$conditions[] = "t.TABLE_TYPE = 'BASE TABLE'";
$sql .= ' WHERE ' . implode(' AND ', $conditions);
/** @var array> $metadata */
$metadata = $this->_conn->executeQuery($sql, $params)
->fetchAllAssociativeIndexed();
$tableOptions = [];
foreach ($metadata as $table => $data) {
$data = array_change_key_case($data, CASE_LOWER);
$tableOptions[$table] = [
'engine' => $data['engine'],
'collation' => $data['table_collation'],
'charset' => $data['character_set_name'],
'autoincrement' => $data['auto_increment'],
'comment' => $data['table_comment'],
'create_options' => $this->parseCreateOptions($data['create_options']),
];
}
return $tableOptions;
}
/**
* @return string[]|true[]
*/
private function parseCreateOptions(?string $string): array
{
$options = [];
if ($string === null || $string === '') {
return $options;
}
foreach (explode(' ', $string) as $pair) {
$parts = explode('=', $pair, 2);
$options[$parts[0]] = $parts[1] ?? true;
}
return $options;
}
}
PK T~. src/Schema/View.phpnu ٘ _setName($name);
$this->sql = $sql;
}
/**
* @return string
*/
public function getSql()
{
return $this->sql;
}
}
PK T<