4 * @task xaction Transaction Management
6 abstract class AphrontDatabaseConnection
8 implements PhutilQsprintfInterface
{
10 private $transactionState;
12 private $queryTimeout;
13 private $locks = array();
14 private $lastActiveEpoch;
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();
25 public function __destruct() {
26 // NOTE: This does not actually close persistent connections: PHP maintains
27 // them in the connection pool.
31 final public function setLastActiveEpoch($epoch) {
32 $this->lastActiveEpoch
= $epoch;
36 final public function getLastActiveEpoch() {
37 return $this->lastActiveEpoch
;
40 final public function setPersistent($persistent) {
41 $this->persistent
= $persistent;
45 final public function getPersistent() {
46 return $this->persistent
;
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);
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);
62 public function supportsAsyncQueries() {
66 public function supportsParallelQueries() {
70 public function setReadOnly($read_only) {
71 $this->readOnly
= $read_only;
75 public function getReadOnly() {
76 return $this->readOnly
;
79 public function setQueryTimeout($query_timeout) {
80 $this->queryTimeout
= $query_timeout;
84 public function getQueryTimeout() {
85 return $this->queryTimeout
;
88 public function asyncQuery($raw_query) {
89 throw new Exception(pht('Async queries are not supported.'));
92 public static function resolveAsyncQueries(array $conns, array $asyncs) {
93 throw new Exception(pht('Async queries are not supported.'));
97 * Is this connection idle and safe to close?
99 * A connection is "idle" if it can be safely closed without loss of state.
100 * Connections inside a transaction or holding locks are not idle, even
101 * though they may not actively be executing queries.
103 * @return bool True if the connection is idle and can be safely closed.
105 public function isIdle() {
106 if ($this->isInsideTransaction()) {
110 if ($this->isHoldingAnyLock()) {
118 /* -( Global Locks )------------------------------------------------------- */
121 public function rememberLock($lock) {
122 if (isset($this->locks
[$lock])) {
125 'Trying to remember lock "%s", but this lock has already been '.
130 $this->locks
[$lock] = true;
135 public function forgetLock($lock) {
136 if (empty($this->locks
[$lock])) {
139 'Trying to forget lock "%s", but this connection does not remember '.
144 unset($this->locks
[$lock]);
149 public function forgetAllLocks() {
150 $this->locks
= array();
155 public function isHoldingAnyLock() {
156 return (bool)$this->locks
;
160 /* -( Transaction Management )--------------------------------------------- */
164 * Begin a transaction, or set a savepoint if the connection is already
170 public function openTransaction() {
171 $state = $this->getTransactionState();
172 $point = $state->getSavepointName();
173 $depth = $state->getDepth();
175 $new_transaction = ($depth == 0);
176 if ($new_transaction) {
177 $this->query('START TRANSACTION');
179 $this->query('SAVEPOINT '.$point);
182 $state->increaseDepth();
189 * Commit a transaction, or stage a savepoint for commit once the entire
190 * transaction completes if inside a transaction stack.
195 public function saveTransaction() {
196 $state = $this->getTransactionState();
197 $depth = $state->decreaseDepth();
200 $this->query('COMMIT');
208 * Rollback a transaction, or unstage the last savepoint if inside a
213 public function killTransaction() {
214 $state = $this->getTransactionState();
215 $depth = $state->decreaseDepth();
218 $this->query('ROLLBACK');
220 $this->query('ROLLBACK TO SAVEPOINT '.$state->getSavepointName());
228 * Returns true if the connection is transactional.
230 * @return bool True if the connection is currently transactional.
233 public function isInsideTransaction() {
234 $state = $this->getTransactionState();
235 return ($state->getDepth() > 0);
240 * Get the current @{class:AphrontDatabaseTransactionState} object, or create
241 * one if none exists.
243 * @return AphrontDatabaseTransactionState Current transaction state.
246 protected function getTransactionState() {
247 if (!$this->transactionState
) {
248 $this->transactionState
= new AphrontDatabaseTransactionState();
250 return $this->transactionState
;
257 public function beginReadLocking() {
258 $this->getTransactionState()->beginReadLocking();
266 public function endReadLocking() {
267 $this->getTransactionState()->endReadLocking();
275 public function isReadLocking() {
276 return $this->getTransactionState()->isReadLocking();
283 public function beginWriteLocking() {
284 $this->getTransactionState()->beginWriteLocking();
292 public function endWriteLocking() {
293 $this->getTransactionState()->endWriteLocking();
301 public function isWriteLocking() {
302 return $this->getTransactionState()->isWriteLocking();