OracleMutex.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\mutex;
  8. use PDO;
  9. use yii\base\InvalidConfigException;
  10. /**
  11. * OracleMutex implements mutex "lock" mechanism via Oracle locks.
  12. *
  13. * Application configuration example:
  14. *
  15. * ```
  16. * [
  17. * 'components' => [
  18. * 'db' => [
  19. * 'class' => 'yii\db\Connection',
  20. * 'dsn' => 'oci:dbname=LOCAL_XE',
  21. * ...
  22. * ]
  23. * 'mutex' => [
  24. * 'class' => 'yii\mutex\OracleMutex',
  25. * 'lockMode' => 'NL_MODE',
  26. * 'releaseOnCommit' => true,
  27. * ...
  28. * ],
  29. * ],
  30. * ]
  31. * ```
  32. *
  33. * @see http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm
  34. * @see Mutex
  35. *
  36. * @author Alexander Zlakomanov <zlakomanoff@gmail.com>
  37. * @since 2.0.10
  38. */
  39. class OracleMutex extends DbMutex
  40. {
  41. /** available lock modes */
  42. const MODE_X = 'X_MODE';
  43. const MODE_NL = 'NL_MODE';
  44. const MODE_S = 'S_MODE';
  45. const MODE_SX = 'SX_MODE';
  46. const MODE_SS = 'SS_MODE';
  47. const MODE_SSX = 'SSX_MODE';
  48. /**
  49. * @var string lock mode to be used.
  50. * @see http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm#CHDBCFDI
  51. */
  52. public $lockMode = self::MODE_X;
  53. /**
  54. * @var bool whether to release lock on commit.
  55. */
  56. public $releaseOnCommit = false;
  57. /**
  58. * Initializes Oracle specific mutex component implementation.
  59. * @throws InvalidConfigException if [[db]] is not Oracle connection.
  60. */
  61. public function init()
  62. {
  63. parent::init();
  64. if (strncmp($this->db->driverName, 'oci', 3) !== 0 && strncmp($this->db->driverName, 'odbc', 4) !== 0) {
  65. throw new InvalidConfigException('In order to use OracleMutex connection must be configured to use Oracle database.');
  66. }
  67. }
  68. /**
  69. * Acquires lock by given name.
  70. * @see http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm
  71. * @param string $name of the lock to be acquired.
  72. * @param int $timeout time (in seconds) to wait for lock to become released.
  73. * @return bool acquiring result.
  74. */
  75. protected function acquireLock($name, $timeout = 0)
  76. {
  77. $lockStatus = null;
  78. // clean vars before using
  79. $releaseOnCommit = $this->releaseOnCommit ? 'TRUE' : 'FALSE';
  80. $timeout = abs((int) $timeout);
  81. // inside pl/sql scopes pdo binding not working correctly :(
  82. $this->db->useMaster(function ($db) use ($name, $timeout, $releaseOnCommit, &$lockStatus) {
  83. /** @var \yii\db\Connection $db */
  84. $db->createCommand(
  85. 'DECLARE
  86. handle VARCHAR2(128);
  87. BEGIN
  88. DBMS_LOCK.ALLOCATE_UNIQUE(:name, handle);
  89. :lockStatus := DBMS_LOCK.REQUEST(handle, DBMS_LOCK.' . $this->lockMode . ', ' . $timeout . ', ' . $releaseOnCommit . ');
  90. END;',
  91. [':name' => $name]
  92. )
  93. ->bindParam(':lockStatus', $lockStatus, PDO::PARAM_INT, 1)
  94. ->execute();
  95. });
  96. return $lockStatus === 0 || $lockStatus === '0';
  97. }
  98. /**
  99. * Releases lock by given name.
  100. * @param string $name of the lock to be released.
  101. * @return bool release result.
  102. * @see http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm
  103. */
  104. protected function releaseLock($name)
  105. {
  106. $releaseStatus = null;
  107. $this->db->useMaster(function ($db) use ($name, &$releaseStatus) {
  108. /** @var \yii\db\Connection $db */
  109. $db->createCommand(
  110. 'DECLARE
  111. handle VARCHAR2(128);
  112. BEGIN
  113. DBMS_LOCK.ALLOCATE_UNIQUE(:name, handle);
  114. :result := DBMS_LOCK.RELEASE(handle);
  115. END;',
  116. [':name' => $name]
  117. )
  118. ->bindParam(':result', $releaseStatus, PDO::PARAM_INT, 1)
  119. ->execute();
  120. });
  121. return $releaseStatus === 0 || $releaseStatus === '0';
  122. }
  123. }