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
[DBAL-630] Incorrect PostgreSQL boolean handling #564
Changes from 10 commits
4736075
0b4a087
63d3e88
a73268d
17e9deb
c3df91d
cf44e4b
038c524
c8ffb84
a328758
f68c998
30589a5
956d5a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,28 @@ class PostgreSqlPlatform extends AbstractPlatform | |
*/ | ||
private $useBooleanTrueFalseStrings = true; | ||
|
||
/** | ||
* @var array PostgreSQL booleans literals | ||
*/ | ||
private $booleanLiterals = array( | ||
'true' => array( | ||
't', | ||
'true', | ||
'y', | ||
'yes', | ||
'on', | ||
'1' | ||
), | ||
'false' => array( | ||
'f', | ||
'false', | ||
'n', | ||
'no', | ||
'off', | ||
'0' | ||
) | ||
); | ||
|
||
/** | ||
* PostgreSQL has different behavior with some drivers | ||
* with regard to how booleans have to be handled. | ||
|
@@ -703,6 +725,60 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options | |
return $sql; | ||
} | ||
|
||
/** | ||
* Converts a single boolean value. | ||
* | ||
* First converts the value to its native PHP boolean type | ||
* and passes it to the given callback function to be reconverted | ||
* into any custom representation. | ||
* | ||
* @param mixed $value The value to convert. | ||
* @param callable $callback The callback function to use for converting the real boolean value. | ||
* | ||
* @return mixed | ||
*/ | ||
private function convertSingleBooleanValue($value, $callback) | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To reduce nested complexity this can be rewritten as:
|
||
if (null === $value) { | ||
return $callback(false); | ||
} | ||
|
||
if (is_bool($value) || is_numeric($value)) { | ||
return $callback($value ? true : false); | ||
} | ||
|
||
if (is_string($value) && in_array(trim(strtolower($value)), $this->booleanLiterals['false'])) { | ||
return $callback(false); | ||
} | ||
|
||
return $callback(true); | ||
} | ||
|
||
/** | ||
* Converts one or multiple boolean values. | ||
* | ||
* First converts the value(s) to their native PHP boolean type | ||
* and passes them to the given callback function to be reconverted | ||
* into any custom representation. | ||
* | ||
* @param $item The value(s) to convert. | ||
* @param $callback The callback function to use for converting the real boolean value(s). | ||
* | ||
* @return mixed | ||
*/ | ||
private function doConvertBooleans($item, $callback) | ||
{ | ||
if (is_array($item)) { | ||
foreach ($item as $key => $value) { | ||
$item[$key] = $this->convertSingleBooleanValue($value, $callback); | ||
} | ||
|
||
return $item; | ||
} | ||
|
||
return $this->convertSingleBooleanValue($item, $callback); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
|
@@ -714,19 +790,29 @@ public function convertBooleans($item) | |
return parent::convertBooleans($item); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're not using TrueFalseStrings, convertBooleans('t') won't convert it to neither 1 nor 0. |
||
if (is_array($item)) { | ||
foreach ($item as $key => $value) { | ||
if (is_bool($value) || is_numeric($item)) { | ||
$item[$key] = ($value) ? 'true' : 'false'; | ||
} | ||
return $this->doConvertBooleans( | ||
$item, | ||
function ($boolean) { | ||
return true === $boolean ? 'true' : 'false'; | ||
} | ||
} else { | ||
if (is_bool($item) || is_numeric($item)) { | ||
$item = ($item) ? 'true' : 'false'; | ||
} | ||
); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function convertBooleansToDatabaseValue($item) | ||
{ | ||
if ( ! $this->useBooleanTrueFalseStrings) { | ||
return parent::convertBooleansToDatabaseValue($item); | ||
} | ||
|
||
return $item; | ||
return $this->doConvertBooleans( | ||
$item, | ||
function ($boolean) { | ||
return (int) $boolean; | ||
} | ||
); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong indentation. You have 4 spaces to much here from below the array opener. Also I wonder if we even need this map. If you look at your current implementation, we basically only DO and NEED TO check for values evaluating to
false
, no? Everything else being converted istrue
anyways. Like if I pass an object or a resource or anything else (stupid but possible) that is notnull
,false
,0
,'f'
,'false'
,'n'
,'no'
,'off'
or'0'
. What do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise if we would strictly stick tho this map we would have to throw exceptions for unexpected values which we cannot do IMO because that might break BC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Smart" indent, sorry.
I'm not sure. Initially, I was checking only for false values and converting everything else to true. But if I'm using Postgres boolean literals for true, I will use one of these six. If I'm inputing a literal for true different than these, I would expect an error.
This is Postgres behavior btw:
As for what do I think: I really think that false should be equal to 0, false or null, true otherwise, like C. Diverging from that you get Ruby awesome 0 equals to true. :) [/troll]