PageRenderTime 83ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/inc/MDB2/Driver/Reverse/sqlite.php

https://github.com/chregu/fluxcms
PHP | 413 lines | 281 code | 33 blank | 99 comment | 72 complexity | 11959776441f71330e2e015b4ce79af8 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, Apache-2.0, LGPL-2.1
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP versions 4 and 5 |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox, |
  6. // | Stig. S. Bakken, Lukas Smith |
  7. // | All rights reserved. |
  8. // +----------------------------------------------------------------------+
  9. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
  10. // | API as well as database abstraction for PHP applications. |
  11. // | This LICENSE is in the BSD license style. |
  12. // | |
  13. // | Redistribution and use in source and binary forms, with or without |
  14. // | modification, are permitted provided that the following conditions |
  15. // | are met: |
  16. // | |
  17. // | Redistributions of source code must retain the above copyright |
  18. // | notice, this list of conditions and the following disclaimer. |
  19. // | |
  20. // | Redistributions in binary form must reproduce the above copyright |
  21. // | notice, this list of conditions and the following disclaimer in the |
  22. // | documentation and/or other materials provided with the distribution. |
  23. // | |
  24. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
  25. // | Lukas Smith nor the names of his contributors may be used to endorse |
  26. // | or promote products derived from this software without specific prior|
  27. // | written permission. |
  28. // | |
  29. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  30. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  31. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
  32. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
  33. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
  34. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  35. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  36. // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
  37. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  38. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  39. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
  40. // | POSSIBILITY OF SUCH DAMAGE. |
  41. // +----------------------------------------------------------------------+
  42. // | Author: Lukas Smith <smith@pooteeweet.org> |
  43. // +----------------------------------------------------------------------+
  44. //
  45. // $Id$
  46. //
  47. require_once 'MDB2/Driver/Reverse/Common.php';
  48. /**
  49. * MDB2 SQlite driver for the schema reverse engineering module
  50. *
  51. * @package MDB2
  52. * @category Database
  53. * @author Lukas Smith <smith@pooteeweet.org>
  54. */
  55. class MDB2_Driver_Reverse_sqlite extends MDB2_Driver_Reverse_Common
  56. {
  57. function _getTableColumns($sql)
  58. {
  59. $db =& $this->getDBInstance();
  60. if (PEAR::isError($db)) {
  61. return $db;
  62. }
  63. $start_pos = strpos($sql, '(');
  64. $end_pos = strrpos($sql, ')');
  65. $column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
  66. $column_sql = split(',', $column_def);
  67. $columns = array();
  68. $count = count($column_sql);
  69. if ($count == 0) {
  70. return $db->raiseError('unexpected empty table column definition list');
  71. }
  72. $regexp = '/^([^ ]+) (CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|INT|INTEGER|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( UNSIGNED)?( PRIMARY KEY)?( \(([1-9][0-9]*)(,([1-9][0-9]*))?\))?( DEFAULT (\'[^\']*\'|[^ ]+))?( NOT NULL)?$/i';
  73. for ($i=0, $j=0; $i<$count; ++$i) {
  74. if (!preg_match($regexp, trim($column_sql[$i]), $matches)) {
  75. return $db->raiseError('unexpected table column SQL definition: "'.$column_sql[$i].'"');
  76. }
  77. $columns[$j]['name'] = $matches[1];
  78. $columns[$j]['type'] = strtolower($matches[2]);
  79. if (isset($matches[3]) && strlen($matches[3])) {
  80. $columns[$j]['unsigned'] = true;
  81. }
  82. if (isset($matches[4]) && strlen($matches[4])) {
  83. $columns[$j]['autoincrement'] = true;
  84. }
  85. if (isset($matches[6]) && strlen($matches[6])) {
  86. $columns[$j]['length'] = $matches[6];
  87. }
  88. if (isset($matches[8]) && strlen($matches[8])) {
  89. $columns[$j]['decimal'] = $matches[8];
  90. }
  91. if (isset($matches[10]) && strlen($matches[10])) {
  92. $default = $matches[10];
  93. if (strlen($default) && $default[0]=="'") {
  94. $default = str_replace("''", "'", substr($default, 1, strlen($default)-2));
  95. }
  96. if ($default === 'NULL') {
  97. $default = null;
  98. }
  99. $columns[$j]['default'] = $default;
  100. }
  101. if (isset($matches[11]) && strlen($matches[11])) {
  102. $columns[$j]['notnull'] = true;
  103. }
  104. ++$j;
  105. }
  106. return $columns;
  107. }
  108. // {{{ getTableFieldDefinition()
  109. /**
  110. * get the stucture of a field into an array
  111. *
  112. * @param string $table name of table that should be used in method
  113. * @param string $field_name name of field that should be used in method
  114. * @return mixed data array on success, a MDB2 error on failure
  115. * @access public
  116. */
  117. function getTableFieldDefinition($table, $field_name)
  118. {
  119. $db =& $this->getDBInstance();
  120. if (PEAR::isError($db)) {
  121. return $db;
  122. }
  123. $result = $db->loadModule('Datatype', null, true);
  124. if (PEAR::isError($result)) {
  125. return $result;
  126. }
  127. $query = "SELECT sql FROM sqlite_master WHERE type='table' AND name='$table'";
  128. $sql = $db->queryOne($query);
  129. if (PEAR::isError($sql)) {
  130. return $sql;
  131. }
  132. $columns = $this->_getTableColumns($sql);
  133. foreach ($columns as $column) {
  134. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  135. if ($db->options['field_case'] == CASE_LOWER) {
  136. $column['name'] = strtolower($column['name']);
  137. } else {
  138. $column['name'] = strtoupper($column['name']);
  139. }
  140. } else {
  141. $column = array_change_key_case($column, $db->options['field_case']);
  142. }
  143. if ($field_name == $column['name']) {
  144. list($types, $length, $unsigned) = $db->datatype->mapNativeDatatype($column);
  145. $notnull = false;
  146. if (array_key_exists('notnull', $column)) {
  147. $notnull = $column['notnull'];
  148. }
  149. $default = false;
  150. if (array_key_exists('default', $column)) {
  151. $default = $column['default'];
  152. if (is_null($default) && $notnull) {
  153. $default = '';
  154. }
  155. }
  156. $autoincrement = false;
  157. if (array_key_exists('autoincrement', $column) && $column['autoincrement']) {
  158. $autoincrement = true;
  159. }
  160. $definition = array();
  161. foreach ($types as $key => $type) {
  162. $definition[$key] = array(
  163. 'type' => $type,
  164. 'notnull' => $notnull,
  165. );
  166. if ($length > 0) {
  167. $definition[$key]['length'] = $length;
  168. }
  169. if ($unsigned) {
  170. $definition[$key]['unsigned'] = true;
  171. }
  172. if ($default !== false) {
  173. $definition[$key]['default'] = $default;
  174. }
  175. if ($autoincrement !== false) {
  176. $definition[$key]['autoincrement'] = $autoincrement;
  177. }
  178. }
  179. return $definition;
  180. }
  181. }
  182. return $db->raiseError(MDB2_ERROR, null, null,
  183. 'getTableFieldDefinition: it was not specified an existing table column');
  184. }
  185. // }}}
  186. // {{{ getTableIndexDefinition()
  187. /**
  188. * get the stucture of an index into an array
  189. *
  190. * @param string $table name of table that should be used in method
  191. * @param string $index_name name of index that should be used in method
  192. * @return mixed data array on success, a MDB2 error on failure
  193. * @access public
  194. */
  195. function getTableIndexDefinition($table, $index_name)
  196. {
  197. $db =& $this->getDBInstance();
  198. if (PEAR::isError($db)) {
  199. return $db;
  200. }
  201. $index_name = $db->getIndexName($index_name);
  202. $query = "SELECT sql FROM sqlite_master WHERE type='index' AND name='$index_name' AND tbl_name='$table' AND sql NOT NULL ORDER BY name";
  203. $sql = $db->queryOne($query, 'text');
  204. if (PEAR::isError($sql)) {
  205. return $sql;
  206. }
  207. if (!$sql) {
  208. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  209. 'getTableIndexDefinition: it was not specified an existing table index');
  210. }
  211. $sql = strtolower($sql);
  212. $start_pos = strpos($sql, '(');
  213. $end_pos = strrpos($sql, ')');
  214. $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
  215. $column_names = split(',', $column_names);
  216. if (preg_match("/^create unique/", $sql)) {
  217. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  218. 'getTableIndexDefinition: it was not specified an existing table index');
  219. }
  220. $definition = array();
  221. $count = count($column_names);
  222. for ($i=0; $i<$count; ++$i) {
  223. $column_name = strtok($column_names[$i]," ");
  224. $collation = strtok(" ");
  225. $definition['fields'][$column_name] = array();
  226. if (!empty($collation)) {
  227. $definition['fields'][$column_name]['sorting'] =
  228. ($collation=='ASC' ? 'ascending' : 'descending');
  229. }
  230. }
  231. if (!array_key_exists('fields', $definition)) {
  232. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  233. 'getTableIndexDefinition: it was not specified an existing table index');
  234. }
  235. return $definition;
  236. }
  237. // }}}
  238. // {{{ getTableConstraintDefinition()
  239. /**
  240. * get the stucture of a constraint into an array
  241. *
  242. * @param string $table name of table that should be used in method
  243. * @param string $index_name name of index that should be used in method
  244. * @return mixed data array on success, a MDB2 error on failure
  245. * @access public
  246. */
  247. function getTableConstraintDefinition($table, $index_name)
  248. {
  249. $db =& $this->getDBInstance();
  250. if (PEAR::isError($db)) {
  251. return $db;
  252. }
  253. $index_name = $db->getIndexName($index_name);
  254. $query = "SELECT sql FROM sqlite_master WHERE type='index' AND name='$index_name' AND tbl_name='$table' AND sql NOT NULL ORDER BY name";
  255. $sql = $db->queryOne($query, 'text');
  256. if (PEAR::isError($sql)) {
  257. return $sql;
  258. }
  259. if (!$sql) {
  260. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  261. 'getTableIndexDefinition: it was not specified an existing table index');
  262. }
  263. $sql = strtolower($sql);
  264. $start_pos = strpos($sql, '(');
  265. $end_pos = strrpos($sql, ')');
  266. $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
  267. $column_names = split(',', $column_names);
  268. if (!preg_match("/^create unique/", $sql)) {
  269. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  270. 'getTableConstraintDefinition: it was not specified an existing table constraint');
  271. }
  272. $definition = array();
  273. $definition['unique'] = true;
  274. $count = count($column_names);
  275. for ($i=0; $i<$count; ++$i) {
  276. $column_name = strtok($column_names[$i]," ");
  277. $collation = strtok(" ");
  278. $definition['fields'][$column_name] = array();
  279. if (!empty($collation)) {
  280. $definition['fields'][$column_name]['sorting'] =
  281. ($collation=='ASC' ? 'ascending' : 'descending');
  282. }
  283. }
  284. $result->free();
  285. if (!array_key_exists('fields', $definition)) {
  286. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  287. 'getTableConstraintDefinition: it was not specified an existing table constraint');
  288. }
  289. return $definition;
  290. }
  291. // }}}
  292. // {{{ tableInfo()
  293. /**
  294. * Returns information about a table
  295. *
  296. * @param string $result a string containing the name of a table
  297. * @param int $mode a valid tableInfo mode
  298. *
  299. * @return array an associative array with the information requested.
  300. * A MDB2_Error object on failure.
  301. *
  302. * @see MDB2_Driver_Common::tableInfo()
  303. * @since Method available since Release 1.7.0
  304. */
  305. function tableInfo($result, $mode = null)
  306. {
  307. $db =& $this->getDBInstance();
  308. if (PEAR::isError($db)) {
  309. return $db;
  310. }
  311. if (is_string($result)) {
  312. /*
  313. * Probably received a table name.
  314. * Create a result resource identifier.
  315. */
  316. $id = $db->queryAll("PRAGMA table_info('$result');", null, MDB2_FETCHMODE_ASSOC);
  317. $got_string = true;
  318. } else {
  319. return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null,
  320. 'This DBMS can not obtain tableInfo' .
  321. ' from result sets');
  322. }
  323. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  324. if ($db->options['field_case'] == CASE_LOWER) {
  325. $case_func = 'strtolower';
  326. } else {
  327. $case_func = 'strtoupper';
  328. }
  329. } else {
  330. $case_func = 'strval';
  331. }
  332. $count = count($id);
  333. $res = array();
  334. if ($mode) {
  335. $res['num_fields'] = $count;
  336. }
  337. $db->loadModule('Datatype', null, true);
  338. for ($i = 0; $i < $count; $i++) {
  339. if (strpos($id[$i]['type'], '(') !== false) {
  340. $bits = explode('(', $id[$i]['type']);
  341. $type = $bits[0];
  342. $len = rtrim($bits[1],')');
  343. } else {
  344. $type = $id[$i]['type'];
  345. $len = 0;
  346. }
  347. $flags = '';
  348. if ($id[$i]['pk']) {
  349. $flags.= 'primary_key ';
  350. }
  351. if ($id[$i]['notnull']) {
  352. $flags.= 'not_null ';
  353. }
  354. if ($id[$i]['dflt_value'] !== null) {
  355. $flags.= 'default_' . rawurlencode($id[$i]['dflt_value']);
  356. }
  357. $flags = trim($flags);
  358. $res[$i] = array(
  359. 'table' => $case_func($result),
  360. 'name' => $case_func($id[$i]['name']),
  361. 'type' => $type,
  362. 'length' => $len,
  363. 'flags' => $flags,
  364. );
  365. $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
  366. $res[$i]['mdb2type'] = $mdb2type_info[0][0];
  367. if ($mode & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) {
  368. $res[$i]['name'] = preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $res[$i]['name']);
  369. }
  370. if ($mode & MDB2_TABLEINFO_ORDER) {
  371. $res['order'][$res[$i]['name']] = $i;
  372. }
  373. if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
  374. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  375. }
  376. }
  377. return $res;
  378. }
  379. }
  380. ?>