Index: branches/RC/core/kernel/utility/temp_handler.php =================================================================== diff -u -N -r11923 -r11933 --- branches/RC/core/kernel/utility/temp_handler.php (.../temp_handler.php) (revision 11923) +++ branches/RC/core/kernel/utility/temp_handler.php (.../temp_handler.php) (revision 11933) @@ -1,6 +1,6 @@ savedIDs[ $master['Prefix'] ]; } + /** + * Create separate connection for locking purposes + * + * @return kDBConnection + */ + function &_getSeparateConnection() + { + static $connection = null; + + if (!isset($connection)) { + $connection = new kDBConnection(SQL_TYPE, Array(&$this->Application, 'handleSQLError') ); + $connection->debugMode = $this->Application->isDebugMode(); + $connection->Connect(SQL_SERVER, SQL_USER, SQL_PASS, SQL_DB, true); + } + + return $connection; + } + function UpdateChangeLogForeignKeys($master, $live_id, $temp_id) { $main_prefix = $this->Application->GetTopmostPrefix($master['Prefix']); @@ -766,7 +784,60 @@ function SaveEdit($master_ids = Array()) { - return $this->DoCopyTempToOriginal($this->Tables, null, $master_ids); + // SessionKey field is required for deleting records from expired sessions + $conn =& $this->_getSeparateConnection(); + + $sleep_count = 0; + do { + // aquire lock + $conn->ChangeQuery('LOCK TABLES '.TABLE_PREFIX.'Semaphores WRITE'); + + $sql = 'SELECT SessionKey + FROM ' . TABLE_PREFIX . 'Semaphores + WHERE (MainPrefix = ' . $conn->qstr($this->Tables['Prefix']) . ')'; + $another_coping_active = $conn->GetOne($sql); + + if ($another_coping_active) { + // another user is coping data from temp table to live -> release lock and try again after 1 second + $conn->ChangeQuery('UNLOCK TABLES'); + $sleep_count++; + sleep(1); + } + } while ($another_coping_active && ($sleep_count <= 30)); + + if ($sleep_count > 30) { + // another coping process failed to finished in 30 seconds + $error_message = $this->Application->Phrase('la_error_TemporaryTableCopingFailed'); + $this->Application->SetVar('_temp_table_message', $error_message); + + return false; + } + + // mark, that we are coping from temp to live right now, so other similar attempt (from another script) will fail + $fields_hash = Array ( + 'SessionKey' => $this->Application->GetSID(), + 'Timestamp' => adodb_mktime(), + 'MainPrefix' => $this->Tables['Prefix'], + ); + + $conn->doInsert($fields_hash, TABLE_PREFIX.'Semaphores'); + $semaphore_id = $conn->getInsertID(); + + // unlock table now to prevent permanent lock in case, when coping will end with SQL error in the middle + $conn->ChangeQuery('UNLOCK TABLES'); + + $ids = $this->DoCopyTempToOriginal($this->Tables, null, $master_ids); + + // remove mark, that we are coping from temp to live + $conn->Query('LOCK TABLES '.TABLE_PREFIX.'Semaphores WRITE'); + + $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Semaphores + WHERE SemaphoreId = ' . $semaphore_id; + $conn->ChangeQuery($sql); + + $conn->ChangeQuery('UNLOCK TABLES'); + + return $ids; } function CancelEdit($master=null)