Skip to content

[Messenger] use database platform to convert correctly the DateTime #32456

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

Merged
merged 1 commit into from
Oct 25, 2019
Merged

[Messenger] use database platform to convert correctly the DateTime #32456

merged 1 commit into from
Oct 25, 2019

Conversation

roukmoute
Copy link
Contributor

Q A
Branch? 4.3
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets #32427
License MIT

In Doctrine Messenger the method \Symfony\Component\Messenger\Transport\Doctrine\Connection::formatDateTime() is used to format dateTime into this: Y-m-d\TH:i:s.
But this is not supported in all databases platform.

Here we use the database platform to convert correctly the dateTime.

':created_at' => self::formatDateTime($now),
':available_at' => self::formatDateTime($availableAt),
':created_at' => $this->convertDateTime($now),
':available_at' => $this->convertDateTime($availableAt),
Copy link
Member

Choose a reason for hiding this comment

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

We could even use the datetime directly as parameter and pass the Doctrine DBAL types (in the third argument) for these, so that DBAL handles the conversion calls for us (and would avoid the need to create a public convertDateTime too, as tests can do the same)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I don't understand.
Because in this file, the executeQuery method is not the same method of https://github.com/doctrine/dbal/blob/66f28111d57fdc3508d4c97989257291702176cf/lib/Doctrine/DBAL/Connection.php#L883.

Copy link
Member

Choose a reason for hiding this comment

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

it is the same. The driverConnection propert contains a Doctrine\DBAL\Connection, which is exactly the code I linked.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think @stof means we could use \Doctrine\DBAL\Connection::executeQuery which has the types as parameter.

*
* @deprecated since Symfony 4.4, use convertDateTime instead
*/
public static function formatDateTime(\DateTimeInterface $dateTime, AbstractPlatform $platform)
Copy link
Member

Choose a reason for hiding this comment

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

adding a required argument is a BC break, which is a bad idea for a method kept as a BC layer...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, I will add a new commit to fix this.

*
* @deprecated since Symfony 4.4, use convertDateTime instead
*/
public static function formatDateTime(\DateTimeInterface $dateTime, AbstractPlatform $platform = null)
Copy link
Member

Choose a reason for hiding this comment

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

You've added a second parameter to the method, but I can find any calls with that parameter being set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See my commits in order and you will understand my logic :)

Copy link
Member

Choose a reason for hiding this comment

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

that's a BC break, can't be done

Copy link
Member

Choose a reason for hiding this comment

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

it is not a question of commits in order. The existing method that you want to deprecate must not change signature.

Copy link
Member

Choose a reason for hiding this comment

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

you should remove the new argument and always use $dateTime->format('Y-m-d\TH:i:s') in this deprecated method. Your current change still requires changing the caller to add the platform to benefit from the fix, at which point it is as easy to switch to the new method instead.

@nicolas-grekas nicolas-grekas changed the title [Messenger] - use database platform to convert correctly the DateTime [Messenger] use database platform to convert correctly the DateTime Jul 10, 2019
@nicolas-grekas nicolas-grekas added this to the 4.3 milestone Jul 10, 2019
/**
* @throws DBALException
*
* @deprecated since Symfony 4.4, use convertDateTime instead
Copy link
Member

Choose a reason for hiding this comment

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

the deprecation must be done in a separate PR, targetting 4.4 (this PR is for 4.3)

*
* @deprecated since Symfony 4.4, use convertDateTime instead
*/
public static function formatDateTime(\DateTimeInterface $dateTime, AbstractPlatform $platform = null)
Copy link
Member

Choose a reason for hiding this comment

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

you should remove the new argument and always use $dateTime->format('Y-m-d\TH:i:s') in this deprecated method. Your current change still requires changing the caller to add the platform to benefit from the fix, at which point it is as easy to switch to the new method instead.

}

/**
* @throws DBALException
Copy link
Member

Choose a reason for hiding this comment

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

should be removed. We don't care about this one here (and anyway, this API should not be public, so this would be useless)

Copy link
Contributor

@Tobion Tobion left a comment

Choose a reason for hiding this comment

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

#32456 (comment)
@roukmoute do you have time to finish the PR?

@roukmoute
Copy link
Contributor Author

roukmoute commented Jul 26, 2019

yep, I've finally received my laptop today so I can fix it this week-end

@roukmoute
Copy link
Contributor Author

@Tobion Any news?


private function getDateTimeType()
{
if (!class_exists('\Doctrine\DBAL\Types\Types')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

please use \Doctrine\DBAL\Types\Types::class

':redeliver_limit' => self::formatDateTime($redeliverLimit),
':redeliver_limit' => $redeliverLimit,
], [
':now' => Type::getType($this->getDateTimeType()),
Copy link
Contributor

Choose a reason for hiding this comment

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

do you really need to pass the type instance here with Type::getType? I don't think so. Passing the constant value should be enough.

@@ -281,22 +290,17 @@ private function createQueryBuilder(): QueryBuilder
->from($this->configuration['table_name'], 'm');
}

private function executeQuery(string $sql, array $parameters = [])
private function executeQuery(string $sql, array $parameters = [], ?array $types = [])
Copy link
Contributor

Choose a reason for hiding this comment

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

why does $types need to be nullable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without that, few tests does not passes.

1) Symfony\Component\Messenger\Tests\Transport\Doctrine\ConnectionTest::testGetAMessageWillChangeItsStatus
TypeError: Argument 3 passed to Symfony\Component\Messenger\Transport\Doctrine\Connection::executeQuery() must be of the type array, null given, called in /home/roukmoute/workspace/symfony/src/Symfony/Component/Messenger/Transport/Doctrine/Connection.php on line 150

Would you prefer I change theses tests to pass without this nullable?

Copy link
Contributor

Choose a reason for hiding this comment

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

The mocks probably need to be fixed so it's does not return a null which violates the contract.

@@ -306,20 +314,20 @@ private function getSchema(): Schema
{
$schema = new Schema([], [], $this->driverConnection->getSchemaManager()->createSchemaConfig());
$table = $schema->createTable($this->configuration['table_name']);
$table->addColumn('id', Type::BIGINT)
$table->addColumn('id', Types::BIGINT)
Copy link
Contributor

Choose a reason for hiding this comment

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

you cannot require Types as it's not released in dbal yet. please just keep using Type everywhere. we can handle the deprecation of it in the future when it's actually released.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right!
Would like a method equivalent with checking if the class exist?

Copy link
Contributor

Choose a reason for hiding this comment

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

only use Type in this PR please. feel free to create a second PR with a solution for the future deprecation

Copy link
Contributor

Choose a reason for hiding this comment

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

It's still not consistent. Here you implemented the fallback

private function getDateTimeType()
{
if (!class_exists(\Doctrine\DBAL\Types\Types::class)) {
return Type::DATETIME;
}
return \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE;
but
$table->addColumn('created_at', Type::DATETIME)
does not use it. Either use the getDateTimeType helper everywhere, or don't add it at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I asked to not use the new Types class because in order to fully fix the deprcation of Type, you would also need to add the fallback of the other types like "text" in

$table->addColumn('headers', Type::TEXT)
which you did not. So it's not complete and irrelevant to this PR.

@@ -108,7 +108,7 @@ public function send(string $body, array $headers, int $delay = 0): string
$now = new \DateTime();
$availableAt = (clone $now)->modify(sprintf('+%d seconds', $delay / 1000));

$queryBuilder = $this->driverConnection->createQueryBuilder()
$queryBuilder = $this->createQueryBuilder()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why changing this? It's doing an insert, so the helper method seems useless here.

null,
null,
null,
Type::getType($this->getDateTimeType()),
Copy link
Contributor

Choose a reason for hiding this comment

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

you don't need to call Type::getType. it should be enough to pass the string constant like it's done in other places

return Type::DATETIME;
}

return \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE;
Copy link
Member

Choose a reason for hiding this comment

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

please add a use statement

@roukmoute
Copy link
Contributor Author

Ping @Tobion @stof

@@ -280,12 +292,11 @@ private function createQueryBuilder(): QueryBuilder
->from($this->configuration['table_name'], 'm');
}

private function executeQuery(string $sql, array $parameters = [])
private function executeQuery(string $sql, array $parameters = [], array $types = [])
{
$stmt = null;
Copy link
Contributor

@Tobion Tobion Oct 23, 2019

Choose a reason for hiding this comment

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

not needed anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you sure?
image

Copy link
Contributor

Choose a reason for hiding this comment

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

I commented on the line $stmt = null;. The initialization is not needed anymore because it's always set.

null,
null,
null,
Type::getType(Type::DATETIME),
Copy link
Contributor

Choose a reason for hiding this comment

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

In #32456 (comment) I asked to only pass the constant. No need to call Type::getType anywhere. Please it's very painful to review this PR over and over and the same problems keep being there.

Copy link
Contributor

@Tobion Tobion left a comment

Choose a reason for hiding this comment

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

Please squash all commits to a single commit. Our tool cannot do it because of different commiters.

@Tobion
Copy link
Contributor

Tobion commented Oct 25, 2019

... and one more bug fixed, thank you @roukmoute.

Tobion added a commit that referenced this pull request Oct 25, 2019
… DateTime (roukmoute)

This PR was merged into the 4.3 branch.

Discussion
----------

[Messenger] use database platform to convert correctly the DateTime

| Q             | A
| ------------- | ---
| Branch?       | 4.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass?   | yes
| Fixed tickets | #32427
| License       | MIT

In Doctrine Messenger the method `\Symfony\Component\Messenger\Transport\Doctrine\Connection::formatDateTime()` is used to format dateTime into this: `Y-m-d\TH:i:s`.
But this is not supported in all databases platform.

Here we use the database platform to convert correctly the dateTime.

Commits
-------

cfa1156 Format DateTime depending on database platform
@Tobion Tobion merged commit cfa1156 into symfony:4.3 Oct 25, 2019
@roukmoute
Copy link
Contributor Author

Thanks for all your time @Tobion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants