PageRenderTime 47ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/common/libraries/plugin/pear/MDB2/Driver/Manager/sqlite.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 1560 lines | 988 code | 139 blank | 433 comment | 162 complexity | c0b5218d35f35f85d1300d19150306a0 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP versions 4 and 5 |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
  6. // | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton |
  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. // | Authors: Lukas Smith <smith@pooteeweet.org> |
  43. // | Lorenzo Alberton <l.alberton@quipo.it> |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: sqlite.php 137 2009-11-09 13:24:37Z vanpouckesven $
  47. //
  48. require_once 'MDB2/Driver/Manager/Common.php';
  49. /**
  50. * MDB2 SQLite driver for the management modules
  51. *
  52. * @package MDB2
  53. * @category Database
  54. * @author Lukas Smith <smith@pooteeweet.org>
  55. * @author Lorenzo Alberton <l.alberton@quipo.it>
  56. */
  57. class MDB2_Driver_Manager_sqlite extends MDB2_Driver_Manager_Common
  58. {
  59. // {{{ createDatabase()
  60. /**
  61. * create a new database
  62. *
  63. * @param string $name name of the database that should be created
  64. * @param array $options array with charset info
  65. *
  66. * @return mixed MDB2_OK on success, a MDB2 error on failure
  67. * @access public
  68. */
  69. function createDatabase($name, $options = array())
  70. {
  71. $db = & $this->getDBInstance();
  72. if (PEAR :: isError($db))
  73. {
  74. return $db;
  75. }
  76. $database_file = $db->_getDatabaseFile($name);
  77. if (file_exists($database_file))
  78. {
  79. return $db->raiseError(MDB2_ERROR_ALREADY_EXISTS, null, null, 'database already exists', __FUNCTION__);
  80. }
  81. $php_errormsg = '';
  82. $handle = @sqlite_open($database_file, $db->dsn['mode'], $php_errormsg);
  83. if (! $handle)
  84. {
  85. return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, (isset($php_errormsg) ? $php_errormsg : 'could not create the database file'), __FUNCTION__);
  86. }
  87. if (! empty($options['charset']))
  88. {
  89. $query = 'PRAGMA encoding = ' . $db->quote($options['charset'], 'text');
  90. @sqlite_query($query, $handle);
  91. }
  92. @sqlite_close($handle);
  93. return MDB2_OK;
  94. }
  95. // }}}
  96. // {{{ dropDatabase()
  97. /**
  98. * drop an existing database
  99. *
  100. * @param string $name name of the database that should be dropped
  101. * @return mixed MDB2_OK on success, a MDB2 error on failure
  102. * @access public
  103. */
  104. function dropDatabase($name)
  105. {
  106. $db = & $this->getDBInstance();
  107. if (PEAR :: isError($db))
  108. {
  109. return $db;
  110. }
  111. $database_file = $db->_getDatabaseFile($name);
  112. if (! @file_exists($database_file))
  113. {
  114. return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null, 'database does not exist', __FUNCTION__);
  115. }
  116. $result = @unlink($database_file);
  117. if (! $result)
  118. {
  119. return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null, (isset($php_errormsg) ? $php_errormsg : 'could not remove the database file'), __FUNCTION__);
  120. }
  121. return MDB2_OK;
  122. }
  123. // }}}
  124. // {{{ _getAdvancedFKOptions()
  125. /**
  126. * Return the FOREIGN KEY query section dealing with non-standard options
  127. * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
  128. *
  129. * @param array $definition
  130. * @return string
  131. * @access protected
  132. */
  133. function _getAdvancedFKOptions($definition)
  134. {
  135. $query = '';
  136. if (! empty($definition['match']))
  137. {
  138. $query .= ' MATCH ' . $definition['match'];
  139. }
  140. if (! empty($definition['onupdate']) && (strtoupper($definition['onupdate']) != 'NO ACTION'))
  141. {
  142. $query .= ' ON UPDATE ' . $definition['onupdate'];
  143. }
  144. if (! empty($definition['ondelete']) && (strtoupper($definition['ondelete']) != 'NO ACTION'))
  145. {
  146. $query .= ' ON DELETE ' . $definition['ondelete'];
  147. }
  148. if (! empty($definition['deferrable']))
  149. {
  150. $query .= ' DEFERRABLE';
  151. }
  152. else
  153. {
  154. $query .= ' NOT DEFERRABLE';
  155. }
  156. if (! empty($definition['initiallydeferred']))
  157. {
  158. $query .= ' INITIALLY DEFERRED';
  159. }
  160. else
  161. {
  162. $query .= ' INITIALLY IMMEDIATE';
  163. }
  164. return $query;
  165. }
  166. // }}}
  167. // {{{ _getCreateTableQuery()
  168. /**
  169. * Create a basic SQL query for a new table creation
  170. * @param string $name Name of the database that should be created
  171. * @param array $fields Associative array that contains the definition of each field of the new table
  172. * @param array $options An associative array of table options
  173. * @return mixed string (the SQL query) on success, a MDB2 error on failure
  174. * @see createTable()
  175. */
  176. function _getCreateTableQuery($name, $fields, $options = array())
  177. {
  178. $db = & $this->getDBInstance();
  179. if (PEAR :: isError($db))
  180. {
  181. return $db;
  182. }
  183. if (! $name)
  184. {
  185. return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, 'no valid table name specified', __FUNCTION__);
  186. }
  187. if (empty($fields))
  188. {
  189. return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, 'no fields specified for table "' . $name . '"', __FUNCTION__);
  190. }
  191. $query_fields = $this->getFieldDeclarationList($fields);
  192. if (PEAR :: isError($query_fields))
  193. {
  194. return $query_fields;
  195. }
  196. if (! empty($options['primary']))
  197. {
  198. $query_fields .= ', PRIMARY KEY (' . implode(', ', array_keys($options['primary'])) . ')';
  199. }
  200. if (! empty($options['foreign_keys']))
  201. {
  202. foreach ($options['foreign_keys'] as $fkname => $fkdef)
  203. {
  204. if (empty($fkdef))
  205. {
  206. continue;
  207. }
  208. $query_fields .= ', CONSTRAINT ' . $fkname . ' FOREIGN KEY (' . implode(', ', array_keys($fkdef['fields'])) . ')';
  209. $query_fields .= ' REFERENCES ' . $fkdef['references']['table'] . ' (' . implode(', ', array_keys($fkdef['references']['fields'])) . ')';
  210. $query_fields .= $this->_getAdvancedFKOptions($fkdef);
  211. }
  212. }
  213. $name = $db->quoteIdentifier($name, true);
  214. $result = 'CREATE ';
  215. if (! empty($options['temporary']))
  216. {
  217. $result .= $this->_getTemporaryTableQuery();
  218. }
  219. $result .= " TABLE $name ($query_fields)";
  220. return $result;
  221. }
  222. // }}}
  223. // {{{ createTable()
  224. /**
  225. * create a new table
  226. *
  227. * @param string $name Name of the database that should be created
  228. * @param array $fields Associative array that contains the definition
  229. * of each field of the new table
  230. * @param array $options An associative array of table options
  231. *
  232. * @return mixed MDB2_OK on success, a MDB2 error on failure
  233. * @access public
  234. */
  235. function createTable($name, $fields, $options = array())
  236. {
  237. $result = parent :: createTable($name, $fields, $options);
  238. if (PEAR :: isError($result))
  239. {
  240. return $result;
  241. }
  242. // create triggers to enforce FOREIGN KEY constraints
  243. if (! empty($options['foreign_keys']))
  244. {
  245. $db = & $this->getDBInstance();
  246. if (PEAR :: isError($db))
  247. {
  248. return $db;
  249. }
  250. foreach ($options['foreign_keys'] as $fkname => $fkdef)
  251. {
  252. if (empty($fkdef))
  253. {
  254. continue;
  255. }
  256. //set actions to default if not set
  257. $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']);
  258. $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']);
  259. $trigger_names = array('insert' => $fkname . '_insert_trg', 'update' => $fkname . '_update_trg',
  260. 'pk_update' => $fkname . '_pk_update_trg', 'pk_delete' => $fkname . '_pk_delete_trg');
  261. //create the [insert|update] triggers on the FK table
  262. $table_fields = array_keys($fkdef['fields']);
  263. $referenced_fields = array_keys($fkdef['references']['fields']);
  264. $query = 'CREATE TRIGGER %s BEFORE %s ON ' . $name . ' FOR EACH ROW BEGIN' . ' SELECT RAISE(ROLLBACK, \'%s on table "' . $name . '" violates FOREIGN KEY constraint "' . $fkname . '"\')' . ' WHERE (SELECT ';
  265. $aliased_fields = array();
  266. foreach ($referenced_fields as $field)
  267. {
  268. $aliased_fields[] = $fkdef['references']['table'] . '.' . $field . ' AS ' . $field;
  269. }
  270. $query .= implode(',', $aliased_fields) . ' FROM ' . $fkdef['references']['table'] . ' WHERE ';
  271. $conditions = array();
  272. for($i = 0; $i < count($table_fields); $i ++)
  273. {
  274. $conditions[] = $referenced_fields[$i] . ' = NEW.' . $table_fields[$i];
  275. }
  276. $query .= implode(' AND ', $conditions) . ') IS NULL; END;';
  277. $result = $db->exec(sprintf($query, $trigger_names['insert'], 'INSERT', 'insert'));
  278. if (PEAR :: isError($result))
  279. {
  280. return $result;
  281. }
  282. $result = $db->exec(sprintf($query, $trigger_names['update'], 'UPDATE', 'update'));
  283. if (PEAR :: isError($result))
  284. {
  285. return $result;
  286. }
  287. //create the ON [UPDATE|DELETE] triggers on the primary table
  288. $restrict_action = 'SELECT RAISE(ROLLBACK, \'%s on table "' . $name . '" violates FOREIGN KEY constraint "' . $fkname . '"\')' . ' WHERE (SELECT ';
  289. $aliased_fields = array();
  290. foreach ($table_fields as $field)
  291. {
  292. $aliased_fields[] = $name . '.' . $field . ' AS ' . $field;
  293. }
  294. $restrict_action .= implode(',', $aliased_fields) . ' FROM ' . $name . ' WHERE ';
  295. $conditions = array();
  296. $new_values = array();
  297. $null_values = array();
  298. for($i = 0; $i < count($table_fields); $i ++)
  299. {
  300. $conditions[] = $table_fields[$i] . ' = OLD.' . $referenced_fields[$i];
  301. $new_values[] = $table_fields[$i] . ' = NEW.' . $referenced_fields[$i];
  302. $null_values[] = $table_fields[$i] . ' = NULL';
  303. }
  304. $conditions2 = array();
  305. for($i = 0; $i < count($referenced_fields); $i ++)
  306. {
  307. $conditions2[] = 'NEW.' . $referenced_fields[$i] . ' <> OLD.' . $referenced_fields[$i];
  308. }
  309. $restrict_action .= implode(' AND ', $conditions) . ') IS NOT NULL' . ' AND (' . implode(' OR ', $conditions2) . ')';
  310. $cascade_action_update = 'UPDATE ' . $name . ' SET ' . implode(', ', $new_values) . ' WHERE ' . implode(' AND ', $conditions);
  311. $cascade_action_delete = 'DELETE FROM ' . $name . ' WHERE ' . implode(' AND ', $conditions);
  312. $setnull_action = 'UPDATE ' . $name . ' SET ' . implode(', ', $null_values) . ' WHERE ' . implode(' AND ', $conditions);
  313. if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete'])
  314. {
  315. $db->loadModule('Reverse', null, true);
  316. $default_values = array();
  317. foreach ($table_fields as $table_field)
  318. {
  319. $field_definition = $db->reverse->getTableFieldDefinition($name, $field);
  320. if (PEAR :: isError($field_definition))
  321. {
  322. return $field_definition;
  323. }
  324. $default_values[] = $table_field . ' = ' . $field_definition[0]['default'];
  325. }
  326. $setdefault_action = 'UPDATE ' . $name . ' SET ' . implode(', ', $default_values) . ' WHERE ' . implode(' AND ', $conditions);
  327. }
  328. $query = 'CREATE TRIGGER %s' . ' %s ON ' . $fkdef['references']['table'] . ' FOR EACH ROW BEGIN ';
  329. if ('CASCADE' == $fkdef['onupdate'])
  330. {
  331. $sql_update = sprintf($query, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . $cascade_action_update . '; END;';
  332. }
  333. elseif ('SET NULL' == $fkdef['onupdate'])
  334. {
  335. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action . '; END;';
  336. }
  337. elseif ('SET DEFAULT' == $fkdef['onupdate'])
  338. {
  339. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action . '; END;';
  340. }
  341. elseif ('NO ACTION' == $fkdef['onupdate'])
  342. {
  343. $sql_update = sprintf($query . $restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . '; END;';
  344. }
  345. elseif ('RESTRICT' == $fkdef['onupdate'])
  346. {
  347. $sql_update = sprintf($query . $restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . '; END;';
  348. }
  349. if ('CASCADE' == $fkdef['ondelete'])
  350. {
  351. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . $cascade_action_delete . '; END;';
  352. }
  353. elseif ('SET NULL' == $fkdef['ondelete'])
  354. {
  355. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action . '; END;';
  356. }
  357. elseif ('SET DEFAULT' == $fkdef['ondelete'])
  358. {
  359. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action . '; END;';
  360. }
  361. elseif ('NO ACTION' == $fkdef['ondelete'])
  362. {
  363. $sql_delete = sprintf($query . $restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . '; END;';
  364. }
  365. elseif ('RESTRICT' == $fkdef['ondelete'])
  366. {
  367. $sql_delete = sprintf($query . $restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . '; END;';
  368. }
  369. if (PEAR :: isError($result))
  370. {
  371. return $result;
  372. }
  373. $result = $db->exec($sql_delete);
  374. if (PEAR :: isError($result))
  375. {
  376. return $result;
  377. }
  378. $result = $db->exec($sql_update);
  379. if (PEAR :: isError($result))
  380. {
  381. return $result;
  382. }
  383. }
  384. }
  385. if (PEAR :: isError($result))
  386. {
  387. return $result;
  388. }
  389. return MDB2_OK;
  390. }
  391. // }}}
  392. // {{{ dropTable()
  393. /**
  394. * drop an existing table
  395. *
  396. * @param string $name name of the table that should be dropped
  397. * @return mixed MDB2_OK on success, a MDB2 error on failure
  398. * @access public
  399. */
  400. function dropTable($name)
  401. {
  402. $db = & $this->getDBInstance();
  403. if (PEAR :: isError($db))
  404. {
  405. return $db;
  406. }
  407. //delete the triggers associated to existing FK constraints
  408. $constraints = $this->listTableConstraints($name);
  409. if (! PEAR :: isError($constraints) && ! empty($constraints))
  410. {
  411. $db->loadModule('Reverse', null, true);
  412. foreach ($constraints as $constraint)
  413. {
  414. $definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
  415. if (! PEAR :: isError($definition) && ! empty($definition['foreign']))
  416. {
  417. $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']);
  418. if (PEAR :: isError($result))
  419. {
  420. return $result;
  421. }
  422. }
  423. }
  424. }
  425. $name = $db->quoteIdentifier($name, true);
  426. return $db->exec("DROP TABLE $name");
  427. }
  428. // }}}
  429. // {{{ vacuum()
  430. /**
  431. * Optimize (vacuum) all the tables in the db (or only the specified table)
  432. * and optionally run ANALYZE.
  433. *
  434. * @param string $table table name (all the tables if empty)
  435. * @param array $options an array with driver-specific options:
  436. * - timeout [int] (in seconds) [mssql-only]
  437. * - analyze [boolean] [pgsql and mysql]
  438. * - full [boolean] [pgsql-only]
  439. * - freeze [boolean] [pgsql-only]
  440. *
  441. * @return mixed MDB2_OK success, a MDB2 error on failure
  442. * @access public
  443. */
  444. function vacuum($table = null, $options = array())
  445. {
  446. $db = & $this->getDBInstance();
  447. if (PEAR :: isError($db))
  448. {
  449. return $db;
  450. }
  451. $query = 'VACUUM';
  452. if (! empty($table))
  453. {
  454. $query .= ' ' . $db->quoteIdentifier($table, true);
  455. }
  456. return $db->exec($query);
  457. }
  458. // }}}
  459. // {{{ alterTable()
  460. /**
  461. * alter an existing table
  462. *
  463. * @param string $name name of the table that is intended to be changed.
  464. * @param array $changes associative array that contains the details of each type
  465. * of change that is intended to be performed. The types of
  466. * changes that are currently supported are defined as follows:
  467. *
  468. * name
  469. *
  470. * New name for the table.
  471. *
  472. * add
  473. *
  474. * Associative array with the names of fields to be added as
  475. * indexes of the array. The value of each entry of the array
  476. * should be set to another associative array with the properties
  477. * of the fields to be added. The properties of the fields should
  478. * be the same as defined by the MDB2 parser.
  479. *
  480. *
  481. * remove
  482. *
  483. * Associative array with the names of fields to be removed as indexes
  484. * of the array. Currently the values assigned to each entry are ignored.
  485. * An empty array should be used for future compatibility.
  486. *
  487. * rename
  488. *
  489. * Associative array with the names of fields to be renamed as indexes
  490. * of the array. The value of each entry of the array should be set to
  491. * another associative array with the entry named name with the new
  492. * field name and the entry named Declaration that is expected to contain
  493. * the portion of the field declaration already in DBMS specific SQL code
  494. * as it is used in the CREATE TABLE statement.
  495. *
  496. * change
  497. *
  498. * Associative array with the names of the fields to be changed as indexes
  499. * of the array. Keep in mind that if it is intended to change either the
  500. * name of a field and any other properties, the change array entries
  501. * should have the new names of the fields as array indexes.
  502. *
  503. * The value of each entry of the array should be set to another associative
  504. * array with the properties of the fields to that are meant to be changed as
  505. * array entries. These entries should be assigned to the new values of the
  506. * respective properties. The properties of the fields should be the same
  507. * as defined by the MDB2 parser.
  508. *
  509. * Example
  510. * array(
  511. * 'name' => 'userlist',
  512. * 'add' => array(
  513. * 'quota' => array(
  514. * 'type' => 'integer',
  515. * 'unsigned' => 1
  516. * )
  517. * ),
  518. * 'remove' => array(
  519. * 'file_limit' => array(),
  520. * 'time_limit' => array()
  521. * ),
  522. * 'change' => array(
  523. * 'name' => array(
  524. * 'length' => '20',
  525. * 'definition' => array(
  526. * 'type' => 'text',
  527. * 'length' => 20,
  528. * ),
  529. * )
  530. * ),
  531. * 'rename' => array(
  532. * 'sex' => array(
  533. * 'name' => 'gender',
  534. * 'definition' => array(
  535. * 'type' => 'text',
  536. * 'length' => 1,
  537. * 'default' => 'M',
  538. * ),
  539. * )
  540. * )
  541. * )
  542. *
  543. * @param boolean $check indicates whether the function should just check if the DBMS driver
  544. * can perform the requested table alterations if the value is true or
  545. * actually perform them otherwise.
  546. * @access public
  547. *
  548. * @return mixed MDB2_OK on success, a MDB2 error on failure
  549. */
  550. function alterTable($name, $changes, $check, $options = array())
  551. {
  552. $db = & $this->getDBInstance();
  553. if (PEAR :: isError($db))
  554. {
  555. return $db;
  556. }
  557. foreach ($changes as $change_name => $change)
  558. {
  559. switch ($change_name)
  560. {
  561. case 'add' :
  562. case 'remove' :
  563. case 'change' :
  564. case 'name' :
  565. case 'rename' :
  566. break;
  567. default :
  568. return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, 'change type "' . $change_name . '" not yet supported', __FUNCTION__);
  569. }
  570. }
  571. if ($check)
  572. {
  573. return MDB2_OK;
  574. }
  575. $db->loadModule('Reverse', null, true);
  576. // actually sqlite 2.x supports no ALTER TABLE at all .. so we emulate it
  577. $fields = $db->manager->listTableFields($name);
  578. if (PEAR :: isError($fields))
  579. {
  580. return $fields;
  581. }
  582. $fields = array_flip($fields);
  583. foreach ($fields as $field => $value)
  584. {
  585. $definition = $db->reverse->getTableFieldDefinition($name, $field);
  586. if (PEAR :: isError($definition))
  587. {
  588. return $definition;
  589. }
  590. $fields[$field] = $definition[0];
  591. }
  592. $indexes = $db->manager->listTableIndexes($name);
  593. if (PEAR :: isError($indexes))
  594. {
  595. return $indexes;
  596. }
  597. $indexes = array_flip($indexes);
  598. foreach ($indexes as $index => $value)
  599. {
  600. $definition = $db->reverse->getTableIndexDefinition($name, $index);
  601. if (PEAR :: isError($definition))
  602. {
  603. return $definition;
  604. }
  605. $indexes[$index] = $definition;
  606. }
  607. $constraints = $db->manager->listTableConstraints($name);
  608. if (PEAR :: isError($constraints))
  609. {
  610. return $constraints;
  611. }
  612. if (! array_key_exists('foreign_keys', $options))
  613. {
  614. $options['foreign_keys'] = array();
  615. }
  616. $constraints = array_flip($constraints);
  617. foreach ($constraints as $constraint => $value)
  618. {
  619. if (! empty($definition['primary']))
  620. {
  621. if (! array_key_exists('primary', $options))
  622. {
  623. $options['primary'] = $definition['fields'];
  624. //remove from the $constraint array, it's already handled by createTable()
  625. unset($constraints[$constraint]);
  626. }
  627. }
  628. else
  629. {
  630. $c_definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
  631. if (PEAR :: isError($c_definition))
  632. {
  633. return $c_definition;
  634. }
  635. if (! empty($c_definition['foreign']))
  636. {
  637. if (! array_key_exists($constraint, $options['foreign_keys']))
  638. {
  639. $options['foreign_keys'][$constraint] = $c_definition;
  640. }
  641. //remove from the $constraint array, it's already handled by createTable()
  642. unset($constraints[$constraint]);
  643. }
  644. else
  645. {
  646. $constraints[$constraint] = $c_definition;
  647. }
  648. }
  649. }
  650. $name_new = $name;
  651. $create_order = $select_fields = array_keys($fields);
  652. foreach ($changes as $change_name => $change)
  653. {
  654. switch ($change_name)
  655. {
  656. case 'add' :
  657. foreach ($change as $field_name => $field)
  658. {
  659. $fields[$field_name] = $field;
  660. $create_order[] = $field_name;
  661. }
  662. break;
  663. case 'remove' :
  664. foreach ($change as $field_name => $field)
  665. {
  666. unset($fields[$field_name]);
  667. $select_fields = array_diff($select_fields, array($field_name));
  668. $create_order = array_diff($create_order, array($field_name));
  669. }
  670. break;
  671. case 'change' :
  672. foreach ($change as $field_name => $field)
  673. {
  674. $fields[$field_name] = $field['definition'];
  675. }
  676. break;
  677. case 'name' :
  678. $name_new = $change;
  679. break;
  680. case 'rename' :
  681. foreach ($change as $field_name => $field)
  682. {
  683. unset($fields[$field_name]);
  684. $fields[$field['name']] = $field['definition'];
  685. $create_order[array_search($field_name, $create_order)] = $field['name'];
  686. }
  687. break;
  688. default :
  689. return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, 'change type "' . $change_name . '" not yet supported', __FUNCTION__);
  690. }
  691. }
  692. $data = null;
  693. if (! empty($select_fields))
  694. {
  695. $query = 'SELECT ' . implode(', ', $select_fields) . ' FROM ' . $db->quoteIdentifier($name, true);
  696. $data = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
  697. }
  698. $result = $this->dropTable($name);
  699. if (PEAR :: isError($result))
  700. {
  701. return $result;
  702. }
  703. $result = $this->createTable($name_new, $fields, $options);
  704. if (PEAR :: isError($result))
  705. {
  706. return $result;
  707. }
  708. foreach ($indexes as $index => $definition)
  709. {
  710. $this->createIndex($name_new, $index, $definition);
  711. }
  712. foreach ($constraints as $constraint => $definition)
  713. {
  714. $this->createConstraint($name_new, $constraint, $definition);
  715. }
  716. if (! empty($select_fields) && ! empty($data))
  717. {
  718. $query = 'INSERT INTO ' . $db->quoteIdentifier($name_new, true);
  719. $query .= '(' . implode(', ', array_slice(array_keys($fields), 0, count($select_fields))) . ')';
  720. $query .= ' VALUES (?' . str_repeat(', ?', (count($select_fields) - 1)) . ')';
  721. $stmt = & $db->prepare($query, null, MDB2_PREPARE_MANIP);
  722. if (PEAR :: isError($stmt))
  723. {
  724. return $stmt;
  725. }
  726. foreach ($data as $row)
  727. {
  728. $result = $stmt->execute($row);
  729. if (PEAR :: isError($result))
  730. {
  731. return $result;
  732. }
  733. }
  734. }
  735. return MDB2_OK;
  736. }
  737. // }}}
  738. // {{{ listDatabases()
  739. /**
  740. * list all databases
  741. *
  742. * @return mixed array of database names on success, a MDB2 error on failure
  743. * @access public
  744. */
  745. function listDatabases()
  746. {
  747. $db = & $this->getDBInstance();
  748. if (PEAR :: isError($db))
  749. {
  750. return $db;
  751. }
  752. return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'list databases is not supported', __FUNCTION__);
  753. }
  754. // }}}
  755. // {{{ listUsers()
  756. /**
  757. * list all users
  758. *
  759. * @return mixed array of user names on success, a MDB2 error on failure
  760. * @access public
  761. */
  762. function listUsers()
  763. {
  764. $db = & $this->getDBInstance();
  765. if (PEAR :: isError($db))
  766. {
  767. return $db;
  768. }
  769. return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'list databases is not supported', __FUNCTION__);
  770. }
  771. // }}}
  772. // {{{ listViews()
  773. /**
  774. * list all views in the current database
  775. *
  776. * @return mixed array of view names on success, a MDB2 error on failure
  777. * @access public
  778. */
  779. function listViews()
  780. {
  781. $db = & $this->getDBInstance();
  782. if (PEAR :: isError($db))
  783. {
  784. return $db;
  785. }
  786. $query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL";
  787. $result = $db->queryCol($query);
  788. if (PEAR :: isError($result))
  789. {
  790. return $result;
  791. }
  792. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  793. {
  794. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  795. }
  796. return $result;
  797. }
  798. // }}}
  799. // {{{ listTableViews()
  800. /**
  801. * list the views in the database that reference a given table
  802. *
  803. * @param string table for which all referenced views should be found
  804. * @return mixed array of view names on success, a MDB2 error on failure
  805. * @access public
  806. */
  807. function listTableViews($table)
  808. {
  809. $db = & $this->getDBInstance();
  810. if (PEAR :: isError($db))
  811. {
  812. return $db;
  813. }
  814. $query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
  815. $views = $db->queryAll($query, array('text', 'text'), MDB2_FETCHMODE_ASSOC);
  816. if (PEAR :: isError($views))
  817. {
  818. return $views;
  819. }
  820. $result = array();
  821. foreach ($views as $row)
  822. {
  823. if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql']))
  824. {
  825. if (! empty($row['name']))
  826. {
  827. $result[$row['name']] = true;
  828. }
  829. }
  830. }
  831. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  832. {
  833. $result = array_change_key_case($result, $db->options['field_case']);
  834. }
  835. return array_keys($result);
  836. }
  837. // }}}
  838. // {{{ listTables()
  839. /**
  840. * list all tables in the current database
  841. *
  842. * @return mixed array of table names on success, a MDB2 error on failure
  843. * @access public
  844. */
  845. function listTables()
  846. {
  847. $db = & $this->getDBInstance();
  848. if (PEAR :: isError($db))
  849. {
  850. return $db;
  851. }
  852. $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name";
  853. $table_names = $db->queryCol($query);
  854. if (PEAR :: isError($table_names))
  855. {
  856. return $table_names;
  857. }
  858. $result = array();
  859. foreach ($table_names as $table_name)
  860. {
  861. if (! $this->_fixSequenceName($table_name, true))
  862. {
  863. $result[] = $table_name;
  864. }
  865. }
  866. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  867. {
  868. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  869. }
  870. return $result;
  871. }
  872. // }}}
  873. // {{{ listTableFields()
  874. /**
  875. * list all fields in a table in the current database
  876. *
  877. * @param string $table name of table that should be used in method
  878. * @return mixed array of field names on success, a MDB2 error on failure
  879. * @access public
  880. */
  881. function listTableFields($table)
  882. {
  883. $db = & $this->getDBInstance();
  884. if (PEAR :: isError($db))
  885. {
  886. return $db;
  887. }
  888. $result = $db->loadModule('Reverse', null, true);
  889. if (PEAR :: isError($result))
  890. {
  891. return $result;
  892. }
  893. $query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
  894. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  895. {
  896. $query .= 'LOWER(name)=' . $db->quote(strtolower($table), 'text');
  897. }
  898. else
  899. {
  900. $query .= 'name=' . $db->quote($table, 'text');
  901. }
  902. $sql = $db->queryOne($query);
  903. if (PEAR :: isError($sql))
  904. {
  905. return $sql;
  906. }
  907. $columns = $db->reverse->_getTableColumns($sql);
  908. $fields = array();
  909. foreach ($columns as $column)
  910. {
  911. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  912. {
  913. if ($db->options['field_case'] == CASE_LOWER)
  914. {
  915. $column['name'] = strtolower($column['name']);
  916. }
  917. else
  918. {
  919. $column['name'] = strtoupper($column['name']);
  920. }
  921. }
  922. else
  923. {
  924. $column = array_change_key_case($column, $db->options['field_case']);
  925. }
  926. $fields[] = $column['name'];
  927. }
  928. return $fields;
  929. }
  930. // }}}
  931. // {{{ listTableTriggers()
  932. /**
  933. * list all triggers in the database that reference a given table
  934. *
  935. * @param string table for which all referenced triggers should be found
  936. * @return mixed array of trigger names on success, a MDB2 error on failure
  937. * @access public
  938. */
  939. function listTableTriggers($table = null)
  940. {
  941. $db = & $this->getDBInstance();
  942. if (PEAR :: isError($db))
  943. {
  944. return $db;
  945. }
  946. $query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL";
  947. if (! is_null($table))
  948. {
  949. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  950. {
  951. $query .= ' AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
  952. }
  953. else
  954. {
  955. $query .= ' AND tbl_name=' . $db->quote($table, 'text');
  956. }
  957. }
  958. $result = $db->queryCol($query);
  959. if (PEAR :: isError($result))
  960. {
  961. return $result;
  962. }
  963. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  964. {
  965. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  966. }
  967. return $result;
  968. }
  969. // }}}
  970. // {{{ createIndex()
  971. /**
  972. * Get the stucture of a field into an array
  973. *
  974. * @param string $table name of the table on which the index is to be created
  975. * @param string $name name of the index to be created
  976. * @param array $definition associative array that defines properties of the index to be created.
  977. * Currently, only one property named FIELDS is supported. This property
  978. * is also an associative with the names of the index fields as array
  979. * indexes. Each entry of this array is set to another type of associative
  980. * array that specifies properties of the index that are specific to
  981. * each field.
  982. *
  983. * Currently, only the sorting property is supported. It should be used
  984. * to define the sorting direction of the index. It may be set to either
  985. * ascending or descending.
  986. *
  987. * Not all DBMS support index sorting direction configuration. The DBMS
  988. * drivers of those that do not support it ignore this property. Use the
  989. * function support() to determine whether the DBMS driver can manage indexes.
  990. * Example
  991. * array(
  992. * 'fields' => array(
  993. * 'user_name' => array(
  994. * 'sorting' => 'ascending'
  995. * ),
  996. * 'last_login' => array()
  997. * )
  998. * )
  999. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1000. * @access public
  1001. */
  1002. function createIndex($table, $name, $definition)
  1003. {
  1004. $db = & $this->getDBInstance();
  1005. if (PEAR :: isError($db))
  1006. {
  1007. return $db;
  1008. }
  1009. $table = $db->quoteIdentifier($table, true);
  1010. $name = $db->getIndexName($name);
  1011. $query = "CREATE INDEX $name ON $table";
  1012. $fields = array();
  1013. foreach ($definition['fields'] as $field_name => $field)
  1014. {
  1015. $field_string = $field_name;
  1016. if (! empty($field['sorting']))
  1017. {
  1018. switch ($field['sorting'])
  1019. {
  1020. case 'ascending' :
  1021. $field_string .= ' ASC';
  1022. break;
  1023. case 'descending' :
  1024. $field_string .= ' DESC';
  1025. break;
  1026. }
  1027. }
  1028. $fields[] = $field_string;
  1029. }
  1030. $query .= ' (' . implode(', ', $fields) . ')';
  1031. return $db->exec($query);
  1032. }
  1033. // }}}
  1034. // {{{ dropIndex()
  1035. /**
  1036. * drop existing index
  1037. *
  1038. * @param string $table name of table that should be used in method
  1039. * @param string $name name of the index to be dropped
  1040. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1041. * @access public
  1042. */
  1043. function dropIndex($table, $name)
  1044. {
  1045. $db = & $this->getDBInstance();
  1046. if (PEAR :: isError($db))
  1047. {
  1048. return $db;
  1049. }
  1050. $name = $db->getIndexName($name);
  1051. return $db->exec("DROP INDEX $name");
  1052. }
  1053. // }}}
  1054. // {{{ listTableIndexes()
  1055. /**
  1056. * list all indexes in a table
  1057. *
  1058. * @param string $table name of table that should be used in method
  1059. * @return mixed array of index names on success, a MDB2 error on failure
  1060. * @access public
  1061. */
  1062. function listTableIndexes($table)
  1063. {
  1064. $db = & $this->getDBInstance();
  1065. if (PEAR :: isError($db))
  1066. {
  1067. return $db;
  1068. }
  1069. $table = $db->quote($table, 'text');
  1070. $query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
  1071. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  1072. {
  1073. $query .= 'LOWER(tbl_name)=' . strtolower($table);
  1074. }
  1075. else
  1076. {
  1077. $query .= "tbl_name=$table";
  1078. }
  1079. $query .= " AND sql NOT NULL ORDER BY name";
  1080. $indexes = $db->queryCol($query, 'text');
  1081. if (PEAR :: isError($indexes))
  1082. {
  1083. return $indexes;
  1084. }
  1085. $result = array();
  1086. foreach ($indexes as $sql)
  1087. {
  1088. if (preg_match("/^create index ([^ ]+) on /i", $sql, $tmp))
  1089. {
  1090. $index = $this->_fixIndexName($tmp[1]);
  1091. if (! empty($index))
  1092. {
  1093. $result[$index] = true;
  1094. }
  1095. }
  1096. }
  1097. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  1098. {
  1099. $result = array_change_key_case($result, $db->options['field_case']);
  1100. }
  1101. return array_keys($result);
  1102. }
  1103. // }}}
  1104. // {{{ createConstraint()
  1105. /**
  1106. * create a constraint on a table
  1107. *
  1108. * @param string $table name of the table on which the constraint is to be created
  1109. * @param string $name name of the constraint to be created
  1110. * @param array $definition associative array that defines properties of the constraint to be created.
  1111. * Currently, only one property named FIELDS is supported. This property
  1112. * is also an associative with the names of the constraint fields as array
  1113. * constraints. Each entry of this array is set to another type of associative
  1114. * array that specifies properties of the constraint that are specific to
  1115. * each field.
  1116. *
  1117. * Example
  1118. * array(
  1119. * 'fields' => array(
  1120. * 'user_name' => array(),
  1121. * 'last_login' => array()
  1122. * )
  1123. * )
  1124. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1125. * @access public
  1126. */
  1127. function createConstraint($table, $name, $definition)
  1128. {
  1129. $db = & $this->getDBInstance();
  1130. if (PEAR :: isError($db))
  1131. {
  1132. return $db;
  1133. }
  1134. if (! empty($definition['primary']))
  1135. {
  1136. return $db->manager->alterTable($table, array(), false, array('primary' => $definition['fields']));
  1137. }
  1138. if (! empty($definition['foreign']))
  1139. {
  1140. return $db->manager->alterTable($table, array(), false, array('foreign_keys' => array($name => $definition)));
  1141. }
  1142. $table = $db->quoteIdentifier($table, true);
  1143. $name = $db->getIndexName($name);
  1144. $query = "CREATE UNIQUE INDEX $name ON $table";
  1145. $fields = array();
  1146. foreach ($definition['fields'] as $field_name => $field)
  1147. {
  1148. $field_string = $field_name;
  1149. if (! empty($field['sorting']))
  1150. {
  1151. switch ($field['sorting'])
  1152. {
  1153. case 'ascending' :
  1154. $field_string .= ' ASC';
  1155. break;
  1156. case 'descending' :
  1157. $field_string .= ' DESC';
  1158. break;
  1159. }
  1160. }
  1161. $fields[] = $field_string;
  1162. }
  1163. $query .= ' (' . implode(', ', $fields) . ')';
  1164. return $db->exec($query);
  1165. }
  1166. // }}}
  1167. // {{{ dropConstraint()
  1168. /**
  1169. * drop existing constraint
  1170. *
  1171. * @param string $table name of table that should be used in method
  1172. * @param string $name name of the constraint to be dropped
  1173. * @param string $primary hint if the constraint is primary
  1174. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1175. * @access public
  1176. */
  1177. function dropConstraint($table, $name, $primary = false)
  1178. {
  1179. if ($primary || $name == 'PRIMARY')
  1180. {
  1181. return $this->alterTable($table, array(), false, array('primary' => null));
  1182. }
  1183. $db = & $this->getDBInstance();
  1184. if (PEAR :: isError($db))
  1185. {
  1186. return $db;
  1187. }
  1188. //is it a FK constraint? If so, also delete the associated triggers
  1189. $db->loadModule('Reverse', null, true);
  1190. $definition = $db->reverse->getTableConstraintDefinition($table, $name);
  1191. if (! PEAR :: isError($definition) && ! empty($definition['foreign']))
  1192. {
  1193. //first drop the FK enforcing triggers
  1194. $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']);
  1195. if (PEAR :: isError($result))
  1196. {
  1197. return $result;
  1198. }
  1199. //then drop the constraint itself
  1200. return $this->alterTable($table, array(), false, array('foreign_keys' => array($name => null)));
  1201. }
  1202. $name = $db->getIndexName($name);
  1203. return $db->exec("DROP INDEX $name");
  1204. }
  1205. // }}}
  1206. // {{{ _dropFKTriggers()
  1207. /**
  1208. * Drop the triggers created to enforce the FOREIGN KEY constraint on the table
  1209. *
  1210. * @param string $table table name
  1211. * @param string $fkname FOREIGN KEY constraint name
  1212. * @param string $referenced_table referenced table name
  1213. *
  1214. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1215. * @access private
  1216. */
  1217. function _dropFKTriggers($table, $fkname, $referenced_table)
  1218. {
  1219. $db = & $this->getDBInstance();
  1220. if (PEAR :: isError($db))
  1221. {
  1222. return $db;
  1223. }
  1224. $triggers = $this->listTableTriggers($table);
  1225. $triggers2 = $this->listTableTriggers($referenced_table);
  1226. if (! PEAR :: isError($triggers2) && ! PEAR :: isError($triggers))
  1227. {
  1228. $triggers = array_merge($triggers, $triggers2);
  1229. $pattern = '/^' . $fkname . '(_pk)?_(insert|update|delete)_trg$/i';
  1230. foreach ($triggers as $trigger)
  1231. {
  1232. if (preg_match($pattern, $trigger))
  1233. {
  1234. $result = $db->exec('DROP TRIGGER ' . $trigger);
  1235. if (PEAR :: isError($result))
  1236. {
  1237. return $result;
  1238. }
  1239. }
  1240. }
  1241. }
  1242. return MDB2_OK;
  1243. }
  1244. // }}}
  1245. // {{{ listTableConstraints()
  1246. /**
  1247. * list all constraints in a table
  1248. *
  1249. * @param string $table name of table that should be used in method
  1250. * @return mixed array of constraint names on success, a MDB2 error on failure
  1251. * @access public
  1252. */
  1253. function listTableConstraints($table)
  1254. {
  1255. $db = & $this->getDBInstance();
  1256. if (PEAR :: isError($db))
  1257. {
  1258. return $db;
  1259. }
  1260. $table = $db->quote($table, 'text');
  1261. $query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
  1262. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE)
  1263. {
  1264. $query .= 'LOWER(tbl_name)=' . strtolower($table);
  1265. }
  1266. else
  1267. {
  1268. $query .= "tbl_name=$table";
  1269. }
  1270. $query .= " AND sql NOT NULL ORDER BY name";
  1271. $indexes = $db->queryCol($query, 'text');

Large files files are truncated, but you can click here to view the full file