Skip to content

Commit

Permalink
Allow DELETE queries with multiple tables
Browse files Browse the repository at this point in the history
This PR permits a DELETE query to include JOINs on multiple tables, using Joomla's query classes.

Previously, you could not execute the following:

DELETE a, b, c, d
FROM #__table_a AS a
LEFT JOIN #__table_b AS b ON a.id=b.table_a_id
LEFT JOIN #__table_c AS c ON a.id=c.table_a_id
LEFT JOIN #__table_d AS d ON a.id=d.table_a_id
WHERE a.id = 15

This works fine when setting the above to a string $sql and calling $db->setQuery($sql), but when trying to use the query classes such as this:

$query->delete('a')
   ->delete('b')
   ->delete('c')
   ->delete('d');
$query->from($db->qn(#__table_a', 'a'))
   ->leftJoin($db->qn('#__table_b', 'b') . ' ON a.id = b.table_a_id')
   ->leftJoin($db->qn('#__table_c', 'c') . ' ON a.id = c.table_a_id')
   ->leftJoin($db->qn('#__table_d', 'd') . ' ON a.id = d.table_a_id');
$query->where($db->qn('a.id') . ' = ' . (int) $pk['id']);

there is a problem in that the ->delete() class adds the DELETE element to the query, and also the table name to the FROM element, so its not possible to add joins. The query will only execute properly if there is one and only one table in the query. You end up with sql looking like this:

DELETE d
FROM a, b, c, d, #__table_a AS a
LEFT JOIN #__table_b AS b ON a.id=b.table_a_id
LEFT JOIN #__table_c AS c ON a.id=c.table_a_id
LEFT JOIN #__table_d AS d ON a.id=d.table_a_id
WHERE a.id = 15
which won't work. There are other ways to structure the query calls, but all of them seem to end up with invalid sql.

This change modifies the query->delete() to the following:

public function delete($table = null, $addFrom = true)
{
    $this->type = 'delete';
    if(is_null($this->delete))
    {
        $this->delete = new JDatabaseQueryElement('DELETE', $table);
    }
    else
    {
        $this->delete->append($table);
    }

    if (!empty($table) && $addFrom)
    {
        $this->from($table);
    }

    return $this;
}

Then you can build a multitable query like this:

$query->delete('a', false)
      ->delete('b', false)
      ->delete('c', false)
      ->delete('d', false);

$query->from($db->qn(#__table_a', 'a'))
      ->leftJoin($db->qn('#__table_b', 'b') . ' ON a.id = b.table_a_id')
      ->leftJoin($db->qn('#__table_c', 'c') . ' ON a.id = c.table_a_id')
      ->leftJoin($db->qn('#__table_d', 'd') . ' ON a.id = d.table_a_id');

$query->where($db->qn('a.id') . ' = ' . (int) $pk['id']);

As an added bonus it does not appear to have any negative side effects on the rest of the codebase.

Two alternatives would be to just use the sql string without all the query classes, or enforcing referential integrity on the database and using a cascading delete. Neither seem as good a solution to me.
  • Loading branch information
gaelicwinter committed Oct 27, 2016
1 parent 27d8418 commit af7af8b
Showing 1 changed file with 21 additions and 13 deletions.
34 changes: 21 additions & 13 deletions libraries/joomla/database/query.php
Original file line number Diff line number Diff line change
Expand Up @@ -683,24 +683,32 @@ public function dump()
* Usage:
* $query->delete('#__a')->where('id = 1');
*
* @param string $table The name of the table to delete from.
* @param string $table The name of the table to delete from.
* @param boolean $addFrom If true (default), also add $table to the FROM clause
*
* @return JDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function delete($table = null)
{
$this->type = 'delete';
$this->delete = new JDatabaseQueryElement('DELETE', null);

if (!empty($table))
{
$this->from($table);
}

return $this;
}
public function delete($table = null, $addFrom = true)
{
$this->type = 'delete';
if(is_null($this->delete))
{
$this->delete = new JDatabaseQueryElement('DELETE', $table);
}
else
{
$this->delete->append($table);
}

if (!empty($table) && $addFrom)
{
$this->from($table);
}

return $this;
}

/**
* Method to escape a string for usage in an SQL statement.
Expand Down

0 comments on commit af7af8b

Please sign in to comment.