PageRenderTime 27ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/RestAPI/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php

https://gitlab.com/martinstti/silex-microframework-rest
PHP | 433 lines | 295 code | 53 blank | 85 comment | 47 complexity | 255da7e6e2ab4861d81d935944373d8b MD5 | raw file
  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\DBALException;
  21. use Doctrine\DBAL\Types\StringType;
  22. use Doctrine\DBAL\Types\TextType;
  23. /**
  24. * Sqlite SchemaManager.
  25. *
  26. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
  27. * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
  28. * @author Jonathan H. Wage <jonwage@gmail.com>
  29. * @author Martin HasoĊˆ <martin.hason@gmail.com>
  30. * @since 2.0
  31. */
  32. class SqliteSchemaManager extends AbstractSchemaManager
  33. {
  34. /**
  35. * {@inheritdoc}
  36. */
  37. public function dropDatabase($database)
  38. {
  39. if (file_exists($database)) {
  40. unlink($database);
  41. }
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function createDatabase($database)
  47. {
  48. $params = $this->_conn->getParams();
  49. $driver = $params['driver'];
  50. $options = array(
  51. 'driver' => $driver,
  52. 'path' => $database
  53. );
  54. $conn = \Doctrine\DBAL\DriverManager::getConnection($options);
  55. $conn->connect();
  56. $conn->close();
  57. }
  58. /**
  59. * {@inheritdoc}
  60. */
  61. public function renameTable($name, $newName)
  62. {
  63. $tableDiff = new TableDiff($name);
  64. $tableDiff->fromTable = $this->listTableDetails($name);
  65. $tableDiff->newName = $newName;
  66. $this->alterTable($tableDiff);
  67. }
  68. /**
  69. * {@inheritdoc}
  70. */
  71. public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
  72. {
  73. $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
  74. $tableDiff->addedForeignKeys[] = $foreignKey;
  75. $this->alterTable($tableDiff);
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
  81. {
  82. $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
  83. $tableDiff->changedForeignKeys[] = $foreignKey;
  84. $this->alterTable($tableDiff);
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function dropForeignKey($foreignKey, $table)
  90. {
  91. $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
  92. $tableDiff->removedForeignKeys[] = $foreignKey;
  93. $this->alterTable($tableDiff);
  94. }
  95. /**
  96. * {@inheritdoc}
  97. */
  98. public function listTableForeignKeys($table, $database = null)
  99. {
  100. if (null === $database) {
  101. $database = $this->_conn->getDatabase();
  102. }
  103. $sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
  104. $tableForeignKeys = $this->_conn->fetchAll($sql);
  105. if ( ! empty($tableForeignKeys)) {
  106. $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'");
  107. $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : '';
  108. if (preg_match_all('#
  109. (?:CONSTRAINT\s+([^\s]+)\s+)?
  110. (?:FOREIGN\s+KEY[^\)]+\)\s*)?
  111. REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))?
  112. (?:
  113. [^,]*?
  114. (NOT\s+DEFERRABLE|DEFERRABLE)
  115. (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))?
  116. )?#isx',
  117. $createSql, $match)) {
  118. $names = array_reverse($match[1]);
  119. $deferrable = array_reverse($match[2]);
  120. $deferred = array_reverse($match[3]);
  121. } else {
  122. $names = $deferrable = $deferred = array();
  123. }
  124. foreach ($tableForeignKeys as $key => $value) {
  125. $id = $value['id'];
  126. $tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && '' != $names[$id] ? $names[$id] : $id;
  127. $tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && 'deferrable' == strtolower($deferrable[$id]) ? true : false;
  128. $tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && 'deferred' == strtolower($deferred[$id]) ? true : false;
  129. }
  130. }
  131. return $this->_getPortableTableForeignKeysList($tableForeignKeys);
  132. }
  133. /**
  134. * {@inheritdoc}
  135. */
  136. protected function _getPortableTableDefinition($table)
  137. {
  138. return $table['name'];
  139. }
  140. /**
  141. * {@inheritdoc}
  142. *
  143. * @license New BSD License
  144. * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
  145. */
  146. protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
  147. {
  148. $indexBuffer = array();
  149. // fetch primary
  150. $stmt = $this->_conn->executeQuery("PRAGMA TABLE_INFO ('$tableName')");
  151. $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC);
  152. usort($indexArray, function($a, $b) {
  153. if ($a['pk'] == $b['pk']) {
  154. return $a['cid'] - $b['cid'];
  155. }
  156. return $a['pk'] - $b['pk'];
  157. });
  158. foreach ($indexArray as $indexColumnRow) {
  159. if ($indexColumnRow['pk'] != "0") {
  160. $indexBuffer[] = array(
  161. 'key_name' => 'primary',
  162. 'primary' => true,
  163. 'non_unique' => false,
  164. 'column_name' => $indexColumnRow['name']
  165. );
  166. }
  167. }
  168. // fetch regular indexes
  169. foreach ($tableIndexes as $tableIndex) {
  170. // Ignore indexes with reserved names, e.g. autoindexes
  171. if (strpos($tableIndex['name'], 'sqlite_') !== 0) {
  172. $keyName = $tableIndex['name'];
  173. $idx = array();
  174. $idx['key_name'] = $keyName;
  175. $idx['primary'] = false;
  176. $idx['non_unique'] = $tableIndex['unique']?false:true;
  177. $stmt = $this->_conn->executeQuery("PRAGMA INDEX_INFO ('{$keyName}')");
  178. $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC);
  179. foreach ($indexArray as $indexColumnRow) {
  180. $idx['column_name'] = $indexColumnRow['name'];
  181. $indexBuffer[] = $idx;
  182. }
  183. }
  184. }
  185. return parent::_getPortableTableIndexesList($indexBuffer, $tableName);
  186. }
  187. /**
  188. * {@inheritdoc}
  189. */
  190. protected function _getPortableTableIndexDefinition($tableIndex)
  191. {
  192. return array(
  193. 'name' => $tableIndex['name'],
  194. 'unique' => (bool) $tableIndex['unique']
  195. );
  196. }
  197. /**
  198. * {@inheritdoc}
  199. */
  200. protected function _getPortableTableColumnList($table, $database, $tableColumns)
  201. {
  202. $list = parent::_getPortableTableColumnList($table, $database, $tableColumns);
  203. // find column with autoincrement
  204. $autoincrementColumn = null;
  205. $autoincrementCount = 0;
  206. foreach ($tableColumns as $tableColumn) {
  207. if ('0' != $tableColumn['pk']) {
  208. $autoincrementCount++;
  209. if (null === $autoincrementColumn && 'integer' == strtolower($tableColumn['type'])) {
  210. $autoincrementColumn = $tableColumn['name'];
  211. }
  212. }
  213. }
  214. if (1 == $autoincrementCount && null !== $autoincrementColumn) {
  215. foreach ($list as $column) {
  216. if ($autoincrementColumn == $column->getName()) {
  217. $column->setAutoincrement(true);
  218. }
  219. }
  220. }
  221. // inspect column collation
  222. $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'");
  223. $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : '';
  224. foreach ($list as $columnName => $column) {
  225. $type = $column->getType();
  226. if ($type instanceof StringType || $type instanceof TextType) {
  227. $column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY');
  228. }
  229. }
  230. return $list;
  231. }
  232. /**
  233. * {@inheritdoc}
  234. */
  235. protected function _getPortableTableColumnDefinition($tableColumn)
  236. {
  237. $parts = explode('(', $tableColumn['type']);
  238. $tableColumn['type'] = trim($parts[0]);
  239. if (isset($parts[1])) {
  240. $length = trim($parts[1], ')');
  241. $tableColumn['length'] = $length;
  242. }
  243. $dbType = strtolower($tableColumn['type']);
  244. $length = isset($tableColumn['length']) ? $tableColumn['length'] : null;
  245. $unsigned = false;
  246. if (strpos($dbType, ' unsigned') !== false) {
  247. $dbType = str_replace(' unsigned', '', $dbType);
  248. $unsigned = true;
  249. }
  250. $fixed = false;
  251. $type = $this->_platform->getDoctrineTypeMapping($dbType);
  252. $default = $tableColumn['dflt_value'];
  253. if ($default == 'NULL') {
  254. $default = null;
  255. }
  256. if ($default !== null) {
  257. // SQLite returns strings wrapped in single quotes, so we need to strip them
  258. $default = preg_replace("/^'(.*)'$/", '\1', $default);
  259. }
  260. $notnull = (bool) $tableColumn['notnull'];
  261. if ( ! isset($tableColumn['name'])) {
  262. $tableColumn['name'] = '';
  263. }
  264. $precision = null;
  265. $scale = null;
  266. switch ($dbType) {
  267. case 'char':
  268. $fixed = true;
  269. break;
  270. case 'float':
  271. case 'double':
  272. case 'real':
  273. case 'decimal':
  274. case 'numeric':
  275. if (isset($tableColumn['length'])) {
  276. if (strpos($tableColumn['length'], ',') === false) {
  277. $tableColumn['length'] .= ",0";
  278. }
  279. list($precision, $scale) = array_map('trim', explode(',', $tableColumn['length']));
  280. }
  281. $length = null;
  282. break;
  283. }
  284. $options = array(
  285. 'length' => $length,
  286. 'unsigned' => (bool) $unsigned,
  287. 'fixed' => $fixed,
  288. 'notnull' => $notnull,
  289. 'default' => $default,
  290. 'precision' => $precision,
  291. 'scale' => $scale,
  292. 'autoincrement' => false,
  293. );
  294. return new Column($tableColumn['name'], \Doctrine\DBAL\Types\Type::getType($type), $options);
  295. }
  296. /**
  297. * {@inheritdoc}
  298. */
  299. protected function _getPortableViewDefinition($view)
  300. {
  301. return new View($view['name'], $view['sql']);
  302. }
  303. /**
  304. * {@inheritdoc}
  305. */
  306. protected function _getPortableTableForeignKeysList($tableForeignKeys)
  307. {
  308. $list = array();
  309. foreach ($tableForeignKeys as $value) {
  310. $value = array_change_key_case($value, CASE_LOWER);
  311. $name = $value['constraint_name'];
  312. if ( ! isset($list[$name])) {
  313. if ( ! isset($value['on_delete']) || $value['on_delete'] == "RESTRICT") {
  314. $value['on_delete'] = null;
  315. }
  316. if ( ! isset($value['on_update']) || $value['on_update'] == "RESTRICT") {
  317. $value['on_update'] = null;
  318. }
  319. $list[$name] = array(
  320. 'name' => $name,
  321. 'local' => array(),
  322. 'foreign' => array(),
  323. 'foreignTable' => $value['table'],
  324. 'onDelete' => $value['on_delete'],
  325. 'onUpdate' => $value['on_update'],
  326. 'deferrable' => $value['deferrable'],
  327. 'deferred'=> $value['deferred'],
  328. );
  329. }
  330. $list[$name]['local'][] = $value['from'];
  331. $list[$name]['foreign'][] = $value['to'];
  332. }
  333. $result = array();
  334. foreach ($list as $constraint) {
  335. $result[] = new ForeignKeyConstraint(
  336. array_values($constraint['local']), $constraint['foreignTable'],
  337. array_values($constraint['foreign']), $constraint['name'],
  338. array(
  339. 'onDelete' => $constraint['onDelete'],
  340. 'onUpdate' => $constraint['onUpdate'],
  341. 'deferrable' => $constraint['deferrable'],
  342. 'deferred'=> $constraint['deferred'],
  343. )
  344. );
  345. }
  346. return $result;
  347. }
  348. /**
  349. * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
  350. * @param \Doctrine\DBAL\Schema\Table|string $table
  351. *
  352. * @return \Doctrine\DBAL\Schema\TableDiff
  353. *
  354. * @throws \Doctrine\DBAL\DBALException
  355. */
  356. private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table)
  357. {
  358. if ( ! $table instanceof Table) {
  359. $tableDetails = $this->tryMethod('listTableDetails', $table);
  360. if (false === $table) {
  361. throw new DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table));
  362. }
  363. $table = $tableDetails;
  364. }
  365. $tableDiff = new TableDiff($table->getName());
  366. $tableDiff->fromTable = $table;
  367. return $tableDiff;
  368. }
  369. private function parseColumnCollationFromSQL($column, $sql)
  370. {
  371. if (preg_match(
  372. '{(?:'.preg_quote($column).'|'.preg_quote($this->_platform->quoteSingleIdentifier($column)).')
  373. [^,(]+(?:\([^()]+\)[^,]*)?
  374. (?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*
  375. COLLATE\s+["\']?([^\s,"\')]+)}isx', $sql, $match)) {
  376. return $match[1];
  377. }
  378. return false;
  379. }
  380. }