Skip to content

Commit 04c8f14

Browse files
committed
Merge branch '2017.12' into 2018.09
2 parents 68fb0f3 + 8e58bc6 commit 04c8f14

File tree

5 files changed

+158
-11
lines changed

5 files changed

+158
-11
lines changed

kernel/classes/ezcontentobject.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,59 @@ static function fetch( $id, $asObject = true )
914914
}
915915
}
916916

917+
/**
918+
* Fetches a content object by ID, using SELECT ... FOR UPDATE
919+
*
920+
* Ensures the table row is locked, blocking other transactions from locking it (read or write).
921+
* Usage must be within a transaction, or it will be locked for the current session.
922+
*
923+
* @param int $id ID of the content object to fetch
924+
* @param bool $asObject Return the result as an object (true) or an assoc. array (false)
925+
*
926+
* @return eZContentObject|mixed|null
927+
*/
928+
static function fetchForUpdate( $id, $asObject = true )
929+
{
930+
global $eZContentObjectContentObjectCache;
931+
932+
$db = eZDB::instance();
933+
$id = (int) $id;
934+
935+
// Select for update, to lock the row
936+
$resArray = $db->arrayQuery( "SELECT * FROM ezcontentobject WHERE id='$id' FOR UPDATE" );
937+
938+
if ( !is_array( $resArray ) || count( $resArray ) !== 1 )
939+
{
940+
eZDebug::writeError( "Object not found ($id)", __METHOD__ );
941+
return null;
942+
}
943+
944+
$objectArray = $resArray[0];
945+
$classId = $objectArray['contentclass_id'];
946+
$contentClassResArray = $db->arrayQuery(
947+
"SELECT
948+
ezcontentclass.serialized_name_list as serialized_name_list,
949+
ezcontentclass.identifier as contentclass_identifier,
950+
ezcontentclass.is_container as is_container
951+
FROM
952+
ezcontentclass
953+
WHERE
954+
ezcontentclass.id='$classId' AND
955+
ezcontentclass.version=0"
956+
);
957+
$objectArray = array_merge($objectArray, $contentClassResArray[0]);
958+
959+
if ( !$asObject )
960+
{
961+
return $objectArray;
962+
}
963+
964+
$obj = new eZContentObject( $objectArray );
965+
$eZContentObjectContentObjectCache[$id] = $obj;
966+
967+
return $obj;
968+
}
969+
917970
/**
918971
* Returns true, if a content object with the ID $id exists, false otherwise
919972
*

kernel/classes/ezcontentobjectversion.php

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,47 @@ static function fetchVersion( $version, $contentObjectID, $asObject = true )
165165
return isset( $ret[0] ) ? $ret[0] : false;
166166
}
167167

168+
static function fetchVersionForUpdate( $version, $contentObjectID, $asObject = true )
169+
{
170+
global $eZContentObjectVersionCache;
171+
172+
$db = eZDB::instance();
173+
$version = (int) $version;
174+
$contentObjectID = (int) $contentObjectID;
175+
176+
// Select for update, to lock the row
177+
$resArray = $db->arrayQuery(
178+
"SELECT * FROM
179+
ezcontentobject_version
180+
WHERE
181+
version='$version' AND
182+
contentobject_id='$contentObjectID'
183+
FOR UPDATE"
184+
);
185+
186+
if ( !is_array( $resArray ) || count( $resArray ) !== 1 )
187+
{
188+
eZDebug::writeError( "Version '$version' for content '$contentObjectID' not found", __METHOD__ );
189+
return null;
190+
}
191+
192+
if ( !isset( $resArray[0] ) )
193+
{
194+
return false;
195+
}
196+
$versionArray = $resArray[0];
197+
198+
if ( !$asObject )
199+
{
200+
return $versionArray;
201+
}
202+
203+
$versionObject = new eZContentObjectVersion( $versionArray );
204+
$eZContentObjectVersionCache[$contentObjectID][$version] = $versionObject;
205+
206+
return $versionObject;
207+
}
208+
168209
static function fetchUserDraft( $objectID, $userID )
169210
{
170211
$versions = eZPersistentObject::fetchObjectList( eZContentObjectVersion::definition(),
@@ -913,6 +954,13 @@ function removeThis()
913954
$contentobjectID = $this->attribute( 'contentobject_id' );
914955
$versionNum = $this->attribute( 'version' );
915956

957+
$db = eZDB::instance();
958+
$db->begin();
959+
960+
// Ensure no one else deletes this version while we are doing it.
961+
$db->query( 'SELECT * FROM ezcontentobject_version
962+
WHERE id=' . $this->attribute( 'id' ) . ' FOR UPDATE' );
963+
916964
$contentObjectTranslations = $this->translations();
917965

918966
foreach ( $contentObjectTranslations as $contentObjectTranslation )
@@ -923,8 +971,6 @@ function removeThis()
923971
$attribute->removeThis( $attribute->attribute( 'id' ), $versionNum );
924972
}
925973
}
926-
$db = eZDB::instance();
927-
$db->begin();
928974
$db->query( "DELETE FROM ezcontentobject_link
929975
WHERE from_contentobject_id=$contentobjectID AND from_contentobject_version=$versionNum" );
930976
$db->query( "DELETE FROM eznode_assignment

kernel/content/ezcontentoperationcollection.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,39 @@ static public function commitTransaction()
127127

128128
static public function setVersionStatus( $objectID, $versionNum, $status )
129129
{
130-
$object = eZContentObject::fetch( $objectID );
130+
$db = eZDB::instance();
131+
$db->begin();
131132

132133
if ( !$versionNum )
133134
{
134-
$versionNum = $object->attribute( 'current_version' );
135+
$objectRows = $db->arrayQuery( "SELECT * FROM ezcontentobject WHERE id = $objectID FOR UPDATE" );
136+
if ( empty( $objectRows ) )
137+
{
138+
$db->commit(); // We haven't made any changes, but commit here to avoid affecting any outer transactions.
139+
return;
140+
}
141+
142+
$versionNum = $objectRows[0]['current_version'];
135143
}
136-
$version = $object->version( $versionNum );
144+
145+
$versionRows = $db->arrayQuery( "SELECT * FROM ezcontentobject_version WHERE version = $versionNum AND contentobject_id = $objectID FOR UPDATE" );
146+
if ( empty( $versionRows ) )
147+
{
148+
$db->commit(); // We haven't made any changes, but commit here to avoid affecting any outer transactions.
149+
return;
150+
}
151+
152+
$version = eZContentObjectVersion::fetch( $versionRows[0]['id'] );
137153
if ( !$version )
154+
{
155+
$db->commit(); // We haven't made any changes, but commit here to avoid affecting any outer transactions.
138156
return;
157+
}
139158

140159
$version->setAttribute( 'status', $status );
141160
$version->store();
161+
162+
$db->commit();
142163
}
143164

144165
static public function setObjectStatusPublished( $objectID, $versionNum )

kernel/content/removeeditversion.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
$Module = $Params['Module'];
99
$http = eZHTTPTool::instance();
10+
$db = eZDB::instance();
1011
$objectID = (int) $http->sessionVariable( "DiscardObjectID" );
1112
$version = (int) $http->sessionVariable( "DiscardObjectVersion" );
1213
$editLanguage = $http->sessionVariable( "DiscardObjectLanguage" );
@@ -28,7 +29,15 @@
2829
if ( $object === null )
2930
return $Module->handleError( eZError::KERNEL_NOT_AVAILABLE, 'kernel' );
3031

31-
$versionObject = $object->version( $version );
32+
$db->begin();
33+
34+
$versionRows = $db->arrayQuery( "SELECT * FROM ezcontentobject_version WHERE version = $version AND contentobject_id = $objectID FOR UPDATE" );
35+
if ( empty( $versionRows ) )
36+
{
37+
$db->commit(); // We haven't made any changes, but commit here to avoid affecting any outer transactions.
38+
return $Module->handleError( eZError::KERNEL_NOT_AVAILABLE, 'kernel' );
39+
}
40+
$versionObject = eZContentObjectVersion::fetch( $versionRows[0]['id'] );
3241
if ( is_object( $versionObject ) and
3342
in_array( $versionObject->attribute( 'status' ), array( eZContentObjectVersion::STATUS_DRAFT, eZContentObjectVersion::STATUS_INTERNAL_DRAFT ) ) )
3443
{
@@ -46,13 +55,19 @@
4655
$allowEdit = false;
4756

4857
if ( !$allowEdit )
58+
{
59+
$db->commit(); // We haven't made any changes, but commit here to avoid affecting any outer transactions.
4960
return $Module->handleError( eZError::KERNEL_ACCESS_DENIED, 'kernel', array( 'AccessList' => $object->accessList( 'edit' ) ) );
61+
}
5062
}
5163

5264
$versionCount= $object->getVersionCount();
5365
$nodeID = $versionCount == 1 ? $versionObject->attribute( 'main_parent_node_id' ) : $object->attribute( 'main_node_id' );
5466
$versionObject->removeThis();
5567
}
68+
69+
$db->commit();
70+
5671
$hasRedirected = false;
5772
if ( $http->hasSessionVariable( 'RedirectIfDiscarded' ) )
5873
{

kernel/private/classes/clusterfilehandlers/dfsbackends/mysqli.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@ public function _purge( $filePath, $onlyExpired = false, $expiry = false, $fname
334334
* @see _purge
335335
*
336336
* @return bool|int false if it fails, number of affected rows otherwise
337-
* @todo This method should also remove the files from disk
338337
*/
339338
public function _purgeByLike( $like, $onlyExpired = false, $limit = 50, $expiry = false, $fname = false )
340339
{
@@ -392,11 +391,16 @@ public function _purgeByLike( $like, $onlyExpired = false, $limit = 50, $expiry
392391
return $this->_fail( "Purging file metadata by like statement $like failed" );
393392
}
394393
$deletedDBFiles = mysqli_affected_rows( $this->db );
395-
$this->dfsbackend->delete( $files );
396394

397-
$this->_commit( $fname );
395+
$commitResult = $this->_commit( $fname );
396+
if ( $commitResult === true || $commitResult === null )
397+
{
398+
$this->dfsbackend->delete( $files );
398399

399-
return $deletedDBFiles;
400+
return $deletedDBFiles;
401+
}
402+
403+
return false;
400404
}
401405

402406
/**
@@ -1351,6 +1355,10 @@ protected function _begin( $fname = false )
13511355
/**
13521356
* Stops a current transaction and commits the changes by executing a COMMIT call.
13531357
* If the current transaction is a sub-transaction nothing is executed.
1358+
*
1359+
* Returns true if the commit was executed successfully, false if it failed, null if it wasn't executed.
1360+
*
1361+
* @return bool|null
13541362
*/
13551363
protected function _commit( $fname = false )
13561364
{
@@ -1360,7 +1368,11 @@ protected function _commit( $fname = false )
13601368
$fname = "_commit";
13611369
$this->transactionCount--;
13621370
if ( $this->transactionCount == 0 )
1363-
$this->_query( "COMMIT", $fname );
1371+
{
1372+
return $this->_query("COMMIT", $fname) !== false;
1373+
}
1374+
1375+
return null;
13641376
}
13651377

13661378
/**

0 commit comments

Comments
 (0)