Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add mysql specific indexes with lengths #2412

Merged

Conversation

bburnichon
Copy link
Contributor

First step to fix #1597 & #2368

@mnapoli
Copy link
Contributor

mnapoli commented Jun 14, 2016

I think length (singular) might be a better name than lengths (plural). Funny I ran into that issue just today.

@@ -2386,12 +2386,18 @@ public function getCustomTypeDeclarationSQL(array $columnDef)
* Obtains DBMS specific SQL code portion needed to set an index
* declaration to be used in statements like CREATE TABLE.
*
* @param array $fields
* @param array|Index $fields
Copy link
Member

Choose a reason for hiding this comment

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

This is weird and we should not do this. This API accepts an array of field definitions and not an index. I see why you are doing this but this is more of a workaround/hack rather than a good solution. Also having lengths option on the index as an array of lengths for all of the index columns isn't better either. What we are missing is an IndexColumn class that has options itself dedicated to the index column but I am not sure we should/can add this in 2.x.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know the API currently accepts an array of field names. My first intention was to change this to only accept an Index parameter. I agree that an IndexColumn could be a solution but it seems heavier to implement this class.

I did not wanted to have a simple array of lengths in the API but I currently do not see another better way to avoid this while keeping BC.

@bburnichon
Copy link
Contributor Author

@mnapoli I made lengths plural to be consistent with columns, flags and so on.

{
if ($fields instanceof Index) {
return implode(', ', $fields->getQuotedColumns($this));
} elseif (!is_array($fields)) {
Copy link
Member

Choose a reason for hiding this comment

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

no need for else

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't you want to check $fields is of proper type before continuing? If I remove the else part, developers could call method with improper data type. Or do you just prefer to have a separate if statement instead of elseif?

Copy link
Member

Choose a reason for hiding this comment

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

You'd just make an if instead of an elseif ;-)

@aimeos
Copy link
Contributor

aimeos commented Aug 12, 2016

Would be great to get this feature into DBAL because it would simplify managing indexes on text columns very much!

@tmaroschik
Copy link

What is necessary to get this pull request merged? Fixing the conflicts only or is the general approach in question?

Copy link
Member

@morozov morozov left a comment

Choose a reason for hiding this comment

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

@tmaroschik please provide a functional test which would fail without the fix. Additionally, as far as I understand the design decision made in AbstractPlatform::getIndexFieldDeclarationListSQL(), the usage of array $fields should be deprecated in favor of Index $index.

lib/Doctrine/DBAL/Platforms/AbstractPlatform.php Outdated Show resolved Hide resolved
@bburnichon bburnichon force-pushed the feature/mysql-indexes-with-length branch 2 times, most recently from 6b01b42 to d0f3e1c Compare August 31, 2018 10:57
@bburnichon
Copy link
Contributor Author

@morozov Is functional test provided sufficient?

@morozov
Copy link
Member

morozov commented Sep 6, 2018

@bburnichon yes, the test looks good. As far as I understand, only MySQL (and probably MariaDB) support.

Would it make sense to move the test up to SchemaManagerFunctionalTestCase and explicitly disable in all classes except MySqlSchemaManagerTest? It looks so to me because it covers the new 'lengths' flag of the index definition which belongs to the DBAL itself, not just to the MySQL schema manager.

lib/Doctrine/DBAL/Platforms/MySqlPlatform.php Outdated Show resolved Hide resolved
lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php Outdated Show resolved Hide resolved
lib/Doctrine/DBAL/Platforms/AbstractPlatform.php Outdated Show resolved Hide resolved
lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php Outdated Show resolved Hide resolved
@bburnichon bburnichon force-pushed the feature/mysql-indexes-with-length branch 2 times, most recently from b3d096c to 3bf7817 Compare September 7, 2018 08:48
@bburnichon bburnichon force-pushed the feature/mysql-indexes-with-length branch 2 times, most recently from f3135bd to 3f9fee0 Compare September 10, 2018 09:27
@morozov morozov self-assigned this Nov 8, 2018
@morozov morozov added this to the 2.9.0 milestone Nov 8, 2018
@morozov
Copy link
Member

morozov commented Nov 8, 2018

@bburnichon please squash commits and I'll merge it.

morozov
morozov previously approved these changes Nov 8, 2018
@bburnichon
Copy link
Contributor Author

@morozov Squashed and all tests green! \o/

@morozov morozov merged commit 822d970 into doctrine:master Nov 8, 2018
@morozov
Copy link
Member

morozov commented Nov 8, 2018

Thank you @bburnichon, merged.

@exptom
Copy link

exptom commented Mar 26, 2020

This doesn't seem to work on primary key definitions. e.g. I have defined this:

<id name="publicKeyCredentialId" type="base64">
            <options>
                <option name="lengths">255</option>
            </options>
</id>

The lengths option is silently ignored.

@bburnichon
Copy link
Contributor Author

@exptom I did not check real behavior but I think that it is not valid anyway. A primary key cannot be partial as it need to ensure all values are unique.

@exptom
Copy link

exptom commented Mar 27, 2020

@bburnichon Yes, you are right, sorry I need to rethink my schema!

@exptom
Copy link

exptom commented Mar 31, 2020

@bburnichon Sorry to bring this up again, but I have refactored my entity and I am now just trying to apply the index lengths option to a normal index, like this:

<field name="publicKeyCredentialId" type="base64" />
<indexes>
            <index name="publicKeyCredentialId" columns="publicKeyCredentialId">
                <options>
                    <option name="lengths">200</option>
                </options>
            </index>
</indexes>

However this still doesn't work because I get this error:

PHP Warning: array_shift() expects parameter 1 to be array, string given in /home/tom/Workspaces/PHP_Libs/zf-mvc-users/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php on line 109

which is because a string "200" is passed in rather than an array containing 200. How would I specify the index lengths correctly in the xml config?

@bburnichon
Copy link
Contributor Author

I am afraid that it is because this feature was not implemented to XmlMapping. This is out of the scope of this PR. Have a look at https://github.com/doctrine/orm/blob/8c259ea5cb632dbb57001b2262048ae7fa52b102/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php#L993

rgrellmann added a commit to Rossmann-IT/dbal that referenced this pull request Apr 22, 2020
Release v2.9.0

This is a minor release of Doctrine DBAL that aggregates over 40 fixes and improvements developed by 18 contributors over the last 5 months.

This release includes all changes of the 2.8.x series, as well as feature additions and improvements that couldn't land in patch releases.

## Backwards Compatibility Breaks

This doesn't contain any intentional Backwards Compatibility (BC) breaks.

## Deprecations

* The usage of `NULL` to specify the absence of an offset in `LIMIT`ed queries is deprecated. Use `0` instead.
* It's not recommended to rely on the default length specified by implementations of `Type`. These values are not used by the library and will be removed.
* It's not recommended to rely on the string representation of `Type` objects.
* Regular-expression based asset filters are deprecated in favor of callback-based as more extensible.
* Calling `Statement::fetchColumn()` with an invalid column index is deprecated.
* The `dbal:import` CLI command is deprecated. Please use other database client applications for import.

Please see details in the [UPGRADE.md](UPGRADE.md) documentation.

## New Features

* Added support for MariaDB 10.3.
* Added support for Windows authentication for SQL Server.
* Added support for column length in index definitions on MySQL.

## Improvements and Fixes

* Implemented handling BLOB objects represented as streams in the MySQL (`mysqli`) driver.
* Implemented handling BLOB objects represented as streams in the IDM DB2 driver.
* DBAL is now continuously tested with the PDO driver for Oracle.
* Implemented handling of URLs in master-slave and pooling-shard connection configuration.
* The codebase is now fully compatible with the Doctrine Coding Standard v5.0.

Total issues resolved: **45**

**Deprecations:**

- [3244: Deprecated dbal:import CLI command](doctrine#3244) thanks to @morozov
- [3253: Deprecated usage of the NULL offset in LIMITed queries](doctrine#3253) thanks to @morozov
- [3256: Deprecate Doctrine\DBAL\Types\Type::getDefaultLength()](doctrine#3256) thanks to @Majkl578
- [3258: Deprecate Doctrine\DBAL\Types\Type::&doctrine#95;&doctrine#95;toString()](doctrine#3258) thanks to @Majkl578
- [3316: Deprecated regex-based asset filters](doctrine#3316) thanks to @morozov
- [3359: Removed DataAccessTest::testFetchColumnNonExistingIndex() since it covers a bug in PDO](doctrine#3359) thanks to @morozov

**New Features:**

- [2412: Add mysql specific indexes with lengths](doctrine#2412) thanks to @bburnichon
- [3278: Add support for MariaDB 10.3](doctrine#3278) thanks to @javiereguiluz
- [3283: MariaDB improvements, support 10.3](doctrine#3283) thanks to @sidz
- [3333: Allow windows (userless/passwordless) authentication for SQL Server](doctrine#3333) thanks to @odinsey

**Bug Fixes:**

- [3355: Implemented comparison of default values as strings regardless of their PHP types](doctrine#3355) thanks to @morozov and @Majkl578

**Improvements:**

- [3201: Fix support for URL to account for master-slave and pooling-shard connections](doctrine#3201) thanks to @stof
- [3217: Fix that MysqliStatement cannot handle streams](doctrine#3217) thanks to @mpdude
- [3235: Use PSR-4 autoloader](doctrine#3235) thanks to @Majkl578
- [3254: Throw ConversionException when unserialization fail for array and object types](doctrine#3254) thanks to @seferov
- [3259: Update export ignores](doctrine#3259) thanks to @Majkl578
- [3309: Implemented handling BLOBs represented as stream resources for IBM DB2](doctrine#3309) thanks to @morozov and @mpdude
- [3331: Fetch all should use the driver statement's fetchAll method](doctrine#3331) thanks to @michaelcullum

**Documentation Improvements:**

- [3223: GitHub template grammar/spelling fixes](doctrine#3223) thanks to @GawainLynch
- [3232: Removed NOW() from QueryBuilder usage examples](doctrine#3232) thanks to @morozov
- [3239: 2.8 in README &amp; branch alias to 2.9](doctrine#3239) thanks to @Majkl578
- [3269: Fixed type hints in DockBlocks](doctrine#3269) thanks to @marforon
- [3275: Add .doctrine-project.json to root of the project.](doctrine#3275) thanks to @jwage
- [3276: Update homepage](doctrine#3276) thanks to @Majkl578
- [3280: Use behaviuor instead of  behavior](doctrine#3280) thanks to @BackEndTea
- [3285: Remove old comment from MysqliStatement](doctrine#3285) thanks to @mpdude
- [3318: Removed link to www.doctrine-project.org from doc blocks](doctrine#3318) thanks to @morozov
- [3319: remove ClassLoader](doctrine#3319) thanks to @garak
- [3337: Fix of links in documentation](doctrine#3337) thanks to @SenseException
- [3350: Remove pdo&doctrine#95;sqlsrv from known vendor issues list](doctrine#3350) thanks to @ostrolucky
- [3357: Fix typo](doctrine#3357) thanks to @BenMorel
- [3370: Removed 2.7 from README](doctrine#3370) thanks to @morozov

**Code Quality Improvements:**

- [3252: Replaced call&doctrine#95;user&doctrine#95;func&doctrine#95;array() of a fixed method with the usage of variadic arguments](doctrine#3252) thanks to @morozov
- [3306: Fixed coding standard violations in the codebase](doctrine#3306) thanks to @morozov
- [3303: Updated doctrine/coding-standard to 5.0, ](doctrine#3303) thanks to @morozov
- [3317: Implemented proper escaping of string literals in platforms and schema managers](doctrine#3317) thanks to @morozov
- [3363: Remove redundant implements](doctrine#3363) thanks to @BenMorel

**Continuous Integration Improvements:**

- [3307: Test against the latest stable sqlsrv extension](doctrine#3307) thanks to @morozov
- [3320: Trying to fix failing DB builds](doctrine#3320) thanks to @morozov
- [3325: Updated PHPUnit to 7.4](doctrine#3325) thanks to @morozov
- [3339: ContinuousPHP configuration for PDO Oracle driver](doctrine#3339) thanks to @morozov
- [3365: Reorganize Travis build matrix](doctrine#3365) thanks to @BenMorel

# gpg: Signature made Tue Dec  4 05:44:06 2018
# gpg:                using RSA key 374EADAF543AE995
# gpg: Can't check signature: public key not found

# Conflicts:
#	README.md
#	lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php
#	lib/Doctrine/DBAL/Version.php
@gggeek
Copy link

gggeek commented Nov 2, 2020

@exptom @bburnichon I just tested creation of a PK(191) on a varchar(255) column and it does work (at least in a mysql 5.6 db configured so that creation of the PK on the full length of the same column fails).

So to have support allowing partial-lenght PKs on Mysql shall I open a separate ticket and PR ?

@bburnichon
Copy link
Contributor Author

@gggeek Even though MySQL allows it does not mean it should. A primary key must ensure all values are different, a partial key leaves room to add duplicates. Anyway, This ticket was merged around 2 years ago, if you wish to add support for partial-length primary key, you shall open a new ticket.

@gggeek
Copy link

gggeek commented Nov 3, 2020

@bburnichon I don't disagree with you - this is possibly one of those features of MySQL that come from those dark times when it was developed according to the 'make it stick' philosophy, regardless of correctness of data and query results ;-)
However it is also a useful feature to work around the dead stupid limitation of maximum index length on varchar columns - especially useful when the db config is such that this limit is as low as 190 characters...
Given the fact that both MySQL and DBAL already allow the definition of a Unique index that checks only a part of the data - a blatant logical nonsense - it stands to reason that we could allow as well the same 'relaxed' constraint in the definition of a PK.
I have thus created issue #4405

@devnix
Copy link

devnix commented Jan 21, 2022

@bburnichon Sorry to bring this up again, but I have refactored my entity and I am now just trying to apply the index lengths option to a normal index, like this:

<field name="publicKeyCredentialId" type="base64" />
<indexes>
            <index name="publicKeyCredentialId" columns="publicKeyCredentialId">
                <options>
                    <option name="lengths">200</option>
                </options>
            </index>
</indexes>

However this still doesn't work because I get this error:

PHP Warning: array_shift() expects parameter 1 to be array, string given in /home/tom/Workspaces/PHP_Libs/zf-mvc-users/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php on line 109

which is because a string "200" is passed in rather than an array containing 200. How would I specify the index lengths correctly in the xml config?

I am afraid that it is because this feature was not implemented to XmlMapping. This is out of the scope of this PR. Have a look at https://github.com/doctrine/orm/blob/8c259ea5cb632dbb57001b2262048ae7fa52b102/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php#L993

Hi @exptom, @bburnichon, sorry for reviving this thread after so long. I guess this comment could be out of scope, but I would like to note that I've been able to use this in XML:

<index name="test" columns="foo,bar,baz">
    <options>
        <option name="lengths">
            <value>100</value>
            <value>100</value>
        </option>
    </options>
</index>

generates the following diff:

CREATE INDEX test ON eventos_rol (foo(100), bar(100), baz);

I've still not found how to generate this diff:

CREATE INDEX test ON eventos_rol (foo, bar(100), baz);

but I guess this is a big step.

I guess that this possibility is not documented in the schema:
image

@bburnichon
Copy link
Contributor Author

bburnichon commented Jan 21, 2022 via email

@devnix
Copy link

devnix commented Jan 21, 2022

Hi @bburnichon, thank you for your fast answer!

In my case, all these options

<option name="lengths">
    <value></value>
    <value>100</value>
</option>
<option name="lengths">
    <value/>
    <value>100</value>
</option>
<option name="lengths">
    <value xsi:nil="true"/>
    <value>100</value>
</option>

are throwing me an Argument 1 passed to Doctrine\DBAL\Schema\Index::Doctrine\DBAL\Schema\{closure}() must be of the type int or null, string given exception, as I guess it's being casted to string in https://github.com/doctrine/orm/blob/8c259ea5cb632dbb57001b2262048ae7fa52b102/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php#L1002

I have doubts if there is a current mechanism here to achieve this. I'm not inverting the order of the params, as I'm trying to map an already existing legacy database, and I'm trying to null the schema diff. I've tested the third approach and it generates a diff too.

@bburnichon
Copy link
Contributor Author

I guess you will have to update parseOptions recursion and detect attribute name like it is done for fixed and unsigned. Add a special case for lengths and add a special evaluation method (evaluateIntegerOrNull in the same way evaluateBoolean).

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DBAL-404: Support of index length for text fields
10 participants