Index.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\DBAL\Schema;
  20. use Doctrine\DBAL\Platforms\AbstractPlatform;
  21. class Index extends AbstractAsset implements Constraint
  22. {
  23. /**
  24. * Asset identifier instances of the column names the index is associated with.
  25. * array($columnName => Identifier)
  26. *
  27. * @var Identifier[]
  28. */
  29. protected $_columns = array();
  30. /**
  31. * @var boolean
  32. */
  33. protected $_isUnique = false;
  34. /**
  35. * @var boolean
  36. */
  37. protected $_isPrimary = false;
  38. /**
  39. * Platform specific flags for indexes.
  40. * array($flagName => true)
  41. *
  42. * @var array
  43. */
  44. protected $_flags = array();
  45. /**
  46. * Platform specific options
  47. *
  48. * @todo $_flags should eventually be refactored into options
  49. *
  50. * @var array
  51. */
  52. private $options = array();
  53. /**
  54. * @param string $indexName
  55. * @param string[] $columns
  56. * @param boolean $isUnique
  57. * @param boolean $isPrimary
  58. * @param string[] $flags
  59. * @param array $options
  60. */
  61. public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array(), array $options = array())
  62. {
  63. $isUnique = $isUnique || $isPrimary;
  64. $this->_setName($indexName);
  65. $this->_isUnique = $isUnique;
  66. $this->_isPrimary = $isPrimary;
  67. $this->options = $options;
  68. foreach ($columns as $column) {
  69. $this->_addColumn($column);
  70. }
  71. foreach ($flags as $flag) {
  72. $this->addFlag($flag);
  73. }
  74. }
  75. /**
  76. * @param string $column
  77. *
  78. * @return void
  79. *
  80. * @throws \InvalidArgumentException
  81. */
  82. protected function _addColumn($column)
  83. {
  84. if (is_string($column)) {
  85. $this->_columns[$column] = new Identifier($column);
  86. } else {
  87. throw new \InvalidArgumentException("Expecting a string as Index Column");
  88. }
  89. }
  90. /**
  91. * {@inheritdoc}
  92. */
  93. public function getColumns()
  94. {
  95. return array_keys($this->_columns);
  96. }
  97. /**
  98. * {@inheritdoc}
  99. */
  100. public function getQuotedColumns(AbstractPlatform $platform)
  101. {
  102. $columns = array();
  103. foreach ($this->_columns as $column) {
  104. $columns[] = $column->getQuotedName($platform);
  105. }
  106. return $columns;
  107. }
  108. /**
  109. * @return string[]
  110. */
  111. public function getUnquotedColumns()
  112. {
  113. return array_map(array($this, 'trimQuotes'), $this->getColumns());
  114. }
  115. /**
  116. * Is the index neither unique nor primary key?
  117. *
  118. * @return boolean
  119. */
  120. public function isSimpleIndex()
  121. {
  122. return !$this->_isPrimary && !$this->_isUnique;
  123. }
  124. /**
  125. * @return boolean
  126. */
  127. public function isUnique()
  128. {
  129. return $this->_isUnique;
  130. }
  131. /**
  132. * @return boolean
  133. */
  134. public function isPrimary()
  135. {
  136. return $this->_isPrimary;
  137. }
  138. /**
  139. * @param string $columnName
  140. * @param integer $pos
  141. *
  142. * @return boolean
  143. */
  144. public function hasColumnAtPosition($columnName, $pos = 0)
  145. {
  146. $columnName = $this->trimQuotes(strtolower($columnName));
  147. $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
  148. return array_search($columnName, $indexColumns) === $pos;
  149. }
  150. /**
  151. * Checks if this index exactly spans the given column names in the correct order.
  152. *
  153. * @param array $columnNames
  154. *
  155. * @return boolean
  156. */
  157. public function spansColumns(array $columnNames)
  158. {
  159. $columns = $this->getColumns();
  160. $numberOfColumns = count($columns);
  161. $sameColumns = true;
  162. for ($i = 0; $i < $numberOfColumns; $i++) {
  163. if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) {
  164. $sameColumns = false;
  165. }
  166. }
  167. return $sameColumns;
  168. }
  169. /**
  170. * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
  171. *
  172. * @param Index $other
  173. *
  174. * @return boolean
  175. */
  176. public function isFullfilledBy(Index $other)
  177. {
  178. // allow the other index to be equally large only. It being larger is an option
  179. // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
  180. if (count($other->getColumns()) != count($this->getColumns())) {
  181. return false;
  182. }
  183. // Check if columns are the same, and even in the same order
  184. $sameColumns = $this->spansColumns($other->getColumns());
  185. if ($sameColumns) {
  186. if ( ! $this->samePartialIndex($other)) {
  187. return false;
  188. }
  189. if ( ! $this->isUnique() && ! $this->isPrimary()) {
  190. // this is a special case: If the current key is neither primary or unique, any uniqe or
  191. // primary key will always have the same effect for the index and there cannot be any constraint
  192. // overlaps. This means a primary or unique index can always fulfill the requirements of just an
  193. // index that has no constraints.
  194. return true;
  195. }
  196. if ($other->isPrimary() != $this->isPrimary()) {
  197. return false;
  198. }
  199. if ($other->isUnique() != $this->isUnique()) {
  200. return false;
  201. }
  202. return true;
  203. }
  204. return false;
  205. }
  206. /**
  207. * Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
  208. *
  209. * @param Index $other
  210. *
  211. * @return boolean
  212. */
  213. public function overrules(Index $other)
  214. {
  215. if ($other->isPrimary()) {
  216. return false;
  217. } elseif ($this->isSimpleIndex() && $other->isUnique()) {
  218. return false;
  219. }
  220. if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other)) {
  221. return true;
  222. }
  223. return false;
  224. }
  225. /**
  226. * Returns platform specific flags for indexes.
  227. *
  228. * @return string[]
  229. */
  230. public function getFlags()
  231. {
  232. return array_keys($this->_flags);
  233. }
  234. /**
  235. * Adds Flag for an index that translates to platform specific handling.
  236. *
  237. * @example $index->addFlag('CLUSTERED')
  238. *
  239. * @param string $flag
  240. *
  241. * @return Index
  242. */
  243. public function addFlag($flag)
  244. {
  245. $this->_flags[strtolower($flag)] = true;
  246. return $this;
  247. }
  248. /**
  249. * Does this index have a specific flag?
  250. *
  251. * @param string $flag
  252. *
  253. * @return boolean
  254. */
  255. public function hasFlag($flag)
  256. {
  257. return isset($this->_flags[strtolower($flag)]);
  258. }
  259. /**
  260. * Removes a flag.
  261. *
  262. * @param string $flag
  263. *
  264. * @return void
  265. */
  266. public function removeFlag($flag)
  267. {
  268. unset($this->_flags[strtolower($flag)]);
  269. }
  270. /**
  271. * @param string $name
  272. *
  273. * @return boolean
  274. */
  275. public function hasOption($name)
  276. {
  277. return isset($this->options[strtolower($name)]);
  278. }
  279. /**
  280. * @param string $name
  281. *
  282. * @return mixed
  283. */
  284. public function getOption($name)
  285. {
  286. return $this->options[strtolower($name)];
  287. }
  288. /**
  289. * @return array
  290. */
  291. public function getOptions()
  292. {
  293. return $this->options;
  294. }
  295. /**
  296. * Return whether the two indexes have the same partial index
  297. * @param \Doctrine\DBAL\Schema\Index $other
  298. *
  299. * @return boolean
  300. */
  301. private function samePartialIndex(Index $other)
  302. {
  303. if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) {
  304. return true;
  305. }
  306. if ( ! $this->hasOption('where') && ! $other->hasOption('where')) {
  307. return true;
  308. }
  309. return false;
  310. }
  311. }