[ticket/11495] Acquire locks for operations that manipulate the tree

PHPBB3-11495
This commit is contained in:
Joas Schilling 2013-04-18 22:16:14 +02:00
parent 153b29c6c9
commit b28180be1d

View file

@ -20,9 +20,18 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
/** @var phpbb_db_driver*/ /** @var phpbb_db_driver*/
protected $db; protected $db;
/** @var phpbb_lock_db */
protected $lock;
/** @var String */ /** @var String */
protected $table_name; protected $table_name;
/**
* Prefix for the language keys returned by exceptions
* @var String
*/
protected $message_prefix = '';
/** /**
* Column names in the table * Column names in the table
* @var String * @var String
@ -145,6 +154,11 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
return false; return false;
} }
if (!$this->lock->acquire())
{
throw new phpbb_nestedset_exception($this->message_prefix . 'LOCK_FAILED_ACQUIRE');
}
$action = ($delta > 0) ? 'move_up' : 'move_down'; $action = ($delta > 0) ? 'move_up' : 'move_down';
$delta = abs($delta); $delta = abs($delta);
@ -180,6 +194,7 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
if (is_null($target)) if (is_null($target))
{ {
$this->lock->release();
// The item is already on top or bottom // The item is already on top or bottom
return false; return false;
} }
@ -231,6 +246,8 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
" . $this->get_sql_where(); " . $this->get_sql_where();
$this->db->sql_query($sql); $this->db->sql_query($sql);
$this->lock->release();
return true; return true;
} }
@ -260,11 +277,17 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
return false; return false;
} }
if (!$this->lock->acquire())
{
throw new phpbb_nestedset_exception($this->message_prefix . 'LOCK_FAILED_ACQUIRE');
}
$move_items = array_keys($this->get_branch_data($current_parent, 'children', true, false)); $move_items = array_keys($this->get_branch_data($current_parent, 'children', true, false));
if (in_array($new_parent[$this->column_item_id], $move_items)) if (in_array($new_parent[$this->column_item_id], $move_items))
{ {
throw new phpbb_nestedset_exception('INVALID_PARENT'); $this->lock->release();
throw new phpbb_nestedset_exception($this->message_prefix . 'INVALID_PARENT');
} }
$diff = sizeof($move_items) * 2; $diff = sizeof($move_items) * 2;
@ -272,7 +295,7 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
$this->db->sql_transaction('begin'); $this->db->sql_transaction('begin');
$this->remove_subset($move_items, $current_parent, false); $this->remove_subset($move_items, $current_parent, false, true);
if ($new_parent[$this->column_item_id]) if ($new_parent[$this->column_item_id])
{ {
@ -287,10 +310,11 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
if (!$new_parent) if (!$new_parent)
{ {
$this->db->sql_transaction('rollback'); $this->db->sql_transaction('rollback');
throw new phpbb_nestedset_exception('INVALID_PARENT'); $this->lock->release();
throw new phpbb_nestedset_exception($this->message_prefix . 'INVALID_PARENT');
} }
$new_right_id = $this->prepare_adding_subset($move_items, $new_parent); $new_right_id = $this->prepare_adding_subset($move_items, $new_parent, true);
if ($new_right_id > $current_parent[$this->column_right_id]) if ($new_right_id > $current_parent[$this->column_right_id])
{ {
@ -324,6 +348,7 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
$this->db->sql_query($sql); $this->db->sql_query($sql);
$this->db->sql_transaction('commit'); $this->db->sql_transaction('commit');
$this->lock->release();
return true; return true;
} }
@ -333,11 +358,17 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
*/ */
public function set_parent(array $item, array $new_parent) public function set_parent(array $item, array $new_parent)
{ {
if (!$this->lock->acquire())
{
throw new phpbb_nestedset_exception($this->message_prefix . 'LOCK_FAILED_ACQUIRE');
}
$move_items = array_keys($this->get_branch_data($item, 'children')); $move_items = array_keys($this->get_branch_data($item, 'children'));
if (in_array($new_parent[$this->column_item_id], $move_items)) if (in_array($new_parent[$this->column_item_id], $move_items))
{ {
throw new phpbb_nestedset_exception('INVALID_PARENT'); $this->lock->release();
throw new phpbb_nestedset_exception($this->message_prefix . 'INVALID_PARENT');
} }
$diff = sizeof($move_items) * 2; $diff = sizeof($move_items) * 2;
@ -345,7 +376,7 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
$this->db->sql_transaction('begin'); $this->db->sql_transaction('begin');
$this->remove_subset($move_items, $item, false); $this->remove_subset($move_items, $item, false, true);
if ($new_parent[$this->column_item_id]) if ($new_parent[$this->column_item_id])
{ {
@ -360,10 +391,11 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
if (!$new_parent) if (!$new_parent)
{ {
$this->db->sql_transaction('rollback'); $this->db->sql_transaction('rollback');
throw new phpbb_nestedset_exception('INVALID_PARENT'); $this->lock->release();
throw new phpbb_nestedset_exception($this->message_prefix . 'INVALID_PARENT');
} }
$new_right_id = $this->prepare_adding_subset($move_items, $new_parent); $new_right_id = $this->prepare_adding_subset($move_items, $new_parent, true);
if ($new_right_id > (int) $item[$this->column_right_id]) if ($new_right_id > (int) $item[$this->column_right_id])
{ {
@ -397,6 +429,7 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
$this->db->sql_query($sql); $this->db->sql_query($sql);
$this->db->sql_transaction('commit'); $this->db->sql_transaction('commit');
$this->lock->release();
return true; return true;
} }
@ -497,10 +530,16 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
* @param array $subset_items Subset of items to remove * @param array $subset_items Subset of items to remove
* @param array $bounding_item Item containing the right bound of the subset * @param array $bounding_item Item containing the right bound of the subset
* @param bool $set_subset_zero Should the parent, left and right id of the item be set to 0, or kept unchanged? * @param bool $set_subset_zero Should the parent, left and right id of the item be set to 0, or kept unchanged?
* @param bool $table_already_locked Is the table already locked, or should we acquire a new lock?
* @return null * @return null
*/ */
protected function remove_subset(array $subset_items, array $bounding_item, $set_subset_zero = true) protected function remove_subset(array $subset_items, array $bounding_item, $set_subset_zero = true, $table_already_locked = false)
{ {
if (!$table_already_locked && !$this->lock->acquire())
{
throw new phpbb_nestedset_exception($this->message_prefix . 'LOCK_FAILED_ACQUIRE');
}
$diff = sizeof($subset_items) * 2; $diff = sizeof($subset_items) * 2;
$sql_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items); $sql_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items);
$sql_not_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items, true); $sql_not_subset_items = $this->db->sql_in_set($this->column_item_id, $subset_items, true);
@ -526,6 +565,11 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
' . $this->column_item_parents . " = '' ' . $this->column_item_parents . " = ''
" . ((!$set_subset_zero) ? ' WHERE ' . $sql_not_subset_items . ' ' . $this->get_sql_where('AND') : $this->get_sql_where('WHERE')); " . ((!$set_subset_zero) ? ' WHERE ' . $sql_not_subset_items . ' ' . $this->get_sql_where('AND') : $this->get_sql_where('WHERE'));
$this->db->sql_query($sql); $this->db->sql_query($sql);
if (!$table_already_locked)
{
$this->lock->release();
}
} }
/** /**
@ -581,6 +625,12 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
{ {
if ($reset_ids) if ($reset_ids)
{ {
if (!$this->lock->acquire())
{
throw new phpbb_nestedset_exception($this->message_prefix . 'LOCK_FAILED_ACQUIRE');
}
$this->db->sql_transaction('begin');
$sql = 'UPDATE ' . $this->table_name . ' $sql = 'UPDATE ' . $this->table_name . '
SET ' . $this->db->sql_build_array('UPDATE', array( SET ' . $this->db->sql_build_array('UPDATE', array(
$this->column_left_id => 0, $this->column_left_id => 0,
@ -627,6 +677,13 @@ abstract class phpbb_nestedset_base implements phpbb_nestedset_interface
} }
$this->db->sql_freeresult($result); $this->db->sql_freeresult($result);
if ($reset_ids)
{
$this->db->sql_transaction('commit');
$this->lock->release();
}
return $new_id; return $new_id;
} }
} }