Changeset View
Changeset View
Standalone View
Standalone View
src/aphront/storage/connection/AphrontIsolatedDatabaseConnection.php
- This file was added.
| <?php | |||||
| final class AphrontIsolatedDatabaseConnection | |||||
| extends AphrontDatabaseConnection { | |||||
| private $configuration; | |||||
| private static $nextInsertID; | |||||
| private $insertID; | |||||
| private $transcript = array(); | |||||
| private $allResults; | |||||
| private $affectedRows; | |||||
| public function __construct(array $configuration) { | |||||
| $this->configuration = $configuration; | |||||
| if (self::$nextInsertID === null) { | |||||
| // Generate test IDs into a distant ID space to reduce the risk of | |||||
| // collisions and make them distinctive. | |||||
| self::$nextInsertID = 55555000000 + mt_rand(0, 1000); | |||||
| } | |||||
| } | |||||
| public function openConnection() { | |||||
| return; | |||||
| } | |||||
| public function close() { | |||||
| return; | |||||
| } | |||||
| public function escapeUTF8String($string) { | |||||
| return '<S>'; | |||||
| } | |||||
| public function escapeBinaryString($string) { | |||||
| return '<B>'; | |||||
| } | |||||
| public function escapeColumnName($name) { | |||||
| return '<C>'; | |||||
| } | |||||
| public function escapeMultilineComment($comment) { | |||||
| return '<K>'; | |||||
| } | |||||
| public function escapeStringForLikeClause($value) { | |||||
| return '<L>'; | |||||
| } | |||||
| private function getConfiguration($key, $default = null) { | |||||
| return idx($this->configuration, $key, $default); | |||||
| } | |||||
| public function getInsertID() { | |||||
| return $this->insertID; | |||||
| } | |||||
| public function getAffectedRows() { | |||||
| return $this->affectedRows; | |||||
| } | |||||
| public function selectAllResults() { | |||||
| return $this->allResults; | |||||
| } | |||||
| public function executeRawQuery($raw_query) { | |||||
| // NOTE: "[\s<>K]*" allows any number of (properly escaped) comments to | |||||
| // appear prior to the allowed keyword, since this connection escapes | |||||
| // them as "<K>" (above). | |||||
| $keywords = array( | |||||
| 'INSERT', | |||||
| 'UPDATE', | |||||
| 'DELETE', | |||||
| 'START', | |||||
| 'SAVEPOINT', | |||||
| 'COMMIT', | |||||
| 'ROLLBACK', | |||||
| ); | |||||
| $preg_keywords = array(); | |||||
| foreach ($keywords as $key => $word) { | |||||
| $preg_keywords[] = preg_quote($word, '/'); | |||||
| } | |||||
| $preg_keywords = implode('|', $preg_keywords); | |||||
| if (!preg_match('/^[\s<>K]*('.$preg_keywords.')\s*/i', $raw_query)) { | |||||
| throw new AphrontNotSupportedQueryException( | |||||
| pht( | |||||
| "Database isolation currently only supports some queries. You are ". | |||||
| "trying to issue a query which does not begin with an allowed ". | |||||
| "keyword (%s): '%s'.", | |||||
| implode(', ', $keywords), | |||||
| $raw_query)); | |||||
| } | |||||
| $this->transcript[] = $raw_query; | |||||
| // NOTE: This method is intentionally simplified for now, since we're only | |||||
| // using it to stub out inserts/updates. In the future it will probably need | |||||
| // to grow more powerful. | |||||
| $this->allResults = array(); | |||||
| // NOTE: We jitter the insert IDs to keep tests honest; a test should cover | |||||
| // the relationship between objects, not their exact insertion order. This | |||||
| // guarantees that IDs are unique but makes it impossible to hard-code tests | |||||
| // against this specific implementation detail. | |||||
| self::$nextInsertID += mt_rand(1, 10); | |||||
| $this->insertID = self::$nextInsertID; | |||||
| $this->affectedRows = 1; | |||||
| } | |||||
| public function executeRawQueries(array $raw_queries) { | |||||
| $results = array(); | |||||
| foreach ($raw_queries as $id => $raw_query) { | |||||
| $results[$id] = array(); | |||||
| } | |||||
| return $results; | |||||
| } | |||||
| public function getQueryTranscript() { | |||||
| return $this->transcript; | |||||
| } | |||||
| } | |||||