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

[DDC-2224] convertToDatabaseValueSQL() is not honored for DQL query parameters #574

Closed
wants to merge 2 commits into from

Conversation

mnapoli
Copy link
Contributor

@mnapoli mnapoli commented Feb 8, 2013

DDC-2224: convertToDatabaseValueSQL() is not honored for DQL query parameters

Fix + test

Please note this is the first time I get into SqlWalker, I hope I inserted the fix at the right place.

@doctrinebot
Copy link

Hello,

thank you for positing this Pull Request. I have automatically opened an issue on our Jira Bug Tracker for you with the details of this Pull-Request. See the Link:

http://doctrine-project.org/jira/browse/DDC-2240

@@ -2072,6 +2072,12 @@ public function walkInputParameter($inputParam)
{
$this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++);

$parameter = $this->query->getParameter($inputParam->name);
if ($parameter && Type::hasType($parameter->getType())) {
Copy link
Member

Choose a reason for hiding this comment

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

Missing newline before this line

Copy link
Member

Choose a reason for hiding this comment

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

Also: this would fail on unknown types anyway. Is the check for Type::hasType necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hasType does isset(self::$_typesMap[$name]) whereas getType throw DBALException::unknownColumnType($name) if it's not found, so I guess the check is necessary and it will not fail if the type is not found.

Copy link
Member

Choose a reason for hiding this comment

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

My point is that a failure on not found type is acceptable, but binding allows custom parameter type anyway... guess you're right :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK I get it. Yes it's possible a user specify a wrong type in setParameter($key, $value, $type), in that case is it better to silently ignore the type given (actual) or have an error of type not found (if the check was removed)?

I'd go for the second actually.

@BenMorel
Copy link
Contributor

BenMorel commented Feb 8, 2013

Works like a charm for me, thanks!

@@ -2072,6 +2072,13 @@ public function walkInputParameter($inputParam)
{
$this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++);

$parameter = $this->query->getParameter($inputParam->name);

if ($parameter && Type::hasType($parameter->getType())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would advocate to remove the hasType() test as well.
IMO, failing silently in the case of an unknown type is a bad idea!

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

@Ocramius I can't see why, can you explain?

Copy link
Member

Choose a reason for hiding this comment

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

There are no db types 101 and 102

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, you're right! I had not realized that you could use non-Type types in the ORM as well (obviously....) so hasType() is necessary.
However, it would be good to avoid failing silently if we pass an invalid type string. Maybe the following code would solve the problem:

if (is_string($parameter)) {
    $parameterType = Type::getType($parameter->getType());

That would filter out all PDO_PARAM_* and Connection::PARAM_* integer values, as well as the NULL value when the parameter does not exist.

By the way, should we accept getParameter() to be NULL as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

I've double-checked the code, and it can only return null if the parameter name does not exist for this query, which is an error IMO. So I'd change the code for:

$parameter = $this->query->getParameter($inputParam->name);

if (! $parameter) {
    throw QueryException::unknownParameter($inputParam->name);
}

if (is_string($parameter->getType())) {
    $parameterType = Type::getType($parameter->getType());
    return $parameterType->convertToDatabaseValueSQL('?', $this->platform);
}

This code has an extra consequence then:
If your DQL contains a parameter such as :name, and you don't call ->setParameter('name', ...), it will throw a QueryException. Right now, this is not the case. @beberlei , could you please comment on this?

Copy link
Member

Choose a reason for hiding this comment

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

@BenMorel this is fine, just moving from PDOException to QueryException here

Copy link
Contributor

Choose a reason for hiding this comment

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

@beberlei Should @mnapoli update his PR with the code above?

Copy link
Member

Choose a reason for hiding this comment

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

yes your code is better for this, i like it very much.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@BenMorel, @beberlei Noted, I'll update the PR

@beberlei
Copy link
Member

The problem with this change is, that its breaking backwards compatibility, currently the following code is valid for a "datetime" field:

$query->setParameter(1, "2012-01-01");

In the new approach it would lead to a fatal error, because the datetime Type expects a DateTime instance.

I am wondering if the check should only transform if the value is an object.

@mnapoli
Copy link
Contributor Author

mnapoli commented Feb 13, 2013

@beberlei Don't you mean this?

$query->setParameter(1, "2012-01-01", "datetime");

If the parameter type is not set (like in the example you gave), then the parameter type inferred will be "string" (cf. ParameterTypeInferer::inferType). So it shouldn't be a problem right?

@BenMorel
Copy link
Contributor

@beberlei Yes, with the example you gave, the type will actually be PDO::PARAM_STR, so will be filtered out by the code above.

@beberlei
Copy link
Member

beberlei commented Apr 1, 2013

@BenMorel @mnapoli not sure what my head was thinking here, this should work.

@beberlei
Copy link
Member

beberlei commented Apr 4, 2013

Edit: Wrong not merged into master.

There is a problem with this patch and caching, which makes it unmergable:

Passing a type into the parameters is not recognized during caching, that means, using a DQL cache, the same DQL statement with:

$query->setParameter(1, "foo", "sometype"); // or
$query->setParameter(1, "foo");

will lead to the same SQL being generated, depending on if the first or the second set parameter query is executed first.

@beberlei
Copy link
Member

beberlei commented Apr 4, 2013

The conversion here has to be done in side the Query object, not inside SQLWalker

@beberlei
Copy link
Member

beberlei commented Apr 4, 2013

Just verified in the code, there is no way to fix this issue, you need to create a custom DQL function to allow this converting to work.

@beberlei beberlei closed this Apr 4, 2013
@BenMorel
Copy link
Contributor

BenMorel commented Apr 4, 2013

@beberlei So we're just abandoning like that..?

@beberlei
Copy link
Member

beberlei commented Apr 4, 2013

@BenMorel you can convert the values before putting them to setParameter(). You have the right context information to do so.

@mnapoli
Copy link
Contributor Author

mnapoli commented Apr 4, 2013

+1 I really see that as a bug because it's really not the expected behavior for the end-user.

Juste to be clear I'm not complaining about the fact that there's a bug, but the fact that it's closed as "Invalid"

@BenMorel
Copy link
Contributor

BenMorel commented Apr 4, 2013

@beberlei It's not that easy, sometimes you rely on SQL functions such as geometry functions that are hard (and useless) to implement in PHP, and as @mnapoli pointed out, the current behaviour is not the one expected by the user of the ORM (the type behaves differently when persisting an entity as opposed to when querying entities with DQL).

I must support the fact that it's a bit sad to close this is as Invalid, instead of leaving it open for further thought.

@beberlei
Copy link
Member

beberlei commented Apr 4, 2013

The assumptions in this PR are completely wrong, how can i leave it open for thought?

While looking at this issue i realized why it wasnt this way beforehand and this is because the context is unknown here for the SqlWalker, there is knowing if we want to convet or not.

Additionally to setParameter() yoiu can also use a DQL function to do the conversion:

SELECT m FROM Foo m WHERE m.id = CONVERT(?)

@BenMorel
Copy link
Contributor

BenMorel commented Apr 4, 2013

I was talking about the issue, not this PR in particular (I agree this wasn't the best place to talk about this, then).

Because it's not feasible with how the ORM is currently architectured doesn't mean it's impossible, does it? My point is, leaving it open reminds us that this is an issue, and if someone rethinks the architecture in the future, he'll have that in mind as well to see if his new ideas fit all the needs (I know that there are other missing features in Doctrine such as value objects, and I suspect that these might bring similar issues).

@mnapoli
Copy link
Contributor Author

mnapoli commented Apr 5, 2013

I was talking about the issue, not this PR in particular (I agree this wasn't the best place to talk about this, then).

same for me, no problem with rejecting the PR (I didn't want to pollute too much the Jira issue with meta-discussions)

@beberlei
Copy link
Member

beberlei commented Apr 5, 2013

I have a solution that could help. We could add a generic DQL function CONVERT(expression, type), so that you can do for example:

SELECT m FROM Foo m WHERE m.value = CONVERT(?, "datetime")

This would then wrap around expression the convertToDatabaseValueSQL() result.

Would that help you two?

@BenMorel
Copy link
Contributor

BenMorel commented Apr 5, 2013

TBH, I have already changed my code to not rely on this functionality anymore (my type doesn't need convertToDatabaseValueSQL() anymore), so I'd personally leave it as it is, until the next big rewrite of Doctrine (if any). Again, to me, the only "correct" solution is to have the SQL function automatically inserted.
But maybe @mnapoli is interested by this solution!

@mnapoli
Copy link
Contributor Author

mnapoli commented Apr 5, 2013

I'm not sure that would help much too, the main problem I have is that it's not symmetrical between entity insertion and querying, so that wouldn't help with that if I understand correctly.

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

Successfully merging this pull request may close these issues.

5 participants