Problem/Motivation
In #2957381: Data model problems with Vocabulary hierarchy we added an update path test which tries to verify that an index exists by looking up its name. However, there are two problems with that approach:
- the real intention of the test was to check whether an index with a given set of columns exists on a table;
- on PostgreSQL, if the name of an index exceeds 63 chars (the identifier limit) and its table is renamed, the new name of the index can not be determined in any way.
Proposed resolution
Add a database schema introspection test trait and use it in \Drupal\Tests\taxonomy\Functional\Update\TaxonomyVocabularyHierarchyUpdateTest::testTaxonomyUpdateParents()
.
Remaining tasks
Review.
User interface changes
Nope.
API changes
A new trait is available for tests that need to check whether an index with a given set of columns exists.
Data model changes
Nope.
Release notes snippet
Nope.
Comment | File | Size | Author |
---|---|---|---|
#26 | 3031479-26-followup.patch | 913 bytes | amateescu |
#18 | interdiff-18.txt | 6.44 KB | amateescu |
#18 | 3031479-18.patch | 16.83 KB | amateescu |
#13 | 3031479-13.patch | 13.69 KB | amateescu |
Comments
Comment #2
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis should do it.
Comment #3
jibranThis function doesn't need to be in a test trait. Maybe move it to some helpful location and call it in assert methods?
Comment #4
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI don't think it needs to be public API yet, that's why I put it in a trait.
Comment #5
jibranLet's see what core committer think. Do we need a test only patch?
Wired whitespace between SELECT and i.
Comment #6
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedIf we improve the new assertion methods to also take the index type as an argument, we can also use them to improve the readability of another existing test.
Fixed #5 as well.
Comment #7
jibranDo we need tests for this method? :D
Comment #8
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI think the assertions added by this patch are enough to demonstrate that the method does its job extremely well.
Comment #10
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedProbably a random fail, queued a retest.
Comment #11
mondrakeCouldn't this be a method on the Schema API? Like
findPrimaryKeyColumns($table)
. That way also contrib drivers could add their own implementation.Edit - see #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test
Comment #12
plachGiven that we already have
::findPrimaryKeyColumns()
, I guess a::findIndexColumns()
method would make sense.Comment #13
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@mondrake, having a method which can be implemented by alternative database drivers makes very much sense. This way they could even try to run more of
\Drupal\KernelTests\Core\Database\SchemaTest
in their own tests.Decided to go with a
introspectIndexSchema()
method in the Schema API, to match the existingintrospectSchema()
already defined by SQLite.Comment #14
jibranPerfect! this looks much better now.
Comment #15
plachI was about to suggest to switch to
BadMethodCallException
, but given that the driver is configurable, a runtime exception makes sense to me.I was initially confused by the
$index_name
variable, since it is unused. Can we remove it?It seems to me that this logic is a bit messed up: all assertions here, especially the last lines, are no-ops. There's no way for that code to fail :)
I guess the original goal was to fail as soon as a non-matching condition was found and only assert TRUE if all were met. Something like this:
Also, would it make sense to use strict comparison here?
What about making the method public? Being able to introspect the actual schema might turn out to be useful while doing fancy stuff with it ;)
Moreover I'm not sure we normally "open" protected methods this way in tests, given that we are not testing the method return value itself.
Which leads me to the next point :)
For consistency would it make sense to add a key name here as well? Otherwise the index name will be an integer in this case and that might cause issues with loose equality in the caller code.
Regardless of that, it seems that we are not actually checking that all drivers are returning the exact same data structure (although it seems they are), given that index name keys are unused. Probably it would be a good idea to add a small test explicitly testing
::introspectIndexSchema()
itself.Comment #16
plachNow that I think about it, that wouldn't work if multiple indexes were returned. However the logic as it is now doesn't seem to work either.
Comment #17
plachMaybe something like this?
Comment #18
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThanks for the review!
Re #15:
introspectSchema()
method, but this one is only about getting the index definitions and I think it should stay "hidden" because it's not very useful on its own.We do a lot of reflection for protected methods in tests, the search results for
new \ReflectionMethod
throughout core will surprise you :DAdded full test coverage for the new
introspectIndexSchema()
method.Comment #19
plachThanks for the new test, it looks great!
1: Good point :)
2: 👍
3: That's what I was asking: would it make sense to be able to assert also the column order? I mean, since you are writing a test you can bother providing the correct order, and if the test starts failing maybe that means something got messed up with the schema. Not feeling strongly about this but I thought it would be good to point it out. Or are we implying that the some drivers may not provide a predictable order?
4:
That's what I meant earlier: we normally make protected methods accessible in tests to assert their result, not to get access to internal APIs. The latter seems to suggest we need a public API instead. However, I'm fine with leaving the method protected, if the goal is to have a complete API instead.
5: Thanks!
Tricky :)
Was this caught by the new test?
Comment #20
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedExactly! More specifically, the pgsql query to get the index column names does not return them in the order they were defined in the schema array, and if we add any kind of ordering to the query, it still won't match the initial schema order.
Now that we have a test for asserting its result, I hope you feel better with leaving it protected :)
It was caught by fixing
assertIndexOnColumns()
, which convinced me that we definitely need a proper test for the whole method.Comment #21
plachYep, this looks good to go to me :)
Comment #22
plachSaving credits
Comment #24
plachCommitted 431b5b4 and pushed to 8.7.x. Thanks!
Comment #25
mondrakeUhm, numeric only index name like '2' can create issues in contrib as these can be interpreted as numbers rather than identifiers, e.g.
Is it a big deal to change those to sth like
?
Comment #26
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedIt's not a big deal at all, let's use those keys as they are used in real-world use cases, by combining the names of the columns.
I tested this locally on all three db drivers supported by core and it works fine as expected.
Comment #27
mondrakeGreat. Thanks :)
Comment #28
plachI was thinking about other ways a contrib driver could be affected by this: should we catch the not-implemented exception and prevent the test from failing if the implementation is missing?
Comment #29
mondrake#28 actually only tests would fail here - not normal runtime code. And only if you run the core testsuite under the contrib driver. So I find the exception raised as a good 'call for action' to contrib driver developers that would have to implement the missing pieces if they want to keep up with the evolving db abstraction layer. BTW this also applies to
::findPrimaryKeyColumns()
.Maybe a change record would be good, anyway?
Comment #30
mondrakeSee https://www.drupal.org/node/2974956 for the change record that was done for #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test
Comment #31
plachWell, this is not a public API so a CR would only be helpful for DB driver developers, but why not? :)
Comment #32
plachYep, I was suggesting to catch the failure in the test, however if core starts using that method in runtime code, it will break when using a driver not implementing that, so I agree it's fine to let the test fail.
Comment #34
plachCommitted #26 as f5d6ace and pushed it to 8.7.x. Thanks!
Let's keep this fixed now ;)