Problem/Motivation
While working on #1980556: Allow NULL default values in @Plugin annotations I found that DrupalSqlbase, in checkRequirements, uses the property $minimum_schema_version which is not set anywhere and that MigrateSource defines a property $minimum_version which is not used anywhere. Reading the doc bloc suggests that migrate drupal source plugins should be using $minimum_version.
/**
* Specifies the minimum version of the source provider.
*
* This can be any type, and the source plugin itself determines how it is
* used. For example, Migrate Drupal's source plugins expect this to be an
* integer representing the minimum installed database schema version of the
* module specified by source_module.
*
* @var mixed
*/
public $minimum_version;See comment 1980556-Comment #42 for more details.
Proposed resolution
Change $minimum_schema_version in DrupalSqlBase to $minimum_version and of course add a test. Add a comment and change the logic to be more readable.
No deprecation or errors added because this is so broken the exception was never thrown.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #16 | 3151732-16.patch | 4.21 KB | quietone |
| #16 | interdiff-12-16.txt | 835 bytes | quietone |
| #12 | 3151732-12.patch | 3.9 KB | quietone |
| #12 | 3151732-12-fail.patch | 2.35 KB | quietone |
| #9 | 3151732-9.patch | 3.9 KB | quietone |
Comments
Comment #2
quietone commentedI double checked that minimum_schema_version is only used in DrupalSqlBase.
This patch changes minimum_schema_version to minimum_version and also fixes this if statement. The ! should be outside the test for less than.
isset($this->pluginDefinition['minimum_schema_version']) && !$this->getModuleSchemaVersion($this->pluginDefinition['source_module']) < $this->pluginDefinition['minimum_schema_version']) {There are two new tests for checkRequirements to test this logic, which had no tests before. The interesting thing here is that they success test also passes in the test with the fail patch. That is becuase of the error mentioned about. To help prove that the fail test includes a fourth test case where the data should throw an exception. The exception is not thrown and testMinimumVersionSuccess succeeds. I hope that makes sense to you.
Comment #5
benjifisherBased on the code comments, I agree with your description of the problem. Both
$minimum_versionand'minimum_schema_version'were intended to do the same thing, and they should have bee named the same. But one is a class property and the other is a key in the plugin definition. (InDrupal\Component\Plugin\PluginBase::__construct(),$plugin_definitionis a parameter, assigned to the class property$pluginDefinition.) They are not logically related: the results of yourgrepexperiments show that.Both
SqlBaseandDrupalSqlBaseare base classes, so we have to consider the implications for child classes. Can we simply ignore theminimum_schema_versionkey if it is defined in a derived source plugin? Suppose someone writing a contrib or custom source plugin needed a version constraint. First she read the documentation and used the property$minimum_version. That had no effect. So she looked at the code and added'minimum_schema_version'to the plugin definition. If we adopt this patch, then we are breaking the check needed for that contrib or custom module.I think, if we want to make this change, we have to make sure that either
'minimum_version'or'minimum_schema_version'version will work and throw a deprecation notice in the latter case.We might also want to set
$this->minimum_version = $this->pluginDefinition['minimum_version'] ?? 0;in the constructor and then use the property incheckRequirements(). Or default to-1? This would have the advantage that we would not have to checkisset($this->pluginDefinition['minimum_version'])incheckRequirements().Thanks for the explanation of the multiple changes in this line:
I often ask for shorter lines of code, and this illustrates one of the reasons. When you make three changes to the same line of code, it is hard to review. (I am counting as one change
s/minimum_schema_version/minimum_version/geven though the string appears twice. The other two changes are adding parentheses and changing<to<=.)Can we replace
!(expr1 <= expr2)withexpr 1 > expr2? This is not a trivial question, sincegetModuleSchemaVersion()can returnFALSE. Assuming that the minimum version is an integer (if it is set at all), I think thatFALSEwill be cast to0, so the answer is that we can.Am I confused, or is the inequality backwards? Looking at the test you added, I think I have it right. The test sets a minimum version of
7000and an actual version of7001and expects aRequirementsExceptionto be thrown. This does not make sense.Given all those questions, I consider it in scope for this issue to rewrite this block of code so that it has more, shorter lines that are easier to understand and maintain. The final conditional could be something like
if ($actual_version < $minimum_version). At the same time, I think we can simplify the code by rearranging the cases and exiting early.The two test methods
testMinimumVersionFail()andtestMinimumVersionSuccess()are identical except for some extra blank lines in the second and the last 1 or 3 lines. Can we combine them into one test with a parameter for success/fail? Alternatively, add a helper method that is called by both test methods.Comment #6
benjifisherOn second thought: I care less about backward compatibility (#5.2) given that the current behavior is so badly broken (#5.4).
Comment #7
quietone commentedMe too!
New patch that now has one new test method and some more test cases. Are more needed? Also the if statement is changed to what I think is something much easier to read. And as a bonus there is now a comment. I think that covers everything, since we aren't adding any deprecation, or have I missed something?
I didn't add an interdiff because this is small and the interdiff is larger than the patch.
Comment #8
benjifisherThis should work, and you can leave it like this if you prefer:
But what I was suggesting at the end of #5.4 was something like this (inside enough
ifstatements to make it safe):If we do that, then I think the code is clear enough that we do not need the comment. I think the reason this bug got here in the first place is that the line was so long and confusing that no one noticed the backwards comparison until now. The shorter lines of code also mean that we are unlikely to need an explanation like the one in #2 about multiple changes to one line of code. That is why I describe the shorter lines as "easier to understand and maintain".
There are already two test methods in this class. Now that we are adding a third, we could decide that DRY is in scope. If you would rather defer that to a follow-up issue, we can. I want a follow-up issue anyway to remove the second
namespaceline and put all theusestatements at the top. These lines could be moved to a helper method:Maybe we can even use a
setUp()method, if the plugin definition can be set with a public method.Can we use
MigrateTestCase::getDatabase(), same astestSourceProviderNotActive()?Add a blank line after that block to separate it from the next lines:
We cannot use
{@inheritdoc}here:Comment #9
quietone commentedFor #8
1. Changed.
2. Prefer followup for all that.
3. Fixed
4. Fixed
5. Refactored the test and this is no longer needed. :-) And the patch is a bit smaller.
Comment #10
benjifisher@quietone, thanks for the updates. I think both the test code and the "real" code are easier to read now.
I added a follow-up issue: #3191990: Simplify code in DrupalSqlBaseTest. I will also add a draft change record.
Getting back to #5.1, #5.3: there is no connection between the class property
$minimum_versionand the'mimimum_version'key in the plugin definition. In #5.3, I suggested setting the property in the constructor based on the plugin definition. If we do that, then we can save one test incheckRequirements(). If we do not do that, then maybe we should deprecate the class property, since we do not use it anywhere. Any child class that wants to use it can declare it.Can you upload a test-only patch? I am not sure how to test this issue, so I would like to see the failures in the test-only patch before calling it RTBC.
Comment #11
benjifisherIn #5.1 and #10, I was mixed up:
minimum_versionis declared in the annotation class. I was thinking it was a property ofMigrateSource. I straightened myself out while drafting a CR.Still NW for a test-only patch.
Comment #12
quietone commentedAh, I guess the fail patch in #2 is truly out of date.
New fail patch and re-upload and rename of the patch in #9 to #12. No interdiff since the success patch is the same as the patch in #9. And no interdiff between the fail patch and the success patch since there are only two files involved.
Comment #13
quietone commentedI read the CR and it looks good to me as does the followup.
@benjifisher, Thanks!
Comment #15
benjifisher@quietone:
Sorry, I did not notice this until I was reviewing the failing tests in the test-only patch: we need some
@paramcomments fortestMinimumVersion().Comment #16
quietone commentedNeither did I. How is this?
Comment #17
benjifisherLooks good. Thanks again!
Comment #20
catchCommitted/pushed to 9.2.x and cherry-picked to 9.1.x, thanks!
Comment #22
wim leersPublished CR. Updated CR to say it shipped not only with https://www.drupal.org/project/drupal/releases/9.2.0 but also https://www.drupal.org/project/drupal/releases/9.1.4.