@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 296 lines 6.6 kB view raw
1<?php 2 3/** 4 * @task xaction Transaction Management 5 */ 6abstract class AphrontDatabaseConnection 7 extends Phobject 8 implements PhutilQsprintfInterface { 9 10 private $transactionState; 11 private $readOnly; 12 private $queryTimeout; 13 private $locks = array(); 14 private $lastActiveEpoch; 15 private $persistent; 16 17 abstract public function getInsertID(); 18 abstract public function getAffectedRows(); 19 abstract public function selectAllResults(); 20 abstract public function executeQuery(PhutilQueryString $query); 21 abstract public function executeRawQueries(array $raw_queries); 22 abstract public function close(); 23 abstract public function openConnection(); 24 25 public function __destruct() { 26 // NOTE: This does not actually close persistent connections: PHP maintains 27 // them in the connection pool. 28 $this->close(); 29 } 30 31 final public function setLastActiveEpoch($epoch) { 32 $this->lastActiveEpoch = $epoch; 33 return $this; 34 } 35 36 final public function getLastActiveEpoch() { 37 return $this->lastActiveEpoch; 38 } 39 40 final public function setPersistent($persistent) { 41 $this->persistent = $persistent; 42 return $this; 43 } 44 45 final public function getPersistent() { 46 return $this->persistent; 47 } 48 49 public function queryData($pattern/* , $arg, $arg, ... */) { 50 $args = func_get_args(); 51 array_unshift($args, $this); 52 return call_user_func_array('queryfx_all', $args); 53 } 54 55 public function query($pattern/* , $arg, $arg, ... */) { 56 $args = func_get_args(); 57 array_unshift($args, $this); 58 return call_user_func_array('queryfx', $args); 59 } 60 61 public function setReadOnly($read_only) { 62 $this->readOnly = $read_only; 63 return $this; 64 } 65 66 public function getReadOnly() { 67 return $this->readOnly; 68 } 69 70 public function setQueryTimeout($query_timeout) { 71 $this->queryTimeout = $query_timeout; 72 return $this; 73 } 74 75 public function getQueryTimeout() { 76 return $this->queryTimeout; 77 } 78 79 public function asyncQuery($raw_query) { 80 throw new Exception(pht('Async queries are not supported.')); 81 } 82 83 public static function resolveAsyncQueries(array $conns, array $asyncs) { 84 throw new Exception(pht('Async queries are not supported.')); 85 } 86 87 /** 88 * Is this connection idle and safe to close? 89 * 90 * A connection is "idle" if it can be safely closed without loss of state. 91 * Connections inside a transaction or holding locks are not idle, even 92 * though they may not actively be executing queries. 93 * 94 * @return bool True if the connection is idle and can be safely closed. 95 */ 96 public function isIdle() { 97 if ($this->isInsideTransaction()) { 98 return false; 99 } 100 101 if ($this->isHoldingAnyLock()) { 102 return false; 103 } 104 105 return true; 106 } 107 108 109/* -( Global Locks )------------------------------------------------------- */ 110 111 112 public function rememberLock($lock) { 113 if (isset($this->locks[$lock])) { 114 throw new Exception( 115 pht( 116 'Trying to remember lock "%s", but this lock has already been '. 117 'remembered.', 118 $lock)); 119 } 120 121 $this->locks[$lock] = true; 122 return $this; 123 } 124 125 126 public function forgetLock($lock) { 127 if (empty($this->locks[$lock])) { 128 throw new Exception( 129 pht( 130 'Trying to forget lock "%s", but this connection does not remember '. 131 'that lock.', 132 $lock)); 133 } 134 135 unset($this->locks[$lock]); 136 return $this; 137 } 138 139 140 public function forgetAllLocks() { 141 $this->locks = array(); 142 return $this; 143 } 144 145 146 public function isHoldingAnyLock() { 147 return (bool)$this->locks; 148 } 149 150 151/* -( Transaction Management )--------------------------------------------- */ 152 153 154 /** 155 * Begin a transaction, or set a savepoint if the connection is already 156 * transactional. 157 * 158 * @return $this 159 * @task xaction 160 */ 161 public function openTransaction() { 162 $state = $this->getTransactionState(); 163 $point = $state->getSavepointName(); 164 $depth = $state->getDepth(); 165 166 $new_transaction = ($depth == 0); 167 if ($new_transaction) { 168 $this->query('START TRANSACTION'); 169 } else { 170 $this->query('SAVEPOINT '.$point); 171 } 172 173 $state->increaseDepth(); 174 175 return $this; 176 } 177 178 179 /** 180 * Commit a transaction, or stage a savepoint for commit once the entire 181 * transaction completes if inside a transaction stack. 182 * 183 * @return $this 184 * @task xaction 185 */ 186 public function saveTransaction() { 187 $state = $this->getTransactionState(); 188 $depth = $state->decreaseDepth(); 189 190 if ($depth == 0) { 191 $this->query('COMMIT'); 192 } 193 194 return $this; 195 } 196 197 198 /** 199 * Rollback a transaction, or unstage the last savepoint if inside a 200 * transaction stack. 201 * 202 * @return $this 203 */ 204 public function killTransaction() { 205 $state = $this->getTransactionState(); 206 $depth = $state->decreaseDepth(); 207 208 if ($depth == 0) { 209 $this->query('ROLLBACK'); 210 } else { 211 $this->query('ROLLBACK TO SAVEPOINT '.$state->getSavepointName()); 212 } 213 214 return $this; 215 } 216 217 218 /** 219 * Returns true if the connection is transactional. 220 * 221 * @return bool True if the connection is currently transactional. 222 * @task xaction 223 */ 224 public function isInsideTransaction() { 225 $state = $this->getTransactionState(); 226 return ($state->getDepth() > 0); 227 } 228 229 230 /** 231 * Get the current @{class:AphrontDatabaseTransactionState} object, or create 232 * one if none exists. 233 * 234 * @return AphrontDatabaseTransactionState Current transaction state. 235 * @task xaction 236 */ 237 protected function getTransactionState() { 238 if (!$this->transactionState) { 239 $this->transactionState = new AphrontDatabaseTransactionState(); 240 } 241 return $this->transactionState; 242 } 243 244 245 /** 246 * @task xaction 247 */ 248 public function beginReadLocking() { 249 $this->getTransactionState()->beginReadLocking(); 250 return $this; 251 } 252 253 254 /** 255 * @task xaction 256 */ 257 public function endReadLocking() { 258 $this->getTransactionState()->endReadLocking(); 259 return $this; 260 } 261 262 263 /** 264 * @task xaction 265 */ 266 public function isReadLocking() { 267 return $this->getTransactionState()->isReadLocking(); 268 } 269 270 271 /** 272 * @task xaction 273 */ 274 public function beginWriteLocking() { 275 $this->getTransactionState()->beginWriteLocking(); 276 return $this; 277 } 278 279 280 /** 281 * @task xaction 282 */ 283 public function endWriteLocking() { 284 $this->getTransactionState()->endWriteLocking(); 285 return $this; 286 } 287 288 289 /** 290 * @task xaction 291 */ 292 public function isWriteLocking() { 293 return $this->getTransactionState()->isWriteLocking(); 294 } 295 296}