-
Notifications
You must be signed in to change notification settings - Fork 175
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
[Database] modify the _implodeWithKeys() function to allow IS NULL in query #3551
[Database] modify the _implodeWithKeys() function to allow IS NULL in query #3551
Conversation
php/libraries/Database.class.inc
Outdated
if ($item===null || $item==='') { | ||
if ($item===null ) { | ||
$output[] = "`$key` IS NULL"; | ||
} elseif ( $item==='') { |
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.
This seems like it deserves unit tests to make sure it doesn't cause any regressions
php/libraries/Database.class.inc
Outdated
@@ -978,7 +978,9 @@ class Database | |||
} | |||
|
|||
foreach ($dataArray as $key => $item ) { | |||
if ($item===null || $item==='') { | |||
if ($item===null) { |
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.
@driusan correct me if i'm wrong but isn't this function also used to build the $set array for UPDATE statements ?
in that case it would generate UPDATE table_name SET variable1=5, variable 2 IS NULL, variable3...
In which case this is highly non-backwards compatible and can lead to some errors
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.
@ridz1208 @driusan @xlecours You are right about that. It should not be the case now as it was just not permitted before.
Does adding an optional parameter for NULL value, link = null
with possible value 'null' default, 'IS' and 'IS NOT'.
This should be an array of the same size of $param as it is possible that some parameter need to be IS NULL while other migth be IS NOT NULL.
This way we will fix it for every possible situation.
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.
@PapillonMcGill think @ridz1208 is talking about the SET
part an UPDATE
query, not the WHERE
part. The SET
part never needs "IS NULL/IS NOT NULL" while the WHERE
part always does.. shouldn't a single extra bool parameter be sufficient?
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.
@driusan Yes, I could add a parameter $clause = 'set' by default and allow 'where', but if in a where clause we later need IS NOT NULL instead of IS NULL, we will still be stuck. So with an array (NULL by default), we could add for each parameter where we need a comparison with null to be either IS NULL or IS NOT NULL as required and for those parameter where it is not a comparison with null or it is in a SET clause, leave that parameter as null and '=' will be used.
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.
I think we should have an _implodeSetWithKeys()
and an _implodeWhereWithKeys()
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.
There's already 2 calls (one for set, and one for where), so you still don't need an array (but @ridz1208's proposal of 2 private functions with different names also makes more sense than an extra param to me..)... but now that I'm looking at the code, the update path is only using implodeWithKeys for printing the query. It uses _implodeAsPrepared for the actual query that it runs.
(Delete should also be using the prepared variant, I'm not sure why it isn't.)
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.
@driusan using _implodeAsPrepared() will be hard for "IS NULL" or "IS NOT NULL" as PDO do not support that in prepared statements.
So I will have to stick with _implodeWithKeys(). But adding a variable $clause = 'set' in the function definition will not solve the future possibility of "IS NOT NULL". Unless we use a dirty trick like using PHP_EOL constant to convert to IS NOT NULL but that might induce other problem.
* | ||
* @return string the string containing the imploded array | ||
* @access private | ||
*/ | ||
function _implodeWithKeys($glue, $dataArray) | ||
function _implodeWithKeys($glue, $dataArray, $clause = 'set_') |
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.
Since $clause
seems to be used as a boolean, I'd be in favour of naming it $isSetClause
with value true for set clauses and false otherwise.
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.
There is already a variable used as "set_" elsewhere in that module. So I used it the same way. It is also more clear to a humain than 0 or 1, no need to put comment. Of course binary would be (a little bit) faster at execution
php/libraries/Database.class.inc
Outdated
if ($exec_vals !== null) { | ||
$exec_vals["$prefix$varname"] = $item; | ||
$varname = str_replace("%", "_percent_", $key); | ||
if (is_null($item) && $prefix == "where_") { |
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.
Nitpick: maybe rewrite this as
if (is_null($item)) {
$output[] = $prefix == 'where_'
? "`$key` IS NULL" : "`$key` = NULL";
} else {
...
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.
would effectively be more concise
@nicolasbrossard Since I can't implement the $clause variable as you requested without lots of modification to existing code, Could you re-review or dismiss your request to change $clause for a boolean variable? |
@nicolasbrossard Could you re-review? see previous comment. |
in IS NULL in the SQL query minor lint fix addition of unit test modify to show it ONLY delete where field IS NULL modified _implodeAsPrepared to support 'IS NULL' and '= NULL' as _implodeWithKeys modified _implodeWithKeys to correctly handle '= NULL' in SET clause added 'set_' or 'where_' to existing call bug fix change if structure as requested lint fix restore deleted lines by some mistake corrected varname
…_allow_delete_where_IS_NULL
95c9bec
to
8e59f02
Compare
@PapillonMcGill could you update the PR description to include steps for testing? |
@zaliqarosli see the steps in the description. Here is the test script I made. Unit test would fail I it require to temporarily make a private function public. <?php
require_once __DIR__ . "/Database.class.inc";
require_once __DIR__ . "/testConfig.inc";
$array = array('VisitID' => '12', 'Label' => null, 'LegacyLabel' => 'mouain');
$db = new Database();
$db = Database::singleton($database, $username, $password, $host);
$setkey = $db->implodeWithKeys(',', $array, 'set_');
$wherekey = $db->implodeWithKeys(' AND ', $array, 'where_');
$empty = array();
$setPrep = $db->implodeAsPrepared(',', $array, $empty, 'set_');
$wherePrep = $db->implodeAsPrepared(' AND ', $array, $empty, 'where_');
$prep = $db->_PDO->prepare("UPDATE visit SET $setPrep WHERE $wherePrep");
$result = $prep->execute($empty);
$query = $prep->debugDumpParams();
print($query."\n");
print("set:\n$setkey\n$setPrep\n\n");
print("where:\n$wherekey\n$wherePrep\n");
?> Just need to put the DB credential in testConfig.inc file. |
Tested using the supplied script and |
@xlecours Please review this one more carefully than usual. |
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.
The unittest need to be changed but the code is working.
@PapillonMcGill could you add test for insert and replace as well?
@xlecours When you say the code is working, did you test it in all of:
|
added test to update data
@xlecours I implemented your request for update but insert you just don't provide data for an attribute and the regular insert function just won't have to call this code. |
@xlecours Could you re-review? |
I think you are right only when default value is null. |
@driusan Yes.
|
@xlecours rigth, I had not saw that realInsert function is using SET in stead of VALUES() |
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.
Could you add a test for replace
as well?
Even if the new tests are not specifically testing _implodeWithKeys
and _implodeAsPrepared
behaviors, I think that we should at least have tests for higher level functions. Thank you
… query (aces#3551) This modifies the Database class to allow null values to be used in WHERE clauses or as SET values.
This pull request
let IS NULL value in the WHERE clause
. Itconvert null value in $param array to IS NULL in the SQL query
.required for an upcoming PR
Step to test.
create a test file to call implodeWithKeys directly with an associative array containing NULL as a value as no code currently in Loris do it and ensure that the query generated by the prepared statement contain and "IS NULL" for that value. For that, you will need to rename the _implodeWithKeys to implodeWithKeys and make it public.