Skip to content

Commit

Permalink
Backport changes from #3959 to 1.3
Browse files Browse the repository at this point in the history
Backport race condition fixes from 2.x to 1.3. This is a severe enough
issue that it warrants a fix.

Refs #3857
  • Loading branch information
markstory committed Jul 14, 2014
1 parent 319fcbf commit 41082b1
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cake/libs/model/datasources/dbo_source.php
Expand Up @@ -1879,7 +1879,7 @@ function defaultConditions(&$model, $conditions, $useAlias = true) {
return $conditions; return $conditions;
} }
$exists = $model->exists(); $exists = $model->exists();
if (!$exists && $conditions !== null) { if (!$exists && ($conditions !== null || !empty($model->__safeUpdateMode))) {
return false; return false;
} elseif (!$exists) { } elseif (!$exists) {
return null; return null;
Expand Down
11 changes: 11 additions & 0 deletions cake/libs/model/model.php
Expand Up @@ -357,6 +357,15 @@ class Model extends Overloadable {
*/ */
var $__affectedRows = null; var $__affectedRows = null;


/**
* Safe update mode
*
* If true, this prevents Model::save() from generating a query with WHERE 1 = 1 on race condition.
*
* @var bool
*/
var $__safeUpdateMode = false;

/** /**
* List of valid finder method options, supplied as the first parameter to find(). * List of valid finder method options, supplied as the first parameter to find().
* *
Expand Down Expand Up @@ -1324,7 +1333,9 @@ function save($data = null, $validate = true, $fieldList = array()) {
$cache = $this->_prepareUpdateFields(array_combine($fields, $values)); $cache = $this->_prepareUpdateFields(array_combine($fields, $values));


if (!empty($this->id)) { if (!empty($this->id)) {
$this->__safeUpdateMode = true;
$success = (bool)$db->update($this, $fields, $values); $success = (bool)$db->update($this, $fields, $values);
$this->__safeUpdateMode = false;
} else { } else {
$fInfo = $this->_schema[$this->primaryKey]; $fInfo = $this->_schema[$this->primaryKey];
$isUUID = ($fInfo['length'] == 36 && $isUUID = ($fInfo['length'] == 36 &&
Expand Down
42 changes: 42 additions & 0 deletions cake/tests/cases/libs/model/datasources/dbo_source.test.php
Expand Up @@ -4675,4 +4675,46 @@ function testFieldsUsingMethodCache() {
$this->testDb->fields($Article, null, array('title', 'body', 'published')); $this->testDb->fields($Article, null, array('title', 'body', 'published'));
$this->assertTrue(empty($this->testDb->methodCache['fields']), 'Cache not empty'); $this->assertTrue(empty($this->testDb->methodCache['fields']), 'Cache not empty');
} }

/**
* Test defaultConditions()
*
* @return void
*/
public function testDefaultConditions() {
$this->loadFixtures('Article');
$Article = ClassRegistry::init('Article');
$db = $Article->getDataSource();

// Creates a default set of conditions from the model if $conditions is null/empty.
$Article->id = 1;
$result = $db->defaultConditions($Article, null);
$this->assertEqual(array('Article.id' => 1), $result);

// $useAlias == false
$Article->id = 1;
$result = $db->defaultConditions($Article, null, false);
$this->assertEqual(array($db->fullTableName($Article, false) . '.id' => 1), $result);

// If conditions are supplied then they will be returned.
$Article->id = 1;
$result = $db->defaultConditions($Article, array('Article.title' => 'First article'));
$this->assertEqual(array('Article.title' => 'First article'), $result);

// If a model doesn't exist and no conditions were provided either null or false will be returned based on what was input.
$Article->id = 1000000;
$result = $db->defaultConditions($Article, null);
$this->assertNull($result);

$Article->id = 1000000;
$result = $db->defaultConditions($Article, false);
$this->assertFalse($result);

// Safe update mode
$Article->id = 1000000;
$Article->__safeUpdateMode = true;
$result = $db->defaultConditions($Article, null);
$this->assertFalse($result);
}

} }

0 comments on commit 41082b1

Please sign in to comment.